Spring源码:手写AOP

news2025/5/21 0:53:09

文章目录

  • 一、概念
    • 1、AOP是什么?
    • 2、相关概念
      • 1)目标对象Target
      • 2)通知Advice
      • 3)连接点Joinpoint
      • 4)切点Pointcut
      • 5)切面Aspect
      • 6)织入Weaving
  • 二、分析
  • 三、实现
    • 1、实现Advice
      • 1)前置通知
      • 2)后置通知
      • 3)环绕通知
      • 4)异常通知
      • 5)最终通知
    • 2、实现Pointcut
      • 1)分析
      • 2)具体代码实现
    • 3、实现Aspect
      • 1)分析
      • 2)具体代码实现
    • 4、织入实现
      • 1)分析
      • 2)具体代码实现
  • 四、最终完整版本
  • 五、总结

一、概念

1、AOP是什么?

AOP[Aspect Oriented Programming] 面向切面编程:在不改类代码的情况下,对类方法进行增强操作
切面:将那些和业务无关的公共逻辑封装起来,方面使用

横切一刀,剖开对象的封装,将多个类的公共行为封装到一个可重用的模块,组成一个切面

2、相关概念

1)目标对象Target

目标对象是实际执行程序的对象,需要被代理的类,切面通过代理目标对象来添加额外的行为。

2)通知Advice

在什么时候进行增强功能

  • 前置
  • 后置
  • 环绕
  • 异常
  • 最终

3)连接点Joinpoint

可以被选择来进行增强的点,包含切点

4)切点Pointcut

需要增强的点

5)切面Aspect

Advice和Pointcut组合成了切面,定义了在何时和何地完成增强功能

6)织入Weaving

将切面应用到目标对象的方法上,并创建代理对象的过程,可以在编译时、类加载时或运行时进行。

二、分析

所以,我们通过AOP来实现对类方法功能的增强。在已有类和方法的情况下,我们需要写的就是,在什么时候进行增强,方法前还是方法后…也就是Advice,需要对什么方法进行增强,也就是Pointcut,两种共同组成了切面,确定了时间和地点,从而进行功能增强。

Advice

用户性:用户根据需求编写
可选时机:可在方法前、后、异常时进行功能增强
多重性:一个切入点可以有多个增强,可以在方法前以及方法后同时增强

Pointcut

用户性:用户可灵活指定
多点性:可以选择在多个点上进行增强

Weaving:无入侵性,不修改原类的代码

在Spring中,它已经实现了,但这里需要我们自己去实现,后面再具体分析下

三、实现

1、实现Advice

定义一个空接口,方便后续实现不同的可选时机(前置Before、后置AfterReturn、环绕Around、异常Throwing、最终After)

public interface Advice {
}

分析一波:无论什么通知,都是对方法进行增强,所以基本都需要方法的相关信息,后面来看看具体实现

1)前置通知

作用:在方法执行前进行增强

所需的参数:

  • 方法所属对象:Object
  • 方法本身:Method
  • 方法的参数:Object[]
  • 返回值:不需要,方法都没执行,哪来的返回值!
    具体实现
public interface MethodBeforeAdvice extends Advice {
	/**
	 * 实现该方法进行前置增强
	 *
	 * @param method 被增强的方法
	 * @param args   方法的参数
	 * @param target 被增强的目标对象
	 * @throws Throwable
	 */
	void before(Method method, Object[] args, Object target) throws Throwable;
}

2)后置通知

作用:在方法执行成功返回后进行增强

所需的参数:

  • 方法所属对象:Object
  • 方法本身:Method
  • 方法的参数:Object[]
  • 方法的返回值:Object,可能有,也可能没有,得看看是否允许在After中更改返回的结果,如果规定只可用、不可修改返回值就不需要返回值;允许更改才需要返回值

具体实现

public interface AfterReturningAdvice extends Advice {
	/**
	 * 实现该方进行后置增强
	 *
	 * @param returnValue 返回值
	 * @param method      被增强的方法
	 * @param args        方法的参数
	 * @param target      方法的所属对象
	 * @throws Throwable
	 */
	void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

3)环绕通知

作用:包裹方法进行增强

