一:准备工作
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.工程目录
7.运行测试类
注:测试发现,各种通知已经织入切点
8.在add方法中制造一个异常
测试发现异常通知也起作用了。
二:原理解析
1.我们从配置类上的@EnableAspectJAutoProxy注解入手,进入发现这个注解上又有一个@Import(AspectJAutoProxyRegistrar.class)注解,了解Spring注解式开发的都知道,@Import是可以导入组件到IOC容器中的,而其中的AspectJAutoProxyRegistrar是实现了ImportBeanDefinitionRegistrar接口,可以实现批量导入组件到IOC容器中,那么我们就研究一下这个类。
2.进入AspectJAutoProxyRegistrar类中,发现其中重写了registerBeanDefinitions()方法,在这个方法上打一个断点,以debug启动测试方法。
3.debug跟进,来到AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);,F5一直跟进,来到AopConfigUtil.registerOrEscalateApcAsRequired();方法
会发现,首先判断当前容器中是否包含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实现了SmartInstantiationAwareBeanPostProcessor及BeanFactoryAware
查看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传入一个配置类,在其构造方法上加一个断点,跟进查看
看到先注册配置类然后调用了refresh()方法,F5进入refresh()方法,看到调用了registerBeanPostProcessors(beanFactory);方法
进入registerBeanPostProcessors(beanFactory);方法,一直来到PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);中,看到先通过beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);获得容器中已经定义了的所有BeanPostProcessor类型的组件名数组,并向容器中加了一下额外的后置处理器。
接着跟进,后面通过循环将后置处理器进行分组,因为后置处理器可以通过实现PriorityOrdered接口,定制优先级。接着下来看到,优先注册实现PriorityOrdered接口的后置处理器,在来注册实现Ordered的接口的后置处理器,最后注册没有实现优先级接口的后置处理器。
走到实现Ordered接口的后置处理器的循环中,发现org.springframework.aop.config.internalAutoProxyCreator组件也在其中
上面我们已经提到过,其实internalAutoProxyCreator就是AnnotationAwareAspectJAutoProxyCreator,而AnnotationAwareAspectJAutoProxyCreator也是一个后置处理器,那么AnnotationAwareAspectJAutoProxyCreator实现了Ordered接口吗?通过查看源码发现,他的父类AbstractAutoProxyCreator继承了ProxyProcessorSupport,而ProxyProcessorSupport实现了Ordered接口,那么就看看AnnotationAwareAspectJAutoProxyCreator是如何被创建出来的。
在for循环中调用了 beanFactory.getBean(ppName, BeanPostProcessor.class);方法,F5跟进,来到AbstractBeanFactory.doGetBean(String, Class<T>, Object[], boolean)方法,一路来到getSingleton()方法,获得bean,但由于容器第一次创建,根本就获取不到,就来到DefaultSingletonBeanRegistry.getSingleton(String, ObjectFactory<?>)方法,就通过singletonFactory.getObject();创建bean,实际就是创建后置处理器并保存到容器中。
那么容器是如何创建internalAutoProxyCreator【AnnotationAwareAspectJAutoProxyCreator】的呢?
跟进singletonFactory.getObject();一直来到AbstractAutowireCapableBeanFactory.doCreateBean(String, RootBeanDefinition, Object[])方法
进入doCreateBean()方法中,对internalAutoProxyCreator进行创建
之后就是对bean进行属性赋值
在populateBean(beanName, mbd, instanceWrapper);后有一个initializeBean(beanName, exposedObject, mbd);就是初始化bean,进入初始化bean方法
有一个invokeAwareMethods(beanName, bean);方法,进到其中发现,在判断bean是否是XXXAware接口的实现,而通过前面分析,我们的internalAutoProxyCreator【AnnotationAwareAspectJAutoProxyCreator】实现了BeanFactoryAware接口,所以这里就是在为其赋值,先调用其父类的方法AbstractAdvisorAutoProxyCreator.setBeanFactory(BeanFactory)
调用父类的setBeanFactory()后,有调用了initBeanFactory((ConfigurableListableBeanFactory) beanFactory);方法,初始化beanFactory,最终调到AnnotationAwareAspectJAutoProxyCreator.initBeanFactory(ConfigurableListableBeanFactory)中的initBeanFactory()方法,在其中创建了反射的通知工厂和通知构建器的适配器。
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当中。
7.以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程,当AnnotationAwareAspectJAutoProxyCreator被创建出来,后来的bean的创建都需要通过这个后置处理器,下面我们就看一下,AnnotationAwareAspectJAutoProxyCreator作为后置处理器都做了些啥,下一个博客继续分享。