IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 05.SpringAOP使用与源码解析 -> 正文阅读

[Java知识库]05.SpringAOP使用与源码解析

Spring思维导图
SpringAOP切面解析流程图
SpringAOP创建代理流程图

AOP (Aspect Oriented Programming)

AOP 面向切面编程,要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理。AOP 的实现并不是因为 Java 提供了神奇的钩子,可以把方法的几个生命周期告诉我们,而是我们要实现一个代理,实际运行的实例其实是生成的代理类的实例

AOP 术语解释

  • 切面(Aspect): 在 AOP 中指切面类 (通常被 @Aspect 标注),管理着通知和切点
  • 连接点(Join Point): 切面和被增强方法所连接的点(即代理方法,我们需要增强的方法)
  • 通知(Advice): 某个特点的连接点上执行的动作,通常包括 前置(Before),后置(After),异常(Exeception),环绕(Around)
  • 目标对象(Target): 需要被增强的对象,即包含业务逻辑类的对象
  • 切点(PointCut): 匹配连接点的断言,由其决定哪些方法需要被增强,哪些不需要被增强,是AOP核心。Spring 默认使用 AspectJ 切点语义
  • 顾问(Advisor): 通知 (Advice) 的一种包装体现。是 Advice 和 PointCut 的结合,无需应用关心
  • 织入(Weaving): 将通知切入连接点的过程
  • 引入(Introductions): 将其他接口和实现动态的引入targetClass中,不常用

AspectJ

AspectJAOP 编程的完全解决方案,它能做许多AOP实现不了的事情。Spring AOP 致力于解决的是企业级开发中最普遍的 AOP 需求(方法织入)。

SpringAOP中的 @Aspect、@PointCut、@Before、@After、@Around 都来自 AspectJ,但功能都有 SpringAOP自己实现

SpringAOP

  • 它基于动态代理来实现。默认地,如果使用接口的,用 JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现
    1、如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP,可以强制使用 CGLIB 实现
    2、如果目标对象没有实现了接口,必须采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 之间转换

  • Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,不需要显式引入依赖

  • Spring AOP 需要依赖于 IOC 容器来管理

  • Spring AOP 只能作用于 Spring 容器中的 Bean,使用纯粹的 Java 代码实现的,只能作用于 Bean 的方法

  • Spring 提供了 AspectJ 的支持,但只用到的AspectJ的切点解析和匹配

Spring AOP 的配置方式

Spring AOP 一共有三种配置方式,并且做到了很好的向下兼容

  • Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的,Spring 的事务就是此方式实现的
  • Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用命名空间
  • Spring 2.0 @AspectJ配置:使用注解的方式来配置
@EnableAspectJAutoProxy

基于接口:

首先定义需要被增强的类:

接口:Calculate.java 实现类:TestCalculate.java

// 接口
public interface Calculate {
	/** 加 **/
	int add(int numA, int numB);
	/** 减 **/
	int sub(int numA, int numB);
    /** 乘 **/
	int multi(int numA, int numB);
	/** 除 **/
	int div(int numA, int numB);
}
// 实现类,将该类交给 Spring 管理才能动态代理实现AOP
public int add(int numA, int numB) {
    System.out.println("Invoke Method:add");
    System.out.println(1/0); // 测试异常
    return numA+numB;
}

public int sub(int numA, int numB) {
    System.out.println("Invoke Method:reduce");
    return numA-numB;
}

public int div(int numA, int numB) {
    System.out.println("Invoke Method:div");
    return numA/numB;
}

public int multi(int numA, int numB) {
    System.out.println("Invoke Method:multi");

    return numA*numB;
}

第二步:配置 adviceInterceptor

// Advice 通知
public class TestLogAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        String methodName = method.getName();
        System.out.println("invoke method " + methodName + "() <BeforeAdvice>,args" + Arrays.asList(args));
    }

}
// Interceptor 拦截器
public class TestLogInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(getClass()+"Before Invoke");
        Object ret=invocation.proceed();
        System.out.println(getClass()+"After Invoke");
        return ret;
    }
}

最后配置配置类:

// 被代理对象
@Bean
public Calculate testCalculate() {
    return new TestCalculate();
}

// Advice 方式
@Bean
public TestLogAdvice testLogAdvice(){
    return new TestLogAdvice();
}