所需的参数:

  • 方法所属对象:Object
  • 方法本身:Method
  • 方法的参数:Object[]
  • 方法的返回值:Object,方法将由它来执行,它需要返回方法的返回值

具体实现

public interface MethodInterceptor extends Advice {
	/**
	 * 实现该方法进行环绕(前置、后置)增强、异常处理增强
	 *
	 * @param method 被增强的方法
	 * @param args   方法的参数
	 * @param target 方法所属对象
	 * @return Object 返回值
	 * @throws Throwable
	 */
	Object invoke(Method method, Object[] args, Object target) throws Throwable;
}

4)异常通知

作用:对方法执行时的异常,进行增强处理

参数说明:

  • 必须的参数:Exception
  • 可能需要的参数:
    • 方法所属对象:Object
    • 方法本身:Method
    • 方法的参数:Object[]

具体实现

public interface ThrowsAdvice extends Advice {
	/**
	 * 实现该方法进行异常处理增强
	 *
	 * @param method 被增强的方法
	 * @param args   方法的参数
	 * @param target 方法所属对象
	 * @param ex     抛出的异常
	 * @return
	 */
	void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable;
}

5)最终通知

作用:在方法执行后进行增强

所需的参数:

  • 方法所属对象:Object
  • 方法本身:Method
  • 方法的参数:Object[]
  • 方法的返回值:Object,可能有,也可能没有,得看看是否允许在After中更改返回的结果,如果规定只可用、不可修改返回值就不需要返回值;允许更改才需要返回值

具体实现

public interface AfterAdvice extends Advice {
	/**
	 * 实现该方法进行最终增强
	 *
	 * @param returnValue 返回值
	 * @param method      被增强的方法
	 * @param args        方法的参数
	 * @param target      方法的所属对象
	 * @throws Throwable
	 */
	void after(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}

通知类的类图类图如下:
在这里插入图片描述

2、实现Pointcut

其实就是指定要增强的方法

1)分析

那么我们应该怎么指定要增强的方法?要求满足用户灵活控制,并且程序也能轻松识别

首先应该怎么描述指定一个方法?方法可能有重载的情况?如下:

com.forlan.test(int,string)
com.forlan.test(int)

还要满足灵活控制,以及支持多个点增强,情况可能有如下很多种:

某包下某个类的某个方法
某包下某个类的所有方法
某包下所有类的所有方法
某包下以Service结尾的类中,以xx开头的方法

所以我们需要一个表达式能够灵活地去描述,匹配到目标方法名,类似:包名.类名.方法名(参数类型)
需要满足的要求:

  • 包名、类名、方法名:支持模糊匹配
  • 参数类型:可能有多个

所以可以使用AspectJ表达式,官网:http://www.eclipse.org/aspectj

语法格式
execution([访问权限类型] 返回值类型 [全限定性类名] 方法名(参数名) [抛出的异常类型])
execution表达式中就是方法的签名
表达式中加[ ]的部分表示可省略
各部分用空格分开
可以使用的符号:
*:0至多个字符
…:用在方法参数中表示任意多个参数,用在包名后表示当前包及其子包路径
+:用在类名后表示当前类及子类,用在接口后表示接口及实现类

例子

  • 任意公共方法:execution(public * *(. .))
  • 任何一个以“set”开始的方法:execution(* set *(. .))
  • 定义在service包里的任意类的任意方法:execution(* com.forlan.service..(. .))

所以,最终就是,用户通过指定AspectJ表达式,程序去解析表达式中需要增强的类中的方法。

2)具体代码实现

定义一个接口,提供需要匹配的类和方法

public interface Pointcut {

	boolean matchsClass(Class<?> targetClass);

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

定义实现类,用aspectj工具包识别表达式,大概思路就是先匹配类,再匹配方法

public class AspectJExpressionPointcut implements Pointcut {

	private static PointcutParser pp = PointcutParser
			.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

	private String expression;

	private PointcutExpression pointcutExpression;

	public AspectJExpressionPointcut(String expression) {
		super();
		this.expression = expression;
		pointcutExpression = pp.parsePointcutExpression(expression);
	}

