Spring源码剖析-拜托面试官别再问我AOP原理了

Posted 墨家巨子@俏如来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码剖析-拜托面试官别再问我AOP原理了相关的知识,希望对你有一定的参考价值。

目录

前言

IOC/DI , AOP 是Spring最重要的两个特性 ,也是面试高频被问到的部分,前面我们已经分析了Spring的IOC相关源码以及DI依赖注入相关源码,从本篇文章开始我们着手分析Spring的AOP源码 。

开始之前,你需要对AOP 原理,JDK动态代理,CGLIB动态代理有一定的理解。这里先上一个图,后面源码分析的时候可以看着图来

在这里插入图片描述

AOP的理解

AOP基本概念

AOP是为面向切面编程,为什么要面向切面,何为切面?我们知道对于OOP面向对象而言在某些开发场景中是有一定不足,由于面向对象的思想是纵向的,它面对的是一个一个的对象,当我们需要在多个类中引入同一个公共的业务时(比如:事务,操作日志等),那么在每个类中都要引入公共的业务代码,从而造成代码大量重复,代码结构不优雅,不方便维护 ,这个时候就需要使用到面向切面的编程来解决这个问题。使用AOP可以把分散在多个类中的公共的代码剥离出来,和业务本身的代码解耦, 然后通过动态代理将公共业务代码作用到多个对象,代码结构也更加优雅。

所以可以认为 面向切面 是对 面向对象 的补充,它的思想是横向的,它面向的是一个切面,如果把一个对象看做一个点,那么多个对象就是一个面,是为切面,AOP多用于:事务,日志,监控,流控等等业务场景。
在这里插入图片描述

AOP实现原理

AOP的实现原理是基于动态代理,动态代理就是在运行时期动态的为某个类(原生类)生成代理类以达到代码增强的目的,且代理类是持有原生类的,可以在代理类中调用原生类以及做一些增强业务。

动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包。两者的区别是JDK动态代理要求原始类(被代理类)需要实现至少一个接口。而CGLIB则是基于继承进行代理,原生类可以不实现任何接口。

对于Spring而言默认采用JDK动态代理,如果原生类没有实现任何接口,Spring会选择CGLIB代理,或者你可以通过配置文件强制指定使用CGLIB代理。

AOP案例

下面我们使用AOP来做一个事务管理案例:在每个Service方法执行前后添加事务的代码

1.创建一个普通类,这个类的方法需要有事务

@Service
public class UserServiceImpl implements IUserService {

    public void insert() {
        System.out.println("UserServiceImpl.insert:保存User...");
    }

    public void delete() {
        System.out.println("UserServiceImpl.delete:删除User");
    }
}

2.创建一个切面类,这个类里面提供公共的业务代码,即:事务代码

@Component
@Aspect
public class TranscationManager {

    //定义切点,表达式作用于所有到service的所有的方法
    @Pointcut("execution(* cn.xxx.*.service.*.*(..))")
    public void pointcut(){}

    //前置通知 , 方法执行前执行,用来开启事务
    @Before("pointcut()")
    public void begin(JoinPoint joinPoint){

        System.out.println("TranscationManager.begin:开启事务...:");
    }
    //后置返回通知 ,方法正常返回后执行, 用来提交事务
    @AfterReturning("pointcut()")
    public void commit(){
        System.out.println("TranscationManager.commit:提交事物...");
    }

    //异常通知,方法出现异常调用,用来回滚事务
    @AfterThrowing(value = "pointcut()",throwing="e")
    public void rollback(JoinPoint joinPoint,Throwable e){
        System.out.println("TranscationManager.rollback:回滚事物咯...:"+e.getMessage());
    }

    //最终通知,不管方法会不会出现异常,都会执行,用来关闭资源
    @After("pointcut()")
    public void close(){
        System.out.println("TranscationManager.close:关闭连接...");
    }


    //环绕通知,拦截原始类的类的方法,可以通过 joinPoint调用原始的类
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        

