I have spring jar for my entity beans created as export--java--JARfile, where i have beans defined as follows:
package com.my.beans;
#Component("expBean")
public class ExpBean {
}
I also have config in this jar
package com.my;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
#Bean
public ExpBean getExpBean() {
return new ExpBean();
}
}
I have client spring application where i am adding above jar having only beans as by external dependency and trying to get my beans when spring app starts using following code in main code in client spring app.
package com.my;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication()
#ComponentScan("com.my")
public class Application {
public static void main (String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
WMyBean bean = (WMyBean) ctx.getBean("expBean");
bean.doSomething();
}
}
But when checked the list of bean definition printed, i do not see my bean from external jar and also i get following error.
"Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'expBean' available"
I tried many options but not sure whats missing.
Option 1
#ComponentScan("com.my")
#SpringBootApplication
public class Application {
Option 2
#ComponentScan({"com.my"})
#SpringBootApplication
public class Application {
Need valuable inputs on step that i am missing out.
Question 1: Is it mandatory for me to do config change in client project to access my dependent jar entities?
Question 2: Can it be dynamic, load all entities without configuration as #bean in client project config?
Question 3: Do i need to build client jar every-time i change my dependent jar file with more entities?
Thanks
First I am assuming the build tool is Gradle, but you can import the jar as a local file like this for the sake of experiment:
dependencies {
implementation files('libs/something_local.jar')}
Then, assuming you use Eclipse you can right click the project folder and do a 'Gradle refresh'
In your config class you can either autowire in the bean for the class as a whole , or choose to create it whenever an instance of the class is created by declaring it within the class constructor:
import org.springframework.beans.factory.annotation.Autowired;
//I also have config in this jar
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class pBeanConfig {
//Autowire in the bean for the whole class.
#Autowired
ExpBean expBean;
public pBeanConfig() {
//Or, you may want to have a bean instance whenever the pBeanConfig class is instantiated.
ExBean exBean;
}
}
I'm making a number of assumptions for this answer like it's a Gradle project, but you can set up a similar dependency for Maven by modifying the POM file to include that file.
Build tools aside, in Eclipse you can simply add the jar file by right clicking the project and build path => configure build path => add external jar file.
UPDATE:
This pic describes how I set up the bean within a separate jar file via eclipse. The steps should show that the bean can be reached with this project even though it's in a jar and not in the project itself.
Related
I have a multi module project (gradle) with following structure:
Root project 'mp-search'
+--- Project ':analyzer'
+--- Project ':common'
| \--- Project ':common:es-model'
...
Description:
:analyzer: contains a spring boot application
Contains #SpringBootApplication and all other needed dependencies (Web, Feign, etc.)
:common:es-model: contains models + repositories for Spring Data Elasticsearch
Contains only the "spring-boot-starter-data-elasticsearch" dependency (no #SpringBootApplication)
Say I have the following classes in :common:es-model with package com.example.esmodel.document.model:
package com.example.esmodel.document.model;
//imports
#org.springframework.data.elasticsearch.annotations.Document(indexName = "document")
public class Document {
#Id
private String documentId;
#Field
private String content;
// Getter + Setter + Constructor
}
package com.example.esmodel.document.repository;
// imports
#Repository
public interface DocumentRepository extends ElasticsearchRepository<Document, String> {
}
Furthermore I created a configuration class with #ComponentScan to find them
package com.example.esmodel.document.configuration;
// Imports
#Configuration
#ComponentScan(basePackages = "com.example.esmodel.document")
public class DocumentConfiguration {
}
And an custom annotation for simple inclusion in the application which imports the configuration:
package com.example.esmodel.document;
//Imports
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(DocumentConfiguration.class)
public #interface EnableDocumentModel {
}
I want to use the DocumentRepository in a controller in my application project (:analyzer). So I want to include it via the EnableDocumentModel annotation like this:
package com.example.analyzer;
import com.example.esmodel.document.EnableDocumentModel;
// Other imports
#SpringBootApplication
#EnableFeignClients
#EnableDocumentModel // [1]
//#Import(DocumentConfiguration.class) // [2]
//#ComponentScan(basePackages = "com.example.esmodel.document") // [3]
public class AnalyzerApplication {
public static void main(String[] args) {
SpringApplication.run(AnalyzerApplication.class, args);
}
}
Of these three tests [1] and [2] are not working. The classes (EnableDocumentModel, DocumentConfiguration) are found and application tries to start, but fails with a UnsatisfiedDependencyException:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.analyzer.controller.DocumentController required a bean of type 'com.example.esmodel.document.repository.DocumentRepository' that could not be found.
But test [3], using the same #ComponentScan(basePackages = "com.example.esmodel.document") from DocumentConfiguration in the AnalyzerApplication, works fine. But this is not what I desire.
Do I miss something? Any ideas?
Thanks in advance!
Edit:
Just to make sure the DocumentConfiguration is considered by spring at all, I added #Import(DocumentRepository.class) in DocumentConfiguration, but it throws an BeanInstantiationException because it's an interface (which is of course reasonable).
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.esmodel.document.repository.DocumentRepository]: Specified class is an interface
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:70) ~[spring-beans-5.3.19.jar:5.3.19]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1326) ~[spring-beans-5.3.19.jar:5.3.19]
... 31 common frames omitted
So Spring definitely considers DocumentConfiguration otherwise the exception wouldn't be thrown. So somehow the configuration is considered, but #ComponentScan(basePackages = "de.sva.medpower.esmodel.document") doesn't do its job...
I also found a tutorial on medium (https://medium.com/trendyol-tech/how-to-write-a-spring-boot-library-project-7064e831b63b) doing it that way, but somehow it doesn't work for me..
#ComponentScan does not trigger Spring Data repository initialization. You'd need an #EnableElasticsearchRepositories somewhere in your configuration (ideally on a class in the package that you'd want to scan for ES repositories).
This is even activated automatically if Boot finds Spring Data Elasticsearch on the execution classpath, but in your case doesn't show any effect as the application's root package is com.example.analyzer (the package that your #SprignBootApplication class resides in).
Annotating DocumentConfiguration with #EnableElasticsearchRepositories and point that to the packages that the ES repositories reside in should fix the issue.
I have two different maven projects in first I am trying to keep two modules one for "repository and entities" and second for the services. The second project is containing only one module with the "controllers". Now I am having many problems first is "Not a managed type" for the entities. Another thing if I keep everything in one module or even in different modules with one parent project, it works flawlessly, however, I am just trying to put the different package in different project and module
The entityscan, enablejparepositries and all others are working, the debug states:
name: default
persistence provider classname: null
classloader: sun.misc.Launcher$AppClassLoader#42a57993
excludeUnlistedClasses: true
JTA datasource: null
Non JTA datasource: HikariDataSource (null)
Transaction type: RESOURCE_LOCAL
PU root URL: file:/F:/Software/MavenRepo/com/company/repo/1.0.0/repo-1.0.0.jar
Shared Cache Mode: UNSPECIFIED
Validation Mode: AUTO
Jar files URLs []
Managed classes names [
com.company.sitemap.repo.Page]
Mapping files names []
Properties []
However, At the last it states
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.company.sitemap.repo.Page
and shows error starting the application.
Can you please help me out with this?
Here is my Application class file
package com.company.sitemap;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.company.sitemap.repo.SitemapRepoConfig;
import com.company.sitemap.service.SitemapConfig;
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = { SitemapRepoConfig.class })
#EntityScan(basePackages = {"com.nie.learn.sitemap.repo"})
#ComponentScan(basePackageClasses = { SitemapConfig.class, SitemapRepoConfig.class })
public class Sitemap extends SpringBootServletInitializer {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Sitemap.class, args);
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
The entity class resides in different project and module. Let say Project-libs and module module-repo. I am trying to add this as a maven dependency.
The entity file is as follows:
package com.company.sitemap.repo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Page {
Repo config to scan repo classes.
package com.company.sitemap.repo;
import org.springframework.context.annotation.Configuration;
#Configuration
public class SitemapRepoConfig {
}
Service config to scan service classes:
package com.company.sitemap.service;
import javax.validation.constraints.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.company.sitemap.repo.PageRepository;
#Configuration
public class SitemapConfig {
#Bean
#NotNull
public SitemapService service(#NotNull PageRepository repo) {
return new SitemapService(repo);
}
}
I'll show you an example that helps us in a somewhat different case. This kind of config is used for Module1TestConf to allow separate modules testing using Spring boot slicing (#DataJpaTest, etc.) in our multi-module Gradle project to avoid loading all the context.
We just limit our module scanning to the current module + additional entities.
#SpringBootConfiguration
#EnableAutoConfiguration
#EntityScan(
basePackages = "your.other.module.package.entity",
basePackageClasses = {SomeEntity1.class, SomeEntity2.class}
)
#ComponentScan(
value = "your.current.module.package",
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter)})
That is basically what is #SpringBootApplication is doing under the hood.
So I'm not 100% sure that it would help you. Is there a valid reason to have the projects kept as separate maven projects (use Gradle! :) )?
Anyway, you just need to make those entities scanned by Spring and become manageable...
Also try specifying entity classes directly in basePackageClasses.
You need to scan the package so that spring can create beans automatically. You are defining Page as bean but not scanning it. For scan you need to add
#ComponentScan(basePackages = { "com.nie.learn.*" })
If you have already added it than please check if Page is annotated with #Entity like below
#javax.persistence.Entity
public class Page {}
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = { com.company.sitemap.repo})
#EntityScan(basePackages = {"com.company.sitemap.repo"})
public class Sitemap extends SpringBootServletInitializer {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Sitemap.class, args);
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
Try the above configuration.
This question already has answers here:
How can I #Autowire a spring bean that was created from an external jar?
(5 answers)
Closed 3 years ago.
I have a Spring Boot project and I can't get components from an external jar to be autowired. When I try to, I got a org.springframework.beans.factory.NoSuchBeanDefinitionException saying that can't find a bean with that name available.
I tried some solutions found in similar questions, like these ones:
How to autowire #service from external Jar in Spring
Spring Boot #autowired does not work, classes in different package
How can I #Autowire a spring bean that was created from an external jar?
..but still can't managed it to work.
Here is an example of what I'm trying to accomplish:
Here is boot class in the Spring Boot project spring-project-example
package com.springdi.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import com.dependency.example.DependencyBasePackageClass;
import com.dependency.example.somepackage.SomeBean;
#SpringBootApplication
#ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class)
public class SpringProjectExampleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringProjectExampleApplication.class, args);
String beanName = SomeBean.class.getName();
System.out.printf("%s can be autowired: %s\n", beanName, String.valueOf(context.containsBean(beanName)).toUpperCase());
}
}
It's just a simple Spring Boot project checking if it is possible to autowire a component present in the dependency jar.
Here is the component in the jar (dependency-example-1.0.0.jar)
package com.dependency.example.somepackage;
import org.springframework.stereotype.Component;
#Component
public class SomeBean {
public void someMethod() {
System.out.println("Some process...");
}
}
And here is the base package class of this same jar
package com.dependency.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Just a class to serve as the root for component
* scanning in "com.dependency.example" and its sub-packages
*/
#Configuration
#ComponentScan
public class DependencyBasePackageClass {
}
I've already tried #Import(DependencyBasePackageClass.class) in SpringProjectExampleApplication and #ComponentScan with basePackages and basePackageClasses, but no success.
I also tried using #SpringBootApplication(scanBasePackageClasses = {SpringProjectExampleApplication.class, DependencyBasePackageClass.class})
and the not type safe #SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"}).
#Configuration #ComponentScan({"com.dependency.example"}) also fails, context.containsBean("com.dependency.example.somepackage.SomeBean") still returns false.
This jar is included in classpath and in the pom.xml as a dependency
<dependencies>
<!-- other dependencies -->
<dependency>
<groupId>com.rbaggio</groupId>
<artifactId>dependency-example</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/dependency-example-1.0.0.jar</systemPath>
</dependency>
</dependencies>
Could it be the location of the jar, the way it is included or some extra configuration needed?
I'd appreciate any help! Thanks in advance.
Okey some basic things, you have mixed up your packages a bit.
#SpringBootApplication will scan all classes in packages below the class this is annotated on. This annotation is an alias for #EnableAutoConfiguration, #Configuration and #ComponentScan means that #ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class) is not needed.
com.springdi.example // class with #SpringBootApplication annotation
|
|
|
com.springdi.example.* // Will find all #Service, #Component, #Configuration
// in subpackages below the #SpringBootApplication
// annotation
You can read more about the annotation here SpringBootApplication
Since your other annotated classes are NOT in the same package structure as the #SpringBootApplication you need to define all the places you want to scan for annotations.
#SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"})
will probably include all the packages that you want to scan through.
I am building a basic program of "hello world" in SpringBoot
Code
MyController.java
package controllers;
import org.springframework.stereotype.Controller;
#Controller
public class MyController {
public String hello() {
System.out.println("Hello World");
return "foo";
}
}
DemoApplication.java
package di.prac;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import controllers.MyController;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println("*******"+Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
I am using eclipse and created this project from http://start.spring.io/ without any dependencies.
I learned that Spring create the bean of MyController class with name myController ,but Spring is not able to find myController bean
ERROR
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'myController' available at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:686)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at
org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089)
at di.prac.DemoApplication.main(DemoApplication.java:16)
Please find and explain the error in the Project
Place your controller under sub package of di.prac like di.prac.controllers or use #ComponentScan on your controller. By default, Spring scans the current and sub packages where your main application is present. If you want to scan other packages too, then you can specify the packages in #SpringBootApplication as an argument like.
#SpringBootApplication(scanBasePackages = {"com.xyz.controllers", "com.abc.models""})
We should avoid putting the #Configuration class in the default package (i.e. by not specifying the package at all). In this case, Spring scans all the classes in all jars in a classpath. That causes errors and the application probably doesn't start.
For your controller to be available in the context of Spring, you need to define that it is managed by the Spring container. Only the #Controller annotation is not enough, it indicates only the stereotype of your bean, as well as the annotations #Repository and #Service.
In cases where the beans have these annotations and are managed by Spring, it is because their packages that the spring is scanning to search for them has been specified programmatically or per xml. In your case, you should annotate your DemoApplication class with 2 other annotations:
#Configuration - Allows access to spring context
#ComponentScan - Packages to be scanned by Spring
#Configuration
#ComponentScan (basePackages = {"controllers"})
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println(Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
Just encountered same problem, solution is simple.You just (me also) created package "controllers" on the wrong place. It should be created not in java folder but under folder with name of your project. Simple but deadly mistake. Your code is written perfectly fine.
I have a problem trying to get my autoconfiguration working. I have two jars as follows, each have a spring.factories file where these two are enabled for EnableAutoConfigurationProperties.
This configuration is in my-package-mock.jar, it depends on my-package-real.jar below:
package org.packages.package.packageA;
#Configuration
#AutoConfigureBefore(AutoConfigurationB.class)
public class AutoConfigurationA {
#Bean
public MyService mockService() {
return new MyMockService();
}
}
This configuration is in my-package-real.jar:
package org.packages.package.packageB;
#Configuration
#ConditionalOnMissingBean(MyService.class)
public class AutoConfigurationB {
#Bean
public MyService realService() {
return new MyRealService();
}
}
Now the idea is that if my-package-mock.jar is included then AutoConfigurationB will not be processed as A is ordered to be before and by the time it gets to B MyService is already defined.
However, it does not work when used in a third project that includes these jars. It looks like the AutoConfigureOrder annotation is skipped when loading these jars from the classpath and these configurations are processed in the order the jvm loads these classes. In my particular case it does B first and at that point MyService is not yet defined and thus will instantiate the RealService bean. How can I get this to work?
Obviously this is a small example where a #Primary annotation on the mock will do the job, but that is not what I'm looking for.
Edit: it seems if the #SpringBootApplication annotated main is not a part of the package where these configurations are then things do work. E.g. the annotation is not in "org.packages.package" but "org.somewhereelse" then things work.
package org.packages.package;
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(Collections.singletonList(TestApplication.class).toArray(), args);
}
}
#AutoConfigureBefore and #AutoConfigureAfter only apply when a configuration class is loaded as a result of auto-configuration being enabled and it being listed in spring.factories. When your auto-configuration classes are in org.packages.package (or a sub-package) and your main application class is in the same package, they're being found by Spring Framework's standard component scanning. This happens because #SpringBootApplication enables component scanning for the package of the class that it's annotating. As a result of this the auto-configuration-specific ordering doesn't apply.
To avoid the problem, you should places your auto-configuration classes in a package that isn't used by any application code.