	@Override
	public boolean matchsClass(Class<?> targetClass) {
		return pointcutExpression.couldMatchJoinPointsInType(targetClass);
	}

	@Override
	public boolean matchsMethod(Method method, Class<?> targetClass) {
		ShadowMatch sm = pointcutExpression.matchesMethodExecution(method);
		return sm.alwaysMatches();
	}

	public String getExpression() {
		return expression;
	}

}

3、实现Aspect

1)分析

其实就是前面的Advice和Pointcut的组合

  • 通过Advice确定何时进行什么增强
  • 通过Pointcut确定何地(目标方法)进行增强

我们参考Spring定义Advisor来描述组合Advice和Pointcut

在Spring框架中,Advisor是一个用于提供切面(Aspect)功能的对象。它用于定义切入点(Pointcut)和通知(Advice)之间的关系。通常,Advisor会根据切入点来确定在何时和何地应用通知。通知可以是在方法执行前、执行后或抛出异常时执行的代码。Advisor可以通过配置或编程方式添加到Spring应用程序中,以实现横切关注点的功能,例如日志记录、事务管理等。

2)具体代码实现

定义一个接口

public interface Advisor {

	String getAdviceBeanName();

	String getExpression();
}

定义基于切点的接口

public interface PointcutAdvisor extends Advisor {

	Pointcut getPointcut();
}

基于AspectJ切点的实现类

public class AspectJPointcutAdvisor implements PointcutAdvisor {

	private String adviceBeanName;

	private String expression;

	private Pointcut pointcut;

	public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
		super();
		this.adviceBeanName = adviceBeanName;
		this.expression = expression;
		this.pointcut = new AspectJExpressionPointcut(this.expression);
	}

	@Override
	public Pointcut getPointcut() {
		return this.pointcut;
	}

	@Override
	public String getAdviceBeanName() {
		return this.adviceBeanName;
	}

	@Override
	public String getExpression() {
		return this.expression;
	}
}

实现的类图结构如下:
在这里插入图片描述

4、织入实现

其实就是把用户提供的增强方法,加到指定的方法上

1)分析

在什么时候织入?

在Bean初始化后,对其进行增强

如何实现织入?

主要讨论的是运行时织入,可以通过使用动态代理或字节码生成技术,在目标对象的方法执行前后插入切面代码。

如何确定Bean需要增强?

Bean类及其方法和与切面进行匹配,匹配到,表示需要增强

如果增强的方法,写在Bean生命周期过程中,也就是在BeanFactory中找到对应的方法,在方法前、后等位置写代码,这样的话,代码会越来越复杂,不好拓展
在这里插入图片描述
考虑到拓展性:在不修改BeanFactory中代码的情况下,实现灵活扩展,可以考虑使用观察者模式,在各个节点加入扩展点,增加一个Bean的处理器BeanPostProcessor,先注册到BeanFactory,后面在初始化前后的处理中,再遍历执行BeanPostProcessor增强1
总的来说,BeanFactory实现相关Bean的增强操作,需要做两件事:

  • 创建相关的BeanPostProcessor,并注册到BeanFactory中
  • BeanFactory在初始化Bean的前后增加判断,判断是否有相关BeanPostProcessor,如果有就进行相关的增强处理

2)具体代码实现

定义BeanPostProcessor接口,定义初始化之前和初始化之后要执行的方法,默认返回原始bean,表示不增强

public interface BeanPostProcessor {

	default Object postProcessBeforeInitialization(Object bean, String beanName) throws Throwable {
		return bean;
	}

	default Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {
		return bean;
	}
}

让BeanFactory支持注册BeanPostProcessor

void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);

DefaultBeanFactory实现支持注册BeanPostProcessor,并且在Bean对象初始化前后,校验是否需要进行增强,主要调整doCreateInstance方法

// Bean增强相关处理类
private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());

@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
	this.beanPostProcessors.add(beanPostProcessor);
}

