前言
在spring aop中,如果我们的类属符合如下条件:被切面的pointcut匹配到、或者属于自定义的Advisor接口实现类,那么spring在bean完成实例化之后,会为类生成代理对象。这是众所周知的aop流程。
此外,spring还为我们提供了TargetSourceCreator
接口,该接口的功能是:在bean实例化之前,就为类生成代理。
现在我们通过查看源码的方式,来了解该接口的功能。
源码剖析
1.提前生成代理
在bean实例化之前,有一个resolveBeforeInstantiation方法,如下
如果方法返回bean不为空,则实例化结束,不进行下面的doCreateBean方法。
我们进入方法看看
再进入applyBeanPostProcessorsBeforeInstantiation方法,
发现spring会遍历所有BeanPostProcessor,如果是InstantiationAwareBeanPostProcessor类型,则进入红框处方法。
AbstractAutoProxyCreator是InstantiationAwareBeanPostProcessor类型,所以此时进入AbstractAutoProxyCreator的方法,方法核心逻辑如下
getCustomTargetSource方法如果返回TargetSource类型,那么if逻辑里会为当前类生成代理,那么getCustomTargetSource方法逻辑是什么呢?我们进去看看
如果customTargetSourceCreators成员有值,于是遍历该成员,调用getTargetSource方法,方法逻辑如下
第一个红框会调用到AbstractBeanFactoryBasedTargetSourceCreator的具体子类方法中,返回TargetSource。AbstractBeanFactoryBasedTargetSourceCreator类就是开头提到的TargetSourceCreator
接口的一个抽象实现类。
第二个红框会创建一个新的BeanFactory,新的BeanFactory会拥有老的BeanFactory的属性。但需要注意的是,新BeanFactory的BeanPostProcessor集合里,去除了AbstractAutoProxyCreator类型,如下
到此,类的代理对象就生成了,之后的spring的实例化逻辑也不会走了。
2.调用代理对象方法
假设生成的是jdk代理。当调用对象方法时,会进入invoke方法。方法首先获取TargetSource对象
然后调用getTarget获取原来的对象
在前言里提到的aop流程中,最终生成的TargetSource是SingletonTargetSource类型,于是获取到的target就是原来的对象。
而在上述流程中,我们是提前生成代理的,生成的TargetSource类型是我们钩子方法里自定义的,所以获取到的target取决于我们如何实现。
下面我们自己来实现TargetSource相关接口。
自定义TargetSource实现类
1.编写类StudentServiceImpl
@Service
public class StudentServiceImpl implements StudentService{
@Override
public void eat(String a) {
System.out.println("=====StudentServiceImpl.eat");
}
}
2.继承AbstractBeanFactoryBasedTargetSourceCreator
通过上述源码剖析流程,我们可以知道继承AbstractBeanFactoryBasedTargetSourceCreator抽象类并重写方法,就能返回自定义TargetSource。
我们此处只针对StudentServiceImpl类型
public class CustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
@Override
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
if(beanClass.isAssignableFrom(StudentServiceImpl.class)) {
return new CustomTargetSource();
}
}
return null;
}
}
CustomTargetSource代码如下
public class CustomTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
}
其中getBeanFactory()的值,是上述流程提到的新的BeanFactory。
由于该BeanFactory的BeanPostProcessor没有了AbstractAutoProxyCreator,所以getBeanFactory().getBean(getTargetBeanName())
获取的bean,一定是原生的bean,而不是代理对象。
3.设置CustomTargetSourceCreator
光写了CustomTargetSourceCreator 还不行,因为它必须作为AbstractAutoProxyCreator类的成员,所以我们需要给AbstractAutoProxyCreator的成员赋值。此处我们自定义BeanPostProcessor
@Component
public class SetCustomTargetSourceCreator implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public int getOrder() {
return 45;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator)bean;
CustomTargetSourceCreator customTargetSourceCreator = new CustomTargetSourceCreator();
customTargetSourceCreator.setBeanFactory(beanFactory);
annotationAwareAspectJAutoProxyCreator.setCustomTargetSourceCreators(customTargetSourceCreator);
}
return bean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
到此,我们就完成了对StudentServiceImpl类型提前生成代理功能的整合。
ScopedProxyMode介绍
1.scope多例问题
在spring中,如果对象的@Scope注解的value值是DefaultListableBeanFactory.SCOPE_PROTOTYPE,表示spring启动时不会创建对象,而是使用时才创建,并且是每次都创建一个。这是众所周知的。
然而,在如下例子,虽然对象是多例,但是每次使用时,都是同一个对象。
先定义一个多例类ScopedProxyBean
@Component
@Scope(value = DefaultListableBeanFactory.SCOPE_PROTOTYPE)
public class ScopedProxyBean {
public void code() {
System.out.println(this.hashCode());
}
}
再在另一个类中依赖注入ScopedProxyBean
@Component
public class MyBean {
@Autowired
private ScopedProxyBean scopedProxyBean;
public void tet() {
scopedProxyBean.code();
}
}
开始测试
@Test
public void test3() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
MyBean bean = applicationContext.getBean(MyBean.class);
for (int i = 0; i < 10; i++) {
bean.tet();
}
}
通过打印结果,发现每次的hashcode都是一样的,说明ScopedProxyBean对象只有一份。
2.修改注解
现在,我们将ScopedProxyBean注解改为如下,
@Scope(value = DefaultListableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
多加了ScopedProxyMode.TARGET_CLASS
再进行测试,发现每次hashcode都是不一样,说明每次都生成了新的对象。
3.源码剖析
为什么加了ScopedProxyMode.TARGET_CLASS
之后,每次都生成新的对象呢?
我们分析源码发现,当spring完成@Component注解扫描,将bean加入BeanDefinition集合之前,会进入如下方法
如果scope注解有ScopedProxyMode.TARGET_CLASS
,那么会继续进入如下核心方法
第2个红框的属性设置,表示原来的ScopedProxyBean对象即使被加入了BeanDefinition集合,但最终不会被依赖注入。
第1个红框创建ScopedProxyFactoryBean,该类是一个FactoryBean类型,表示当bean实例化完成后会调用getObject方法;同时ScopedProxyFactoryBean实现了BeanFactoryAware接口,接口实现逻辑如下
其中设置的TargetSource是spring内置的SimpleBeanTargetSource
最终通过pf.getProxy生成代理对象,并加入spring的BeanDefinition集合。
4.结论
所以,当我们调用ScopedProxyBean对象的code方法时,实际进入的是代理对象的方法。通过上面我们分析的“提前生成代理”可知,代理对象方法里会通过SimpleBeanTargetSource获取target,由于是多例,所以每次获取的都是新的对象。