// Interceptor  类似环绕通知
@Bean
public TestLogInterceptor testLogInterceptor() {
    return new TestLogInterceptor();
}

/**
 * FactoryBean方式单个: ProxyFactoryBean
 * 只能指定单一的Bean的AOP
 * 拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截
 * @return
 */
@Bean
public ProxyFactoryBean calculateProxy(){
    ProxyFactoryBean userService=new ProxyFactoryBean();
    userService.setInterceptorNames("testLogAdvice","testLogInterceptor");  // 根据指定的顺序执行
    userService.setTarget(testCalculate());
    return userService;
} 

测试类与运行结果:

public static void main(String[] args) { 
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EalyAopMainConfig.class);
    Calculate calculateProxy = ctx.getBean("calculateProxy",Calculate.class);
    calculateProxy.div(1,1);
}

--------------------执行结果--------------------
invoke method div() <BeforeAdvice>,args[1, 1]
class zhe.hello.aop.TestLogInterceptor Before invoke Method
Invoke Method:div
class zhe.hello.aop.TestLogInterceptor After invoke Method

从结果可以看到,使用了责任链方式对 advice 和 Interceptor 都进行调用。这个例子理解起来应该非常简单,就是通过调用 FactoryBean(ProxyFactoryBean) 的 getObject() 方法创建一个代理实现。责任链的两个重要依靠:统一抽象、循环或递归。

但是这种方式存在几个指明缺点:

  • 只能指定单一的 Bean 的 AOP,多个 Bean 需要创建多个 ProxyFactoryBean
  • 添加一个通知我们就需要配置一个 MethodXxxxAdvice 或 MethodInterceptor 类
  • 拦截器的粒度只控制到了类级别,类中所有的方法都进行了拦截

在上面的配置中,配置拦截器的时候,interceptorNames 除了指定为 Advice,是还可以指定为 Interceptor 和 Advisor 的。这里我们来理解 Advisor 的概念,它是经过包装后的细粒度控制方式,它内部需要指定一个 Advice,Advisor 决定该拦截哪些方法 (即 Advice + PointCut),拦截后需要完成的工作还是内部的 Advice 来做。在这里插入图片描述
它内部有多个实现,我们以NameMatchMethodPointcutAdvisor为例,顾名思义,就是通过提供方法的名字,复合的就进行拦截。

/**
 * Advisor 种类很多:
 * RegexpMethodPointcutAdvisor 按正则匹配类
 * NameMatchMethodPointcutAdvisor 按方法名匹配
 * DefaultBeanFactoryPointcutAdvisor xml解析的Advisor   <aop:before
 * InstantiationModelAwarePointcutAdvisorImpl  注解解析的advisor(@Before @After....)
 */
@Bean
public NameMatchMethodPointcutAdvisor testLogAspectAdvisor() {
    NameMatchMethodPointcutAdvisor advisor=new NameMatchMethodPointcutAdvisor();
    // 通知(Advice)  :是通知类 没有带切点
    // 通知者(Advisor):是经过包装后的细粒度控制方式 带了切点
    advisor.setAdvice(testLogAdvice());  // 通知者
    advisor.setMappedNames("div"); // 切点
    return  advisor;
}

/**
 * 除此之外按正则匹配也放在这里
 * 按正则匹配
 */
@Bean
public RegexpMethodPointcutAdvisor testLogAspectInterceptor() {
    RegexpMethodPointcutAdvisor advisor=new RegexpMethodPointcutAdvisor();
    advisor.setAdvice(testLogInterceptor());  // 通知者
    advisor.setPattern("test.TestCalculate.*"); // 切点
    return  advisor;
}

/**
 * FactoryBean方式单个: ProxyFactoryBean
 *  控制粒度到方法
 *  问题:只能指定单一的Bean的AOP,
 *  如果多个Bean需要创建多个ProxyFactoryBean 。
 **/
@Bean
public ProxyFactoryBean calculateProxy(){
    ProxyFactoryBean userService = new ProxyFactoryBean();
    userService.setInterceptorNames("testLogAspectAdvisor");
    userService.setTarget(testCalculate());
    return userService;
}

从上述代码中可以看到calculateProxy这个 bean 配置了一个advisor,内部还可以指定切点,设置更细粒度的控制,从方法级别继续拦截,不在局限于类