private Object doCreateInstance(String beanName, BeanDefinition bd) throws Exception {
		Class<?> beanClass = bd.getBeanClass();
		Object instance = null;
		if (beanClass != null) {
			if (StringUtils.isBlank(bd.getFactoryMethodName())) {
				// 构造方法来构造对象
				instance = this.createInstanceByConstructor(bd);
			} else {
				// 静态工厂方法
				instance = this.createInstanceByStaticFactoryMethod(bd);
			}
		} else {
			// 工厂bean方式来构造对象
			instance = this.createInstanceByFactoryBean(bd);
		}

		// 赋值前暴露半成品对象
		this.doEarlyExposeBuildingBeans(beanName, instance);

		// 支持属性依赖
		this.setPropertyDIValues(bd, instance);

		// 赋完值移除
		this.removeEarlyExposeBuildingBeans(beanName);

		// bean初始化前的处理
		instance = this.applyPostProcessBeforeInitialization(instance, beanName);

		// 执行初始化方法
		this.doInit(bd, instance);

		// bean初始化后的处理
		instance = this.applyPostProcessAfterInitialization(instance, beanName);

		return instance;
	}

	private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Exception {
		for (BeanPostProcessor bpp : this.beanPostProcessors) {
			bean = bpp.postProcessBeforeInitialization(bean, beanName);
		}
		return bean;
	}

	private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Exception {
		for (BeanPostProcessor bpp : this.beanPostProcessors) {
			bean = bpp.postProcessAfterInitialization(bean, beanName);
		}
		return bean;
	}

实现织入,通过创建Bean对应的代理对象来处理

代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在调用者和目标对象之间起到中介的作用。
方式有:JDK动态代理,Cglib动态代理,Javassist、ASM

通过 抽象面向接口编程来设计一套支持不同代理实现的代码,类图结构如下:
在这里插入图片描述
定义上层接口

public interface AopProxy {

	Object getProxy();

	Object getProxy(ClassLoader classLoader);
}

JDK动态代理,实现AopProxy,同时实现InvocationHandler,创建动态代理对象,重写invoke方法来处理代理对象的方法调用

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

	private String beanName;
	private Object target;
	private List<Advisor> matchAdvisors;
	private BeanFactory beanFactory;

	public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
		this.beanName = beanName;
		this.target = target;
		this.matchAdvisors = matchAdvisors;
		this.beanFactory = beanFactory;
	}

	@Override
	public Object getProxy() {
		return this.getProxy(target.getClass().getClassLoader());
	}

	@Override
	public Object getProxy(ClassLoader classLoader) {
		return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
	}
}

Cglib动态代理,实现AopProxy,同时实现MethodInterceptor ,创建动态代理对象,重写intercept方法对目标对象的方法进行拦截和增强

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {

	private static Enhancer enhancer = new Enhancer();

	private String beanName;
	private Object target;
	private List<Advisor> matchAdvisors;
	private BeanFactory beanFactory;

	public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
		this.beanName = beanName;
		this.target = target;
		this.matchAdvisors = matchAdvisors;
		this.beanFactory = beanFactory;
	}

	@Override
	public Object getProxy() {
		return this.getProxy(target.getClass().getClassLoader());
	}

	@Override
	public Object getProxy(ClassLoader classLoader) {
		Class<?> superClass = this.target.getClass();
		enhancer.setSuperclass(superClass);
		enhancer.setInterfaces(this.getClass().getInterfaces());
		enhancer.setCallback(this);
		Constructor<?> constructor = null;
		try {
			constructor = superClass.getConstructor(new Class<?>[] {});
		} catch (NoSuchMethodException | SecurityException e) {

		}
		if (constructor != null) {
			return enhancer.create();
		} else {
			BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
			// 利用BeanDefinition的缓存,直接拿到真正的参数
			return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
		}
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
	}
}

创建代理对象的过程中,可能需要有参构造参数,这个在前面创建原Bean的时候,已经解析过,我们调整下BeanDefinition,增加缓存

/**
 * 获取真正的入参
 */
Object[] getConstructorArgumentRealValues();
void setConstructorArgumentRealValues(Object[] values);

GenericBeanDefinition

private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>();

@Override
public Object[] getConstructorArgumentRealValues() {
	return realConstructorArgumentValues.get();
}

@Override
public void setConstructorArgumentRealValues(Object[] values) {
	realConstructorArgumentValues.set(values);
}

