警告:
阅读此文前务必先阅读之前写的《spring如何解决循环引用》,本篇文章高度依赖循环引用。
在循环依赖中我们讲了spring实例化bean的入口,refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,看doGetBean中的如下代码
代码语言:javascript复制 if (mbd.isSingleton()) {
// 实例化bean
sharedInstance = getSingleton(beanName, () -> {
try {
// 真正的完成bean的创建
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
createBean(beanName, mbd, args);
方法真正的完成了bean的实例化,包括循环依赖、AOP、生命周期等。
AOP是在何时完成的?
所谓的AOP无非就是将bean加强,在bean的方法前后加上其他的方法而已,bean的class在虚拟机启动的时候就加载到JVM里了,我们不会通过修改class来动态扩展bean的功能,但是可以新生成一个类(动态代理类),这个类呢,包含了bean的所有功能,同时又进行了加强,然后将这个动态代理类实例化,替换掉原有的bean,最后放到spring单例池中。不知道我这毫无文采的大白话有没有讲清楚。
所以,AOP肯定是跟bean的实例化息息相关的,注意,我说的是实例化,而不是生命周期。也就是说,我们AOP肯定是在bean实例化好了后再进行动态代理,想想JDK的动态代理,是需要一个实例化的被代理对象的,在《spring如何解决循环引用》这篇文章中我们详细讲了实例化及循环依赖。所以,AOP是在两个地方完成代理的:
- 所有的bean实例化完成,且循环依赖也完成,紧接着开始AOP动态代理,前提是你所代理的类没有被别的类依赖。
- 如果你的类被别的类依赖了,那么在依赖获取的过程中进行AOP动态代理。 这是《spring如何解决循环引用》文章最后三级缓存的总结
问什么需要二级缓存?为什么需要bean的半成品需要提前暴露一个工厂? AOP就是一个原因吧!我现在有一个实例A,实例B要依赖我,但是A需要被代理,也就是说,A被代理后才能注入给B,那我B现在就要注入你,总不能等整个容器所有bean都实例化好后再来注入吧,讲道理也不是不可能,只是spring觉着太麻烦没那个必要,干脆,在注入的时候就直接AOP吧(后面会将源码)!讲清楚了没有!
OK,下马开始讲AOP源码!
AOP的准备工作
先把测试代码写出来
定义一个切面
代码语言:javascript复制@Component
@Aspect
public class UserAspect {
@Pointcut("execution(* com.config.aop.AopTest.*(..))")
public void pintCut(){
System.out.println("point cut");
}
@Before("com.config.aop.UserAspect.pintCut()")
public void beforeAdvice(){
System.out.println("before");
}
}
被代理的业务类
代码语言:javascript复制@Service
public class AopTest {
public void aop(){
System.out.println("This is my aop test");
}
}
代码语言:javascript复制public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class);
context.refresh();
AopTest aopTest = context.getBean(AopTest.class);
System.out.println(aopTest);
aopTest.aop();
}
}
随着项目的进行,我们的业务类会越来越多,spring首先要过滤掉哪些类需要代理,哪些不需要。其实,就是判断类上的注解啦!
跟着上面的源码createBean(beanName, mbd, args);
继续走。
再次声明 !!! 这里不明白上文的源码的,补习《spring如何解决循环引用》,不要放弃。
进入createBean源码,找到如下代码
代码语言:javascript复制// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
看英文翻译,给Bean后置处理器一个机会,返回一个替代目标对象的代理对象。
注意,此时还没后面的bean初始化代码,这里只是做准备工作,做什么准备?继续看。
原来,AOP实现是靠后置处理器完成的,本专题以前的博文讲了BeanFactoryPostProcessor,而这里是 BeanPostProcessors ,两者有啥区别?BeanFactoryPostProcessor后置处理器干预了BeanDefinition的生成,而BeanPostProcessors 干预了bean的实例化。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
是AOP第一次开始调用后置处理器,进去看看吧!
直接定位到核心代码
代码语言:javascript复制bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
有些读者会怀疑,不是说逐行阅读吗?怎么最近都是直接定位了!讲道理,如果之前的博文你都读过,非核心的代码你都能自行分析了,太简单的没必要在这列出了,毕竟大家的时间都很宝贵!
进入源码
代码语言:javascript复制 protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
for循环,拿到所有的BeanPostProcessors。
这么多后置处理器用到哪一个呢,自己调试去,看我吹没用。
AnnotationAwareAspectJAutoProxyCreator,就他!F5跟进代码,代码很难,简单讲一部分,等讲生命周期(更难)的时候还会讲
代码语言:javascript复制 @Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
if (this.advisedBeans.containsKey(cacheKey))
查看advisedBeans集合是否包含这个类的名字,如果包含就直接返回null了。
private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
advisedBeans 就是一个集合,用来保存不需要代理的类。比如我们上面定义的切面本身是不需要被代理的,还有加了@Configuration注解的Config配置类,也是不需要代理的,Config其实已经被代理了,之前讲过。
代码语言:javascript复制if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName))
这行代码就是判断我们这个业务类是否需要被代理,进入isInfrastructureClass代码:
代码语言:javascript复制 protected boolean isInfrastructureClass(Class<?> beanClass) {
return (super.isInfrastructureClass(beanClass) ||
(this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
}
调用父类的方法
代码语言:javascript复制 protected boolean isInfrastructureClass(Class<?> beanClass) {
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
Pointcut.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
if (retVal && logger.isTraceEnabled()) {
logger.trace("Did not attempt to auto-proxy infrastructure class [" beanClass.getName() "]");
}
return retVal;
}
首先会调用父类的方法
class1.isAssignableFrom(class2) 判定此 Class1 对象所表示的类或接口与指定的 Class2 参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。
Advice、Pointcut、Advisor等是跟切面相关的,不需要代理。
如果父类方法返回false,继续判断该类是不是一个切面,是切面的话也是不需要被代理的。
shouldSkip(beanClass, beanName)
会根据你的AOP配置,找到
符合条件的放入advisedBeans 集合中,后面会根据这个集合判断业务类是否需要被代理。
OK,趁热打铁,我们看AOP代理实现,我们现在跳回createBean源码,从Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
继续往后看Object beanInstance = doCreateBean(beanName, mbdToUse, args);
这行代码,进入到方法内,找到下面代码
//处理循环依赖
populateBean(beanName, mbd, instanceWrapper);
//开启生命周期!!!!!!
exposedObject = initializeBean(beanName, exposedObject, mbd);
populateBean
完成了bean的实例化及循环依赖,继续看initializeBean
方法,这个方法就是开启了声明周期,AOP实现也是在这里面,进入看源码倒数第二行wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
,这个方法就是完成了AOP代理的实现,他是怎么实现的呢???
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
擦!还是后置处理器诶!
进去
进入wrapIfNecessary
方法。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
尼玛,spring源码太难了! 我花了好久好久才研究到这里。
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey)))
和if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName))
,这两行判断跟上面的一样,不解释。
如果这两个判断都不成立,下面的代码就是代理的创建!创建!创建!
createProxy里面创建了代理,具体怎么通过CGLIB创建的?读者自行查看吧!读spring源码一定要动手调试,光看,没用!
返回到exposedObject = initializeBean(beanName, exposedObject, mbd);
这个时候,exposedObject 就是代理对象了
继续返回,
然后继续F6往下调试代码
此时,就注册到spring单例池中的,这个addSingleton方法是在getSingleton函数里完成的,为啥在这个函数里咧?在这篇文章里,我一言难尽,看《spring如何解决循环引用》。
AOP,就是这么实现的,调用过程很复杂,其实就是靠后置处理器。
还没完,如果你的要代理的业务类被其他类循环依赖了,那么AOP的生成时机就不同了。
先加上两个循环依赖的测试代码
代码语言:javascript复制@Service
public class AopTest {
@Autowired
AopTest2 aopTest2;
public void aop(){
System.out.println("This is my aop test");
}
}
@Service
public class AopTest2 {
@Autowired
AopTest aopTest;
public void aop(){
System.out.println("This is my aop test");
}
}
refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean,在该方法里的第三行:
Object sharedInstance = getSingleton(beanName);
,我们讲过,你的业务类实例化后会提前暴露一个工厂类,依赖你的类执行到这里时,会先调用这行代码,我们分析过这行代码,这里再分析,完全两个味道
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 这个singletonObjects就是微观层面的IOC容器,循环创建刚开始时,IOC确实是空的,
// 但是我前面一开始的getBean()方法是存在递归调用现象的
/**
* 直接举2个例子:
* 第一:假如现在在实例化A,结果有发现需要给A注入B,
* 那Spring是不是得获得B,怎么获得呢? 递归使用getBean(BName)完成,
* 第二个例子: A被添加上了@Lazy注解,是懒加载的,但是终究有一个会通过getBean(AName)获取A,
* 这是发现A是实例化需要B,B肯定已经实例化完事了,同样是通过递归getBean(BName)实现注入,
* 在这两个过程中就是getSingleton()保证不会重复创建已经存在的实例
*/
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
看这行
代码语言:javascript复制singletonObject = singletonFactory.getObject();
F5跟进源码,进入到下面代码
对吧,就是提前暴露的工厂,F5跟进
代码语言:javascript复制 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
尼玛,又是后置处理器
跟进去
如果,有人,能看到这里,应该不需要我解释了。这就是AOP生成的第二个时机,在循环依赖过程中实现AOP,也是半成品的bean实例化完后为什么要暴露一个工厂的原因,而不是一个简单的bean对象,因为工行能够提供方法呀,在方法里我们就能处理这个对象啊,这里的处理单指AOP代理,不知道我讲没有讲清楚。
上面讲过了,一模一样的,代码复用啦
这里注意Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
方法就是找到切面的,怎么找到的,读者可以跟进代码读一下,就是拿到所有的beanDefinition,然后找所有带有@Aspect注解的类,因为我们可以定义多个切面,所以继续找到我们业务类所在的那个切面,也就时遍历所有的切面,然后根据@Pointcut("execution(* com.config.aop.AopTest.*(..))")
进行匹配。
好吧,不卖关子了,我们看看getProxy
方法吧,跟进去
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
继续跟进createAopProxy
方法
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
跟进createAopProxy
代码语言:javascript复制 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: "
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
关于AOP的配置信息全部在config参数中,看一下我们最关心的两个值,切面和目标对象。
对吧,都有了,那就代理吧!
判断是否实现了接口,实现接口了就用JDK自带的动态代理技术,不然就用CGLIB动态代理技术。我们这里显然用了CGLIB动态代理技术,然后一直返回到下图所示
进入getProxy
创建代理对象
至此,代理对象就创建完成了,我讲的大概的过程,没有细讲每行代码,感兴趣的自行读吧,我实在写不下去了,肩膀疼!
我们知道aopTest2,是在populateBean(beanName, mbd, instanceWrapper);完成了属性注入对吧,注入完成后我们看一下情况
看见没,aopTest2依赖了aopTest,此时注入的就是代理的aopTest了。
面试中,AOP会经常被问到,笔者给别人面试也会问。其实面试官不是想问你AOP的概念和如何使用,他会考察你对spring的了解程度,或者看你是否阅读过源码。面试造火箭,工作拧螺丝。确实,工作中很少去看spring源码,但是现在java程序员泛滥了,你如何脱颖而出,面试官也很为难啊,我知道A和B都会用,但是A读过源码,说明他对技术感兴趣,而且肯钻研,工作会游刃有余!
你面试如果给面试官分析上面的动态代理过程,差不多能秒杀大部分面试官了!面试官也不见得所有的技术都去读源码的!
文章的最后,给大家留个悬念! Spring中的事务是怎么回事?原理呢?源码如何实现?其实也是用到了AOP,你读懂了这篇文章就很容易解决。 加油