// ProxyFactoryBean
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EalyAopMainConfig.class);
Calculate calculateProxy = ctx.getBean("calculateProxy",Calculate.class);
System.out.println(calculateProxy.getClass());
calculateProxy.div(1,1);
--------------------执行结果--------------------
invoke method div() <BeforeAdvice>,args[1, 1]
Invoke Method:div

但是面所有的实现都存在一个共同的问题,它们都得为每个 bean 都配置一个代理,之后获取 bean 的时候需要获取这个代理类的 bean 实例(例如ctx.getBean("calculateProxy",Calculate.class))。那么怎么才能实现自动根据类型注入呢?下面介绍 autoproxy 的解决方案。

autoproxy:顾名思义就是实现自动代理,当 Spring 发现一个 bean 需要被切面织入的时候,Spring 会自动生成这个 bean 的一个代理来拦截方法的执行,确保定义的切面能被执行。

下面我们可以去掉原来的 ProxyFactoryBean 的配置,改为使用 BeanNameAutoProxyCreator 来配置:

/**
 * autoProxy: BeanPostProcessor手动指定Advice方式 BeanNameAutoProxyCreator
 **/
@Bean
public BeanNameAutoProxyCreator autoProxyCreator() {
    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
    //设置要创建代理的那些Bean的名字
    // 以test开头的类都创建代理
    beanNameAutoProxyCreator.setBeanNames("test*"); 
    //设置拦截链名字(这些拦截器是有先后顺序的)
    // setInterceptorNames(String... interceptorNames) 可以设置多个
    beanNameAutoProxyCreator.setInterceptorNames("testLogAspectAdvisor");
    return beanNameAutoProxyCreator;
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EalyAopMainConfig.class);
Calculate calculateProxy = ctx.getBean("calculateProxy",Calculate.class);
System.out.println(calculateProxy.getClass());
calculateProxy.div(1,1);
calculateProxy.add(1,1);
--------------------执行结果--------------------
class com.sun.proxy.$Proxy11
invoke method div() <BeforeAdvice>,args[1, 1]
invoke method div() <BeforeAdvice>,args[1, 1]
Invoke Method:div
Invoke Method:add

从执行结果可以发现,我们在使用时已经完全不需要关心代理,直接使用原来的类型即可。例如我们使用BeanNameAutoProxyCreator,它只需要指定被拦截类名的模式 (如 *ServiceImpl),它可以配置多次,这样就可以用来匹配不同模式的类了。

当然,我们也可以不配置BeanNameAutoProxyCreator autoProxyCreator()直接使用下面的配置,让所有的 Advisor 自动生效。也就不需要autoProxyCreator()的配置了

@Bean
public DefaultAdvisorAutoProxyCreator autoProxyCreator() {
    return new DefaultAdvisorAutoProxyCreator();
}

到这里,已经可以看出能够十分方便的创建代理了,基于注解形式的也就是在此基础上进行改造读取切面类,及方法上的注解而已。

下面是基于注解形式的实现

切面类:

@Aspect
@Order
@Component
public class TestLogAspect {

    /*引入: 不常用*/
    @DeclareParents(value="cn.zhe.hello.aop.TestCalculate",   // 动态实现的类
            defaultImpl = SimpleProgramCalculate.class)  // 引入的接口的默认实现
    public static ProgramCalculate programCalculate;    // 引入的接口

    @Pointcut("execution(* cn.zhe.hello.aop.TestCalculate.*(..))") // 切点
    public void pointCut(){};

    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Invoke Method "+methodName+"() <Before Advice>,args"+ Arrays.asList(joinPoint.getArgs()));
    }

    @After(value = "pointCut()")
    public void methodAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Invoke Method "+methodName+"() <After Advice>,args"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void methodReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Invoke Method "+methodName+"() <Return Advice>,args"+Arrays.asList(joinPoint.getArgs()));
    }

    @AfterThrowing(value = "pointCut()")
    public void methodAfterThrowing(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Invoke Method "+methodName+"() <Exception Advice>,args"+Arrays.asList(joinPoint.getArgs()));
    }

}

测试类及运行结果:

public class TestMainClass {
    public static void main(String[] args) {
       AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
        Calculate calculate = (Calculate) ctx.getBean("testCalculate");
        int retVal = calculate.div(2,4);
    }
}
--------------------执行结果--------------------
Invoke Method div() <Before Advice>,args[2, 4]
Invoke Method:div
Invoke Method div() <Return Advice>,args[2, 4]
Invoke Method div() <After Advice>,args[2, 4]