        return null;
    }

}

3.配置Spring支持AOP注解

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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-4.0.xsd
       http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!--开启IOC注解支持-->
    <context:component-scan base-package="cn.xx" />

    <!--开启AOP注解支持,默认使用jdk动态代理,如果指定 proxy-target-class=true 则实例CGLIB代理-->
    <aop:aspectj-autoproxy />

</beans>

4.测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:application_aop.xml")
public class AopTest {

    @Autowired
    private IUserService userService ;

    @Test
    public void testAop(){
        userService.insert();
        System.out.println("======================================");
        userService.delete();
    }
}

控制台效果

TranscationManager.begin:开启事务…:
UserServiceImpl.insert:保存User…
TranscationManager.commit:提交事物…
TranscationManager.close:关闭连接…
======================================
TranscationManager.begin:开启事务…:
UserServiceImpl.delete:删除User…
TranscationManager.commit:提交事物…
TranscationManager.close:关闭连接…

这里我们看到了,使用AOP可以把公共的代码剥离出去和业务变身代码解耦,同时也方便扩展。

怎么理解这个AOP案例,首先要明白需求:在多个service方法执行前,否 或 出现异常做出相应的事务处理。我们把UserService看着原生类, 把TranscationManager 切面看着是增强代码,那么Spring是如何做到把 TranscationManager 的增强逻辑 加入到 UserService的方法前后的呢?

  1. 找到所有原生类(被代理类):我们在 TranscationManager中定义了一个切点:@Pointcut("execution(* cn.xxx.*.service.*.*(..))") :这切点定义了一个表达式,这个表达式的含义就是找到cn.xxx包下所有的service的所有方法,Spring就知道了需要拦截这些方法的执行。

  2. 找到了service的方法,如何在方法前,后做事情?我们在TranscationManager中定义了@Before(“pointcut()”) 前置通知,@AfterReturning(“pointcut()”)后置通知等,当我们调用userService的方法之前就会触发 Before 前置通知中的逻辑 ,方法执行完成就会触发 AfterReturning 后置通知的逻辑,异常通知和最终通知也是一个道理。

  3. 实现原理就是动态代理,其实Spring为所有切点切到的类都生成了代理类,也就是说在test类中注入的 @Autowired private IUserService userService ; 其实并不是我们写的那个UserService,而是基于UserService代理出来的一个代理类。代理类持有原生类,且代理类和原生类有相同的方法,所以你感觉你在调用UserService,其实在调用代理类。

    在代理类中的方法对原生类方法做了增强 ,而增强的逻辑就是 TranscationManager 中的逻辑,比如调用userService.insert 实则进入了代理类的insert方法,方法中先调用TranscationManager@Before 前置通知业务,然后调用原生类的insert,方法结尾调用TranscationManager@AfterReturning 后置通知,出现异常调用TranscationManager@AfterThrowing异常通知等等。这样是不是就达到了上面的增强效果了。

如果没有去研究过JDK动态代理和CGLIB代理可能你看起来比较晕,对于Spring切面的东西这里不做太多解释了,不是本篇主要内容,下面我就对于这个AOP案例做源码分析。

AOP解析器

在上面的案例中我们通过 <aop:aspectj-autoproxy /> 配置来开启AOP支持,稍微动动脑袋能想到Spring一定会有一个类来处理该配置。 在Spring中有一个接口叫 NamespaceHandlerSupport ,它提供了Spring配置文件的namespace的解析支持。
在这里插入图片描述
其中有一个实现叫 AopNamespaceHandler , 它是针对于 <aop: 开头的namespace 解析支持,看一下这个类的源码

AOP解析器:AspectJAutoProxyBeanDefinitionParser

//aop命名空间的NamespaceHandler 。
//为<aop:config>标签提供一个BeanDefinitionParser 。 config标签可以包括嵌套的pointcut 、 advisor和aspect标签。
public class AopNamespaceHandler extends NamespaceHandlerSupport {

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.5+ XSDs
		//<aop:config 解析器
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		//【主角在这里】注册针对于<aop:aspectj-autoproxy 的解析器 AspectJAutoProxyBeanDefinitionParser
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace in 2.5+
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

}

