I have multiple Spring #Configuration classes that define beans. The main class where I run application has #SpringBootApplication(scanBasePackageClasses = BasePackage.class).
My question, what is the order these components are scanned and beans are created?
#SpringBootApplication(scanBasePackageClasses = BasePackage.class)
public class MyApplication {
public static void main(final String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
And configurations
#Configuration
class Config1{
// defines beans
}
#Configuration
class Config2{
// defines beans
}
Another maven dependency also has Config3
#Configuration
class Config3{
// defines beans
}
I tried to search the documentation, but I did not find which order these components are scanned and initialized.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.html
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.html
Update
I asked this question because I have a bean that has #ConditionalOnMissingBean, and multiple configurations that define it. Where is in the official documentation statement, which order #Configuration classes are scanned.
In a first step Spring scans all classes relative to your base package for beans. Then spring will create the beans and inject it.
If you need to have the beans created in certain order you can do so by adding #DependsOn
From the docs:
The depends-on attribute can explicitly force one or more beans to be
initialized before the bean using this element is initialized.
Related
I was trying to configure promethues in my code and I just had to create a bean like below. I was wondering how spring recognised CollectorRegistry. How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
#Component
public class TestProm{
public TestProm(CollectorRegistry registry){
// Some initialization code here
}
}
However, when I tried to define it in another way by defining a #Bean in my #Configuration class, it didn't seem to work properly as my own constructor for CollectorRegistry didn't have all the necessary properties.
#Configuration
public class PromConfiguration{
#Bean
public TestProm getTestProm() {
return new TestProm(new CollectorRegistry());
}
}
public class TestProm{
public TestProm(CollectorRegistry registry){
//Some code here
}
}
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custome implementation.
How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
In the first example you require a Bean of type CollectorRegistry and Spring will actually create such bean for you if you have spring-boot-starter-actuator and Prometheus dependencies on your classpath and you have autoconfiguration enabled :
if you use #EnableAutoConfiguration
if you are using Spring Boot and #SpringBootApplication annotation (#EnableAutoConfiguration is part of this annotation underneath) :
...
#EnableAutoConfiguration
#ConfigurationPropertiesScan
...
public #interface SpringBootApplication
In this case Spring will scan the classpath and load all configurations. The bean that you are interested in is part of PrometheusMetricsExportAutoConfiguration :
#Bean
#ConditionalOnMissingBean
public CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
In the second example the instance of CollectorRegistry is not managed by Spring because you create it through new keyword. However it should work as CollectorRegistry has default constructor which initializes autoDescribe field to false. And the default Bean of this class which is created by Spring (in a way described above) has this field set to true. So the value of this field is the source of your differences.
Also if this instance is not managed by Spring - it prevents it to be injected into other components which require it. As the scope of CollectorRegistry is Singleton (shown above) other beans might require to share the instance to work properly (for example some beans might want to register/deregister collectors) but if you create CollectorRegistry like that with new keyword - you will not get the singleton instance but a new instance which cannot be shared across other beans.
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custom implementation?
If you want to use the default CollectorRegistry (assuming you want to use the bean that is created by default in the way described above) just inject bean of this type to your beans and it should be enough.
I have a spring-boot application (Java8, spring-boot 2.1.4-RELEASE).
One of the services in the business layer need to #Autowire a bean from a certain jars in my classpath. In order to achieve that i had to add #ComponentScan({"package.to.my.bean.inside.the.jar"}) and it was magically scanned and wired successfully (this was added to the main spring-boot class which declare the main method).
However, since then my controllers aren't scanned hence the DispatcherServlet is returning 404 for every request i trigger (default dispatcher).
Actually my entire spring-boot app annotations is being ignored - no scan is performed.
Just to emphasis - the application worked perfectly before adding the #ComponentScan.
Main spring-boot app class:
package com.liav.ezer;
// This is the problematic addition that cause the endpoints to stop
// inside a jar in the classpath, in com.internal.jar package resides an
// object which i need to wire at run time
#ComponentScan({"com.internal.jar"})
#SpringBootApplication
public class JobsApplication {
public static void main(String[] args) {
SpringApplication.run(JobsApplication .class, args);
}
}
Controller example:
package com.liav.ezer.controller;
#RestController
#EnableAutoConfiguration
#RequestMapping(path = "/jobs")
public class JobController {
#GetMapping(path="/create", produces = "application/json")
#ResponseStatus(HttpStatus.OK)
String createJob(#RequestParam() String jobName) String jobName){
return "job created...";
}
}
I tried adding my spring-boot app base package to the list of packages in the #ComponentScan with no luck.
I tried narrowing down the scope of the package declaration to be only on the class which i need with no luck.
Here is the code
According to Spring documentation
Configures component scanning directives for use with #Configuration
classes. Provides support parallel with Spring XML's
element. Either basePackageClasses() or
basePackages() (or its alias value()) may be specified to define
specific packages to scan. If specific packages are not defined,
scanning will occur from the package of the class that declares this
annotation.
In your case when you are adding
#ComponentScan({"com.internal.jar"})
you are disabling scanning of
com.liav.ezer.controller
To fix it you can do the following configuration
#ComponentScan(basePackages = {"com.internal.jar", "com.liav.ezer.controller"})
If so, remove #ComponentScan, can declare that bean in yourself configuration.
try below
#SpringBootApplication
public class JobsApplication {
public static void main(String[] args) {
SpringApplication.run(JobsApplication .class, args);
}
#Bean
public BeanInOtherJar xxBean(){
return new com.internal.jar.XXX();
}
}
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.
According to official doc:
Annotation Type Configuration
Indicates that a class declares one or more #Bean methods and may be
processed by the Spring container to generate bean definitions...
#Configuration classes may be composed using the #Import annotation,
not unlike the way that works in Spring XML. Because
#Configuration objects are managed as Spring beans within the
container..
But i can also use #Configuration annotation without #Import. I have tested the code listed below and it works as expected. So what is the purpose to use #Import?
DispatcherServletInitializer
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebMvcConfigurerAdapter
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "package.name" })
// #Import(OptionalConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
// ...
}
OptionalConfig
#Configuration
public class OptionalConfig {
#Bean(name = "myClass")
public MyClass myClass() {
return new MyClass();
}
}
Service
#Service
public class MyServiceImpl implements MyService {
#Autowired
private MyClass myClass; // yes, it works
// ...
}
If component scanning is enabled, you can split bean definitions in multi #Configuration classes without using #Import. And you don't need to provide all of them to the application context constructor.
I think the main purpose for #Import is to provide you a way to simplify multiple configurations registration if you'd like to avoid component scanning (as of Spring Framework 4.2, per reference manual).
There's a note in Spring Reference Documentation about #Import usage:
As of Spring Framework 4.2, #Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you’d like to avoid component scanning, using a few configuration classes as entry points for explicitly defining all your components.
Thus far, we've seen how to break up bean definitions into multiple #Configuration classes and how to reference those beans across #Configuration boundaries. These scenarios have required providing all #Configuration classes to the constructor of a JavaConfigApplicationContext, and this is not always ideal. Often it is preferable to use an aggregation approach, where one #Configuration class logically imports the bean definitions defined by another.
The #Import annotation provides just this kind of support, and it is the direct equivalent of the <import/> element found in Spring beans XML files.
http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html
I found a use of using the #Import annotation. I don't think it's the use case.
If you are developing a set of libraries using Spring, probably you don't have a SpringBootApplication. So, you have not enabled any auto-scan to resolve beans.
If a bean declared in a configuration file in the library library-a is referred through dependency injection in library-b you need to use #Import to tell Spring how to resolve the bean.
As we said, in library-a you have this configuration file.
#Configuration
public class ConfigurationA {
#Bean
public BeanA beanA() {
return new BeanA();
}
}
In library-b you must have this configuration file if you want to use BeanA
#Configuration
#Import(ConfigurationA.class)
public class ConfigurationB {
#Bean
public BeanB beanB(BeanA beanA) {
return new BeanB(beanA);
}
}
Hope it helps.
With component scanning enabled it's difficult to immediately see where #Import adds value if your view of the world is limited to your own application and its packages. Where it can help is if you are importing bean libraries with their own package structure that you don't want to component scan.
You can place such libraries on your classpath and use #Import to cherry-pick #Configuration classes from within them. That's why it's often referred to as composition because you are composing your #Configuration class from multiple sources.
A typical use case of #Import is when teams develop REST services and realize they need some common configuration.
Every service will have its own namespace i.e. org.mybusiness.specific and will have its #SpringBootApplication (and therefore component scan root) start at this package.
On the other hand, the common library will have its namspace set to org.mybusiness.common and will therefore be out of reach for component scanning.
Thus the need to reference the common configuration class using #Import(CommonConfig.class)
#Import works great when for some reasons you need to register not all but some of the components from a package. In such case #ComponentScan would be too complicated.
See details in the Spring boot component scan include a single class question.
I found this piece of code in my codebase. Actually the class:
package my.services.config;
#Configuration
#ImportResource("classpath:spring/*.xml")
#ComponentScan("my.services.jms.server")
public class MyServicesConfiguration {
#Bean
public ApplicationLifecycle lifecycle() {
return new MyServicesLifecycle();
}
}
I'm trying to understand:
So, it uses all spring/*.xml files/beans before/while staring up, then it injects ApplicationLifecycle bean into the spring context (along with other beans from spring/*xml and from beans from 'my.services.jms.server' packages). So, in the end we gonna have one global context with all beans (?)
The question: How does it possible to launch this application (if, as I understand this class is only one entry point to the app) ?
It's gonna be some main(String[] args) {} function that would able to launch it by 'my.services.config' path, let's say, as an argument.
So, in the end we gonna have one global context with all beans (?)
That's right. From Spring perspective #Configuration class is just a different way to define beans, equivalent to XML. Both Java configuration and XML configuration is merged and treated equally later.
And this is how you can start you context from withing main():
ApplicationContext ctx =
new AnnotationConfigApplicationContext(MyServicesConfiguration.class);
and later:
ApplicationLifecycle applicationLifecycle =
ctx.getBean(ApplicationLifecycle.class);