I've tried to use ProxyFactoryBean with @Autowired, but it throws an exception at the setInstrument method.
The question - why does it work with previously called getBean method or @DependsOn? I'm trying to understand what Spring performs for these additional steps.
Thank you in advance!
I have two interfaces - Singer and Instrument. And two implementations for them - GuitarSinger.class and Guitar.class.
Singer.class:
public interface Singer {
void singSong();
Instrument getInstrument();
}
GuitarSinger.class (Singer implementation):
@Service
@Lazy
public class GuitarSinger implements Singer {
private Instrument guitar;
@Autowired
public void setInstrument(Instrument instrument) {
this.guitar = instrument;
}
@Override
public Instrument getInstrument() {
return guitar;
}
@Override
public void singSong() {
System.out.println("I'm singing a song");
guitar.play();
}
}
Instrument.class interface:
public interface Instrument {
void play();
}
and Guitar.class (Instrument.class interface implementation):
public class Guitar implements Instrument {
@Override
public void play() {
System.out.println("I'm a guitar!");
}
}
And one interface for proxy introduction - GuitarChecker.class:
public interface GuitarChecker {
boolean isGuitarOk();
}
Mixin class - GuitarCheckerMixin.class:
public class GuitarCheckerMixin extends DelegatingIntroductionInterceptor implements GuitarChecker {
@Override
public boolean isGuitarOk() {
System.out.println("I don't know how to check the guitar");
return true;
}
}
And eventually configuration class with main() method:
@Configuration
@ComponentScan("com.annotation.test")
public class Config {
@Bean
public ProxyFactoryBean checkedGuitar() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
Instrument guitar = new Guitar();
proxyFactoryBean.setTarget(guitar);
proxyFactoryBean.addAdvisor(new DefaultIntroductionAdvisor(new GuitarCheckerMixin()));
proxyFactoryBean.setProxyTargetClass(true);
return proxyFactoryBean;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext appContext
= new AnnotationConfigApplicationContext(Config.class);
Singer singer = appContext.getBean(Singer.class);
singer.singSong();
Instrument instrument = singer.getInstrument();
GuitarChecker guitarChecker = (GuitarChecker) instrument;
guitarChecker.isGuitarOk();
}
}
If I run "main()", I will get:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.annotation.test.Instrument' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:759)
... 17 more
But if I add one of these two steps (works ok with only one of them), it will work correctly and print result.
1 Step) - add @DependsOn("checkedGuitar") to GuitarSinger.class:
@Service
@Lazy
@DependsOn("checkedGuitar")
public class GuitarSinger implements Singer {
2 Step) - add one more line with appContext.getBean("checkedGuitar") to main().
public static void main(String[] args) {
AnnotationConfigApplicationContext appContext
= new AnnotationConfigApplicationContext(Config.class);
appContext.getBean("checkedGuitar");
Singer singer = appContext.getBean(Singer.class);
singer.singSong();
Instrument instrument = singer.getInstrument();
GuitarChecker guitarChecker = (GuitarChecker) instrument;
guitarChecker.isGuitarOk();
}
With one of these two updates code will print the following lines:
I'm singing a song
I'm a guitar!
I don't know how to check the guitar
Could you please help me with understanding of the issue? Why does it work with @DependsOn or getBean()?
Thanks!
CodePudding user response:
It is saying it can't find any Bean that implements an interface Instrument. The only class that you have implementing Instrument is class Guitar. Annotate it with @Component so Spring can find it.
CodePudding user response:
I am not a Spring user, so I do not know how proxy bean factories and the other stuff you use are meant to be used canonically. But how about his?
package de.scrum_master.spring.q70623926;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("de.scrum_master.spring.q70623926")
public class Config {
@Bean
public Instrument instrument() {
return (Instrument) checkedGuitar().getObject();
}
@Bean
public ProxyFactoryBean checkedGuitar() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
Instrument instrument = new Guitar();
proxyFactoryBean.setTarget(instrument);
proxyFactoryBean.addAdvisor(new DefaultIntroductionAdvisor(new GuitarCheckerMixin()));
proxyFactoryBean.setProxyTargetClass(true);
return proxyFactoryBean;
}
public static void main(String[] args) {
try (AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(Config.class)) {
Singer singer = appContext.getBean(GuitarSinger.class);
singer.singSong();
Instrument instrument = singer.getInstrument();
GuitarChecker guitarChecker = (GuitarChecker) instrument;
guitarChecker.isGuitarOk();
}
}
}
This yields:
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.887 [main] DEBUG org.springframework.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
12:39:49.979 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'guitarSinger'
I'm singing a song
I'm a guitar!
I don't know how to check the guitar
12:39:50.023 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@61baa894, started on Sun Jan 09 12:39:49 ICT 2022
