Spring源码:手写AOP

文章目录

  • 一、概念
    • 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);}@Overridepublic boolean matchsClass(Class<?> targetClass) {return pointcutExpression.couldMatchJoinPointsInType(targetClass);}@Overridepublic 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);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}@Overridepublic String getAdviceBeanName() {return this.adviceBeanName;}@Overridepublic 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;}@Overridepublic Object getProxy() {return this.getProxy(target.getClass().getClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);}@Overridepublic 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;}@Overridepublic Object getProxy() {return this.getProxy(target.getClass().getClassLoader());}@Overridepublic 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());}}@Overridepublic 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 {// 获取要对当前方法进行增强的adviceList<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 {@Overridepublic 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,没有就用Cglibreturn 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;// 标识是否获取过了所有的Advisorsprivate volatile boolean gettedAllAdvisors = false;@Overridepublic 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中得到用户配置的所有切面Advisorif (!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);// 用于存放匹配的AdvisorList<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();}@Overridepublic 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.rhkb.cn/news/270998.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开源物联网平台】使用MQTT.fx模拟设备接入FastBee物联网平台

​&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、接入步骤 1.1 创建产品&#xff…

springboot3.x集成SpringDoc Swagger3

近期将springboox2.x升级到了3.x&#xff0c;索性将swagger2也同步升级到swagger3&#xff0c;具体过程如下。 一、添加maven依赖 <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>…

Mac版2024 CleanMyMac X 4.14.6 核心功能详解以及永久下载和激活入口

CleanMyMac 是 macOS 上久负盛名的系统清理工具&#xff0c;2018 年&#xff0c;里程碑式版本 CleanMyMac X 正式发布。不仅仅是命名上的变化&#xff0c;焕然一新的 UI、流畅的动画也让它显得更加精致。新增的系统优化、软件更新等功能&#xff0c;使得在日常使用 macOS 时有了…

正向代理和反向代理区别

正向代理和反向代理的区别&#xff1a; 特点正向代理反向代理位置位于客户端和目标服务器之间位于目标服务器和客户端之间代理对象代理服务器代表客户端发送请求到目标服务器代理服务器代表目标服务器接收客户端的请求配置客户端需要手动配置代理服务器客户端不需要知道代理服…

TCP收发——计算机网络——day02

今天主要讲了TCP的收发 TCP发端步骤 ①socket ②connect ③send ④closeTCP收端步骤 ①socket ②bind ③listen ④accept ⑤recv ⑥clise其函数主要有 connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);功能:发送链接请求参数:sockfd:套接…

【linuxC语言】系统调用IO文件操作

文章目录 前言一、文件描述符介绍二、系统调用IO API介绍2.1 open函数2.2 close函数2.3 read函数2.4 write函数2.5 lseek函数 三、示例代码总结 前言 在Linux系统中&#xff0c;C语言通过系统调用实现对文件的输入输出&#xff08;I/O&#xff09;操作。系统调用提供了访问操作…

一款Mac系统NTFS磁盘读写软件Tuxera NTFS 2023 for Mac

当您获得一台新 Mac 时&#xff0c;它只能读取 Windows NTFS 格式的 USB 驱动器。要将文件添加、保存或写入您的 Mac&#xff0c;您需要一个附加的 NTFS 驱动程序。Tuxera 的 Microsoft NTFS for Mac 2023是一款易于使用的软件&#xff0c;可以在 Mac 上打开、编辑、复制、移动…

sudo command not found

文章目录 一句话Intro其他操作 一句话 sudo 某命令 改成 sudo -i 某命令 试试。 -i 会把当前用户的环境变量带过去&#xff0c;这样在sudo的时候&#xff0c;有更高的权限&#xff0c;有本用户的环境变量(下的程序命令)。 -i, --login run login shell as the target user; a …

Javaweb之Web后端开发总结的详细解析

4. Web后端开发总结 到此基于SpringBoot进行web后端开发的相关知识我们已经学习完毕了。下面我们一起针对这段web课程做一个总结。 我们来回顾一下关于web后端开发&#xff0c;我们都学习了哪些内容&#xff0c;以及每一块知识&#xff0c;具体是属于哪个框架的。 web后端开…

MySQL 使用 pt-archiver 删除数据

