mini-spring 学习笔记—AOP

最近在学习 mini-spring 项目,记录笔记以总结心得

IoC篇:mini-spring 学习笔记—IoC

扩展篇:mini-spring 学习笔记—扩展篇

高级篇:mini-spring 学习笔记—高级篇

AOP 中有一些很重要的概念:切点、切面、连接点等等。如果对这些概念不熟悉可能会难以理解本篇内容,不熟悉的朋友可以看看这篇博客

切点表达式

ClassFilter 和 MethodMatcher

这两个接口都定义了一个叫做 mathes 的方法,用于匹配

ClassFilter 接口规范了类匹配器的行为

boolean matches(Class<?> clazz);

MethodMatcher 接口规范了方法匹配器的行为

boolean matches(Method method, Class<?> targetClass);

Pointcut 和 AspectJExpressionPointcut

Pointcut 接口是对切点的抽象,包含类型匹配器和方法匹配器

ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();

AspectJExpressionPointcut 类用于完成切点表达式的匹配任务,实现了 PointcutClassFilterMethodMatcher 三个接口

其中使用 Set 来存储支持的切入点原语,在项目中只支持 EXECUTION 原语

private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);

使用 PointcutExpression 类型变量存储解析后的切入点表达式

private final PointcutExpression pointcutExpression;

带参构造函数对传入的字符串表达式进行解析,将解析好的结果赋值给 pointcutExpression

// AspectJExpressionPointcut 类
// 构造方法,接受一个表示 AspectJ 表达式的字符串参数
public AspectJExpressionPointcut(String expression) {
	// 创建一个 PointcutParser 对象,用于解析 AspectJ 表达式
	PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader());
	// 解析传入的表达式并将结果赋给 pointcutExpression
	pointcutExpression = pointcutParser.parsePointcutExpression(expression);
}

两个 matches 方法用于判断解析结果是否准确

public boolean matches(Class<?> clazz) {
	return pointcutExpression.couldMatchJoinPointsInType(clazz);
}
public boolean matches(Method method, Class<?> targetClass) {
	// 使用 AspectJ 表达式判断方法是否匹配,并始终返回 true
	return pointcutExpression.matchesMethodExecution(method).alwaysMatches();
}

基于 JDK 的动态代理

动态代理是代理模式在 Spring 中的应用,对代理模式不了解的朋友可以看看这篇博客

TargetSource 和 AdvisedSupport

TargetSource 类用于封装被代理的目标对象,只有一个成员变量 target

private final Object target;

AdvisedSupport 类用于保存与切面相关的配置,有三个成员变量

private TargetSource targetSource;
// 保存方法拦截器的实例
// 方法拦截器是在目标方法执行前后添加额外逻辑的组件
private MethodInterceptor methodInterceptor;
// 保存方法匹配器的实例
private MethodMatcher methodMatcher;

ReflectiveMethodInvocation

该类实现了 MethodInvocation 具有了执行目标方法的功能,有三个成员变量

private final Object target;
private final Method method;
private final Object[] arguments;

关键的方法为 proceed 方法,调用 invoke 方法执行 target 对象具有的方法

public Object proceed() throws Throwable {
	return method.invoke(target, arguments);
}

AopProxy 和 JdkDynamicAopProxy

AopProxy 接口是 AOP 代理的抽象,提供了 getProxy 方法用于获得一个新的代理对象

Object getProxy();

JdkDynamicAopProxy 类是 JDK 动态代理的实现类,它实现了 AopProxyInvocationHandler 接口,具有新建代理对象和通过代理对象调用方法的功能。只有一个 AdvisedSupport 类型的成员变量

private final AdvisedSupport advised;

getProxy 使用 java 中的 Proxy 类进行实现,返回一个新建的代理对象

public Object getProxy() {
	return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getTargetClass(), this);
}

invoke 函数被用于委托调用被代理对象的方法。首先使用切点表达式的 mathes 方法对被调用的方法进行匹配,如果是切点表达式所记录的方法,就交给代理对象的方法拦截器执行(第一个 return),否则就直接执行(第二个 return

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	if(advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
		//代理方法
		MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
		return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args));
	}
	return method.invoke(advised.getTargetSource().getTarget(), args);
}

