Spring AOP的介绍:
什么是Spring AOP:
面向切面编程,基于动态代理实现的,如果动态代理不了解的,可以翻阅我其它的博客;也就是在执行方法前、方法后和出现异常后做拦截或者做增强处理,我们常用的使用方式就是@AspectJ注解、自己可以使用spring后置处理器自己来实现或者通过实现接口的方式、XML配置的方式;
AOP与AspectJ的关系:
Aspect属于Eclipse基金会,增强的方式是静态织入,它是通过修改代码来实现的,要使用它还要使用它特定的编译器;
SringAO和AspectJ没有什么关系,只是使用了AspectJ的一些概念,用到了其切点解析和匹配。
Spring Aop的发展:
Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这样在使用CGLB代理是不需要显示的引入这两个依赖。
- Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的,想看源码的同学可以从这里起步。
- Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
- Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫 做@AspectJ,但是这个和 AspectJ 其实没啥关系。
先来介绍Spring 1.2中基于接口的Aop ,直接上代码,不做介绍了,因为太简单了。
定义advice和intercepter:
public class MyLogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//Object invoke = method.invoke(target, args);
String name = method.getName();
System.out.println("执行目标方法:"+name+"的前置通知,入参为:"+ Arrays.asList(args));
}
}
public class MyInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
System.out.println("执行目标方法前置通知:"+method.getName());
Object proceed = invocation.proceed();
System.out.println("执行目标方法后置置通知:"+method.getName());
return proceed;
}
}
定义一个目标方法:
```java
@Component
public class MyCalculate implements Calculate {
private int num = 0;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public int add(int numA, int numB) {
System.out.println("执行目标方法:add");
//1 / 0
return numA + numB;
}
@Override
public int sub(int numA, int numB) {
System.out.println("执行目标方法:sub");
return numA - numB;
}
}
加入到容器中
@Bean
public Calculate calculate() {
return new MyCalculate();
}
@Bean
public MethodBeforeAdvice myLogAdvice() {
return new MyLogAdvice();
}
@Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
然后利用FactoryBean创建代理类;
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames("myLogAdvice","myInterceptor");
proxyFactoryBean.setTarget(calculate());
return proxyFactoryBean;
}
执行一下
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
Calculate calculate = context.getBean("proxyFactoryBean",Calculate.class);
//calculate.sub(7, 8);
calculate.add(1, 2);
结果为:
从结果看,使用了责任链的方式对advice和Intercept都进行了调用,使用了FactoryBean的getObject方法创建一个代理类实现的。
下面介绍Advisor,它包含了advice增强和porintCut切点。Advisor有好几个实现类,比如NameMatchMethodPointcutAdvisor
@Bean
public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
advisor.setAdvice(myLogAdvice());
advisor.setMappedName("add");
return advisor;
}
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames("nameMatchMethodPointcutAdvisor");
proxyFactoryBean.setTarget(calculate());
return proxyFactoryBean;
}
只对add方法进行拦截
自动生成代理类:BeanNameAutoProxyCreator
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setInterceptorNames("myLogAdvice");
beanNameAutoProxyCreator.setBeanNames("calculate");
return beanNameAutoProxyCreator;
}
其实 Advisor 还有一个更加灵活的实现类 RegexpMethodPointcutAdvisor,它能实现正则匹配
DefaultAdvisorAutoProxyCreator,它的配置非常简单,直接使用下面这段配置就可以了,它 就会使得所有的 Advisor 自动生效,无须其他配置。(记得把之前的autoProxyCreator配置去掉,无需创建2次代理)
@Bean
public DefaultAdvisorAutoProxyCreator autoProxyCreator(){
return new DefaultAdvisorAutoProxyCreator();
}
下面进入正题:Spring Aop @Aspect注解的源码解析
先上自己画的流程图:
直接上代码:不做过多解释,因为代码上都有注释:
使用AOP前要在配置类加上@EnableAspectJAutoProxy注解,其有proxyTargetClass属性,当为ture则强制使用CGLB,如果不设置默认为false,自己判断使用JDK还是CGLB,它的作用就是加入AOP代理对象的创建类AnnotationAwareAspectJAutoProxyCreator
因为Aop代理对象实现了后置处理器SmartInstantiationAwareBeanPostProcessor,所以容器启动的过程中,会为符合AOP规则的bean创建动态代理加入到容器中;
容器在启动的时候会在九处调用后置处理器,如下图,也是自己画的:
那么接下来直接上代码:
容器启动的时候会执行org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation:
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
//获取当前bean是否可以创建AOP的缓存
Object cacheKey = getCacheKey(beanClass, beanName);
//当前是否为是 TargetSource对象,通过自定义targetSource对象可以获取对象,这里就不对此用法做出详解
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//判断当前bean是否被解析过
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//1、判断当前Bean是否A为OP的相关基础类Advice.class Pointcut.class Advisor.class AopInfrastructureBean
//2、判断当前bean是否为AspectJPointcutAdvisor :如果 <aop:aspect ref="beanName"> 就会生成此类
//3、如何符合上述条件,则说明不需要进行AOP,加入到缓存中advisedBeans.put(cacheKey, Boolean.FALSE);
//shouldSkip这个里面的逻辑还是比较多的:
// a、它会获取xml配置的Advisor和原生接口的AOP的Advisor ,在第一次doCreateBean时会把获取
//到的beanName缓存到BeanFactoryAdvisorRetrievalHelper.cachedAdvisorBeanNames缓存中,后面会接着使用
// b、获取@Aspect中配置的Advisor,把得到的Advisor的beanName缓存BeanFactoryAspectJAdvisorsBuilder.aspectBeanNames,
// 的到的对象缓存到Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>()
//c、接着判断当前bean是否是AspectJPointcutAdvisor
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
//如果是自定义的 TargetSource对象,则不进行后面doCreateBean的操作,直接进行AOP的操作
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;
}
接着执行org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization,也就是这一步创建了代理对象。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
//获取当前Bean相关AOP使用的Key;
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//因为循环依赖创建的AOP动态代理,则不在创建,并且直接移除
if (this.earlyProxyReferences.remove(cacheKey) != bean) {