I am writing a new spring boot project where my root package name is "com.example". In my gradle configuration, I am adding a dependency of a third party library, which has root package name as "org.base". Now, this library has classes with #Component annotation which I want to use in my code leveraging #Autowired
In my Config class I am scanning base package of third party library as well.
#Configuration
#ComponentScan({"org.base", "com.example"})
public class ServiceConfig{...}
When I run the application, spring is not able to find/create beans from this library and I am getting a bean not found error.
For Component Scan to work, does the root package have to be same?
Error Details: UserService required a bean of type 'org.base.util.CommonUtils' that could not be found.
UserService is my service
#ComponentScan will only work if the classes are annotated with spring annotations like #Component or #Service etc. How you can fix this is to use declarative approach, use #Bean to class which annotated with #Configuration will fix this problem.
The issue was not having proper annotations. I finally fixed this as below
#Component
public Class UserService {
#Autowired
#Lazy
private CommonUtils commonUtils;
}
I had to use #Lazy annotations for dependencies from third party Library. During eager initialization, spring was not able to find the dependency bean.
Related
I have a base package which is using guice for DI. I am using this as a library in my other project where I am using Spring boot So can I autowire dependencies from that jar to my spring boot project.
Let's say the artifact of the base package is com.package.dependency and my spring boot project is com.example.spring-boot
I have tried
#ComponentScan(basePackages = {"com.example.spring-boot","com.package.dependency"})
but it does not work.
You should be able to autowire dependencies from an external jar by using componentscan if there are classes annotated as autowire candidates (#Bean, #Component,..)
#ComponentScan would not find any candidates for autowiring unless there are any candidates which are qualified for autowiring is available in the external library.
Instead, you can define an #Bean annotation and return a new instance of the class you want to return in your #Configuration class.
#Configuration
public class MyConfig {
#Bean
public MyExternalBean myExternalBean() {
return new MyExternalBean();
}
}
and in the class you need to use it, write :
#Autowired
private MyExternalBean myExternalBean;
If you need autoconfiguration, refer : https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/boot-features-developing-auto-configuration.html. Here, you make use of spring.factories and specify the classes available for autoconfiguration so that you need not specify #ComponentScan in every project you use that external jar (applicable when the jar is developed by you or your company for use in other projects and not for third-party jars).
I am developing a spring boot custom starter which pom contains some dependencies (other starters, libraries) and this starter does some config about jwt filtering to allow the filter at security level. The issue is when I add my custom starter as a pom dependency in another project (starter-consumer), it seems it detects the class I want to import but IntelliJ does nothing.
Maybe I didn't packaged the starter correctly, or at least the classes that I coded inside. The other dependencies that the starter contains in the pom are successfully added as a dependencies of the starter-consumer. For example, I use some jwt utils which dependency is in the parent starter. So that thing works ok.
import io.jsonwebtoken.impl.DefaultClaims;
The problem is when I try to import a custom class which I coded in the starter. This is my package structure:
I want to use the JwtConfig class in my starter-consumer. It appears but I can't import. It does nothing.
And then If I manually check package availability I see this:
Pepito is missing :( and theinit is the package name of the starter-consumer. The jar is installed in the local m2 so I get the dependency like this:
<dependency>
<groupId>com.pepito</groupId>
<artifactId>starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Any insights on this?
Edit 1:
I removed the boot maven plugin as a you said, it seems now it is not packaged as boot app and my starter-consumer can import the clases I coded . One additional thing, what happens with beans? Does the autoconfigure starts the bean by itself or the starter-consumer needs to declare it?
Edit 2:
So as part of the solution of this post, I am trying to inject a bean from the starter into the starter-consumer.
Apart from another beans, here we have the jwtTokenAuthenticationFilter which I want to inject into my starter-consumer security config.
#Configuration
#ConditionalOnProperty(name = "security.jwt-enabled", havingValue = "true")
public class JwtAutoConfiguration extends AbstractHttpConfigurer<JwtAutoConfiguration, HttpSecurity> {
#Bean
public JwtConfig jwtConfig() {
return new JwtConfig();
}
#Bean
public FilterRegistrationBean registration(#Qualifier("jwtFilter") JwtTokenAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
public JwtTokenAuthenticationFilter jwtFilter() {
return new JwtTokenAuthenticationFilter(jwtConfig());
}
#Override
public void init(HttpSecurity http) throws Exception {
// initialization code
}
}
This is my spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pepito.starter.configuration.security.jwt.JwtAutoConfiguration
And in the starter-consumer I have the following
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenAuthenticationFilter jwtFilter;
And here is where I see the error in intellij that can't autowire that bean because it does not exist. I suppose is because of something about scan but in my starter-consumer I have #SpringBootApplication which is suppose it contains componentscan annotation.
I believe A couple of issues that I see here and some clarifications will help you to find an answer:
Module with starter is a regular jar. The only thing that differs is that it has META-INF/spring.factories which I see exist.
Having said that - I see in the Pepito starter module class SpringBootMicroserviceStarterApplication. This is wrong. If the starter is a spring boot application, then the chances are that you're using spring boot maven plugin to prepare it as an application.
But the jar created by this plugin is not really a Jar, more specifically it stores other jars in BOOT-INF lib and in general can't be considered a Jar build-tool-wise and IDE-wise.
To put it simple you cannot declare a dependency on something that gets packaged as a spring boot application!
Update 1
To address your OP's question in comments:
The starter is not a spring boot application. It should not have a method annotated with #SpringBootApplication, it should not be packaged as spring boot application and so forth.
The best way to view the starter is as an self-contained "feature" or "facility" (trying to find the most appropriate word in English) that can be used by Spring boot applications by importing the starter module.
In terms of testing, #SpringBootTest should not be used in starter's module because it mimics the startup of spring boot application which obviously does not exist here.
Its possible to test it with Unit Test or with Spring Testing Framework:
#RunWith(SpringRunner.class)
#ContextConfiguration(<here comes the class of AutoConfiguration in starter)
One last thing to clarify here:
In spring Factories you can specify the autoconfiguration that in turn declares a series of beans required by the starter.
These beans are resolved by spring boot application just like other beans, the difference is that the Configuration is identified out of spring.factories file and not by package structure or explicit configuration.
So to answer your question:
Would you know if I can declare a bean in the starter and then autowire it in the consumer?
Yes, as long as the starter gets loaded by spring boot application, you can autowire (or inject in any other way) beans from starter into the beans of the spring boot application itself.
I am learning spring boot. This is my spring boot project structure. I do intentionally keep my application.java in a different package to learn about #ComponentScan
Project source - https://github.com/kitkars/spring-boot
project structure
Error :
The application failed to start due to below error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Field productRepository in com.test.service.ProductService required a bean of type 'com.test.repository.ProductRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test.repository.ProductRepository' in your configuration.
Process finished with exit code 1
This is my Application.java
#SpringBootApplication
#ComponentScan(basePackages = "com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now If I move my Application.java under com.test, everything works just great.
What If my Application.java is not under com.test - can it not use the ComponentScan packages and start from there? All the controller, services, repositories etc are present under com.test.
The class annotated with #SpringBootApplication should be in your root package (by default all classes in this package and subpackages are scanned) or you need to specify other packages (controller, entity and others) in #ComponentScan.
Official documentation states:
We generally recommend that you locate your main application class in a root package above other classes.
Spring Boot relies heavily on default configuration. Consider this excerpt from #EnableAutoConfiguration annotation.
The package of the class that is annotated with
#EnableAutoConfiguration, usually via #SpringBootApplication, has
specific significance and is often used as a 'default'. For example,
it will be used when scanning for #Entity classes. It is generally
recommended that you place #EnableAutoConfiguration (if you're not
using #SpringBootApplication) in a root package so that all
sub-packages and classes can be searched.
The same logic implies to #ComponentScan, #EnableJpaRepositories and the above mentioned #EntityScan annotations. So, if you want to take control of component scanning, you should specify base packages explicitly for all of those annotations.
#SpringBootApplication(scanBasePackages = "com.test")
#EnableJpaRepositories(basePackages = "com.test")
#EntityScan(basePackages = "com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also note that in your case there is no need to use another #ComponentScan as you can provide arguments to Spring Boot's #ComponentScan via scanBasePackages attribute.
Please check the dependency 'org.springframework:spring-context-indexer if you have one, of course. In my case, this library has caused problems with submodules.
The problem was that only beans of the current gradle project were included in the index.
resolving do this in my application class.
#Configuration
#SpringBootApplication
#ComponentScan(basePackages = "{lamc.bar.*}")
For me, removing the #ComponentScan did the job.
SpringBoot should pick up all your components automatically, so long as your application structure is like this:
|-->package1
|-->package2
|Main.java
You should remove #Repository in com/test/repository/ProductRepository.java.
I have a source project, here are some code
#Configuration
public class AnalyzeSyntaxServiceConfig {
#Bean
public AnalyzeSyntaxService analyzeSyntaxService() {
return new AnalyzeSyntaxService();
}
}
and use it in the source project like this
#Autowired
private AnalyzeSyntaxService analyzeSyntaxService;
it works well
then I package it as a jar file, and add it to the target project as a dependency in pom.xml, and I try to use this above service in the same way
#Autowired
private AnalyzeSyntaxService analyzeSyntaxService;
but it's null, why?
Are the package names different between the source and consuming/dependent code base?
A Spring Boot application will scan from the package the SpringBootApplication is placed in and any child packages.
If you have it within the same project, and your configuration class is structured that it is within the same package or a child package e.g.
com.myapp or com.myapp.configs it will be scanned and picked up.
When importing it to a different project you will need to manually component scan for the configuration via the #ComponentScan annotation and provide it with a package to scan for your configuration.
https://github.com/Flaw101/springbootmixin/tree/example/componentscanning
Because the JacksonConfig.class is in a parent (different) package the ComponentScan does not work. The Application class scans everything in com.darrenforsythe.mixinabstractclass and it's child packages. To make Spring Boot scan the JacksonConfig we have to be explicit and add the #ComponentScan("com.darrenforsythe") to the application.
If you uncomment the #ComponentScan within the MixinasbtractclassApplication the tests then pass again as it will load the JacksonConfig again.
Additionally, I would recommend using constructor. injection this would avoid the Autowired dependency being null and inform you on init of the ApplicationContext rather than at runtime.
Make sure that package in which the class available was scanned. Here is the annotation for that, put this annotation in SpringBoot application class.
#ComponentScan("com.abc.xyz")
Also, see if by any chance the class which has this autowired one is getting instantiated. If that class is instantied then ofcource the autowired ones will be null.
I have a module/jar that I've created and am using as a util library. I created a service in there like so:
#Service
public class PermissionsService { ... }
... where this resides in a package here: com.inin.architect.permissions and in my main application, I'm referencing/loading this jar (i.e. set as a dependency in the maven POM.xml file for the app) like so:
<dependency>
<groupId>com.inin.architect</groupId>
<artifactId>permissions</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
and within the application I want to use that service like:
#Autowired
PermissionsService permissions
In the application's spring setup, I've got this:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.inin.generator", "com.inin.architect.permissions" })
public class WebConfig extends WebMvcConfigurerAdapter implements ServletContextAware { }
However when I run my application under tomcat, it complains that there isn't a bean for the PermissionsService: "org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ..."
So, how can I bring over the bean from the lib into my application? Surely there's a way. Do you have to set the library up as a full blown spring MVC application so that this can work? i.e. do you have to have #Configuration and #ComponentScan setup in the lib as well?
You have to scan at least the package containing the class you want to inject. For example, with Spring 4 annotation:
#Configuration
#ComponentScan("com.package.where.my.class.is")
class Config {
...
}
It is the same principle for XML configuration.
Just a note on this, but you could decouple your dependency from spring. In your #Configuration class create
#Bean public PermissionsService permissionsService(){
return new PermissionsService()
}
This will also allow it to be injected. Not that you have to remove your spring annotation, just an option making it potentially usable outside of spring.
Ok - i had exactly the same problem - i wanted to autowire a mongo db repository interface from an external jar.
I could autowire every bean from that jar with using
#SpringBootApplication(scanBasePackages = {"com.myrootpackage"})
However - autowiring the interface always failed with "Could not find blablabla..."
But the interface was in the same package as the beans i could import.
It turned out that searching for the mongo db interfaces is NOT taking the scanBasePackages from the #SpringBootApplication into consideration!
It has to be explicitly configured via
#EnableMongoRepositories(basePackages = {"com.myrootpackage"})
Or you could move the main class "up" so the default searching works also for the mongo interfaces. So i understood the problem and found a solution. But i am still a bit unhappy because i need to configure the same lookup path twice. I find it stupid honestly.
I faced the same issue while scanning other classes from other project dependencies, The scanning solution depends on the type of classes you need to scan as follows:
if they are normal #Component, #Service annotations use
#ComponentScan({"com.mypackge1","com.mypackage2"})
If the type of classes are domain objects based on entities use
#EntityScan("com.mypackge1.domain")
If JPA repository classes
#EnableJpaRepositories(basePackages = {"com.mypackage.repository"})
If Redis repository classes use
#EnableRedisRepositories(basePackages = {"com.mypackage.repository"})
Same for Mongo, etc.
You can import application-context.xml for com.inin.architect.permissions in the following manner inside your main application.
<import resource="classpath:/permissionApplicationContext.xml" />
This will enable you to autowire beans from com.inin.architect.permissions that you have defined.