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.
Related
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.
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?
So, I have an application running on WildFly10, which uses JSF, Spring (DI), JPA, Spring Data;
Right now we're trying to move it to CDI and remove Spring(DI). For now we'll keep Spring Data.
So, I set up CDI and made an EntityManager producer.
#Produces
#Dependent
#PersistenceContext
public EntityManager entityManager;
So, I'm able to inject repositories with CDI and all.
However on my original environment we had a custom repository factory,that was defined in my SpringConfiguration like this:
#EnableJpaRepositories(basePackages = {"com.foo.repository" }, repositoryFactoryBeanClass=CustomJpaRepositoryFactoryBean.class)
So, the question is, how can I define this repositoryFactoryBeanClass=CustomJpaRepositoryFactoryBean.class on a CDI environment?
The current implementation doesn't allow the configuration of the JpaRepositoryFactoryBean as one can see from the code where it gets instantiated.
So I guess you have the following options:
reimplement the instantiation process.
open a feature request.
do 2. and 1. in a reusable way and submit the result as a PR for the issue.
When trying to solve this problem I found that the custom impl was not being picked up. The solution suggested in this question helped me however: https://stackoverflow.com/a/38541669/188624
Basically is uses Java8 default interface method to provide the additional functionality. I had to use the "CDI.current().select" method to get hold of the entity manager though as property injection of course won't work.
I tested with Spring Data JPA 2.0.0
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
Spring Boot 1.4 offers some fantastic testing improvements. One is the #DataJpaTest annotation where it wires up just the parts needed for JPA testing. What would the equivalent look like for just wiring up the parts needed for JdbcTemplate tests?
I'm fine constructing my own composite annotation that mimics the #DataJpaTest one.
Good question. Ironically enough, that one was raised during the testing talk yesterday at SpringOne Platform. Let's see what it takes to implement such dedicated test annotation.
TL;DR check the code on github
First of all you need to create the annotation. This annotation reuses some bits from the spring-boot-test-autoconfigure module. You may want to auto-configure an in-memory database (like DataJpaTest does). You also want to make sure that caching is configured and disabled by default (in case you have #EnableCaching on your Spring Boot application). You also want that all your tests are #Transactional by default so you should add that.
Next, you want that slicing effectively kicks in. All you need at this point is a DataSource, a JdbcTemplate, database migrations (flyway/liquibase) and a transaction manager to process #Transactional. To avoid the other auto-configurations to kick in you should add the following:
#OverrideAutoConfiguration(enabled = false)
Then, you want to explicitly enable the auto-configurations above. In order to do so, you add #ImportAutoConfiguration and you add the following content in META-INF/spring.factories
# AutoConfigureDataJpa auto-configuration imports
com.example.test.autoconfigure.jdbc.DataJdbcTest=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
The key in spring.factories should match the FQN of your annotation. Whenever Spring Boot finds #ImportAutoConfiguration with no extra attributes, it will look for a key matching the annotation type in spring.factories.
Next up you want to be able to include additional components (component scan) with a filter. In order to do that, you can add #TypeExcludeFilters(DataJdbcTypeExcludeFilter.class) where DataJdbcTypeExcludeFilter is pretty much the same thing as DataJpaTypeExcludeFilter (so we might want to extract a common class for that).
Once you've done that, you only need to add your annotation and your JdbcTemplate is auto-configured for you
#RunWith(SpringRunner.class)
#DataJdbcTest
public class DataJdbcSampleTests {
#Autowired
private JdbcTemplate jdbcTemplate;
...
}
I think the option will be #JdbcTest, you could found further info on doc.