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)
Related
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)
Is it possible to achieve the following?
I want to determine the type of a class to be used at runtime with Spring Boot. Hence, I defined an interface MyInterface defining the required methods:
public interface MyInterface<C extends DocumentContext> {
MyResult<C> doForX(C documentContext);
MyResult<C> doForY(C documentContext);
}
An abstract base class implements this interface and provide some common methods:
#Slf4j
#Component
public abstract class BaseClass<C extends DocumentContext> implements MyInterface<C> {
private MyInterface myInterface;
...
public MyResult<C> doForX(C documentContext) { ... }
public Myresult<C> doForY(C documentContext) { ... }
}
This base class is the parent for two subclasses:
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "REGEX", matchIfMissing = false)
public class FirstClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "SAX", matchIfMissing = false)
public class SecondClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
A class using one of the two concrete subclasses:
public class MyUsingClass extends AnotherClass {
public MyUsingclass(MyInterface<ADocumentContext> myInterfaceImpl, ...)
...
}
This compiles but when I try to run the application I get:
*************************** APPLICATION FAILED TO START
Description:
Parameter 1 of constructor in MyUsingClass required a bean of type
'MyInterface' that could not be found.
Action:
Consider defining a bean of type 'MyInterface' in your configuration.
In my application.properties I have:
# ... [SAX | REGEX]
#sanitizerType=REGEX
...and I have a Properties class:
#Value("${sanitizerType:REGEX}")
private SanitizerType sanitizerType;
, whereby SanitizerType is just:
public enum SanitizerType {
REGEX, SAX
}
You said you would like to "determine the type of a class to be used at runtime with Spring Boot".
I'm not sure if you want your "class to be used" change while your application is running depending on current condidionts or set once when the application boots and use it always until the app is running.
If you uncomment this line of your application.properties #sanitizerType=REGEX
Your code should work - but it simply defines the SanitizerType (hence your MyInterface implementation) once when you start your application.
Please note that by setting a private field of Properties class you don't set the property globally (at least not the way you showed in the question), but only set the private field value either according what's set in your properties file or (if value is missing in properties) to "REGEX".
If you want to use it in different places of your program you need to inject it differently
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
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.
Let's say I have #Configuration class which registers bean of type RestClient conditionally using #ConditionalOnProperty.
#Configuration
public class RestClientConfig {
#Bean("restClient")
#ConditionalOnProperty(prefix = "rest.client", name = "enabled", havingValue = "false", matchIfMissing = true)
public RestClient restClient(RestProperties properties) {
return new HttpRestClient(...);
}
#Bean("restClient")
#ConditionalOnProperty(prefix = "rest.client", name = "enabled", havingValue = "true")
public RestClient mockRestClient(RestProperties properties) {
return new MockRestClient();
}
}
When I run this application, everything works. Implementation of given type is chosen correctly when I autowire RestClient in another bean.
However, when I view this setup in Intellij IDEA, it reports:
Is there a way to instruct Intellij to know about #ConditionalOnProperty or do it in different way in Spring Boot?