I'm learning the bean AOP with Annotation and xml config but Spring does not create the beans.
Interface
package com.spring.studying.SpringAop.FrameworkService.AspectJAnnotation;
public interface Artist {
void perform();
}
Guitar Class
package com.spring.studying.SpringAop.FrameworkService.AspectJAnnotation;
import org.springframework.stereotype.Component;
#Component("grammyGuitarist")
public class GrammyGuitarist implements Artist{
#Override
public void perform() {
System.out.println("Start to play guitar");
}
public void playGuitar(String brand)
{
System.out.println("Get Guitar "+brand);
}
public void rest()
{
System.out.println("zzZZ!!!!");
}
}
Documentarist
package com.spring.studying.SpringAop.FrameworkService.AspectJAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component(value="documentaries")
public class Documentarist {
private GrammyGuitarist guitarist;
public void executed()
{
guitarist.playGuitar("LakeWood");
guitarist.perform();
guitarist.rest();
}
#Autowired
public void setGuitarist(GrammyGuitarist guitarist) {
this.guitarist = guitarist;
}
}
Advice Class
package com.spring.studying.SpringAop.FrameworkService.AspectJAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Component("annotatedAdvice")
#Aspect
public class AnnotatedAdvice {
#Pointcut("execution(* rest*(..)) && args(brand)")
public void restExecution(String brand)
{
}
#Pointcut("bean(grammyGuitarist)")
public void isGrammyGuitarist()
{
}
#Before("restExecution(brand) && isGrammyGuitarist")
public void simpleBeforeAdvice(JoinPoint joinPoint , String brand)
{
System.out.println("Before Advice processing");
System.out.println("Playing guitar :" + brand);
System.out.println("Executing :"+ joinPoint.getSignature().getDeclaringTypeName() +" "+joinPoint.getSignature().getName());
}
}
Demo Class
package com.spring.studying.SpringAop.FrameworkService.AspectJAnnotation;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.util.Arrays;
public class AspectJAnnotationDemo {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("aop-aspectj-annotation-style.xml");
ctx.refresh();
Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
Documentarist documentarist = ctx.getBean("documentaries",Documentarist.class);
documentarist.executed();
}
}
My Xml Config File
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:annotation-config/>
<context:component-scan base-package="com.spring.studying.SpringAop"/>
<aop:aspectj-autoproxy/>
</beans>
None of my beans were created just 6 default beans from spring.
I have try to create bean with tag and it worked fine . Just don't know why my code did not work with the annotation.
restExecution #Pointcut is wrong, because rest method of GrammyGuitarist doesn't take any arguments. playGuitar method takes brand argument, so you need to change your pointcut a bit:
#Component("annotatedAdvice")
#Aspect
public class AnnotatedAdvice {
#Pointcut("execution(* play*(..)) && args(brand)")
public void playExecution(String brand) {
}
#Pointcut("bean(grammyGuitarist)")
public void isGrammyGuitarist() {
}
#Before("playExecution(brand) && isGrammyGuitarist()")
public void simpleBeforeAdvice(JoinPoint joinPoint, String brand) {
System.out.println("Before Advice processing");
System.out.println("Playing guitar :" + brand);
System.out.println("Executing :" + joinPoint.getSignature().getDeclaringTypeName() + " " + joinPoint.getSignature().getName());
}
}
When you do this, aspects will indeed be created, but you will start getting exceptions:
Bean named 'grammyGuitarist' is expected to be of type 'com.example.demo.aspect.GrammyGuitarist' but was actually of type 'jdk.proxy2.$Proxy14'
This is because GrammyGuitarist implements an interface, and in this case Spring AOP uses JDK dynamic proxies
The simplest solution would be to force the use of a CGLIB proxy. To do this, you need to add an annotation above the aspect:
#EnableAspectJAutoProxy(proxyTargetClass = true)
After that, the program will give the expected result:
annotatedAdvice
documentaries
grammyGuitarist
org.springframework.aop.config.internalAutoProxyCreator
Before Advice processing
Playing guitar :LakeWood
Executing :com.example.demo.aspect.GrammyGuitarist playGuitar
Get Guitar LakeWood
Start to play guitar
zzZZ!!!!
In Spring Boot, CGLIB enforcement is enabled by default. Check this question for better understanding
Related
I have following class
import com.ujjwal.model.Speaker;
import com.ujjwal.repository.SpeakerRepository;
import javax.annotation.PostConstruct;
import java.util.List;
public class SpeakerServiceImpl implements SpeakerService {
private SpeakerRepository repository;
public SpeakerServiceImpl() {
System.out.println("inside No Args Constructor");
}
public SpeakerServiceImpl(SpeakerRepository repository) {
System.out.println("inside Args Constructor");
this.repository = repository;
}
#PostConstruct
private void initialize() {
System.out.println("Post Construct Init");
}
public void setSpeakerRepository(SpeakerRepository repository) {
System.out.println("In setSpeakerRepository");
this.repository = repository;
}
public List<Speaker> findAll() {
return repository.findAll();
}
}
And following is my xml config file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="speakerRepository"
class="com.ujjwal.repository.HibernateSpeakerRepositoryImpl"/>
<!--<bean name="speakerRepositoryUj"
class="com.ujjwal.repository.UjjwalSpeakerRepositoryImpl"/>-->
<bean name="speakerService"
class="com.ujjwal.service.SpeakerServiceImpl">
<constructor-arg index="0" ref="speakerRepository"/>
</bean>
</beans>
Also, I made sure that my pom.xml has javax.annotation libraray and maven included it in external libraries.
Now, following is my Application class which has main method:
import com.ujjwal.service.SpeakerService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
public static void main(String args[]) {
ApplicationContext appContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
SpeakerService service = appContext.getBean("speakerService", SpeakerService.class);
System.out.println(service.findAll().get(0).getFirstName());
}
}
Now, I am not seeing print statement of initialize method in my output. And following is the output:
inside Args Constructor
Ujjwal
But I get the print statement of initialize method when instead of using xml config I use Java Configuration for Beans.
Can anyone please explain what is happening here?
I'm learning Springs with annotations and auto wiring. I tried three types of auto wiring
Constructor
Setter injection
Method
This is my configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- add entry for component scanning -->
<context:component-scan base-package="com.aht.spring.entity"></context:component-scan>
</beans>
These are my entities
Coach.java
package com.aht.spring.entity.coach;
public interface Coach {
String getDailyWorkOut();
String getDailyFortune();
}
FortuneService
package com.aht.spring.entity.fortune;
public interface FortuneService {
String getFortune();
}
HappyFortuneService
package com.aht.spring.entity.fortune;
import org.springframework.stereotype.Component;
#Component
public class HappyFortuneService implements FortuneService {
public String getFortune() {
return "Feel energetic for first half of trainning";
}
}
FootBallCoach
package com.aht.spring.entity.coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.aht.spring.entity.fortune.FortuneService;
#Component
public class FootBallCoach implements Coach {
private FortuneService fortuneService;
#Autowired
public FootBallCoach(FortuneService fortuneService) {
this.fortuneService = fortuneService;
}
public String getDailyWorkOut() {
return "Practice one-on-one for 2 hours";
}
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
CricketCoach
package com.aht.spring.entity.coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.aht.spring.entity.fortune.FortuneService;
#Component
public class CricketCoach implements Coach {
private FortuneService fortuneService;
public CricketCoach() {
System.out.println("Default constructor");
}
#Autowired
public void setFortuneService(FortuneService fortuneService) {
this.fortuneService = fortuneService;
}
public String getDailyWorkOut() {
return "Practice out field tips";
}
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
BaseBallCoach
package com.aht.spring.entity.coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.aht.spring.entity.fortune.FortuneService;
#Component
public class BaseBallCoach implements Coach {
private FortuneService fortuneService;
public String getDailyWorkOut() {
return "Practice curve whole day";
}
public String getDailyFortune() {
return fortuneService.getFortune();
}
#Autowired
public void customAutoWire(FortuneService fortuneService) {
this.fortuneService = fortuneService;
}
}
I've three classes for executing three ways of auto wiring, Constructor and Setter worked fine, but when method wise auto wiring was done a wrong constructor was called. One thing I missed in my in my BaseBallCoach class was a default constructor, but anyhow compiler will automatically generate one for me right?
This is my CoachMethodInjectionApp.java where I executed method auto wiring
package com.aht.spring.app;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.aht.spring.entity.coach.Coach;
public class CoachMethodInjectionApp {
public static void main(String[] args) {
// read config-file
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// get beans
Coach coach = context.getBean("baseBallCoach", Coach.class);
// get dependencies
System.out.println(coach.getDailyFortune());
// get daily workout
System.out.println(coach.getDailyWorkOut());
// close context
context.close();
}
}
This was the output
Default constructor
Feel energetic for first half of trainning
Practice curve whole day
First line of output is what I don't understand Default constructor, why the constructor of CricketCoach executing??
As CricketCoach class is annotated with #Component and the package is scanned when the Spring container starts it will create an instance of type CricketCoach by calling the no-arg constructor
All the beans defined in the xml file or annotated with #Component or any extension of #Component from scanned packages will be created at start time of the spring container no matter if they will be used or not
You're creating a new instance of Coach class here:
Coach coach = context.getBean("baseBallCoach", Coach.class);
Everytime new instance is created it is executing a constructor. In which you have call to System.out.println("Default constructor");. You may try removing #Component from Cricket Coach imlpementation.
I am trying to understand the sequence of static initialization, instance initialization, InitializingBean and BeanPostProcessor, so I created below example, everything is working except that BeanPostProcessor are not getting invoked, am I missing something?
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Component;
#Component
public class SpringTest {
#Autowired
Person person;
public static void main(String[] args) {
testApplicationContext();
}
private static void testApplicationContext() {
// here static and instance initializers of Person class will be invoked right away, even when we are not calling getBean method
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("C:\\E_Drive\\Projects\\Workspace\\Test\\CS101\\src\\com\\learn\\stackoverflow\\general\\bean.xml");
ApplicationContext applicationContext2 = new FileSystemXmlApplicationContext("C:\\E_Drive\\Projects\\Workspace\\Test\\CS101\\src\\com\\learn\\stackoverflow\\general\\bean.xml");
// notice that above you are creating 2 ioc containers, and since they are different ioc containers so it is possible to have 2 singletons in JVM.
SpringTest springTest = (SpringTest) applicationContext.getBean("springTest");
}
}
Person class:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope(value="singleton")
public class Person implements InitializingBean, BeanPostProcessor {
#Autowired
SpringTest springTest;
static{
System.out.println("Static initialization");
}
{
System.out.println("Instance initialization");
}
public void sayHello(){
System.out.println("Hello");
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean.afterPropertiesSet");
}
#Override
public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
System.out.println("BeanPostProcessor.postProcessAfterInitialization : " + arg1);
return arg0;
}
#Override
public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
System.out.println("BeanPostProcessor.postProcessBeforeInitialization : " + arg1);
return arg0;
}
}
Bean XML file:
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.learn.stackoverflow.general"/>
<context:annotation-config />
<!-- <bean id = "person" class = "com.learn.stackoverflow.general.Person" scope="singleton">
</bean> -->
<bean id = "springTest" class = "com.learn.stackoverflow.general.SpringTest" scope="singleton">
</bean>
</beans>
Output:
Static initialization
Instance initialization
InitializingBean.afterPropertiesSet
Instance initialization
InitializingBean.afterPropertiesSet
Is there anything else required for BeanPostProcessor methods to be invoked?
A bean cannot self-post-process, the post-processor needs to be a separate bean; see tutorial.
I am getting familiar with Spring. Strangely code below invokes constructor twice whereas I expected it to be called once. Can someone help?
package com.tutorialspoint;
import java.util.List;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.ApplicationListener;
public class DemoClass implements ApplicationListener<ContextStartedEvent> {
private String message;
private int nrOfMessages;
public DemoClass(String mes, int nr) {
message = mes;
nrOfMessages = nr;
System.out.println("Demo class constructor. Parameters: " + mes + " " + nr);
}
// a setter method to set List
public void setNrOfMessages(int nr) {
this.nrOfMessages = nr;
}
// Message setter
public void setMessage(String message) {
this.message = message;
}
// prints and returns all the elements of the list.
public void dumpContents() {
System.out.println("Message: " + message + " Nr of messages: " + nrOfMessages);
}
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
The bean and main:
package com.tutorialspoint;
import java.io.FileNotFoundException;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
public class MainApp {
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
context.start();
// Create an object
DemoClass obj = (DemoClass) context.getBean("democlassBean");
// Dump contents
obj.dumpContents();
}
}
bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="democlassBean" class="com.tutorialspoint.DemoClass"
scope="prototype">
<constructor-arg value="Hello world beans."/>
<constructor-arg value="300"/>
</bean>
<bean class="com.tutorialspoint.InitHelloWorld"></bean>
</beans>
This is the output:
Demo class constructor. Parameters: Hello world beans. 300
BeforeInitialization : democlassBean
AfterInitialization : democlassBean
ContextStartedEvent Received
Demo class constructor. Parameters: Hello world beans. 300
BeforeInitialization : democlassBean
AfterInitialization : democlassBean
Message: Hello world beans. Nr of messages: 300
You can see constructor is being called twice - why???
Here is also code for initHelloWorld:
package com.tutorialspoint;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InitHelloWorld implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeforeInitialization : " + beanName);
return bean; // you can return any other object as well
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("AfterInitialization : " + beanName);
return bean; // you can return any other object as well
}
}
It seems, it is not good idea to have prototype bean to act as ApplicationListener.
Check http://forum.spring.io/forum/spring-projects/container/35965-applicationlistener-interface-makes-beans-eagerly-instantiated
for details.
You can make it work, but with some exta steps are required (described in lined post). Note that linked post is somewhat old (year 2007) and it is likely that some of the implementation details covered there are not valid anymore.
If you really care about number of instances being created- what about creating two classes- one as prototype, and another (singlelton) that acts as ApplicationListener?
Friends below is my code, I am trying to run dependency Injection with Spring
I have an interface, two class implementations of that interface.
One bean.xml and One main method class.
Interface IWriter.java
package DI;
public interface IWriter {
public void writer(String s);
}
Class Writer.java
package DI;
import org.springframework.stereotype.Service;
#Service
public class Writer implements IWriter {
public void writer (String s){
System.out.println(s);
}
}
Class NiceWriter.java
package DI;
import org.springframework.stereotype.Service;
#Service
public class NiceWriter implements IWriter {
public void writer (String s){
System.out.println("The string is " + s);
}
}
Another class
package DI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
#Service
public class MySpringBeanWithDependency {
#Autowired
private IWriter writer;
public void run() {
String s = "This is my test";
writer.writer(s);
}
}
Main.java
package DI;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import DI.MySpringBeanWithDependency;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BeanFactory factory = context;
MySpringBeanWithDependency test = (MySpringBeanWithDependency) factory.getBean("mySpringBeanWithDependency");
test.run();
}
}
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="DI" />
</beans>
When I run the code Spring container gives the output of the method of Writer.java class. I haven't anywhere specified which implementation to pick. How is Spring picking up the implementation of Writer.java??
change your code as follows.
Class Writer.java
package DI;
import org.springframework.stereotype.Service;
#Service("writer")
public class Writer implements IWriter {
public void writer (String s){
System.out.println(s);
}
}
Class NiceWriter.java
package DI;
import org.springframework.stereotype.Service;
#Service("niceWriter")
public class NiceWriter implements IWriter {
public void writer (String s){
System.out.println("The string is " + s);
}
}
Another class
package DI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
#Service
public class MySpringBeanWithDependency {
#Autowired
#Qualifier("writer")//if you need to autowire Writer service
private IWriter writer;
#Autowired
#Qualifier("niceWriter")//if you need to autowire NiceWriter service
private IWriter niceWriter
public void run() {
String s = "This is my test";
writer.writer(s);
}
}
When there is more than one implementation of interface and you use #Autowired in that case spring bind any of the class. but if you want to autowire specific implementation then you can use
#Qualifier( "<implementing class name>" )
#Qualifier documentation
Few things that you must know about Spring is
All spring beans are managed - they "live" inside a container, called "application context".
Each application has an entry point to that context. Also, there is a place where the application context is bootstrapped and all beans - autowired. In web applications this can be a startup listener.
Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean. Both classes should be beans, i.e. they should be defined to live in the application context.
Try this one.
Class Writer.java
package DI;
import org.springframework.stereotype.Service;
#Service("writer")
public class Writer implements IWriter {
public void writer (String s){
System.out.println(s);
}
}
Class NiceWriter.java
package DI;
import org.springframework.stereotype.Service;
#Service("niceWriter")
public class NiceWriter implements IWriter {
public void writer (String s){
System.out.println("The string is " + s);
}
}
Another class
package DI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
#Service
public class MySpringBeanWithDependency {
#Autowired
private IWriter writer;
#Autowired
private IWriter niceWriter
public void run() {
String s = "This is my test";
writer.writer(s);
}
}
I'd like to show one more option using application.properties.
Benefits:
You don't need to change code when you add/change implementation of the interface
Works well with unit tests and other environments
Sample code based on #ConditionalOnProperty attribute
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
#Service
#ConditionalOnProperty(value = "writer.type", havingValue = "default")
public class Writer implements IWriter {
#Override
public void writer(String s) {
System.out.println("The string is " + s);
}
}
And
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
#Service
#ConditionalOnProperty(value = "writer.type", havingValue = "nice")
public class NiceWriter implements IWriter {
#Override
public void writer(String s) {
System.out.println("Nice string is " + s);
}
}
When application.properties contains writer.type=nice NiceWriter will be instantiated for IWriter interface.
Instead of #ConditionalOnProperty there are other options like Conditional, #ConditionalOnExpression.
It depends on the use-case, you can select implementation of the interface using spring profiles, custom annotations or as mentioned by other answers using #Qualifier (injection by name) which is equivalent to JSR-330's #Named annotation