调整DefaultBeanFactory的createInstanceByConstructor方法,在解析完有参构造参数后,缓存一份到BeanDefinition

private Object createInstanceByConstructor(BeanDefinition bd) throws Exception {
	// 1、获取参数值
	Object[] args = this.getConstructorArgumentValues(bd);
	// 缓存构造参数,方便后续创建代理
	bd.setConstructorArgumentRealValues(args);
	// 2、确定调用什么构造方法来创建实例
	Constructor constructor = this.determineConstructor(bd, args);
	return constructor.newInstance(args);
}

不管用哪种方式来生成代理对象,最终增强的逻辑代码是一样的,那么就可以提炼抽取为公共代码

public class AopProxyUtils {

	/**
	 * 对方法应用增强
	 */
	public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors,
									  Object proxy, BeanFactory beanFactory) throws Throwable {
		// 获取要对当前方法进行增强的advice
		List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors, beanFactory);
		if (CollectionUtils.isEmpty(advices)) {
			return method.invoke(target, args);
		} else {
			// 存在增强的advice,进行增强
			AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
			return chain.invoke();
		}
	}

	private static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Exception {
		if (CollectionUtils.isEmpty(matchAdvisors)) {
			return null;
		}
		List<Object> advices = new ArrayList<>();
		for (Advisor ad : matchAdvisors) {
			if (ad instanceof PointcutAdvisor) {
				if (((PointcutAdvisor) ad).getPointcut().matchsMethod(method, beanClass)) {
					advices.add(beanFactory.getBean(ad.getAdviceBeanName()));
				}
			}
		}

		return advices;
	}
}

对于多种通知类型,有执行顺序,我们通过责任链模式来实现

public class AopAdviceChainInvocation {
	private static Method invokeMethod;

	static {
		try {
			invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
	}

	private Object proxy;
	private Object target;
	private Method method;
	private Object[] args;
	private List<Object> advices;

	public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
		super();
		this.proxy = proxy;
		this.target = target;
		this.method = method;
		this.args = args;
		this.advices = advices;
	}

	// 责任链执行记录索引号
	private int index = 0;

	public Object invoke() throws Throwable {
		if (index < this.advices.size()) {
			Object advice = this.advices.get(index++);
			if (advice instanceof MethodBeforeAdvice) {
				// 执行前置增强
				((MethodBeforeAdvice) advice).before(method, args, target);
			} else if (advice instanceof MethodInterceptor) {
				// 执行环绕增强,这里的method和对象是invoke方法和链对象
				return ((MethodInterceptor) advice).invoke(invokeMethod, null, this);
			} else if (advice instanceof AfterReturningAdvice) {
				// 执行后置增强,返回成功才触发
				Object returnValue = this.invoke();
				((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
				return returnValue;
			} else if (advice instanceof AfterAdvice) {
				// 执行最终增强
				Object returnValue = null;
				try {
					returnValue = this.invoke();
				} finally {
					((AfterAdvice) advice).after(returnValue, method, args, target);
				}
				return returnValue;
			} else if (advice instanceof ThrowsAdvice) {
				try {
					return this.invoke();
				} catch (Exception e) {
					// 执行异常增强,发生异常才触发
					((ThrowsAdvice) advice).afterThrowing(method, args, target, e);
				}
			}

			return this.invoke();
		} else {
			return method.invoke(target, args);
		}
	}
}

定义AOP代理工厂,方便我们获取不同的代理

public interface AopProxyFactory {

	AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory);

	static AopProxyFactory getDefaultAopProxyFactory() {
		return new DefaultAopProxyFactory();
	}
}

具体AOP代理工厂实现

public class DefaultAopProxyFactory implements AopProxyFactory {
	@Override
	public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
		// 是该用jdk动态代理还是cglib?
		if (shouldUseJDKDynamicProxy(bean, beanName)) {
			return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
		} else {
			return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
		}
	}

	private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
		// 有实现接口就用JDK,没有就用Cglib
		return bean.getClass().isInterface();
	}
}

