Spring Boot: use autowired constructor with class from configuration file - spring-boot

I have a Spring Boot 2.3 application with a controller:
#RestController
public class StatusController {
private final ServerStatusCheck serverStatusCheck;
private final ServerStatusMapper serverStatusMapper;
#Autowired
public StatusController(AService aService, ServerStatusMapper serverStatusMapper) {
this.serverStatusCheck = aService;
this.serverStatusMapper = serverStatusMapper;
}
// (...)
}
The class AService implements the interface ServerStatusCheck. There is also a BService class, also implementing ServerStatusCheck interface.
What I need to do: the injected AService object should be configurable in a configuration file, so that the service injected is either "AService" or "BService", depending on the configuration file values. What is the best way to achieve this using Spring Boot? If possible, I would like to keep the constructor-based autowiring (instead of field-based autowiring).

You can create the different beans in a configuration class with condition like https://reflectoring.io/spring-boot-conditionals/
#Configuration
public class ServiceConfiguration {
#ConditionalOnProperty(value="service.a.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckA() {
return new AService();
}
#ConditionalOnMissingBean
#ConditionalOnProperty(value="service.b.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckB() {
return new BService();
}
}
and then wire the bean into the constructor

Related

where does spring boot set proxy as CGLib [duplicate]

Recently i found spring documentation page that says:
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used.
But it doesn't seem to be the case in my application. I wanted to write a small benchmark to compare the performance of both types of proxying.
There are two similar classes. Both have one method annotated with the #Transactional annotation. One class implements the interface and the other does not:
#Service
public class Cglib {
#Transactional
public void method() {}
}
public interface Dynamic {
void method();
}
#Service
public class DynamicImpl implements Dynamic {
#Override
#Transactional
public void method() {}
}
And based on the documentation for the first class, a CGLIB proxy should be created, and for the second, a JDK dynamic proxy.
But in my case CGLIB was used for both classes:
#Component
#RequiredArgsConstructor
public class Runner implements ApplicationRunner {
private final Cglib cglib;
private final Dynamic dynamic;
#Override
public void run(ApplicationArguments args) {
System.out.println(cglib.getClass());
System.out.println(dynamic.getClass());
}
}
class com.example.demo.proxy.cglib.Cglib$$EnhancerBySpringCGLIB$$767ff22
class com.example.demo.proxy.dynamic.DynamicImpl$$EnhancerBySpringCGLIB$$20a564d6
There are no additional configurations in the application. Only #SpringBootApplication class generated via spring initializr
Am I doing something wrong? The code was run on Spring Boot 2.7.2 and JDK 17.
That is due to spring-boot autoconfiguation:
#Configuration(proxyBeanMethods = false)
#ConditionalOnBean(TransactionManager.class)
#ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
#Configuration(proxyBeanMethods = false)
#EnableTransactionManagement(proxyTargetClass = false)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
public static class JdkDynamicAutoProxyConfiguration {
}
#Configuration(proxyBeanMethods = false)
#EnableTransactionManagement(proxyTargetClass = true)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
it turns #EnableTransactionManagement(proxyTargetClass = true) (Indicate whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false)) when the property spring.aop.proxy-target-class is not set (matchIfMissing = true)

What is the best way to select bean implementation from application.yaml

I have a spring boot application in which I want to Autowire a bean for which implementation is specified in application.yaml. What is the best way to achieve it?
#Component
public class FooFormatter implements Formatter {}
#Component
public class BarFormatter implements Formatter {}
public class MyService {
#Autowired
#Qualifier("value_from_config")// The implementation is specified in application.yaml file
private Formatter formatter;
}
The best way to achieve it is to use #ConditionalOnProperty.
So given the followings :
#Component
#ConditionalOnProperty(prefix = "app.formatter", name = "impl", havingValue = "foo",matchIfMissing = true)
public class FooFormatter implements Formatter {
}
#Component
#ConditionalOnProperty(prefix = "app.formatter", name = "impl", havingValue = "bar")
public class BarFormatter implements Formatter {
}
Then to enable FooFormatter only , configure the application properties as :
app.formatter.impl=foo
To enable BarFormatter only , configure the application properties as :
app.formatter.impl=bar
If no app.formatter.impl is defined in application properties , it will default to FooFormatter (because of the matchIfMissing = true)