这里说一下为什么要进行判断,因为切点表达式中所记录的方法是需要进行特殊处理的 methodInterceptor.invoke 是调用的具体类型的拦截器,这个类型需要实现 MethodInterceptor 接口

// WorldServiceInterceptor 类
public Object invoke(MethodInvocation invocation) throws Throwable {
	System.out.println("Do something before the earth explodes");
	Object result = invocation.proceed();
	System.out.println("Do something after the earth explodes");
	return result;
}

可以看到,在被切点表达式记录的方法调用时,需要进行调用前与调用后的处理。调用底层方法的语句为上面的 Object result = invocation.proceed();

public Object proceed() throws Throwable {
	return method.invoke(target, arguments);
}

可以看到,最终也是调用的 method.invoke

测试

这里有一点困惑了我很久,在 DynamicProxyTest 中没有调用 JdkDynamicAopProxyinvoke 方法,那是怎么进入到 invoke 中的?

经过调试得知,调用被代理对象的方法时,会自动进入到 invoke 方法中

image

被代理对象方法调用

image

自动进入到 invoke

基于 CGLIB 的动态代理

基于 jdk 的动态代理有一个最大的缺点:被代理类必须为接口实现类,这就给项目开发带来了不便。

关于为什么 jdk 的动态代理对象必须为接口实现类,可以看这篇文章

而基于 CGLIB 的动态代理则不要求被代理对象为接口实现类

CglibAopProxy

该类也实现了 AopProxy 接口,使用 getProxy 方法创建代理对象。

可以观察到,首先创建了增强类 enhancer,将被代理对设置为代理对象的父类,同时继承了被代理对象的接口

public Object getProxy() {
	Enhancer enhancer = new Enhancer();
	// 设置被代理对象的类型为父类型
	enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
	// 设置被代理对象的接口为接口
	enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
	// 设置回调类
	enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
	// 创建代理对象
	return enhancer.create();
}

DynamicAdvisedInterceptor 类实现了 CGLIB 的 MethodInterceptor 接口,用于方法的拦截,它仅仅包含被代理对象一个成员变量

private final AdvisedSupport advised;

实现了 intercept 方法用于拦截函数调用(类似于 jdk 动态代理中的 invoke 函数),当代理对象调用被代理对象的函数时,自动进入此方法。同样也对方法进行了匹配检查。

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
	CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy);
	if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
		//代理方法
		return advised.getMethodInterceptor().invoke(methodInvocation);
	}
	return methodInvocation.proceed();
}

CglibMethodInvocation 类为 ReflectiveMethodInvocation 的子类,只有一个成员变量 methodProxy

private final MethodProxy methodProxy;

实现了 proceed 方法

public Object proceed() throws Throwable {
	return this.methodProxy.invoke(this.target, this.arguments);
}

AOP 代理工厂

AdvisedSupport

AdvisedSupport 类增添属性成员 proxyTargetClass,用于判断是否使用 CGLIB 代理

private boolean proxyTargetClass = false;

ProxyFactory

该类仅包含成员属性 advisedSupport 表示被代理对象

private AdvisedSupport advisedSupport;

使用 createAopProsy 返回动态代理类

private AopProxy createAopProxy() {
	// 判断用哪种代理
	if (advisedSupport.isProxyTargetClass()) {
		return new CglibAopProxy(advisedSupport);
	}

	return new JdkDynamicAopProxy(advisedSupport);
}

几种常用的 Advice

advice 表示建言,即在方法执行的(之前/之后/之前与之后)执行的方法。Spring 有多种建言,本章仅仅简单实现了 before 建言(有空的话我会补上)

BeforeAdvice 和 MethodBeforeAdvice

BeforeAdvice 继承了 Advice 接口,目前是个空接口,用于规范 before 建言的行为

public interface BeforeAdvice extends Advice {}

MethodBeforeAdvice 继承了 BeforeAdvice 接口,定义了 before 方法,即方法调用前执行的方法