前面我们的增强通过代理来实现,那什么时候需要进行增强,在什么地方增强,就需要获取我们定义的切面、切点、通知类,这些都在Spring容器中了,我们只需要获取到BeanFactory对象,就能拿到它们,那么,怎么在BeanPostProcessor中获取到这个对象?我们可以参考Spring的做法

在Springboot中,通过实现这些"Aware"接口,Spring容器会在适当的时候将相应的资源或环境注入到实现类中,使其能够访问和利用这些资源或环境。例如,实现BeanFactoryAware接口的类可以获取对BeanFactory的引用。

定义Aware接口,Aware的意思就是意识,xxxAware可以理解为具备xxx意识

public interface Aware {

}

定义BeanFactoryAware继承Aware接口

public interface BeanFactoryAware extends Aware {
	void setBeanFactory(BeanFactory bf);
}

调整DefaultBeanFactory的registerBeanPostProcessor,在注册BeanPostProcessor的同时,判断实现了BeanFactoryAware接口的话,就把当前BeanFactory设置给BeanPostProcessor,方便BeanPostProcessor去使用

@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
	this.beanPostProcessors.add(beanPostProcessor);
	if (beanPostProcessor instanceof BeanFactoryAware) {
		((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
	}
}

新增AdvisorAutoProxyCreator类,实现BeanPostProcessor和BeanFactoryAware,重写postProcessAfterInitialization进行Bean初始化后的增强,重写setBeanFactory,在注册的时候,自动设置BeanFactory,方便后续使用

public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

	private BeanFactory beanFactory;

	private List<Advisor> advisors;

	// 标识是否获取过了所有的Advisors
	private volatile boolean gettedAllAdvisors = false;

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
		// 跳过指定类型的增强
		if (bean instanceof Advisor || bean instanceof Advice) {
			return bean;
		}

		// 判断Bean是否需要增强,根据配置的切面,找出匹配的切面
		List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);

		// 如有匹配的切面,创建代理来实现增强
		if (CollectionUtils.isNotEmpty(matchAdvisors)) {
			bean = this.createProxy(bean, beanName, matchAdvisors);
		}

		return bean;
	}

	private List<Advisor> getMatchedAdvisors(Object bean, String beanName) throws Exception {
		// 第一次执行该方法,先从BeanFactory中得到用户配置的所有切面Advisor
		if (!gettedAllAdvisors) {
			synchronized (this) {
				if (!gettedAllAdvisors) {
					advisors = this.beanFactory.getBeansOfTypeList(Advisor.class);
					gettedAllAdvisors = true;
				}
			}
		}

		// 如果没有配置切面,直接返回
		if (CollectionUtils.isEmpty(this.advisors)) {
			return null;
		}

		// 有配置切面,获取Bean的类以及所有的方法
		Class<?> beanClass = bean.getClass();
		List<Method> allMethods = this.getAllMethodForClass(beanClass);

		// 用于存放匹配的Advisor
		List<Advisor> matchAdvisors = new ArrayList<>();

		// 遍历Advisor来匹配
		for (Advisor advisor : this.advisors) {
			if (advisor instanceof PointcutAdvisor) {
				if (isPointcutMatchBean((PointcutAdvisor) advisor, beanClass, allMethods)) {
					matchAdvisors.add(advisor);
				}
			}
		}
		return matchAdvisors;
	}


	// 获得本类以及所实现的接口的方法
	private List<Method> getAllMethodForClass(Class<?> beanClass) {
		List<Method> allMethods = new LinkedList<>();
		Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
		classes.add(beanClass);
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method m : methods) {
				allMethods.add(m);
			}
		}
		return allMethods;
	}

	private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
		Pointcut p = pa.getPointcut();

		// 首先判断类是否匹配
		if (!p.matchsClass(beanClass)) {
			return false;
		}

		// 再判断方法是否匹配
		for (Method method : methods) {
			if (p.matchsMethod(method, beanClass)) {
				return true;
			}
		}
		return false;
	}

	private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Exception {
		// 通过AopProxyFactory工厂去选择、和创建代理对象
		return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory)
				.getProxy();
	}

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

四、最终完整版本

在这里插入图片描述

五、总结