到这里我们看到了针对于 <aop:aspectj-autoproxy 的解析器 AspectJAutoProxyBeanDefinitionParser,它实现了BeanDefinitionParser,解析逻辑都在parse方法中,源码如下:

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

	//Element就是 <aop:aspectj-autoproxy 元素的封装
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		//注册一个 AspectJAnnotationAutoProxyCreator
		//用于处理当前应用程序上下文中的所有 AspectJ 切面,以及 Spring Advisor。
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		//处理子节点
		extendBeanDefinition(element, parserContext);
		return null;
	}

	...省略...

}


该方法的作用主要是注册了一个 AspectJAnnotationAutoProxyCreator 自动代理创建器,它的作用就是用来处理Aop。 AspectJAutoProxyBeanDefinitionParser 的 parse 方法是在IOC启动过程中,通过DefaultBeanDefinitionDocumentReader#parseBeanDefinitions 委派 BeanDefinitionParserDelegate#parseCustomElement去解析的。

注册AnnotationAwareAspectJAutoProxyCreator

parse方法中比较重要的就是 AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary , 它创建了一个 AnnotationAwareAspectJAutoProxyCreator 代理自动创建器 ,AOP的功能重要就是通过它来完成的,它可以根据 Point 解定义的切点来自动代理相匹配的 bean,跟一下该方法源码:

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
		//【重要】通过BeanDefinitionRegistry注册一个 AnnotationAwareAspectJAutoProxyCreator
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
				
		//对 proxy-target-class 和 expose-proxy 属性处理
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		//注册组件 把 beanDefinition 注册到ParserContext上下文中
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

该方法做了三件事情

  • 通过BeanDefinitionRegistry注册一个 AnnotationAwareAspectJAutoProxyCreator ,该类是AspectJ 切面处理类
  • 对 proxy-target-class 和 expose-proxy 属性处理 ,就是把BeanDefinition中的proxyTargetClass和exposeProxy属性设置为true
  • 这次组件, 把AnnotationAwareAspectJAutoProxyCreator 的 beanDefinition 注册到ParserContext上下文中

我们继续跟踪注册AnnotationAwareAspectJAutoProxyCreator 的代码:AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
			@Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

	//BeanDefinitionRegistry:bean的注册器
	//cls:AnnotationAwareAspectJAutoProxyCreator的类型
	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//如果容器中已经包含了 org.springframework.aop.config.internalAutoProxyCreator 自动代理创建者的BeanDefinition
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			//获取BeanDefinition
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			//class比较
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				//如果已经有自动创建器了,按照优先级比较选择使用哪一个
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		//如果容器中还没有注册自动创建器,就注册一个
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		//通过BeanDefinitionRegistry注册一个 代理自动创建器
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

方法会判断容器中如果已经注册了 自动代理创建器,则按照优先级选择使用哪个,如果么有注册就使用BeanDefinitionRegistry注册一个。

处理proxy-target-class和ExposeProxy

proxy-target-class 的对于 <aop:aspectj-autoproxy proxy-target-class=“true” /> 属性的处理,该属性的作用是指定代理的方式为CGLIB。

//对 proxy-target-class 和 expose-proxy 属性处理
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
	if (sourceElement != null) {
		//获取  <aop:aspectj-autoproxy proxy-target-class="" />  proxy-target-class 属性
		boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
		if (proxyTargetClass) {
			//如果proxyTargetClass=true,则使用CGLIB代理
			//definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
			//把 definition 中的 proxyTargetClass设置为true
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		//当exposeProxy为true时暴露代理对象,会把proxy动态代理对象设置到AopContext上下文中
		boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
		if (exposeProxy) {
			//definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
			//把 definition 中的 exposeProxy 设置为true
			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
		}
	}
}

