Spring注解驱动开发_AOP 原理(一)

一、AOP 基础知识

1、Spring AOP 底层是基于动态代理实现的,AOP 是指在程序运行期间动态的将某段代码切入到制定方法指定位置进行运行的编程方式。
2、通知(增强)
(1)实际增强的逻辑部分称为通知(增强)
(2)通知有多种类型

  • 前置通知(Before):在目标方法被调用之前调用通知功能
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
  • 返回通知(After-returning):在目标方法成功执行之后调用通知
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

二、AOP 案例

定义一个业务逻辑类(MathCalculator):在业务逻辑运行的时候将日志进行打印(在方法执行之前、方法运行结束、方法出现异常等等)

package org.example.aop;

public class MathCalculator {

    public int div(int i, int j){
        System.out.println("除法运算开始。。。。。。。。" + i + "/" + j);
        return i / j;
    }

}

创建切面类,给切面类的目标方法标注何时何地运行(通知注解)

package org.example.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

@Aspect  // 标注此类为切面类
public class LogAspects {

    @Pointcut("execution(public * org.example.aop.MathCalculator.*(..))")
    public void pointcut(){

    }

    @Before("pointcut()")
    public void logStart(JoinPoint joinPoint){
        System.out.println("@Before前置通知执行开始......" +
                "方法名:" + joinPoint.getSignature().getName() +
                "......方法参数:" + Arrays.toString(joinPoint.getArgs()));
    }

    @After("pointcut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println("@After后置通知执行开始......"+
                "方法名:" + joinPoint.getSignature().getName() +
                "......方法参数:" + Arrays.toString(joinPoint.getArgs()));
    }

	// 返回通知可以获取目标方法返回值
    @AfterReturning(value = "pointcut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result){
        System.out.println("@AfterReturning返回通知执行开始......" +
                "方法名:" + joinPoint.getSignature().getName() +
                "......方法参数:" + Arrays.toString(joinPoint.getArgs()) +
                "......返回结果:" + result
        );
    }
}

创建配置类 MainConfigOfAOP,将切面类和业务逻辑类(目标方法所在的类)都加入到容器中

给配置类添加 @EnableAspectJAutoProxy,该注解用于开启基于注解的 AOP 模式

package org.example.config;

import org.example.aop.LogAspects;
import org.example.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {

    @Bean
    public MathCalculator mathCalculator(){
        return new MathCalculator();
    }

    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
}

测试代码

@Test
public void testAop(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
    MathCalculator bean = ac.getBean(MathCalculator.class);
    int div = bean.div(10, 2);
    System.out.println(div);
}

测试结果
在这里插入图片描述

总结:

  • 将业务逻辑组件和切面类都加入到容器中,告诉 Spring 哪个是切面类(@Aspect)
  • 在切面类上的每一个通知方法上标注通知注解,告诉 Spring 何时何地运行(切入点表达式)
  • 开启基于注解的 aop 模式:@EnableAspectJAutoProxy

三、重要注解、类以及方法分析

3.1 @EnableAspectJAutoProxy 注解

要实现 AOP,@EnableAspectJAutoProxy 注解是极其重要的一个注解,该注解主要用于开启基于注解的 AOP 模式,先来分析下该注解。

@EnableAspectJAutoProxy 注解源码:

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

	boolean proxyTargetClass() default false;
	
	boolean exposeProxy() default false;

}

该注解上还标注了一个 @Import 注解,通过 @Import 注解,可以将 AspectJAutoProxyRegistrar 这个实例注入到容器中,重点关注一下该类。

3.2 AspectJAutoProxyRegistrar 类

AspectJAutoProxyRegistrar 源码:

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}

AspectJAutoProxyRegistrar 类实现了 ImportBeanDefinitionRegistrar 接口,ImportBeanDefinitionRegistrar 接口主要用于将一些需要的 BeanDefinition 注册到容器中。关于ImportBeanDefinitionRegistrar 接口的介绍可以具体看下 Spring 注解驱动开发_组件注册-@Import 注解