总的来说,Advice制定触发时机和增强功能;Pointcut制定触发位置,通过AspectJ表达式匹配到目标类的目标方法;Aspect将Advice和Pointcut组装在一起,方便后续Weaving获取;Weaving通过把BeanPostProcessor注册到BeanFactory中,在Bean初始化前后预留方法处理BeanPostProcessor实现增强,通过Aspect获取Pointcut来判断Bean是否匹配,然后通过动态代理来创建代理对象来增强,通过去Aspect获取Advice去执行相关的增强,使用责任链模式来控制增强多少次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1498668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA中新增文件,弹出框提示是否添加到Git点错了,怎么重新设置?

打开一个配置了Git的项目&#xff0c;新增一个文件&#xff0c;会弹出下面这个框。提示是否将新增的文件交给Git管理。 一般来说&#xff0c;会选择ADD&#xff0c;并勾选Dont ask agin&#xff0c;添加并不再询问。如果不小心点错了&#xff0c;可在IDEA中重新设置&#xff08…

经典语义分割(二)医学图像分割模型UNet

经典语义分割(二)医学图像分割模型UNet 我们之前介绍了全卷积神经网络( FCN) &#xff0c;FCN是基于深度学习的语义分割算法的开山之作。 今天我们介绍另一个语义分割的经典模型—UNet&#xff0c;它兼具轻量化与高性能&#xff0c;通常作为语义分割任务的基线测试模型&#x…

海格里斯HEGERLS助力服装业领域数智化转型 配备7000个托盘位 仓库容量增超110%

近年来&#xff0c;用工荒成为服装制造行业的一大痛点。对此&#xff0c;整个生产体系就要不断地向智能化、自动化生产设备进行转型&#xff0c;甚至在研发设计上都要面向自动化做一些新一代服装制造业的开发。 作为较早入局物流赛道的河北沃克&#xff0c;目前已构建起以AI赋能…

P2241 统计方形(数据加强版) python解法

求n*m网格内矩形的数目 - tenos - 博客园 (cnblogs.com) 法一&#xff08;题解推规律暴力枚举得到&#xff09;&#xff1a; n,mmap(int,input().split()) sqr,rec0,0 #正方形和长方形个数 #以长宽做循环&#xff0c;每次求n*m大小的矩形的个数 #题解是从0开始的&#xff0c;我…

Java二级--操作题详解(1)

目录 1.第一套&#xff1a; 1.1 基本操作&#xff1a; 1.2 题解分析&#xff1a; 2.1 简单应用&#xff1a; 2.2 解题分析&#xff1a; 3.1 综合应用&#xff1a; 3.2解题分析&#xff1a; 1.第一套&#xff1a; 1.1 基本操作&#xff1a; 在考生文件夹中存有文件名为J…

浅析扩散模型与图像生成【应用篇】(八)——BBDM

8. BBDM: Image-to-Image Translation with Brownian Bridge Diffusion Models 本文提出一种基于布朗桥&#xff08;Brownian Bridge&#xff09;的扩散模型用于图像到图像的转换。图像到图像转换的目标是将源域 A A A中的图像 I A I_A IA​&#xff0c;映射到目标域 B B B中得…

【重要!!退税!退税!】一年一度个人所得税综合年度汇算开始了!

目录标题 如何退税&#xff1f;2023年度个人所得税综合所得汇算清缴操作指南汇算准备标准申报 退税骗局&#xff1f;1.“您有一笔退税待领取”骗局2.“专业人员帮你多退税”骗局3.“诱导填报虚假个税信息”骗局4.“税务稽查人员联系你”骗局 如何退税&#xff1f; 2023年度个人…

【elementplus】el-image图片预览的显示不全问题(和el-table、el-dialog组合使用时)

问题&#xff1a; 在和el-table、el-dialog组合使用时&#xff0c;el-image图片预览的时候&#xff0c;会可能出现显示不全图片的情况。 解决方法&#xff1a; <el-image-viewer:z-index"3000":teleported"true"/>element文档中有属性&#xff1a;…

【SpringBoot3.x教程03】SpringBoot自动配置详解

