By "arbitrary" I refer to the ability to do an action whenever it's wanted. So, with that, what is intended here is to run #Configuration classes only when it's explicitly called or when it's strictly needed (instead of running automatically as soon spring context is up, which is the default behavior).
Describing a bit of my scenario: the #Configuration class is a database configuration and connection class. Although I don't want it to be created automatically, as soon the application starts (default spring behavior). Instead, I'm looking for a way to programmaticaly build and connect to it, strictly when I call for it to do so.
So the question would be: How to prevent #Configuration classes from automatically running and after that, how should it be "re-enabled" again to be able to run?
Please note that NONE of that below worked out as expected:
Tried using #Conditional, and at first it seemed to be the perfect fit, although #Conditional expressions (or based on some kind of logic or property like #ConditionalOnProperty) runs once after application context is built, which means that the value being passed to the annotation could even change, although that class wouldn't react to the #Conditional expression changes (which would be perfect, like, telling spring "run that configuration class after property name X is set")
Something like that:
#Configuration
#ConditionalOnProperty(name = "X", havingValue = "true")
public class DatabaseTest {
// bean is not created, no value for property X
then, after setting a value to X with
Properties properties = new Properties();
properties.put("X", "anyValue"));
Although it works per as described above, the class do not react to the property changes.
Another attempt was to use #DependsOn and #Order, but still, those beans annotated with it automatically runs as soon the depending beans are created.
Also tried using
#ComponentScan(excludeFilters = {#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = {ConfigurationClass2.class, ConfigurationClass1.class})})
and it do prevent #Configuration classes from running, but I have no idea how to switch it back working.
Related
I have a Spring project, and I need to configure Flyway.
Using the default FlywayAutoConfiguration, Flyway migration is immediatly executed before everything else (caching, PostConstruct annotations, services). This is the behavior I expected (in term or startup workflow)
Unfortunatly, I need to override the default FlywayAutoConfiguration because I use a custom Flyway implementation, but that's not the my main problem here, my issue is really related to Spring Priority for configuration and initialization sequence.
So do use my own flyway, I first copied FlywayAutoConfiguration to my maven module, name it CustomFlywayAutoConfiguration and just adapt imports. I also change the property "spring.flyway.enabled" to false and create another one "spring.flywaycustom.enabled" to be able to activate mine and not the default one.
Doing that fully change the sequence of startup. Now, flyway is executed at the end of the startup sequence (after caching and other #PostConstruct that are in my project)
The following bean defined in CustomFlywayAutoConfiguration is now created onyl at the end of the startup sequence. With the default FlywayAutoConfiguration , was well created on the beginning.
#Bean
#ConditionalOnMissingBean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
I tryied to play a lot with ordering (HIGHEST_PRECEDENCE, and LOWEST_PRECEDENCE)
#AutoConfigureOrder on configuration class
#Order on components
Try to Autowire FlywayMigrationInitializer to force the bean initialisation earlier
It's doesn't change anything, looks like Spring ignore #Order and #AutoConfigureOrder
Any idea why when Configuration is inside spring-boot-autoconfigure dependencies, it started as first priority, and when the same Configuration code is inside my project, I don't have the same ordering?
Thank you so much for your help.
Thanks to your answer, Focusin my attention FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor help me to solve the issue.
#Configuration(proxyBeanMethods = false)
#AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
#EnableConfigurationProperties({ DataSourceProperties.class, FlywayProperties.class })
#Import({ FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor.class }) // Very important to force flyway before
public class CustomFlywayConfiguration {
#Bean
public Flyway flyway(FlywayProperties properties, DataSourceProperties dataSourceProperties,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
#FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks) {
FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader());
DataSource dataSourceToMigrate = configureDataSource(configuration, properties, dataSourceProperties, flywayDataSource.getIfAvailable(), dataSource.getIfUnique());
checkLocationExists(dataSourceToMigrate, properties, resourceLoader);
configureProperties(configuration, properties);
List<Callback> orderedCallbacks = callbacks.orderedStream().collect(Collectors.toList());
configureCallbacks(configuration, orderedCallbacks);
fluentConfigurationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(configuration));
configureFlywayCallbacks(configuration, orderedCallbacks);
List<JavaMigration> migrations = javaMigrations.stream().collect(Collectors.toList());
configureJavaMigrations(configuration, migrations);
return new CustomFlyway(configuration);
}
/**
* FlywayAutoConfiguration.FlywayConfiguration is conditioned to the missing flyway bean. #Import annotation are not executed in this case.
*
* So if we declare our own Flyway bean, we also need to create this bean to trigger the flyway migration.
*
* The main issue is now that bean is create after the #PostConstruct init method in MyInitComponent.
*
*/
#Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
/**
* Thanks to that, it's now working, because make sure it's required before to create EntityManager
*/
// #ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
// #ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
static class FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor() {
super(FlywayMigrationInitializer.class);
}
}
...
So that confirm we can't easily override the Flyway bean without copy some logic from FlywayAutoConfiguration.
I created a small project to reproduce the bug, with the solution I found.
https://github.com/w3blogfr/flyway-issue-demo
I don't know if it's necessary to fix it or not in spring auto-configuration project?
I checked the history to find back the commit
https://github.com/spring-projects/spring-boot/commit/795303d6676c163af899e87364846d9763055cf8
And the ticket was this one.
https://github.com/spring-projects/spring-boot/issues/18382
The change looks technical and probably he missed that was an #ConditionalOnClass
None of the annotations that you've described have an impact on runtime semantics:
#AutoConfigureOrder works only on auto-configurations (so if you've copied the auto-configuration as a user config, it won't even be considered). This is used to order how auto-configurations are parsed (typical use case: make sure an auto-config contribute a bean definition of type X before another one check if a bean X is available).
#Order orders components of the same type. Doesn't have any effect as to "when" something occurs
Forcing initialisation using an injection point is a good idea but it'll only work if the component you're injecting it into is initialised itself at the right time.
The auto-configuration has a bunch of post-processors that create links between various components that use the DataSource and the FlywayMigrationInitializer. For instance, FlywayMigrationInitializerEntityManagerFactoryDependsOnPostProcessor makes sure that FlywayMigrationIntializer is a dependency of the entity manager so that the database is migrated before the EntityManager is made available to other components. That link is what's making sure Flyway is executed at the right time. I don't know why that's not working with your copy, a sample we can run ourselves shared on GitHub could help us figure out.
With all that said, please don't copy an auto-configuration in your own project. I'd advise to describe your use case in more details and open an issue so that we consider improving the auto-configuration.
We have converted an existing web app over to Spring Boot.
All I want to to do is be able to use #Profile("production"). We store that in myProps.properties in the resources folder.
I can access the .properties file all over the app just fine using #Value, but it seems in order to get the #Profile to work, you have to set the active profiles before the ServletContext is set/final which seems to happen during the main() method.
I've tried dozens of things and failed. I've found you can't set a static field with #Value in the main application class. I've tried making a class that implements WebApplicationInitializer like https://www.baeldung.com/spring-profiles but my onStartup() method that I override doesn't ever get run.. I put in a break point and it never gets hit. The only way I can get a break point to hit is if I #Autowire the servletContext on that method, but then the context is already set and cannot be altered.
I've tried AnnotationConfigWebApplicationContext, I've tried ServletContainerInitializer, I've tried ConfigurableEnvironment, etc. I feel like I'm going in circles.
I feel like there is a big piece I'm missing here in order to do things the "Spring way". Can anyone offer Java annotation-configured way for me to get a property and set the active profiles for using later in the product? Thanks.
Why do you need it before main? Why don't you just create multiple properties files and define the profile in the top-level property file?
(parent) application.properties
spring.profiles.active = development-application.properties
(profile) development-application.properties
my.value = foo
Based on your comments, I suggest something like the following as a solution:
Define your tasks as beans with #Components and annotate them with #Profile to divide them up by the environment you want them to be used in (I prefer setting profiles as environment variables).
#Component
#Profile("production")
public class TaskA implements Task {
public void doWork(){}
}
#Component
#Profile("staging")
public class TaskB implements Task {
public void doWork(){}
}
Marking these classes with #Component means Spring will manage them and they can be injected, perhaps into a #Component that executes the tasks:
#Component
public class TaskDoer {
private List<Task> tasksToDo;
#Inject
public TaskDoer(List<Task> tasksToDo) {
this.tasksToDo = tasksToDo;
}
}
When deploying your app, set the profile to use in the environment variables in whatever way is appropriate for your setup:
SPRING_PROFILES_ACTIVE = production, someOtherProfile
Now, when the application starts, Spring will see that the active profiles are "production" and "someOtherProfile" (multiple profiles can be set) and will not load any beans with an #Profile that aren't for that profile.
I'm trying to make a custom spring boot starter that will be used by multiple projects to authenticate with Azure AD. All the Azure AD config has been set up and an individual project hardcoded with all the settings to work with Azure AD works fine too. Now I'm trying to move these settings into a custom Spring Boot starter so that multiple projects can use it. It works for the most part, except for one thing: moving the bean config for a custom AADAppRoleStatelessAuthenticationFilter. If I leave my custom implementation (CustomAADAppRoleStatelessAuthFilter) hardcoded in the actual implementing project, everything works and only CustomAADAppRoleStatelessAuthFilter is created, but as soon as I move it into the custom starter, I only ever get AADAppRoleStatelessAuthenticationFilter instead.
Note that my CustomAADAppRoleStatelessAuthFilter extends the starter's
AADAppRoleStatelessAuthenticationFilter.
The autoconfig for AADAppRoleStatelessAuthenticationFilter in the azure-spring-boot project (https://github.com/microsoft/azure-spring-boot/blob/master/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java) is:
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter azureADStatelessAuthFilter(ResourceRetriever resourceRetriever) {
//bean details omitted
}
My custom autoconfig that should replace the above is as follows:
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
ResourceRetriever resourceRetriever) {
return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}
No amount of #AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) works.
Also, if I change the condition on my custom bean to be the subtype (#ConditionalOnMissingBean(CustomAADAppRoleStatelessAuthFilter.class)), BOTH types get created, and I can autowire my CustomAwareAADAppRoleStatelessAuthFilter and put it in my WebSecurityConfigurerAdapter, but things STILL won't work. I debugged things and found that the CustomAADAppRoleStatelessAuthFilter is the only bean of the ADAppRoleStatelessAuthenticationFilter type in my spring security filter chain, but that once the 'end of the additional filter chain' has completed and the 'original chain proceeds', I find that the ADAppRoleStatelessAuthenticationFilter has fired! And of course it throws an error because my CustomAADAppRoleStatelessAuthFilter has already done things to customize the UserPrincipal. I can't figure out where the ADAppRoleStatelessAuthenticationFilter is getting added to any filter chain, and even if I mark my CustomAADAppRoleStatelessAuthFilter bean with #Primary, the starter ADAppRoleStatelessAuthenticationFilter will still be used instead.
The only 'solutions' that have worked are to define the CustomAADAppRoleStatelessAuthFilter in the actual implementing project instead of the custom starter project, or to exclude the AADAuthenticationFilterAutoConfiguration in my actual implementing project's #SpringBootApplication annotation (Not even excluding it the property-based way works).
Is there a way to make AADAuthenticationFilterAutoConfigurations ADAppRoleStatelessAuthenticationFilter bean definition back off? 'Cause #AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) on my custom auto configuration class that has my CustomAADAppRoleStatelessAuthFilter definition doesn't work, and having all the implementing projects explicitly exclude AADAuthenticationFilterAutoConfiguration isn't the most ideal solution (although at least with that solution they don't all need to declare their own bean definition for CustomAADAppRoleStatelessAuthFilter).
Have you tried using #Order and assign your custom bean a higer precedence. By default all the beans gets lowest priority (Ordered.LOWEST_PRECEDENCE) losing to any other specified order value.
#Order(Ordered.LOWEST_PRECEDENCE - 1)
#Bean
#ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
#ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
ResourceRetriever resourceRetriever) {
return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}
Can you try putting the #Order(Ordered.LOWEST_PRECEDENCE - 1) like i mentioned above? Your bean should then take precedence over the other.
Have you tried adding #Primary to your bean?
I am trying to understand how beans that we make using #Configuration tends to override the beans that are generated by SpringBoot by default. I have been working on a project where in many cases we create beans for things like ZuulConfigs and the assumption is, whatever we are making shall take precedence over the default generated bean. I have been trying to figure this out but can't. Basically,
Is Spring achieving this via some custom class loader
If not how is this precedence working. Can I give some precedence in similar manner to my beans
Can I generate similar hierarchy in my project,if so how
The help is highly appreciated
Spring AutoConfiguration is used to provide a basic configuration if certain classes are in the classpath or not.
If you want to configure the order in which beans are instantiated by spring you can use
#DependsOn("A")
public class B {
...
}
This would create bean "A", then "B". Hence you can order the configuration depending upon the beans need first to be done. Anyways Spring automatically detects the dependencies by analyzing the bean classes.
for more help check this question
Spring Boot AutoConfiguration Order
Alternative :
There is also "#AutoConfigureOrder" annotation(where you can prioritise the configuration), you can have a look in the code for deeper understanding.
Documentation of AutoConfiguration is here
First of all, class loading and bean creation are two different things. We don't need to create a bean to load a class, however, a class has to be loaded in order to create a bean.
Now, coming back to Spring's example, Spring looks into all the packages configured by #componentScan and creates beans of all the classes annotated with #Bean, #Configuration and/or #Component. Spring's container keeps track of all the beans created and hence, when it encounters user defined bean with same name and class type as default bean, it replaces the original definition with user defined one (e.g. we can create our custom #ObjectMapper to override Spring boot's own instance). You can also use #Primary annotation to make you bean take precedence if another definition with same class exists (documentation here).
Below are the answers for your questions:
Spring uses reflection to load the classes and create instances. Although you can load the classes with your custom class loader (more on that here), you don't need to worry about it for #Configuration.
Yes, you can use #Primary annotation to give your bean a precedence. You can also use #Order(here) to define the creation order for your beans.
With #Primary, #Order and #Qualifier annotation you can define your own hierarchy for bean creation.
Can I give some precedence in similar manner to my beans
Yes.
A) To define a specific order your Configuration classes will be handled (by the way, a Configuration class does not have to be annotated with #Configuration (so-called full definition), but it's enough to be annotated with #Component, #ComponentScan, #Import, #ImportResource or just have a method annotated with #Bean - so-called lite definition), you should
1) add your Configuration Candidates to your SpringApplication's primarySource, for example, in your main method like that
SpringApplication.run(
new Class[]{YourSpringBootApplication.class, Config1.class, Config2.class, ...},
args);
2) and annotate each of your Configuration Candidates with #Order annotation, any other ordering means like Ordered interface, #DependsOn etc will be ignored by ConfigurationClassPostProcessor, the order in the primarySource array will also be ignored.
Then ConfigurationClassPostProcessor will sort your Configuration Candidates and handle them according the #Order annotation value you specified.
B) The precedence can also be achieved by defining your own AutoConfiguration classes. Although both Configuration and AutoConfiguration are handled by the same ConfigurationClassPostProcessor, they are essentially distinctive machineries. To do so
1) define in your classpath /META-INF/spring.factories file and put in the EnableAutoConfiguration section of it your AutoConfiguration classes like that
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
your.package.AutoConfig1,your.package.AutoConfig2
2) and annotate your AutoConfiguration classes with #AutoConfigureOrder, #AutoConfigureAfter, or #AutoConfigureAfter annotations, any other ordering means again will be ignored.
Like #Strelok pointed out, AutoConfiguration classes, your own and provided e.g. by spring-boot-autoconfigure library alike, will be added to the end of the list of Configuration Candidates.
Remember, however, that the order the Configuration Candidates will be handled by ConfigurationClassPostProcessor does not necessarily coincide with the order the beans defined by the Configuration classes will be created. For example, you might define your Configuration class that overrides TomcatServletWebServerFactory to make your own customization of Tomcat web server like
#Configuration
public class EmbeddedTomcatConfig {
#Bean
public TomcatServletWebServerFactory containerFactory() {
...
return customizedTomcatWebServerFactory;
}
but this method will be called right at the moment when your Spring Boot application decides to create a Web server, regardless of how you defined the precedence for your EmbeddedTomcatConfig Configuration class.
Is Spring achieving this via some custom class loader
There is no need to. Although you could, as always with Spring, define your own ClassLoader for BeanFactory, standard ClassLoader is good enough if everything you need for Configuration in your application is available in the classpath. Please notice, that at first phase ConfigurationClassPostProcessor does not load (i.e. does not resolve) the Configuration candidates classes (otherwise, most of the classes in spring-boot-autoconfigure library will fail to load). Instead it analyzes their annotations with bytecode analyzer, ASM by default. For that purpose, it is just enough to get a binary form, a byte array, of a class to feed it to bytecode analyzer.
Just know this: Spring Boot (specifically) auto configuration classes are always configured last. After all user beans have been created. Spring Boot auto configuration classes almost always use the #ConditionalXXXX annotations to make sure that any beans of the same type/name and other conditions that are configured in your application will take precedence over the Spring Boot auto-configured beans.
If you want your #Component to take precedence over other #Component while scanning all the components by spring, use #Order(Ordered.LOWEST_PRECEDENCE) i.e. the max value to load your component over other.
#Primary is used to give your bean a default preference, we can override the default preference using #Qualifier
We use #Configuration classes with lots of #Bean annotated methods that essentially read like this:
#Bean
public TeamContactIndexer teamContactIndexer(GroupService groupService, ContactCrudService contactCrudService, ContactRetrieveService contactRetrieveService) {
return new TeamContactIndexer(groupService, contactCrudService, contactRetrieveService);
}
So this returns a new bean and injects other spring declared things via method arguments into the constructor by name.
The only way I know to reduce verbosity is to annotate the beans with #Component and constructor with #Autowired.
For many this is perfectly acceptable but I prefer to not litter my code with Spring annotations just to facilitate plumbing and keep a clean separation between completely spring free business logic and plumbing code in #Configuration annotated classes. I treat them as a more type safe, less verbose replacement for what we used to do in xml.
However, wouldn't it be nice if I could just go ...
#Bean
public TeamContactIndexer teamContactIndexer;
... and have spring figure out that it needs to autowire the constructor of that class (100% spring free) to produce the bean. This is not currently supported in Spring as far as I can see even though it should be quite easy to do as far as I can see. Am I missing something or is there really no way around me micromanaging constructor calls in my #Configuration classes (other than littering my code with annotations)? The vast majority of my #Bean methods should be easily replaced like this.
UPDATE
#bezmax has provided a workable approach here which is to use a component scan annotation.
#Configuration
#ComponentScan(
basePackages={"com.github.jsonj.tools"},
includeFilters = { #Filter(type = FilterType.ASSIGNABLE_TYPE, value = {JsonParser.class})})
public class JsonParserConfig {
}
I've used the above to provide a bean definition for a bean without annotations in a library that I use. This replaces the #Bean annotated factory method I had earlier. It's still somewhat verbose but at least you can put in a comma separated list of classes. The default for type is wrong for this usecase so you must specify it; likewise the package defintion is required even though it could be deduced from the class on the filter.
IMHO there is room for an obvious improvement in Spring, which is to provide an annotation that simply takes a comma separated list of classes. So, instead of scanning a package, simply list the bean classes you want initialized. There are probably still a few hairy issues with autowiring via the constructor.
This feature is implemented in Spring 4.3 (not yet released).
You can read more about that in the changelog (see 6.1)
Added:
As about registering your unannotated classes automatically, there seems to be a flexible way to achieve this using #ComponentScan annotation. This annotation allows you to specify a set of include filters, which, when matched on classes, are automatically registered as beans. I had not actually tried using more complex rules with this filter, and it seems that you have several options there (check out the documentation on #ComponentScan) but the easiest one would be something like this:
#Configuration
#ComponentScan(
value = "some.package.path",
includeFilters = {
#Filter(type = ASSIGNABLE_TYPE, value = {
MyClass1.class,
MyClass2.class,
MyClass3.class
})
})
public class WebConfig extends WebMvcConfigurerAdapter {
...