SpringAOP 源码解析

相信大家都指定 Spring 中的 AOP 是通过动态代理来实现的。Spring 通过一个 @EnableAspectJAutoProxy 注解开启AOP,Spring 通过定义一个切面类 (标注@Aspect) ,并定义一个 @Pointcut 方法,最后再定义一些列的增强方法,就能完成一些列的对象切面操作。增强方法类型如下:

// @环绕通知、前置通知、后置通知、返回通知、异常通知
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class

由我们上面所了解到的知识,可以推测出它大概分为以下几个步骤:

  • 找到所有的切面类并解析 (解析切面类)
  • 解析出所有的 Advice 并保存 (缓存通知)
  • 创建一个动态代理 (JDK OR CGLIB)
  • 调用被代理类的方法时,找到所有增强 (Advice),并增强方法

先来看一下整体流程图
在这里插入图片描述
下面我们来逐步分解源码

一、注册 Bean的后置处理器

在这里插入图片描述
首先 Spring 通过一个 @EnableAspectJAutoProxy 注解开启AOP,点进去我们可以发现,在注解类上面发现了另一个注解 @Import(AspectJAutoProxyRegistrar.class),该类实现了 ImportBeanDefinitionRegistrar ,所以他会通过registerBeanDefinitions()为容器导入 BeanDefinition。

在postProcessBeforeInstantiation中解析切面逻辑将在后文解释。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented1
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}

在这里插入图片描述

二、解析切面类

在我们的创建Bean调用doCreatBean()方法之前会调用 applyBean
在这里插入图片描述

@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
   // 获取容器中所有的后置处理器
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      // 判断后置处理器是不是InstantiationAwareBeanPostProcessor
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         // 把BeanPostProcessor强制转为InstantiationAwareBeanPostProcessor
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         /**
          * @EnableAspectJAutoProxy为容器导入AnnotationAwareAspectJAutoProxyCreator
          * @EnableTransactionManagement:导入了InfrastructureAdvisorAutoProxyCreator
          * 它们都是实现了BeanPostProcessor接口执行InstantiationAwareBeanPostProcessor
          * 方法进行后置处理解析切面
          */
         Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
         if (result != null) {
            return result;
         }
      }
   }
   return null;
}

然后走到AbstractAutoProxyCreatorpostProcessBeforeInstantiation()方法

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
   // 构建我们的缓存key
   Object cacheKey = getCacheKey(beanClass, beanName);
   // 以及被解析过 直接返回
   if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
         return null;
      }
      /**
       * 判断是不是基础的bean
       * 判断是不是应该跳过 (aop解析直接解析出我们的切面信息(并且把切面信息进行保存),
       * 而事务在这里是不会解析的)
       * 跳过 Advice Pointcut Advisor AopInfrastructureBean 及原始Bean
       */
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
          // 进行标记,以后也可以直接进行忽略
          this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return null;
      }
   }
   // 生成代理对象
   // 这个地方一般是不会生成代理对象的(targetSource = null),除非我们的容器中有TargetSourceCreator 
   // 并且我们的bean需要实现TargetSource接口
   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;
}

有两种类型表示该bean不能被增强:1.是切面类型的;2.是原始类型的,原始bean 的后缀是有.ORIGINAL标志的。下面进入 AspectJAwareAdvisorAutoProxyCreator.javashouldSkip方法,注意千万不要找错了。

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
   // 获得所有的通知者
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   for (Advisor advisor : candidateAdvisors) {
      if (advisor instanceof AspectJPointcutAdvisor &&
            ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
         return true;
      }
   }
    // 如果不是切面类,进入父类方法。如果是原始实例,如果是就返回true,表示其也不能被增强
    // 这个方法才是AbstractAutoProxyCreator.java的shouldSkip
   return super.shouldSkip(beanClass, beanName);
}

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    return AutoProxyUtils.isOriginalInstance(beanName, beanClass);
}

然后进入 AnnotationAwareAspectJAutoProxyCreator.javafindCandidateAdvisors()方法,它找到接口,注解相关的 Advisor。

