知识储备:详解Spring中AOP原理(基于注解版)1

一:准备工作

1.创建maven工程导入jar包依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.peng.demo</groupId>
  <artifactId>spring-aop</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-context</artifactId>
  		<version>5.0.10.RELEASE</version>
  	</dependency>
  	
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-aspects</artifactId>
  		<version>5.0.10.RELEASE</version>
  	</dependency>
  	
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.12</version>
  		<scope>test</scope>
  	</dependency>
  	
  </dependencies>
  
</project>

2.编写一个数学计算类,通过AOP对其中的方法进行增强

public class MathCal {
	
	public int add(int a,int b){
		System.out.println("加法计算运行啦!!!!!!!!!!!");
		return a+b;
	}

}

3.编写切面类

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**   
* 类描述:   切面类
* @version     
*/
@Aspect
public class LogAspect {
	
	@Pointcut("execution(public int com.peng.demo.MathCal.add(int,int))")
	public void expression(){};
	
	/**
	 * 
	 * @Description 前置通知
	 */
	@Before("expression()")
	public void logStart(JoinPoint joinPoint){
		String name = joinPoint.getSignature().getName();
		Object[] args = joinPoint.getArgs();
		System.out.println("方法即将开始运行........方法参数为{"+name+"-->"+Arrays.asList(args)+"}");
	}
	
	/**
	 * 
	 * @Description 后置通知
	 */
	@After("expression()")
	public void logEnd(){
		System.out.println("方法运行结束........");
	}
	
	/**
	 * 
	 * @Description 返回通知
	 * joinPoint必须放在参数第一位
	 */
	@AfterReturning(value="expression()",returning="result")
	public void logReturn(JoinPoint joinPoint,Object result){
		System.out.println("方法运行返回........运行结果为{"+result+"}");
	}
	
	/**
	 * 
	 * @Description 异常通知
	 */
	@AfterThrowing(value="expression()",throwing="e")
	public void logExeception(Exception e){
		System.out.println("方法运行出现异常啦........异常为{"+e+"}");
	}

}

注:一定要加@Aspect注解,告诉容器这是一个切面类,方法参数中JoinPoint必须在第一位

4.编写配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.peng.demo.LogAspect;
import com.peng.demo.MathCal;

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {

	@Bean
	public MathCal mathCal(){
		return new MathCal();
	}
	
	
	@Bean
	public LogAspect logAspect(){
		return new LogAspect();
	}
	

}

注:一定要加@EnableAspectJAutoProxy,开启Aspect自动代理,将两个组件通过@Bean加入IOC容器中。

5.编写测试类

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.peng.demo.config.AspectConfig;

public class AspectTest {

	
	@Test
	public void test(){
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AspectConfig.class);
		MathCal bean = applicationContext.getBean(MathCal.class);
		int add = bean.add(2, 10);
		System.out.println(add+"----test!!!!!!!!!!!!!!!");
	}
	
}

6.工程目录

《知识储备:详解Spring中AOP原理(基于注解版)1》

7.运行测试类

《知识储备:详解Spring中AOP原理(基于注解版)1》

注:测试发现,各种通知已经织入切点

8.在add方法中制造一个异常

《知识储备:详解Spring中AOP原理(基于注解版)1》

测试发现异常通知也起作用了。

《知识储备:详解Spring中AOP原理(基于注解版)1》

二:原理解析

1.我们从配置类上的@EnableAspectJAutoProxy注解入手,进入发现这个注解上又有一个@Import(AspectJAutoProxyRegistrar.class)注解,了解Spring注解式开发的都知道,@Import是可以导入组件到IOC容器中的,而其中的AspectJAutoProxyRegistrar是实现了ImportBeanDefinitionRegistrar接口,可以实现批量导入组件到IOC容器中,那么我们就研究一下这个类。

2.进入AspectJAutoProxyRegistrar类中,发现其中重写了registerBeanDefinitions()方法,在这个方法上打一个断点,以debug启动测试方法。