3.3 registerOrEscalateApcAsRequired 方法

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry) 方法的调用逻辑如下:AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)

最终代码执行到 AopConfigUtils 类中的 registerOrEscalateApcAsRequired 方法

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
		Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		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);
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;
}

看一下 AUTO_PROXY_CREATOR_BEAN_NAME 的定义:
在这里插入图片描述

该方法主要做了两件事:

  • 判断 BeanDefinitionRegistry 中是否包含名为 “org.springframework.aop.config.internalAutoProxyCreator” 的 BeanDefinition(该 BeanDefinition 实际上就是一个自动代理创建器)。如果已经存在且与现在的不一致,根据优先级来判断到底使用哪一个。
  • 设置 BeanDefinition(自动代理创建器)的属性,并将设置完成后 BeanDefinition 注册到 BeanDefinitionRegistry 中。

实际断点跟踪情况如下:
在这里插入图片描述
这里向 BeanDefinitionRegistry 中存放的是名为 “org.springframework.aop.config.internalAutoProxyCreator” 的 AnnotationAwareAspectJAutoProxyCreator。

3.4 AnnotationAwareAspectJAutoProxyCreator 分析

先来看一下 AnnotationAwareAspectJAutoProxyCreator 的继承关系图
在这里插入图片描述
可以看出,AnnotationAwareAspectJAutoProxyCreator 既实现了 BeanFactoryAware 接口,也实现了 BeanPostProcessor(后置处理器) 接口。

重点关注 BeanFactory 接口的 setBeanFactory 方法的实现情况
在这里插入图片描述
以及 BeanPostProcessor 中的 postProcessBeforeInitialization 方法和 postProcessAfterInitialization 方法的实现情况。
在这里插入图片描述
setBeanFactory 方法以及postProcessBeforeInitialization 方法和 postProcessAfterInitialization 方法都在 AbstractAutoProxyCreator 这个抽象类中得到了实现,但是 setBeanFactory 方法又被子类 AbstractAdvisorAutoProxyCreator 重写了。
在这里插入图片描述

在这里插入图片描述

3.5 AbstractAdvisorAutoProxyCreator 中的setBeanFactory 方法分析

@Override
public void setBeanFactory(BeanFactory beanFactory) {
	super.setBeanFactory(beanFactory);
	if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
		throw new IllegalArgumentException(
				"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
	}
	initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
}

setBeanFactory 方法做了两件事:

  • 调用父类的 setBeanFactory
  • 调用 initBeanFactory 方法

来看下父类 AnnotationAwareAspectJAutoProxyCreator 中的 setBeanFactory 方法:

@Override
public void setBeanFactory(BeanFactory beanFactory) {
	this.beanFactory = beanFactory;
}

该方法将传入的参数 beanFactory 赋值给本类中变量 beanFactory,以供后续程序使用。

再来分析 initBeanFactory,先看下继承关系图:
在这里插入图片描述
根据继承关系图可以看出 AbstractAdvisorAutoProxyCreator 中的initBeanFactory 方法,实际上被子类 AnnotationAwareAspectJAutoProxyCreator 给重写了,这也就意味着 AbstractAdvisorAutoProxyCreator 中的 setBeanFactory 方法在调用 initBeanFactory 方法时,并不是调用的本类中 initBeanFactory 方法,而是调用了子类中的 initBeanFactory,来看下子类 AnnotationAwareAspectJAutoProxyCreator 中 initBeanFactory 方法:

@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	super.initBeanFactory(beanFactory);
	if (this.aspectJAdvisorFactory == null) {
		this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
	}
	this.aspectJAdvisorsBuilder =
			new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}
private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspectJAdvisorsBuilder {

	public BeanFactoryAspectJAdvisorsBuilderAdapter(
			ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
		// 调用父类的构造方法
		super(beanFactory, advisorFactory);
	}

	@Override
	protected boolean isEligibleBean(String beanName) {
		return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
	}
}