@Override
protected List<Advisor> findCandidateAdvisors() {
   // 找出事务相关的advisor
   List<Advisor> advisors = super.findCandidateAdvisors();
   // 找出Aspect相关的信息之后封装为一个advisor
   if (this.aspectJAdvisorsBuilder != null) {
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   // 返回我们所有的通知
   return advisors;
}

protected List<Advisor> findCandidateAdvisors() {
	// 通过通知者探测器来帮助找到通知(事务相关,这里事务源码中研究)
	return this.advisorRetrievalHelper.findAdvisorBeans();
}

/**
* 首先尝试从拿到缓存的advisors,如果没有缓存,那么就从bean工厂中尝试找出从bean工厂中寻找继承了
* Advisor.class接口的类,并过滤出单例类型的beanName返回。·如果项目中没有继承接口Advisor的bean,
* 那么直接返回new一个了ArrayList返回。如果有继承Advisor接口的bean,那么就从bean工厂中实例化出来添
* 加到advisors list中返回上层执行后续的代码
*/
public List<Advisor> findAdvisorBeans() {
		// 尝试从缓存中拿切面的beanName
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
			// 找到Advisor类型的beanName,如果没有返回的是{}
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		}
		// 若在容器中没有找到,直接返回一个空的集合
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}

		List<Advisor> advisors = new ArrayList<>();
		// ioc容器中找到了我们配置的 BeanFactoryTransactionAttributeSourceAdvisor
		for (String name : advisorNames) {
			// 判断他是不是一个合适的
			if (isEligibleBean(name)) {
				// BeanFactoryTransactionAttributeSourceAdvisor是不是正在创建的bean
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				} else {
					try {
						// 显示的调用getBean方法创建BeanFactoryTransactionAttributeSourceAdvisor返回
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						// 此处代码省略
					}
				}
			}
		}
		return advisors;
	}

然后进入buildAspectJAdvisors()方法