《知识储备:详解Spring中AOP原理(基于注解版)1》

3.debug跟进,来到AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);,F5一直跟进,来到AopConfigUtil.registerOrEscalateApcAsRequired();方法

《知识储备:详解Spring中AOP原理(基于注解版)1》

会发现,首先判断当前容器中是否包含org.springframework.aop.config.internalAutoProxyCreator组件的定义,debug发现当前容器中没有,if没有进去,接着往下走。cls为org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator类型,通过RootBeanDefinition其进行包装,通过registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);将其加入到IOC容器中,并命名为org.springframework.aop.config.internalAutoProxyCreator,到这里容器中就有internalAutoProxyCreator类型的组件了,其实这个组件实际类型为AnnotationAwareAspectJAutoProxyCreator。那么向容器中加入AnnotationAwareAspectJAutoProxyCreator有啥用呢?我们就来研究一下AnnotationAwareAspectJAutoProxyCreator。

4.进入AnnotationAwareAspectJAutoProxyCreator,通过查看其父类AbstractAutoProxyCreator发现AbstractAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware

《知识储备:详解Spring中AOP原理(基于注解版)1》

查看SmartInstantiationAwareBeanPostProcessor发现他是BeanPostProcessor的子接口,BeanPostProcessor是后置处理器的总接口(如果有不知道后置处理器的可查看https://blog.csdn.net/qq_36625757/article/details/83616097这篇博客,其中有介绍后置处理器的实现原理),那么也就说明AbstractAutoProxyCreator是后置处理器,而他又实现BeanFactoryAware,所以在初始化AbstractAutoProxyCreator的时候,会通过setBeanFactory()向其注入BeanFactory,接下来就看看AbstractAutoProxyCreator作为后置处理器和BeanFactory都干了些啥。

5.首先我们需要加一些断点,辅助我们调试,我们需要在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.setBeanFactory(BeanFactory)上打一个断点,查看作为BeanFactoryAware啥时候调用了setBeanFactory()方法。然后在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessBeforeInstantiation(Class<?>, String)org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(Object, String)上个加一个断点,查看作为后置处理器啥时候调用了这两个方法。之后在org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.setBeanFactory(BeanFactory)上加一个断点,因为他重写了父类的setBeanFactory()方法。最后在org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.initBeanFactory(ConfigurableListableBeanFactory)上加一个断点,因为他重写了父类的initBeanFactory()方法。

6.从头看起,测试方法中,开始先new了一个AnnotationConfigApplicationContext传入一个配置类,在其构造方法上加一个断点,跟进查看

《知识储备:详解Spring中AOP原理(基于注解版)1》

看到先注册配置类然后调用了refresh()方法,F5进入refresh()方法,看到调用了registerBeanPostProcessors(beanFactory);方法

《知识储备:详解Spring中AOP原理(基于注解版)1》

进入registerBeanPostProcessors(beanFactory);方法,一直来到PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);中,看到先通过beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);获得容器中已经定义了的所有BeanPostProcessor类型的组件名数组,并向容器中加了一下额外的后置处理器。

《知识储备:详解Spring中AOP原理(基于注解版)1》

接着跟进,后面通过循环将后置处理器进行分组,因为后置处理器可以通过实现PriorityOrdered接口,定制优先级。接着下来看到,优先注册实现PriorityOrdered接口的后置处理器,在来注册实现Ordered的接口的后置处理器,最后注册没有实现优先级接口的后置处理器。

《知识储备:详解Spring中AOP原理(基于注解版)1》

走到实现Ordered接口的后置处理器的循环中,发现org.springframework.aop.config.internalAutoProxyCreator组件也在其中

《知识储备:详解Spring中AOP原理(基于注解版)1》

