Conflict with config class in dependency (Spring AsyncConfig) - spring

My team uses some starter code that's included in every internal Spring project by default. This is included as a dependency (not parent) in the pom.xml file of my project. This starter code contains a default implementation of a configuration interface which Spring does not allow duplicates of (AsyncConfigurer), but I need to create my own custom implementation. I am not sure how to resolve this. Is there a way for me to exclude this configuration class but keep the rest of the dependency? Or can I somehow keep the given config class, but modify its properties?
The specific exception I get is: java.lang.IllegalStateException: Only one AsyncConfigurer may exist
//Thank you

You can define the AsyncConfigurer on parameter excludes from #SpringBootApplication. Example:
#SpringBootApplication(exclude=AsyncConfigurer.class)
Notice that this will excludes the entire #Configuration .
Another solution is to use bean override, to define only the beans you need to replace.
Add this to your application.properties (or yaml):
spring.main.allow-bean-definition-overriding=true
And you will be able to override it.

Related

Spring Boot - Load bean only if it is enabled by a property

I have a Spring Boot application with different submodules which also contains spring components.
And in the main web modules I use 70% of the beans from the submodules. It depends on the application.yml properties, if the property group (which points to a bean) is enabled or not.
First I wanted to create Aspect-s, so when a method of a bean (which is not enabled by it's property) is called, then throw an exception. This solution could work, but then I would need to create Aspect classes, method annotations, import more and more dependencies.
So I am just wondering, would be there any other easier solution to disable a bean, or do not load at all to the spring boot container?
I would imagine something like #DependsOn, but for this you need to give a name of a bean name, but you cannot use this annotation to work with yml property.
Other easy solution is to #Bean or #Import every bean I want to managed by spring container, instead of #Import everything once from submodules, but then it is a static setting, cannot be overwrite by a single property from yml.
Spring introduced the concept of conditionals quite some time ago. Spring Boot uses this to a great extend to conditionally enable features. It even created a lot of conditional rules which you can use.
One of those rules is the conditional on a property rule. To use this rule add an #ConditionalOnProperty annotation to your bean. Now it will only be included if said property is enabled or has the specific value.
#ConditionalOnProperty(name="your.property.name")

Wiring a bean from dependency module

I have created a configuration project which essentially creates couple of beans with configuration stereotype. Then, I want this project to be reused across by my clients.
I have added this config project as a maven dependency, but my client project is not having those beans i have created as part of configuration project.
Could someone help
Ok, the answer is the following: you should place
#ComponentScan("you.configurations.base.package")
on one of your configuration (in the current application, one that #SpringBootApplication sees) or on the class with #SpringBootApplication annotation.
The explanation is as follows: #SpringBootApplication under the hood contains #ComponentScan without specifying a base package. That means that it says to Spring to scan the package where the class annotated with #SpringBootApplication resides and all the packages recursively. And that's it. If you place you #Configuration somewhere there - it will create it during startup, otherwise not.
We can resolve this by enabling spring-boot autoconfiguration
Create classpath->resources->META-INF->spring.factories file
org.springframework.boot.autoconfigure.EnableAutoConfiguration=[add your class with you need to be loaded during application load time]

Import by name in Spring Java configs

Say I have 3 Spring/Maven projects:
api-spec: Contains interface MyService.
api-impl: Contains class MyServiceImpl which implements MyService. Also contains class MyServiceConfiguration which is a Spring #Configuration, that defines a bean of type MyServiceImpl.
main: Contains a Spring application setup with Spring JavaConfig (e.g. a #SpringBootApplication). It has a bean with an #Autowired MyService myService field, which works as its configuration class is annotated with #Import(MyServiceConfiguration.class).
I would like the main-project to have api-spec as a Maven compile dependency and to have api-impl as a runtime dependency (to prevent us from making "hard" dependencies from the main project to the api-impl project by mistake). This is not possible, because #Import takes an array of Classes - e.g.: #Import(MyServiceConfiguration.class). I would like something like #Import("my.package.MyServiceConfiguration") instead.
Using class path scanning is not an option (we have seen too many beans getting picked up by accident), and I would prefer not having to use XML files. We could use SpringApplicationBuilder.source(..) as it accepts a class name as a String - but I can't find a way to use that in my tests...
Compile time check is one of the advantages of java config, so I don't think that it's possible to do such thinks with Java. As for me you should use XML to handle this. It doesn't mean that you should do all your configuration in XML, most of the beans of your api-impl module can be in Java and just imported to XML where will be only beans that you are going to change in runtime.
If you don't want to use XML maybe you should consider to use Groovy config instead:
https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4

How to add properties programmatically(just like adding key-value into application.properties)?

I have some common properties that every projects should set, such as
feign.hystrix.enabled=false
feign.httpclient.enabled=true
I don't want to repeatedly add these props in every project so I'm going to create an extra jar file containing #Configruation class. How to add properties in #Configuration class? Thanks!
PropertySources
You may load an application.properties from another jar this way:
#PropertySources({
#PropertySource("classpath:common.properties")
})
#Configuration
public class SomeJavaConfig {
}
You can find the reference in Spring's documentation:
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order:
...
#PropertySource annotations on your #Configuration classes.
Spring-cloud-config
I won't go in all the details, but another option is to use spring-cloud-config to define these properties in a git (using spring-cloud-config-server). Then, have your spring-boot application load the application.properties using spring-cloud-config-client directly from git.
Check this:
https://cloud.spring.io/spring-cloud-config/spring-cloud-config.html
https://spring.io/guides/gs/centralized-configuration/

Annotating Groovy beans to load within Spring

Our application uses #Bean to define create beans and load them into the Spring context.
We now need to externalize these, so as to enable the application to be configured without touching the java source code.
We wish to replace the #Bean's with Groovy classes.
Is there a way to annotate a Groovy bean so that it will be picked up by Spring?
Note that we cannot simply reference each Groovy bean in the Spring XML, as we need to add and modify beans without touching the Spring code.
Thanks very much.
Use Spring config inheritance.
Move all shared code in a common "base" project that each individual / specific project depends on. Use Maven for this.
Create a common / base Spring config and put that into the "base" project. This config doesn't contain a definition for ProcessDefinition
In the specific project, create one bean which inherits from ProcessDefinition. Create a Spring config which imports the base config and define the single specific bean in it.

Resources