public List<Advisor> buildAspectJAdvisors() {
    // 用于保存切面的名称,aspectNames 是类级别的缓存,用户缓存已经解析出来的切面信息
    List<String> aspectNames = this.aspectBeanNames;
    // 缓存字段aspectNames没有值 表示实例化第一个单实例bean的时候就会触发解析切面的操作
    if (aspectNames == null) {
        // 做了dcl检查
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                // 用于保存所有解析出来的Advisors集合对象
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                /**
				* aop功能中在这里传入的是Object对象,代表去容器中获取到所有的组件的名
				* 称,然后再进行遍历,这个过程是十分的消耗性能的,所以spring会再这里加
				* 入了保存切面信息的缓存。但是事务功能不一样,事务模块的功能是直接去容
				* 器中获取Advisor类型的,选择范围小,切不消耗性能。所以spring在事务模
				* 块中没有加入缓存来保存我们的事务相关的advisor
				*/
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Object.class, true, false);
                // 遍历IOC容器中获取处的所有bean的名称
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // 通过beanName去容器中获取到对应class对象
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // 根据class对象判断是不是切面
                    if (this.advisorFactory.isAspect(beanType)) {
                        // 是切面类加入到缓存中
                        aspectNames.add(beanName);
                        // 把beanName和class对象构建成为一个AspectMetadata
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 构建切面注解的实例工厂
                            MetadataAwareAspectInstanceFactory factory =
                                new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // 真正的去获取的实例工厂
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            // 加入到缓存中
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                        else {
                           // 此处省略部分代码
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    // 不是第一次进入,直接区缓存中取
    List<Advisor> advisors = new ArrayList<>();
    for (String aspectName : aspectNames) {
        // 从缓存取
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

然后就进入到了getAdvisors()方法,执行解析切面类的详细逻辑

@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
   // 获取标记为Aspect的类
   Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   // 获取切面类的名称
   String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
   // 校验切面类
   validate(aspectClass);

   // 使用的是包装模式来包装我们的MetadataAwareAspectInstanceFactory
   // 构建为 MetadataAwareAspectInstanceFactory
   MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

   List<Advisor> advisors = new ArrayList<>();
   // 获取到切面类中的所有方法,但是该方法不会解析标注了@PointCut注解的方法
   for (Method method : getAdvisorMethods(aspectClass)) {
      // 逐个解析切面中的方法
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   // If it's a per target aspect, emit the dummy instantiating aspect.
   if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
   }

   // Find introduction fields.
   for (Field field : aspectClass.getDeclaredFields()) {
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   return advisors;
}

然后看一下getAdvisor()方法

@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

   // 切面的方法上构建 切点表达式(解析切点)
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
   // 实例化我们的切面通知对象 (创建切面)
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

最后解析构建的通知对象ReflectiveAspectJAdvisorFactorygetAdvice()方法

@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
      MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
   //获取切面类的class对象
   Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   validate(candidateAspectClass);
   // 获取切面方法上的注解
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   // 解析出来的注解信息是否为null
   if (aspectJAnnotation == null) {
      return null;
   }

   // 判断class对象是不是切面信息对象
   if (!isAspect(candidateAspectClass)) {
      throw new AopConfigException("Advice must be declared inside an aspect type: " +
            "Offending method '" + candidateAdviceMethod + "' in class [" +
            candidateAspectClass.getName() + "]");
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Found AspectJ method: " + candidateAdviceMethod);
   }

   AbstractAspectJAdvice springAdvice;
   // 判断标注在方法上的注解类型
   switch (aspectJAnnotation.getAnnotationType()) {
      // 是PointCut注解 那么就抛出异常
      case AtPointcut:
         if (logger.isDebugEnabled()) {
            logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
         }
         return null;
      // 环绕通知 构建AspectJAroundAdvice
      case AtAround:
         springAdvice = new AspectJAroundAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      // 前置通知  构建AspectJMethodBeforeAdvice
      case AtBefore:
         springAdvice = new AspectJMethodBeforeAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      // 后置通知 AspectJAfterAdvice
      case AtAfter:
         springAdvice = new AspectJAfterAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         break;
      // 返回通知 AspectJAfterReturningAdvice
      case AtAfterReturning:
         springAdvice = new AspectJAfterReturningAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterReturningAnnotation.returning())) {
            springAdvice.setReturningName(afterReturningAnnotation.returning());
         }
         break;
      // 异常通知   AspectJAfterThrowingAdvice
      case AtAfterThrowing:
         springAdvice = new AspectJAfterThrowingAdvice(
               candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
         AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
         if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
            springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
         }
         break;
      default:
         throw new UnsupportedOperationException(
               "Unsupported advice type on method: " + candidateAdviceMethod);
   }

   // 配置构建出来的通知对象
   springAdvice.setAspectName(aspectName);
   springAdvice.setDeclarationOrder(declarationOrder);
   String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
   if (argNames != null) {
      springAdvice.setArgumentNamesFromStringArray(argNames);
   }
   springAdvice.calculateArgumentBindings();

   return springAdvice;
}

至此我们解析切面就结束了,此时我们的注解、接口、xml标注的切面、切点等都会被解析。

三、创建代理

创建代理又可以分为三个步骤

  • 获取 Advisors
  • Advisors 匹配切点
  • 创建代理
    在这里插入图片描述
    在这里插入图片描述

① 获取 Advisors

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   // 获取我们容器中的所有的bean的后置处理器
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
	  // 在这里是后置处理器的【第九次调用】 aop和事务都会在这里生存代理对象
      Object current = processor.postProcessAfterInitialization(result, beanName);
      //若只有有一个返回null 那么直接返回原始的
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

// 在该后置方法中 事务和aop的代理对象都是在这生成的
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 获取缓存key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 找到合适的就会被代理
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

然后进入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;
   }
   // 是不是基础的bean 是不是需要跳过的
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 如果有通知,就创建代理对象
   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;
}

再进入到 getAdvicesAndAdvisorsForBean()方法,找到合适的通知,在这里我们会发现又链到了上面我吗解析切面类中的的findCandidateAdvisors()方法,由于我们**之前已经解析了切面、切点并进行了缓存 (切面类的BeanName) 所以只需要到缓存中去取就行了**。

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   // 找到ioc容器中候选的通知(拿到了接口方式的AOP,事务AOP就是从这里拿到的)
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   // 判断我们的通知能不能作用到当前的类上
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   // 对我们的advisor进行排序
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}

② Advisors 匹配切点

从上一步的代码中进入到findAdvisorsThatCanApply()方法,匹配切点

protected List<Advisor> findAdvisorsThatCanApply(
      List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
   // 用来记录当前正在创建的被代理对象的名称
   ProxyCreationContext.setCurrentProxiedBeanName(beanName);
   try {
      // 从候选的通知器中找到合适正在创建的实例对象
      return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
   }
   finally {
      ProxyCreationContext.setCurrentProxiedBeanName(null);
   }
}