上面我们已经提到过,其实internalAutoProxyCreator就是AnnotationAwareAspectJAutoProxyCreator,而AnnotationAwareAspectJAutoProxyCreator也是一个后置处理器,那么AnnotationAwareAspectJAutoProxyCreator实现了Ordered接口吗?通过查看源码发现,他的父类AbstractAutoProxyCreator继承了ProxyProcessorSupport,而ProxyProcessorSupport实现了Ordered接口,那么就看看AnnotationAwareAspectJAutoProxyCreator是如何被创建出来的。

《知识储备:详解Spring中AOP原理(基于注解版)1》

《知识储备:详解Spring中AOP原理(基于注解版)1》

在for循环中调用了 beanFactory.getBean(ppName, BeanPostProcessor.class);方法,F5跟进,来到AbstractBeanFactory.doGetBean(String, Class<T>, Object[], boolean)方法,一路来到getSingleton()方法,获得bean,但由于容器第一次创建,根本就获取不到,就来到DefaultSingletonBeanRegistry.getSingleton(String, ObjectFactory<?>)方法,就通过singletonFactory.getObject();创建bean,实际就是创建后置处理器并保存到容器中。

《知识储备:详解Spring中AOP原理(基于注解版)1》

那么容器是如何创建internalAutoProxyCreator【AnnotationAwareAspectJAutoProxyCreator】的呢?

跟进singletonFactory.getObject();一直来到AbstractAutowireCapableBeanFactory.doCreateBean(String, RootBeanDefinition, Object[])方法

《知识储备:详解Spring中AOP原理(基于注解版)1》

进入doCreateBean()方法中,对internalAutoProxyCreator进行创建

《知识储备:详解Spring中AOP原理(基于注解版)1》

之后就是对bean进行属性赋值

《知识储备:详解Spring中AOP原理(基于注解版)1》

populateBean(beanName, mbd, instanceWrapper);后有一个initializeBean(beanName, exposedObject, mbd);就是初始化bean,进入初始化bean方法

《知识储备:详解Spring中AOP原理(基于注解版)1》

有一个invokeAwareMethods(beanName, bean);方法,进到其中发现,在判断bean是否是XXXAware接口的实现,而通过前面分析,我们的internalAutoProxyCreator【AnnotationAwareAspectJAutoProxyCreator】实现了BeanFactoryAware接口,所以这里就是在为其赋值,先调用其父类的方法AbstractAdvisorAutoProxyCreator.setBeanFactory(BeanFactory)

《知识储备:详解Spring中AOP原理(基于注解版)1》

《知识储备:详解Spring中AOP原理(基于注解版)1》

调用父类的setBeanFactory()后,有调用了initBeanFactory((ConfigurableListableBeanFactory) beanFactory);方法,初始化beanFactory,最终调到AnnotationAwareAspectJAutoProxyCreator.initBeanFactory(ConfigurableListableBeanFactory)中的initBeanFactory()方法,在其中创建了反射的通知工厂和通知构建器的适配器。

《知识储备:详解Spring中AOP原理(基于注解版)1》

invokeAwareMethods(beanName, bean);方法执行完毕后会调用applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);方法,这是在调用所有后置处理器的postProcessBeforeInitialization()方法(这一步在https://blog.csdn.net/qq_36625757/article/details/83616097这个博客中也有说明),之后又调用invokeInitMethods(beanName, wrappedBean, mbd);方法,这是在调用所有的bean的初始化方法,之后有调用applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);方法,这是在调用所有后置处理器的postProcessAfterInitialization()方法。

至此,后处理器就创建完成了,然后调用beanFactory.addBeanPostProcessor(postProcessor);将BeanPostProcessor添加到beanFactory当中。

《知识储备:详解Spring中AOP原理(基于注解版)1》

7.以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程,当AnnotationAwareAspectJAutoProxyCreator被创建出来,后来的bean的创建都需要通过这个后置处理器,下面我们就看一下,AnnotationAwareAspectJAutoProxyCreator作为后置处理器都做了些啥,下一个博客继续分享。下一篇地址:https://blog.csdn.net/qq_36625757/article/details/83658735

    原文作者:AOP
    原文地址: https://blog.csdn.net/qq_36625757/article/details/83652173
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