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?
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 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.
My problem references to this issue here on github https://github.com/mapstruct/mapstruct/issues/1427
I got at least two versions of mappers with the same name. I want to use springs getBean/Autowired possibilities but this doesn't work out of the mapstructs box yet. :-)
I followed the second workaround mentioned in the upper link: extend Springs bean naming strategy. Did someone ever get this well ment proposal working?
If i follow the code parts from there the bean naming doesn't take place. For me its's clear why not: there aren't any components to scan and especially to find.
If i add a componenModel = "spring" to the mapper annotation i get a ConflictingBeanDefinitionException. Don't know why. Maybe there's a cat in the tail problem?
As stated from Filip here https://github.com/mapstruct/mapstruct/issues/1427 i followed his approach and with a few modifications it worked. I added a solution comment in the link.
The main changes are:
i added componentModel = "spring" to my mappers and used a filter to exclude all of my mapper classes (the interface all of my mappers are implementing: MapperInterface.class) within the Spring Boot application.
To my Spring Boot application class i added:
#ComponentScan(basePackages = { "com.application.spring_boot_class" }, excludeFilters = { #ComponentScan.Filter(value = { MapperInterface.class }, type = FilterType.ASSIGNABLE_TYPE) })
I had this issue before, and I resolved it using the Spring bean definition in a configuration class, a class annotated with #Configuration, with the Mapstruct mapper call like below:
#Bean
public IMyMapper offerWebMapper() {
return Mappers.getMapper(IMyMapper.class);
}
And then you can inject the mapper using #Autowired or getBean.
I'm writing a library that uses spring, for others to use. In my library, I have class A that has an interface B as a field (with #Autowired).
I have a default implementation of that field, but the user can implement a custom implementation by himself. What I want to happen is the following:
If the user implemented B, I want that bean to be injected to A, otherwise I want my default implementation to be injected.
Something like the opposite of #Primary
I know that the user can add the #Primary annotation in order for that to happen, but I don't want him to add any other annotation besides #Component (because it is not clear for the user why he must add the #Primary annotation)
Is there a way to achieve this behavior? I've also tried #Order and #Priority, but no luck - the user must add another annotation.
Thanks!
You should create your own auto configuration. Auto configuration classes are always processed last so user's own configuration is processed first and when using #Conditional... annotations user's beans will take precedence.
Your auto configuration class should look like this:
#Configuration
public class MyAutoConfiguration {
#ConditionalOnMissingBean(B.class)
#Bean
public B defaultImplementation() { return A(); }
#Bean
public UsesB classThatUsesB(B b) { return UsesB(b); }
}
In this case if the user of your library defines a bean of type B it will always be used first and if they don't the bean created by the defaultImplementation method will be used.
I don't believe so. Spring's #Autowired is rather specific. Making it perform differently without any configuration changes (either to XML or the Spring configuration class) is pretty much impossible.
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 {
...