void before(Method method, Object[] args, Object target) throws Throwable;

MethodBeforeAdviceInterceptor

该类为 before 建言的拦截器,实现了 MethodInterceptor 接口,仅有一个成员变量 advice

private MethodBeforeAdvice advice;

实现了 invoke 方法

public Object invoke(MethodInvocation invocation) throws Throwable {
	// 在执行被代理方法之前,先执行before advice操作
	this.advice.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
	return invocation.proceed();
}

测试

要提供 before 建言,需要实现 MethodBeforeAdvice 接口,重写 before 方法

public class WorldServiceBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("BeforeAdvice: do something before the earth explodes");
	}
}

PointcutAdvisor:Pointcut 和 Advice 的组合

Advisor 和 PointcutAdvisor

Advisor 接口规范了 getAdvice 方法,用于获取建言

Advice getAdvice();

PointcutAdvisor 接口规范了 getPointcut 方法同时继承了 Advisor 接口,能够获取建言和切点

Pointcut getPointcut();

MethodBeforeAdviceInterceptor

添加了无参构造函数和 adviceset 方法,能够修改建言

public MethodBeforeAdviceInterceptor() {}
public void setAdvice(MethodBeforeAdvice advice) {this.advice = advice;}

AspectJExpressionPointcutAdvisor

实现了 PointcutAdvisor 接口,包含三个成员变量

private AspectJExpressionPointcut pointcut;
private Advice advice;
private String expression;

动态代理融入 bean 生命周期

AspectJExpressionPointcutAdvisor

增加 getPoingcut 方法

public Pointcut getPointcut() {
	if (pointcut == null) {
		pointcut = new AspectJExpressionPointcut(expression);
	}
	return pointcut;
}

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor 接口为 BeanPostProcessor 的子接口,用于规范 bean 实例化前返回代理对象的行为,仅定义了 postProcessBeforeInstantiation 一个方法

Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator 实现了 InstantiationAwareBeanPostProcessor 接口,用于对 bean 创建代理对象,主要方法为 postProcessBeforeInstantiation 方法,因为篇幅限制,这里就不放该方法的完整代码了,只粘贴其核心部分

// 避免死循环
if (isInfrastructureClass(beanClass)) {
	return null;
}
Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
try {
	// 这一步就使用代理对象包裹 bean
	for (AspectJExpressionPointcutAdvisor advisor : advisors) {
		ClassFilter classFilter = advisor.getPointcut().getClassFilter();
		// 判断是不是表达式要拦截的类
		if (classFilter.matches(beanClass)) {
			AdvisedSupport advisedSupport = new AdvisedSupport();
			BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
			Object bean = beanFactory.getInstantiationStrategy().instantiate(beanDefinition);
			TargetSource targetSource = new TargetSource(bean);
			advisedSupport.setTargetSource(targetSource);
			advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
			advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
			//返回代理对象
			return new ProxyFactory(advisedSupport).getProxy();
		}
	}

其中 isInfrastructureClass 方法实现如下

private boolean isInfrastructureClass(Class<?> beanClass) {
	return Advice.class.isAssignableFrom(beanClass)
		|| Pointcut.class.isAssignableFrom(beanClass)
			|| Advisor.class.isAssignableFrom(beanClass);
}

为什么这能够防止死循环呢?

AbstractAutowireCapableBeanFactory

createBean 方法中加入判断需不需要代理 bean

// 如果bean需要代理,则直接返回代理对象
Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (bean != null) {
	return bean;
}

resolveBeforeInstantiation 方法如下,主要调用 bean 初始化前后的 BeanProcessor

protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
	Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
	if (bean != null) {
		bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
	}
	return bean;
}

applyBeanPostProcessorsBeforeInstantiation 方法通过判断 beanPostProcessor 有没有实现 InstantiationAwareBeanPostProcessor 接口来判断用不用代理封装 bean,否则返回 null

if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
	Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
	if (result != null) {
		return result;
	}
}

本章小结

至此,bean 的生命周期如下

image

posted @ 2023-12-08 12:52  Frodo1124  阅读(56)  评论(0编辑  收藏  举报