文章目录 前言1. 环境准备1.1 模拟造数1.2 工具安装 2. 删除数据2.1 批次删除表2.2 原理解析2.3 批处理思路 后记 前言 在线核心业务都会有日志表&#xff0c;随着业务持续运行&#xff0c;日志表每天都在增大&#xff0c;最后超过阈值触发空间使用率告警。DBA 处理空间告警时…

调用Mybatis plus中的saveBatch方法报找不到表的问题

1.问题现象 在用Mybatis plus开发的项目中&#xff0c;用自带的API批量保存的方法saveBatch操作时&#xff0c;发现报没有找到表的错误。 错误日志截图如下&#xff1a; 表实际是存在的&#xff0c;且发现其他的方法都没有问题&#xff0c;包括save、update等单个的方法&…

第十五届蓝桥杯青少组STEMA测评SPIKE初级真题试卷 2024年1月

第十五届蓝桥杯青少组STEMA测评SPIKE初级真题试卷 2024年1月 ​​​​​​​ 来自&#xff1a;6547网 http://www.6547.cn/doc/vywur8eics

数智化转型的新篇章:企业如何在「数据飞轮」理念中寻求增长?_光点科技

在当今的数字化浪潮中&#xff0c;企业对数据的渴求与日俱增。数据不再仅是辅助决策的工具&#xff0c;而是成为推动业务增长的核心动力。自从「数据中台」概念降温后&#xff0c;企业纷纷探寻新的数智化路径。在这个过程中&#xff0c;「数据飞轮」作为一种新兴的理念&#xf…

鸿蒙NEXT实战开发:【截屏】

展示全屏截图和屏幕局部截图。通过[screenshot]模块实现屏幕截图 &#xff0c;通过[window]模块实现隐私窗口切换&#xff0c;通过[display]模块查询当前隐私窗口。 效果预览 全屏截图局部截图选择区域局部截图 使用说明&#xff1a; 点击右上角图标打开弹窗&#xff0c;选…

【笔记】Android 漫游定制SPN定制有关字段

一、SPN模块简介 【笔记】SPN和PLMN 运营商网络名称显示 Android U 配置 WiFiCalling 场景下PLMN/SPN 显示的代码逻辑介绍 【笔记】Android Telephony 漫游SPN显示定制&#xff08;Roaming Alpha Tag&#xff09; 二、相关配置字段 non_roaming_operator_string_array 是否…

Linux系统——Keepalive群集部署及认识

目录 一、Keepalive的认识 1.Keepalive基础——VRRP 2.Keepalived工具介绍 2.1Keepalived介绍 2.2Keepalived架构 2.2.1用户空间核心组件 2.2.2WatchDog&#xff1a;监控进程&#xff08;整个架构是否有问题&#xff09; 二、安装Keepalived及相关配置文件详解 1.安装…

下属OKR与上级OKR对齐时,有几种方法?

下属的OKR&#xff08;Objectives and Key Results&#xff0c;即目标与关键成果&#xff09;与上级的OKR对齐&#xff0c;是确保组织目标一致性和团队协同工作的关键步骤。以下是几种常用的对齐方法&#xff1a; 直接映射法&#xff1a;下属的OKR直接反映并支撑上级的OKR。例如…

【Linux篇】gdb的使用

&#x1f49b;不要有太大压力&#x1f9e1; &#x1f49b;生活不是选择而是热爱&#x1f9e1; &#x1f49a;文章目录&#x1f49a; 1. 背景知识2. 使用 1. 背景知识 1. 程序发布的方式有两种&#xff0c;debug模式和release模式 2. Linux下&#xff0c;gcc和g编译生成的可执行…

VUE3中ArcGIS JsAPI 4.27 Map 隐藏地图黑色边框

问题&#xff1a; vue3中引入arcgis jsapi 地图加载后&#xff0c;点击地图会出现黑色边框&#xff0c;看起来很不协调 解决方案&#xff1a; 新建自定义CSS文件&#xff0c;输入一下样式内容&#xff0c;并在vue页面直接用import引入即可。 注意&#xff1a;直接写到vue页面…

10、Linux项目部署-WAR包

目录 一、部署步骤 第一步&#xff0c;把War包解压&#xff0c;再重新打包成Zip。 第二步&#xff0c;在Linux里创建一个项目文件夹&#xff0c;将Zip的内容解压在这个文件夹内。 第三步&#xff0c;修改Tomcat配置表 第四步&#xff0c;启动Tomcat 第五步&#xff0c;浏…