父类 BeanFactoryAspectJAdvisorsBuilder 中的构造方法:

public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
	Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
	Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
	this.beanFactory = beanFactory;
	this.advisorFactory = advisorFactory;
}

initBeanFactory 做了三件事:

  • 首先调用了父类 AbstractAdvisorAutoProxyCreator 中的 initBeanFactory 方法

  • 接着创建 ReflectiveAspectJAdvisorFactory 实例,该实例主要用于调用相应的通知方法。ReflectiveAspectJAdvisorFactory 类,可以从遵循 AspectJ 5 注释语法的 AspectJ 类中创建 Spring AOP 顾问,使用反射来调用相应的通知方法。
    具体看下 ReflectiveAspectJAdvisorFactory 类中方法,可以通过该类获取到指定类的通知、切入点等信息。
    在这里插入图片描述

  • 根据 BeanFactory 实例以及 ReflectiveAspectJAdvisorFactory 实例来创建 BeanFactoryAspectJAdvisorsBuilderAdapter 实例,该适配器实际上是
    AnnotationAwareAspectJAutoProxyCreator 的一个私有内部类,其主要作用用于创建 BeanFactoryAspectJAdvisorsBuilder 实例。BeanFactoryAspectJAdvisorsBuilder 用于从 BeanFactory 中获取所有标记有 @AspectJ 注解的 bean 并基于它们构建 Spring Advisor,用于自动代理。
    再来看下 BeanFactoryAspectJAdvisorsBuilder 中的具体方法,该类中定义了两个构造方法,一个 buildAspectJAdvisors 方法,该方法返回值是一个 List 集合,其泛型是 Advisor,该方法主要用于获取所有标记有 @AspectJ 注解的 bean
    在这里插入图片描述

3.6 AnnotationAwareAspectJAutoProxyCreator 中的 postProcessAfterInitialization 方法 分析

AOP 既然是对目标对象的目标方法进行增强,那么必然需要目标对象创建完成之后才能实现动态代理,所以此处不分析 PostProcessBeforeInitialization 方法,另一个原因是 PostProcessBeforeInitialization 方法处理的是自定义的 TargetSource(接口) 的动态代理,并不是此次分析的重点。

postProcessAfterInitialization 源码:

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		// earlyProxyReferences 是一个 Map 集合,移除当前 cacheKey值,根据移除后的返回值,判断该 bean 是否适合被代理
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

/**
* 为给定的 bean 类和 bean 名称构建缓存键。
* 注意:从 4.2.3 开始,此实现不再返回串联的类名字符串,而是返回最有效的缓存键:一个普通的 bean 名称,在 FactoryBean 的情况下以 BeanFactory.FACTORY_BEAN_PREFIX(& 取地址符) 开头;
* 或者,如果没有指定 bean 名称,那么给定的 beanClass。
*
*/
protected Object getCacheKey(Class<?> beanClass, @Nullable String beanName) {
	if (StringUtils.hasLength(beanName)) {
		return (FactoryBean.class.isAssignableFrom(beanClass) ?
				BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);
	}
	else {
		return beanClass;
	}
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// 判断给定 bean 在 PostProcessBeforeInitialization 方法中是否已经处理过
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	// 无需增强
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	// 给定的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;
}

postProcessAfterInitialization 主要做了以下几件事:

  • 根据给定的 bean 的 class 和 name 构建出一个 key
  • 判断给定的 bean 是否适合被代理,如果适合,则需要封装指定 bean
  • 调用 wrapIfNecessary 对 bean 进行封装

wrapIfNecessary 方法主要做了以下几件事:

  • 判断给定 bean 在 PostProcessBeforeInitialization 方法中是否已经处理过
  • 判断当前给定的 bean 是否无需增强
  • 判断 给定的bean是否代表一个基础设施类,基础设施类不应代理,或者配置了指定 bean 不需要自动代理
  • 根据给定的 class 以及 beanName 获取增强方法和增强器
  • 根据获取到的增强进行代理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值