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的方法前后的呢?
-
找到所有原生类(被代理类):我们在 TranscationManager中定义了一个切点:
@Pointcut("execution(* cn.xxx.*.service.*.*(..))")
:这切点定义了一个表达式,这个表达式的含义就是找到cn.xxx包下所有的service的所有方法,Spring就知道了需要拦截这些方法的执行。 -
找到了service的方法,如何在方法前,后做事情?我们在TranscationManager中定义了@Before(“pointcut()”) 前置通知,@AfterReturning(“pointcut()”)后置通知等,当我们调用userService的方法之前就会触发 Before 前置通知中的逻辑 ,方法执行完成就会触发 AfterReturning 后置通知的逻辑,异常通知和最终通知也是一个道理。
-
实现原理就是动态代理,其实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上下文中,为什么要这麽做?如果有如下代码:
这里的 this 是原生类,并不是代理类,也就意味这b的public class AServiceImpl implements AService{ @Transactional public void a(){ this.b(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void b(){ } }
@Transactional(propagation = Propagation.REQUIRES_NEW)
事务增强代码是不会执行的,对于这个问题我们可以做如下处理
- 修改配置:<aop:aspectj-autoproxy expose-proxy=“true” />
- 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原理了的主要内容,如果未能解决你的问题,请参考以下文章