前言&#xff1a;什么是自动配置 自动配置的原理 Spring Boot自动配置尝试根据添加到项目中的jar依赖、定义的bean以及各种属性设置来自动配置Spring应用。这是通过EnableAutoConfiguration注解实现的&#xff0c;该注解通常是通过SpringBootApplication注解间接应用的。Spring…

如何实现数据中心布线变更管理?

前言 随着科技的不断发展&#xff0c;数据中心作为企业的核心基础设施之一&#xff0c;承载着大量重要的业务数据。在数据中心运维过程中&#xff0c;变更管理流程变得尤为重要&#xff0c;它是确保数据中心基础设施稳定运行和保障数据安全的关键环节。变更管理的定义是指在维…

阿珊解说Vue中`$route`和`$router`的区别

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

栈和队列OJ题:有效的括号,用栈实现队列,用队列实现栈,设计循环队列(C语言版,图文并茂,超级详细)

目录 前言 1. 有效的括号 &#xff08;1&#xff09;题目及示例 &#xff08;2&#xff09;思路及解法 2.用栈实现队列 &#xff08;1&#xff09;题目及示例 &#xff08;2&#xff09;解析及思路 &#xff08;3&#xff09;各部分代码 2.3.1 数据结构设计和创造队列…

官网:随便搞个?那不如不搞,搞不好就给公司减分了。

官网建设确实需要认真对待&#xff0c;不能随便搞。一个粗制滥造的官网可能会给公司带来负面影响&#xff0c;降低品牌形象和用户体验。以下是一些官网建设的重要原则&#xff1a; 专业性&#xff1a;官网应该展示公司的专业性和专业知识。它应该以专业的设计、内容和功能来展示…

1.4 Word2Vec是如何工作的? Word2Vec与LDA 的区别和联系?

1.4 Word2Vec&#xff1a;词嵌入模型之一 场景描述 谷歌2013年提出的Word2Vec是目前最常用的词嵌入模型之一。 Word2Vec实际是一种浅层的神经网络模型,它有两种网络结构&#xff0c;分别是CBOW(Continues Bag of Words)和Skip-gram。 知识点 Word2Vec,隐狄利克雷模型(LDA),…

nginx部署前端工程替代方案gateway

nginx部署前端工程替代方案gateway 有市场要求部署的前端vue工程不使用nginx中间件。想弄国产替代的东方通之类的&#xff0c;公司没有购买该产品&#xff0c;我参考了网上的一些java网关框架&#xff0c;springcloud组件&#xff1a;gateway实现代替。 注意后台都是用java编…

网络安全-appcms-master

一、环境 gethub上面自己找appcms-master 二、分析一下源码以及闯关思路 首先是有一个函数循环以及函数过滤&#xff0c;我们的post会将我们所传的所有val值去进行一个循环&#xff0c;之后通过htmlspecialchars这个函数进行过滤和转换所以val值不能通过单双引号闭合注入的方…

微信私域运营时如何有效降本增效?

在如今这个以流量为王的时代&#xff0c;成功地将流量转化为商业价值显得尤为重要。许多企业选择将流量转移到微信的私域流量中&#xff0c;以提高转化率和营销效果。 但是由于微信平台的限制&#xff0c;比如一台设备在正常情况下只能登录一个账号&#xff0c;无法实现聚合管理…

Pytorch从零开始实战20

Pytorch从零开始实战——指定生成手势图像 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——指定生成手势图像环境准备模型选择模型训练可视化分析生成指定图像总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c…

✅图片上传组件使用

简述 图片压缩、图片预览、图片多图上传、默认高清压缩 前情提示 暂仅支持:bmp, gif, jpg, jpeg, png格式,暂不支持svg、webp等格式【升级后支持】 一只哈基米~~ 截图 使用方式(主打一个代码可直接复制) 单张图:缩略图、头像、营业执照 <a-form-model-item label=…

如何将中科方德桌面操作系统加入Windows域

往期文章&#xff1a;自定义SSH客户端连接时的显示信息 | 统信UOS | 麒麟KYLINOS Hello&#xff0c;大家好啊&#xff0c;今天我非常高兴地给大家带来一篇关于如何将中科方德桌面操作系统加入Windows域的教程文章。对于使用中科方德桌面操作系统的用户来说&#xff0c;将其加入…