// 进入到AOPUtil的findAdvisorsThatCanApply
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            continue;
        }
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

然后再进入canApply()会对筛选出来的Advisor 拿到切点后进行初筛精筛,获取到合适的增强。关于初筛和精筛就不过多的叙述了,主要功能如下:

  • **初筛: **类级别的过来,通过 AspectJ
  • 精筛: 匹配所有方法,按切点表达式和方法匹配器匹配,只要有一个匹配上就创建代理
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
   Assert.notNull(pc, "Pointcut must not be null");
   // 初筛
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }
   // .... 省略代码 ....
   //  // 精筛 循环所有class对象
    for (Class<?> clazz : classes) {
        // 找到class对象所有的方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                // 通过切点表达式来匹配 AspectJ
                introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                // 通过方法匹配器匹配,内置AOP接口方式
                methodMatcher.matches(method, targetClass)) {
                // 只有有一个方法匹配上就创建代理
                return true;
            }
        }
    }

   return false;
}

③ 创建代理

再次回到wrapIfNecessary()方法,调用createProxy()方法,原理就是:先判断是否设置了 proxyTargetClass=true 如果是创建 CGLIB 代理,在判断是否有接口,有接口创建 JDK 代理,无接口创建 CGLIB 代理。

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // 创建一个代理对象工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    // 为proxyFactory设置创建jdk代理还是cglib代理
    // 是否设置了 proxyTargetClass = true  => cglib代理
    // 有接口 => jdk 代理  无接口 => cglib 代理
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 把我们的specificInterceptors数组中的Advisor转化为数组形式的
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 为我们的代理工加入通知器,
    proxyFactory.addAdvisors(advisors);
    // 设置targetSource对象
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 真正的创建代理对象
    return proxyFactory.getProxy(getProxyClassLoader());
}

四、代理类的调用

前面已经自己做了基于 Advisor 的 AOP,那么 Spring 也是这样,在执行时,只需要将这些增强添加到被代理的类上即可。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   // 获取当前被代理类
   TargetSource targetSource = this.advised.targetSource;
   Object target = null;
   // equals,hashcode等方法不做代理,直接调用
   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         return hashCode();
      }
      // 若执行的class对象是DecoratingProxy 也不要拦截器执行
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      // 将代理对象放到线程本地变量中
      if (this.advised.exposeProxy) {
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // 将增加器装换为方法执行拦截器链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // 将拦截器链包装为ReflectiveMethodInvocation并执行
         // 责任链统一抽象
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // 执行此方法
          retVal = invocation.proceed();
      }
		//.... 省略代码 ....
   }
}

在这里插入图片描述

然后将增强器装换为方法拦截器链,最终包装为ReflectiveMethodInvocation执行它的proceed方法,以 JDK 代理为例

@Override
@Nullable
public Object proceed() throws Throwable {
   //  当执行到最后一个拦截器的时候才会进入
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
   // 获取集合当前需要运行的拦截器
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      // 执行拦截器方法
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

然后进入到ExposeInvocationInterceptor.javainvoke()方法

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   MethodInvocation oldInvocation = invocation.get();
   invocation.set(mi);
   try {
      return mi.proceed();
   }
   finally {
      invocation.set(oldInvocation);
   }
}

AspectJAfterAdviceinvoke()方法,返回拦截器,方法执行失败,不会调用

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   Object retVal = mi.proceed();
   this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
   return retVal;
}

AspectJAfterAdviceinvoke()方法,后置拦截器,总是执行

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

MethodBeforeAdviceInterceptorinvoke()方法,后置拦截器,总是执行

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}

ThrowsAdviceInterceptorinvoke()方法,异常拦截器,异常时执行

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   try {
      return mi.proceed();
   }
   catch (Throwable ex) {
      Method handlerMethod = getExceptionHandler(ex);
      if (handlerMethod != null) {
         invokeHandlerMethod(mi, ex, handlerMethod);
      }
      throw ex;
   }
}

看到上面的几个通知执行方法没有?都在调用mi.proceed()就是责任链的递归调用,将所有的通知切入我们的增强类中,SpringAOP就实现了。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-13 12:40:04  更:2021-12-13 12:41:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:03:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码