Instantiate a service according to a property on Spring Boot

A good practice is defining a service as an interface and its implementation on a class.
Assuming I have 2 classes which implement the same interface, and I'd like to differentiate them according a property (not to a profile). I mean, if I have #Autowire private MyServiceInterface myService; I'd like to receive an instance of PotatoServiceImpl if I have myproperty=potato or an instance of TomatoServiceImpl if I have myproperty=tomato.
I'm not using profiles.
P.S. When I say a property,I mean a property in application.properties
Look:
public interface MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "potato", matchIfMissing = false)
public class MyPotatoImpl implements MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "tomato", matchIfMissing = false)
public class MyTomatoImpl implements Myinterface {
}
#Component
public class Consumer {
#Autowire
private MyInterface tomatoOrPotato; //depending on property myproperty value
}
This is for me a very elegant solution to implement the strategy creational design pattern spring styled.
Look here for docs about #ConditionalOnProperty annotation.

Spring bean with same method name but different qualifier fail to load

I have two Spring Configuration classes defined as follows
#Configuration
public class ClsA {
#Bean
#Qualifier("ClasA")
public String getSomething(){
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean
#Qualifier("ClsB")
public String getSomething(){
return "somethingB";
}
}
Both have the same method name. Even though qualifiers are different, the application doesn't load as it only injects one and wherever the other one is injected, if fails with noBeanDefinition exception let's say for ClsB bean qualifier.
When I keep the method name different and everything loads hunky dory.
Is this behavior normal ? Why doesn't spring load these beans just fine as they have different qualifiers ?
The #Qualifier annotation is supposed to be used at injection points to resolve ambiguity as to which bean to inject. But in the example, you use it at bean declaration site. At declaration site, you can give a name to each bean by specifying it in #Bean annotation. So if you leave your methods with the same name, then a valid example can be as follows:
#Configuration
public class ClsA {
#Bean("ClasA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("ClasB")
public String getSomething() {
return "somethingB";
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ClsB.class, ClsA.class})
public class ClsTest {
#Autowired
#Qualifier("ClasA") //this is the place where #Qualifier can be used
String smthA;
#Autowired
#Qualifier("ClasB")
String smthB;
#Test
public void test() {
System.out.println(smthA);
System.out.println(smthB);
}
}
Output:
somethingA
somethingB
1. Customize Bean Naming
Each bean name must be unique.
1.3.1. Naming Beans
1.12.3. Using the #Bean Annotation > Customizing Bean Naming
#Configuration
public class ClsA {
#Bean("clasAText")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
public String getSomething() {
return "somethingB";
}
}
2. (Optional) Add qualifier metadata
1.10.5. Defining Bean Metadata within Components
#Configuration
public class ClsA {
#Bean("clasAText")
#Qualifier("clsA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
#Qualifier("clsB")
public String getSomething() {
return "somethingB";
}
}
3-1. Inject by name
1.9.7. Injection with #Resource
#Component
public class MyComponent {
#Resource(name = "clasAText")
private String text;
// ...
}
Note:
if you intend to express annotation-driven injection by name, do not primarily use #Autowired (snip). Instead, use the JSR-250 #Resource annotation
3-2. Inject by qualifier
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
If qualifiers have been added, these are available.
#Component
public class MyComponent {
#Autowired
#Qualifier("clsA")
private String text;
// ...
}
This answer is a fallback behavior. Name and qualifier are different one.
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
For a fallback match, the bean name is considered a default qualifier value.

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Resources