这里做了两个事情

  • 根据配置:<aop:aspectj-autoproxy proxy-target-class=“true” /> 将 definition 中的 proxyTargetClass设置为true ,生成代理将会使用CGLIB方式,否则默认使用JDK动态代理
  • 根据配置:<aop:aspectj-autoproxy proxy-target-class=“true” expose-proxy=“true” /> 将 definition 中的 exposeProxy设置为true ,目的是把创建的代理对象添加到AopContext上下文中,为什么要这麽做?如果有如下代码:
    public class AServiceImpl implements AService{
    	@Transactional
    	public void a(){
    		this.b();
    	}
    	@Transactional(propagation = Propagation.REQUIRES_NEW)
    	public void b(){
    	}
    }
    
    这里的 this 是原生类,并不是代理类,也就意味这b的 @Transactional(propagation = Propagation.REQUIRES_NEW)
    事务增强代码是不会执行的,对于这个问题我们可以做如下处理
  1. 修改配置:<aop:aspectj-autoproxy expose-proxy=“true” />
  2. this.b() 修改为:((AService)AopContext.currentProxy()).b(); 得到代理类执行b()方法

增强器

上面的一个大堆流程主要是创建了一个 AnnotationAwareAspectJAutoProxyCreator ,那么它是如何来处理AOP的呢?看一下它的继承体系
在这里插入图片描述
AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor,当Spring实例化这个Bean时会在这个Bean的initMethod初始化之后调用postProcessAfterInitialization方法,Aop的实现就在这里开始。AbstractAutoProxyCreator#postProcessAfterInitialization源码如下:


	 * Create a proxy with the configured interceptors if the bean is
	 * identified as one to proxy by the subclass.
	 * @see #getAdvicesAndAdvisorsForBean
	 */
	 //如果 bean 被子类标识为代理,则使用配置的拦截器创建一个代理。
	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
		if (bean != null) {
			//根据Bean的class和name,创建一个缓存的key  ,
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
				//对适合代理的Bean进行封装
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}


	//创建代理
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		//如果已经处理过,就直接返回Bean
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//不需要增强
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		//是基础设施类(比如 Pointcut.class,Aspect.class等基础类不需要代理),
		// 或者是原始类不做代理,比如 beanName以 .ORIGINAL 结尾就会跳过代理
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		//获取增强,即:获取到上面案例中的TranscationManager中的增强方法,封装成一个一个,InstantiationModelAwarePointcutAdvisorImpl,该类可以是对切面方法的封装
		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));
			//把代理的type放入proxyTypes
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}
		//缓存的key放入advisedBeans
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

方法中先判断了Bean是否需要创建代理,比如当前bean是Aspect,pintcut等基础设施类就不需要代理,然后调用 getAdvicesAndAdvisorsForBean 获取到增强器 ,最后 调用createProxy方法创建代理对象。

获取增强器

获取增强器是在 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean中完成的 ,我们来一步步跟踪一下它的获取流程


	//[第一步] 找到增强器
	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		//找打增强器,提取成Advisor
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

	//[第二步]  找到所有增强器,以及找到bean适用的增强器
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		//找到所有增强方法
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		//找到当前bean适用的增强方法
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

上面逻辑做了两件事情, 找到所有增强器,以及找到bean适用的增强器,查找所有增强器在 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors中完成的,源码如下:

@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		//调用父类,查找所有的增强方法
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
		//调用 aspectJAdvisorsBuilder.buildAspectJAdvisors 构建增强器
			advisors.addAll(this以上是关于Spring源码剖析-拜托面试官别再问我AOP原理了的主要内容,如果未能解决你的问题,请参考以下文章

拜托!面试请不要再问我Spring Cloud底层原理

求求你,下次面试别再问我什么是 Spring AOP 和代理了!

拜托,面试别再问我基数排序了!!!

拜托!别再问我hashmap是否线程安全

拜托,面试别再问我堆(排序)了!

拜托,别再问我桶排序了!!!