文章来源:https://zhuanlan.zhihu.com/p/364658016
public interface IService {
void doService();
}
@Service
public class ServiceImpl implements IService{
@Override
public void doService() {
System.out.println("do service !!!");
}
}
配置文件spring.xml
<context:component-scan base-package="mytest"/>
<!--开启AOP-->
<aop:aspectj-autoproxy />
启动类
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
/** 同
@Autowired
private IService iService;
**/
IService service=applicationContext.getBean(IService.class);
//打印:do service !!!
service.doService();
/** 同
@Autowired
private ServiceImpl iService;
**/
ServiceImpl serviceImpl=applicationContext.getBean(ServiceImpl.class);
//打印:do service !!!
serviceImpl.doService();
}
说明我们平时引用bean,可以通过其父类接口来引用,也可以通过具体实现类来引用,然而并没有这么简单,现在我新加了一个切面类
@Aspect
@Component
public class AspectTest {
@Pointcut("execution(* *.doService(..))")
public void test(){
}
@Before("test()")
public void before(){
System.out.println(" before -------");
}
@After("test()")
public void after(){
System.out.println(" after -------");
}
}
再执行启动类,控制台会打印
而当我稍微修改下配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
则一切又正常了
这是为什么呢?首先不得不介绍下spring生成代理类的两种模式:jdk自带的Proxy代理与开源的CGLIB代理。当需要对bean生成代理类时,Spring会先判断是否配置了proxy-target-class=“true”,为true表示生成CGLIB代理类,否则会先判断bean是否有实现interface,有的话则生成Proxy代理类,不然就生成CGLIB代理类。
两种模式的区别就在于:
- Proxy代理类是把代理对象(ServiceImpl)实现的interface(IService)自己实现了一遍,等于是给IService生成了个新的ServiceImpl2实现类,所以生成的代理类是IService的子类,和ServiceImpl类型无关
- CGLIB代理类则是给代理对象(ServiceImpl)生成了个子类,所以代理类更是IService的子类
而applicationContext.getBean(xx.class)方法(@Autowired实际也会调用该方法)的实现中,会逐步调用如下方法:
- DefaultListableBeanFactory#getBean
- DefaultListableBeanFactory#resolveNamedBean
- DefaultListableBeanFactory#getBeanNamesForType
- DefaultListableBeanFactory#doGetBeanNamesForType(获取所有beans,遍历执行)
- ResolvableType#isInstance
- ResolvableType#isAssignableFrom(主要逻辑就是通过 xx.isAssignableFrom(other)方法判断)
isAssignableFrom就是判断某某是不是某某的父/同类, IService.class.isAssignableFrom(ServiceImpl.class)为true,反过来就是false
所以在未配置proxy-target-class="true"时,ServiceImpl实际上是Proxy代理类 P r o x y X X ,是 I S e r v i c e 的子类,所以通过 I S e r v i c e 能获取成功,而通过 S e r v i c e I m p l 获取时, S e r v i c e I m p l . c l a s s . i s A s s i g n a b l e F r o m ( ProxyXX,是IService的子类,所以通过IService能获取成功,而通过ServiceImpl获取时,ServiceImpl.class.isAssignableFrom( ProxyXX,是IService的子类,所以通过IService能获取成功,而通过ServiceImpl获取时,ServiceImpl.class.isAssignableFrom(ProxyXX.class)为false,所以getBean时会报NoSuchBeanDefinitionException异常,而注解注入时,当类型获取不到bean时会通过名称(‘serviceImpl’)来获取,再将得到的Proxy代理类反射设置属性时,与ServiceImpl类型不匹配,会报BeanNotOfRequiredTypeException异常;而配置proxy-target-class="true"后,IService/ServiceImpl.class.isAssignableFrom(Cglib代理.class)都为true,所以都能获取成功。