How to disable "exclusion" in `spring.autoconfigure.exclude` property list - java

Suppose I have this in my application.yml file:
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
- ..... <other exclusions>
Then, in application-redis.yml profile I want to re-enable this auto-configuration.
What I could do apart from copying the whole list without just one entry?
Please don't suggest the solutions based on code modification (like specific annotations etc. - I need to do it with properties only). Also, I cannot make this auto-configuration enabled initially and add some kind of the "default" profile where it's disabled.

Profile/Import approach
Create a (custom, auto-scanned, possibly empty) #Configuration.
Annotate it with #Profile("redis").
Annotate it additionally with #Import(RedisAutoConfiguration.class).
Done! Looks like:
package com.my.package;
import org.springframework.context.annotation.*;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
#Configuration
#Profile("redis")
#Import(RedisAutoConfiguration.class)
// config more here...
class MyRedisConfig {
// ..or here
}
...this will re-import the application.yaml-excludes (for RedicAutoConfig class only), when activating "redis" profile.
Not sure whether this is "quicker", than the copy&paste approach, but sounds exactly what, configuration+import+profile were "designed for"!;)
If a #Configuration class is marked with #Profile, all of the #Bean methods and #Import annotations associated with that class will be bypassed unless one or more of the specified profiles are active
Spring Boot Profiles
Configuration javadoc
Import javadoc
Profile javadoc

Related

Spring AutoConfiguration manul import all components

I have the following autoconfigure class.
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#Import({LoggerController.class, LogService.class, LogConfig.class})
public class ProviderAutoConfiguration {
}
I need to import all the components, services, configurations using #import annotation, otherwise for example, when I remove LogService.class (LogController autowires LogService) from #Import parameters, it throws the following error
Consider defining a bean of type 'com.log.LogService' in your configuration.
Do I really have to include all of them manuelly as shown above, or is there any other way to automatically detect classes annotated Service, Component or Configuration?
!This is starter library, not a executable app!
The problem with your "library" is that it already needs a lot of moving parts, like Spring Data JPA, properly setup JPA. If that isn't available it will fail, or if you add #EnableJpaRepositories and/or things like #ComponentScan and #EntityScan it will (quite severely) interfere with the (auto)configuration of Spring Boot or the clients. Which is what you want to prevent.
Instead of all those annotations what you need to do is
Conditionally enable #EnableJpaRepositories or just include the dependency and let the auto-configuration kick in (I suggest the latter)
Don't add #ComponentScan, #EntityScan etc. instead use #AutoConfigurationPackage which is designed for starters.
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#AutoConfigurationPackage
public class ProviderAutoConfiguration {
}
NOTE: This assumes that the ProviderAutoConfiguration is in the same or a higher package then your other classes. If not specify the basePackages in the #AutoConfigurationPackage.
You can use #ComponentScan to specify the packages you want to scan to find configurations and/or components to load.
Assuming you are using Spring Boot, at least so it seems according to your tags, you should take a look at #SpringBootApplication.
#SpringBootApplication encapsulates #Configuration, #EnableAutoConfiguration, and #ComponentScan annotations with their default attributes. The default value for #ComponentScan means that all the sub packages on the package the #ComponentScan is used are scanned. That is why it is usually a good practice to include the main class in the base package of the project.
If your components, services, configurations are under com.log as direct class or in sub-package, you can use #ComponentScan(basePackages="com.log.*")
#Configuration
#ConditionalOnWebApplication
#EnableConfigurationProperties({LoggerProviderProperties.class})
#ComponentScan(basePackages="com.log.*")
public class ProviderAutoConfiguration {
}

Spring configuration class from a specific properties file

Suppose I have a #Configuration class or better yet a #ConfigurationProperties class.
I'd like the configuration to be loaded from a specific application-x.properties file.
How can I do that? (without defining a new Spring profile)
You have to use #PropertySources, ex: #PropertySource("classpath:foo.properties").
Check this to have more insight and see the other options.
https://www.baeldung.com/properties-with-spring

Order of Configuration in SpringBoot

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

Defining beans per multiple spring profiles

I have 3 profiles in my app called: dev, test, prod.
I want to use spring profiles to customize bean injection so that for profile dev and test I will have one bean implementation and for profile prod I will have another.
The question is how to achieve that. How I can setup one bean to be active within two different profiles.
I tried something like this:
#Component
#Profile("dev, test")
class DevTestBean{}
but unfortunatelly spring sees it as a single profile called dev comma space test.
You have to change to #Profile({"dev", "test"})
The value must be declared as Set.
See the documentation
If a #Configuration class is marked with #Profile, all of the #Bean
methods and #Import annotations associated with that class will be
bypassed unless one or more of the specified profiles are active. This
is analogous to the behavior in Spring XML: if the profile attribute
of the beans element is supplied e.g., , the
beans element will not be parsed unless at least profile 'p1' or 'p2'
has been activated. Likewise, if a #Component or #Configuration class
is marked with #Profile({"p1", "p2"}), that class will not be
registered or processed unless at least profile 'p1' or 'p2' has been
activated.
XML solution has not been placed in official documentation:
https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/htmlsingle/#beans-definition-profiles
so for the record I will put it here:
<beans profile="dev,foo,bar">
<!-- (...) -->
</beans>

Load properties file in Spring depending on profile

I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.

Categories

Resources