AOP使用以及基本概念

AOP使用以及基本概念

1.基本概念

首先先给出一段比较专业的术语(来自百度):

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

然后我们举一个比较容易理解的例子

要理解切面编程,就需要先理解什么是切面。用刀把一个西瓜分成两瓣,切开的切口就是切面;炒菜,锅与炉子共同来完成炒菜,锅与炉子就是切面。web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。

2.AOP的使用场景

权限认证、日志、事务处理、增强处理

3.@Aspect的使用以及基本概念

@Aspect属于AspectJ,AspectJ 既可以结合spring框架使用,也可以单独使用,是目前使用最广泛的AOP实现

1.切面类 @Aspect: 定义切面类,加上@Aspect、@Component注解

@Aspect
@Component
//@Order(2) 设置配置类加载的顺序
public class AnnotationAspectTest 

4.切点 @Pointcut

@Pointcut使用

    /**
     * 定义切点,切点为对应controller
     	第一个*表示匹配任意的方法返回值,
		第二个*表示所有controller包下的类,
		第三个*表示所有方法,
		(..)表示任意参数个数。
     */
    @Pointcut("execution(public * com.example.zcs.Aop.controller.*.*(..))")
    public void aopPointCut(){
 
    }

execution表达式:

@Pointcut("execution(

modifiers-pattern? return-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?

)")

其中,各个部分的含义如下:

  • modifiers-pattern:指定方法的修饰符,如 publicprotectedprivatestatic 等。可以使用通配符 * 表示任意修饰符。
  • return-type-pattern:指定方法的返回类型,如 void、具体的类名等。可以使用通配符 * 表示任意返回类型。
  • declaring-type-pattern:指定方法所在的类的全限定名。可以使用通配符 * 表示任意类。
  • name-pattern:指定方法的名称。可以使用通配符 * 表示任意方法名。
  • param-pattern:指定方法的参数列表。可以使用通配符 * 表示任意参数。
  • throws-pattern:指定方法抛出的异常类型。可以使用通配符 * 表示任意异常类型。

execution 表达式中,各个部分之间用空格分隔,带有 ?的 表示可选部分,可以不写。使用 *则表示匹配任意字符。以下是一些示例:

  • 匹配所有 public 方法:

    //第一个*代表任意返回值,第二个*代表任意方法名,省略了类限定名及异常
    @Pointcut("execution(public * *(..))")
    
  • 匹配所有返回类型为 String 的方法:

    //第一个*代表任意访问修饰符,第二个*代表任意方法名,(..)代表任意参数,省略了类限定名及异常
    @Pointcut("execution(* String.*(..))")
    
  • 匹配所有以 get 开头的方法:

    //第一个*代表任意返回值,第二个*代表get开头的任意方法名,第三个(*)表示匹配带有一个参数的方法,省略了访问修饰符,类限定名及异常
    @Pointcut("execution(* get*(*))")
    
  • 匹配所有方法,修饰符是可选的

    //第一个*代表任意返回值,第二个*代表任意方法名,(..)代表任意方法参数,省略了访问修饰符,类限定名及异常
    @Pointcut("execution(* *(..))") 
    

需要注意的是,execution 表达式中的通配符可以用于匹配各种方法签名的组合,从而灵活地指定切入点。通过合理地使用 execution 表达式,可以精确地选择需要应用切面逻辑的连接点,实现精细化的切面逻辑。

Pointcut表达式类型

标准的AspectJ Aop的pointcut的表达式类型是很丰富的,但是Spring Aop只支持下面的前9种,外加Spring Aop自己扩充的一种一共是11(10+1)种类型的表达式,分别如下:

  1. execution:一般用于指定方法的执行,用的最多。
  2. within:指定某些类型的全部方法执行,也可用来指定一个包。
  3. this: SpringAop是基于动态代理的,生成的bean也是一个代理对象,this就是这个代理对象,当这个对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
  4. target:当被代理的对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。
  5. args:当执行的方法的参数是指定类型时生效。
  6. @target:当代理的目标对象上拥有指定的注解时生效。
  7. @args:当执行的方法参数类型上拥有指定的注解时生效。
  8. @within:与@target类似,看官方文档和网上的说法都是@within只需要目标对象的类或者父类上有指定的注解,则@within会生效,而@target则是必须是目标对象的类上有指定的注解。而根据笔者的测试这两者都是只要目标类或父类上有指定的注解即可。
  9. @annotation:当执行的方法上拥有指定的注解时生效。
  10. reference pointcut:(经常使用)表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持
  11. bean:当调用的方法是指定的bean的方法时生效。(Spring AOP自己扩展支持的)

5.Advice,切入时机,或通知

Spring AOP支持五种类型的通知,对应下面5个注解:

前置通知(在方法调用前执行)

后置通知(在方法调用后执行)

返回通知(在方法返回结果后执行)

异常通知(在方法抛出异常后执行)

环绕通知(在方法调用前后执行)

在切入点上执行的增强处理,主要有五个注解

@Before 在切点方法之前执行

@After 在切点方法之后执行

@AfterReturning 切点方法返回后执行

@AfterThrowing 切点方法抛异常执行

@Around 属于环绕增强,能控制在切点执行前执行,或切点执行后执行。 通过ProceedingJoinPoint执行proceed方法的让目标方法执行,写在ProceedingJoinPoint.proceed()前面的代码会在切点执行前执行,之后的就是切点执行后执行

可以直接使用通知注解来实现切面代码的执行,而不需要显式地定义@Pointcut

你在使用Spring AOP时,你可以直接在Advice方法上使用@Before@After@Around等注解,而不需要定义@Pointcut

在这种情况下,你可以直接在Advice方法上使用execution表达式来指定切入点,而无需显式地定义@Pointcut注解。例如:

@Aspect
@Component
public class MyAspect {
    
    @Before("execution(* com.example.service.MyService.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        // 前置通知的具体逻辑
    }

    @After("execution(* com.example.service.MyService.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        // 后置通知的具体逻辑
    }
}

在这个例子中,@Before@After注解直接在Advice方法上使用execution表达式指定了切入点,而没有显式地定义@Pointcut。这样可以简化代码,特别是当切入点只在一个Advice方法中使用时。

6.JoinPoint

1.方法中的参数JoinPoint为连接点对象,它可以获取当前切入的方法的参数、代理类等信息,因此可以记录一些信息,验证一些信息等;

    @Before("...切点方法...")
    public void beforeMethod(JoinPoint joinPoint){

        System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
        System.out.println("目标方法所属类的简单类名:" +        joinPoint.getSignature().getDeclaringType().getSimpleName());
        System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
        System.out.println("目标方法声明类型(访问修饰符):" + Modifier.toString(joinPoint.getSignature().getModifiers()));
       
        //获取传入目标方法的参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            System.out.println("第" + (i+1) + "个参数为:" + args[i]);
        }
        
        System.out.println("被代理的对象(调用目标方法的对象):" + joinPoint.getTarget());
        System.out.println("代理对象自己(调用目标方法的对象):" + joinPoint.getThis());
    }

例如将如下代码**targetClass.joint()**方法定义为切入点

//2、从IOC容器中获取bean的实例
TargetClass targetClass = (TargetClass) ctx.getBean("targetClass");

//3、使用bean
String result = targetClass.joint("spring","aop");

则执行1中代码结果为

目标方法名为:joint
目标方法所属类的简单类名:TargetClass
目标方法所属类的类名:aopdemo.TargetClass
目标方法声明类型:public1个参数为:newSpring
第2个参数为:newAop
被代理的对象:aopdemo.TargetClass@4efc180e
代理对象自己:aopdemo.TargetClass@4efc180e

7.自定义注解

@annotation(annotationType)

匹配指定注解为切入点的方法;

例如

@Before(value = "@annotation(controllerLog)") //此处的controllerLog对应方法参数controllerLog的类Log,也就是Log注解
public void boBefore(JoinPoint joinPoint, Log controllerLog)
{
	TIME_THREADLOCAL.set(System.currentTimeMillis());
}

Log就是注解类

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
}

原理

总结(基于AspectJ的自动代理实现,也是使用最广泛的aop实现)

简介

总的来说,Spring AOP的实现原理是通过动态代理技术,创建代理对象并拦截目标方法调用,通过切点规则执行通知。

Spring AOP使用了动态代理技术来实现AOP功能。

在运行时,Spring会为被通知的对象动态创建一个代理对象,代理对象拦截目标对象方法调用,并在方法调用前后执行额外的逻辑。

Spring AOP提供了两种代理方式:JDK动态代理和CGLIB代理。

如果被通知的对象实现了至少一个接口,则使用JDK动态代理;

如果被通知的对象没有实现接口,则使用CGLIB代理。

Spring AOP的实现方式

有手动创建动态代理与自动创建动态代理,本文主要介绍被广泛使用的自动方式,即:@EnableAspectJAutoProxy结合@Aspect主要就是通过AnnotationAwareAspectJAutoProxyCreator创建动态代理对象,来代理切点所在的类的对象,切点标记的目标类、方法、注解执行时,动态代理在调用前、调用后或者异常抛出时执行相应的通知(Advice)逻辑。

自动创建代理实现的AOP

入口是@EnableAspectJAutoProxy注解

1.AOP的开启

AspectJ+spring,自动创建代理的aop实现方式

@EnableAspectJAutoProxy注解,作用:开启aop,创建aop动态代理对象. (此注解在SpringBoot自动配置场景中自动开启,无特殊情况不需要手动添加)

@EnableAspectJAutoProxy@Aspect注解是Spring AOP中两个重要的注解,前者用于启用AOP功能,后者用于定义切面类。它们共同协作,实现了在Spring中使用AOP的功能。

@EnableAspectJAutoProxy(
	proxyTargetClass = true
)
//proxyTargetClass:表示动态代理实现方式,如果值设置true,表示需要代理类都基于CGLIB来实现;默认情况下值是设置成false表示如果原类如果定义了接口则通过JDK.Proxy实现否则基于CGLIB来实现。
开启注解

实际是开启动态代理的创建,@Transactional 事务、@Async异步功能都是通过aop实现,也依赖于此注解

@EnableAspectJAutoProxy源码

image-20230802155745177

EnableAspectJAutoProxy 通过 @Import 引入了 AspectJAutoProxyRegistrar

进入 AspectJAutoProxyRegistrar.class

image-20230802155911921

看到 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 方法,深入其中,它注册了AnnotationAwareAspectJAutoProxyCreator这个动态代理创建器

image-20230802155935976

image-20230928085558678

最后返回的beanDefinition便是AnnotationAwareAspectJAutoProxyCreator,基于@Aspect注解的代理创建器

image-20230802160023280

解析
public abstract class AopConfigUtils {
    
	// internalAutoProxyCreator 是 Spring 框架中的一个类,用于自动创建代理对象。
	public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
			"org.springframework.aop.config.internalAutoProxyCreator";
			

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

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		
        //检查AnnotationAwareAspectJAutoProxyCreator的bean定义 internalAutoProxyCreator 是否已存在
        //如果存在就把AnnotationAwareAspectJAutoProxyCreator添加到 internalAutoProxyCreator 中
		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;
	}
	
}
  • 可以看出@EnableAspectJAutoProxy注解最主要的作用实际上就是通过@Import注解引入AspectJAutoProxyRegistrar,并通过它来把AnnotationAwareAspectJAutoProxyCreator这个对象注入到spring容器中。
  • 通过上述代码分析,AnnotationAwareAspectJAutoProxyCreator是一个名为internalAutoProxyCreator的bean定义,用来创建aop代理,创建出来的aop代理也同样是spring Bean
  • AnnotationAwareAspectJAutoProxyCreator类间接的实现了BeanPostProcessor接口,实现这个接口的目的就是为了创建动态代理bean。
参数详解
@EnableAspectJAutoProxy(proxyTargetClass = false,exposeProxy = true)

EnableAspectJAutoProxy 注解代码

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

    /**
     * 表明该类采用CGLIB代理还是使用JDK的动态代理:true-CGLIB代理,false-JDK动态代理
     */
    boolean proxyTargetClass() default false;

    /**
     * 解决内部调用不能使用代理的场景  默认为false表示不处理
     * true则表示这个代理对象的副本就可以通过AopContext.currentProxy()获得
     *(通过CglibAopProxy和JdkDynamicAopProxy赋值到ThreadLocal里面的),
     * 从而我们可以很方便得在Spring框架上下文中拿到当前代理对象(处理事务时很方便)
     * 
     * spring的动态代理中,如果目标对象的一个方法使用了this.*,那么该目标对象的代理对象执行这个this.*时,获取到的是目标对		 * 象,而不是代理对象。 
     *
     * 当我们又需要调用代理对象的this.*时,就需要exposeProxy = true,
     * 并将this.*替换为AopContent.currentProxy().*,就可以执行代理的***方法了
     *
     */
    boolean exposeProxy() default false;

}
proxyTargetClass

表示动态代理实现方式,如果值设置true,表示需要代理类都基于CGLIB来实现;默认情况下值是设置成false表示如果原类如果定义了接口则通过JDK.Proxy实现否则基于CGLIB来实现

proxyTargetClass = true

  • ​ 目标对象实现了接口 – 使用CGLIB代理机制

  • ​ 目标对象没有接口(只有实现类) – 使用CGLIB代理机制

proxyTargetClass = false

  • ​ 目标对象实现了接口 – 使用JDK动态代理机制(代理所有实现了的接口)

  • ​ 目标对象没有接口(只有实现类) – 使用CGLIB代理机制


exposeProxy

是否暴露代理对象

  • exposeProxy = true

Spring会把当前对象的代理对象存放在ThreadLocal中,即暴露代理对象,可以通过AopContext.currentProxy()获取到当前的代理对象

应用场景:

内部调用,即被代理目标类的一个方法隐式this调用了它自己的另一个方法,如果不设置exposeProxy = true,就会报错

可以处理的报错:

java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
  • exposeProxy = false

​ 不暴露代理对象,通过AopContext.currentProxy()无法获取到代理对象,在某些需要调用代理对象的场景下会出现问题

示例

@Transactional 事务注解,是通过aop实现的事务功能,其原理也是动态代理。

下面代码生效前提是在配置类上加:@EnableAspectJAutoProxy(exposeProxy = true)


@Service
public class B implements BInterface {

    @Transactional
    @Override
    public void funTemp() {
        ...

        // 希望调用本类方法  但是它抛出异常,希望也能够回滚事务
        BInterface b = BInterface.class.cast(AopContext.currentProxy());
        System.out.println(b);
        b.funB();
    }

    @Override
    public void funB() {
        // ... 处理业务属于  
        System.out.println(1 / 0);
    }
}
常见问题

exposeProxy = true,结合**@Async**使用失效

配置@EnableAspectJAutoProxy(exposeProxy = true),结合@Async使用,AopContext.currentProxy()获取代理失效

  • 报错:
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.

提示exposeProxy没有设置为true

  • 原因:

exposeProxy的布尔值是设置到internalAutoProxyCreator中的,它是一个自动创建动态代理的创建器,上面也有解释。

@EnableAsync给容器注入的是AsyncAnnotationBeanPostProcessor,它用于给@Async生成代理,但是它仅仅是个BeanPostProcessor并不属于自动代理创建器,因此exposeProxy = true对它无效,所以 AopContext.currentProxy() 失败。

  • 解决方案:

如果想要结合@Async生效,除配置@EnableAspectJAutoProxy(exposeProxy = true)外,还需配置:

//给@EnableAsync增加exposeProxy属性
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
        beanDefinition.getPropertyValues().add("exposeProxy", true);
    }
}

exposeProxy赋值代码:

可见源码中就是将exposeProxy设置到了internalAutoProxyCreator

public abstract class AopConfigUtils {


	public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
			"org.springframework.aop.config.internalAutoProxyCreator";
			
			
	public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			//将proxyTargetClass使用哪种代理设置到internalAutoProxyCreator
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
		}
	}

	public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			//将exposeProxy是否暴露代理设置到internalAutoProxyCreator
            definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
		}
	}
}

2.动态代理的创建执行

根据上面第1步的介绍,得知**AnnotationAwareAspectJAutoProxyCreator **即为动态代理创建器,是@EnableAspectJAutoProxy注解Import进spring的BeanDefinition,通过后置处理方法创建代理对象

image-20230928091122192

AnnotationAwareAspectJAutoProxyCreator详解

AnnotationAwareAspectJAutoProxyCreator是Spring框架中的一个关键组件,是BeanPostProcessor的间接子类,所以它是通过后置处理器,并根据@Aspect注解来创建动态代理对象:

  1. Spring启动及初始化阶段,扫描@Aspect注解标记的切面类。分析其中定义的切点Pointcut(@Pointcut注解)和通知Advice(@Before、@After等注解),生成**Advisor(切点+通知)**。

  2. 通过@EnableAspectJAutoProxy注解,spring会创建一个AnnotationAwareAspectJAutoProxyCreator,它会通过**后置处理器(BeanPostProcessor)**来创建动态代理。

  3. 在被代理bean初始化完成之后,AnnotationAwareAspectJAutoProxyCreator后置处理器的(代码在其间接父类AbstractAutoProxyCreator中)postProcessAfterInitialization方法会被调用,方法中会根据Advisor和被代理bean创建一个代理bean。创建代理bean的过程中,会为其创建一个拦截器(MethodInterceptor)。拦截器中会持有一个Advisor列表,这个列表中包含了切点和通知信息。

  4. 最后在运行时,业务调用了被代理的bean的方法,代理bean会用拦截器对其进行拦截,拦截器会根据Advisor列表找到对应的切点和通知,然后执行相应的增强操作。无论是CGLIB代理还是JDK动态代理,都有拦截器链,拦截器链中的拦截器可以在方法调用前后进行相应的操作,从而实现对目标方法调用的增强或修改逻辑。

如图,AnnotationAwareAspectJAutoProxyCreator 中的 isInfrastructureClass 方法判断一个类上是否使用@Aspect注解:

image-20230926162728444

bean后置处理器

作用:使用wrapIfNecessary方法创建AOP动态代理对象

入口在后置处理器AbstractAutoProxyCreator类中,bean初始化完成后,创建对应的AOP动态代理。

(只是调试时代码在这里,实际执行者是**AnnotationAwareAspectJAutoProxyCreator** ,它继承了postProcessAfterInitialization方法)

AbstractAutoProxyCreator类的postProcessAfterInitialization方法:

因为早期实例化缓存的bean是原始对象,如果此处if条件成立,那么本bean必然是代理bean,后续要创建其完整的代理,则会调用wrapIfNecessary方法生成代理:

image-20231005100453522

进入wrapIfNecessary,这里做了两件事:

  • 一是获取适用于当前bean所有的Advisors(即切点和通知,对应我们@Aspect配置类中的配置。Advisor会被AnnotationAwareAspectJAutoProxyCreator中创建)
  • 二是根据获取的Advisors去创建代理

image-20231005102816630

下面逐一分析

先获取Advisors

首先是获取Advisors

AbstractAutoProxyCreator类的getAdvicesAndAdvisorsForBean方法。

具体的逻辑在其实现类中,他有四个实现,这里介绍第一个实现AbstractAdvisorAutoProxyCreator

AbstractAdvisorAutoProxyCreator中,它使用findEligibleAdvisors方法获取Advisors:

image-20231005104222136

进入findEligibleAdvisors方法,查看具体逻辑:

image-20231005104337952

下面是其代码注释解析:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		
    	//里面调用了getbean()方法,来找到适用于自动代理该bean的所有合适的Advisor
    	List<Advisor> candidateAdvisors = findCandidateAdvisors();
		//从上面的Advisor集合中筛选适用于当前bean的
    	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		//对筛选完后的Advisors做扩展,可以添加额外的Advisor
    	extendAdvisors(eligibleAdvisors);
		//如果不为空,进行Advisors排序
    	if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
    	//最后返回Advisors集合
		return eligibleAdvisors;
}
然后是创建代理
根据Advisors创建aop动态代理

根据上面获取的Advisors去创建aop动态代理,在wrapIfNecessary方法内的createProxy中:

同样有两个比较重要的步骤:

  1. 处理Advisors:因为上一步获取Advisors的方法有多个实现,有的实现类返回的是object,所以这里要再进行转换,然后将Advisors赋予到代理工厂中。
  2. 结合Advisors使用工厂创建代理

image-20231005114349170

着重看代理的创建

进入proxyFactory.getProxy(classLoader)方法,如下图:

image-20231005114807035

它又调用了createAopProxy()getProxy(classLoader)

  • createAopProxy:这个方法里就是确定使用 jdk动态代理 还是 cglib动态代理,并返回代理实例JdkDynamicAopProxyObjenesisCglibAopProxy
  • getProxy:根据不同的代理类型,调用AopProxy接口的不同的实现进行aop代理创建

image-20231005115027200

下面解析这两个方法:

createAopProxy

先看createAopProxy()方法:

调用的是ProxyCreatorSupportcreateAopProxy()方法,先用**getAopProxyFactory()**获取aop工厂,在通过工厂创建代理实例。

注意这个**this,它是后续将切面及通知添加到代理对象的关键来源**

image-20231005141603158

这个this其实就是在wrapIfNecessary方法中,调用createProxy方法创建的**aop代理工厂:proxyFactory**。如下,wrapIfNecessary方法代码截图:

image-20231005145103001

这个this的目的,就是为了这里能获取到代理工厂的advisors,并根据它创建出代理对象。

解释完this,我们进入createAopProxy(this)方法的源码:

调用的是DefaultAopProxyFactorycreateAopProxy方法:

image-20231005142200166

到这里,就获取到了包含advisors切面信息的工厂实例。

getProxy

然后,根据工厂实例去创建aop代理对象

根据不同的代理类型有不同的实现:

image-20231005145820851

我们看cglib的:

image-20231005150544102

最重要的就是回调函数

首先在aop中,当目标对象方法执行时,如果满足切点规则,就会被拦截器拦截,并执行代理的增强方法。而拦截器就是回调函数的实现。

看上面截图的getCallbacks(rootClass)方法:

image-20231005151429106

image-20231005151626354

DynamicAdvisedInterceptor

DynamicAdvisedInterceptor是一种拦截器(Interceptor),用于实现动态代理对象的拦截逻辑:

  • DynamicAdvisedInterceptor是在使用CGLIB作为AOP代理方式时,由Spring AOP自动生成的一个拦截器。它实现了MethodInterceptor接口,可以拦截代理对象的方法调用。
  • DynamicAdvisedInterceptor的作用是在代理对象的方法执行前后,调用与该方法匹配的切面(Aspect)中定义的通知(Advice)。通过这种方式,切面中的通知可以在适当的时机织入到代理对象的方法中,从而实现横切关注点的统治。
  • DynamicAdvisedInterceptor中,会根据AOP配置中的Advisor对象来选择合适的通知进行调用。Advisor对象包含了切面的相关信息,包括切入点表达式、通知类型等。
  • DynamicAdvisedInterceptor还会处理其他的AOP功能,如异常处理、事务管理等。

需要注意的是,DynamicAdvisedInterceptor是Spring AOP内部使用的拦截器,对于开发者而言,一般不需要直接与该拦截器进行交互。它是Spring框架在运行时自动生成的,并且由Spring AOP框架负责管理和调用。开发者只需要配置好切面和通知,Spring框架会自动创建代理对象并应用相应的拦截器。

至此,aop动态代理就可以通过DynamicAdvisedInterceptor来拦截被代理对象的方法来执行增强了。继续往下看动态代理的执行

动态代理的执行

AOP动态代理会拦截目标方法的调用,来实现增强,也就是执行切面类中的通知方法。

在目标类方法执行前,会执行在CglibAopProxy类中的内部类DynamicAdvisedInterceptorintercept方法,在其中获取拦截链:

//获取目标方法拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

image-20230928145330048

如果没有拦截器链,就会直接执行目标方法,如果有拦截器链就会创建CglibMethodInvocation对象调用proceed()方法触发拦截器链。

4.总结
  1. Spring启动及初始化阶段,扫描@Aspect注解标记的切面类。分析其中定义的切点Pointcut(@Pointcut注解)和通知Advice(@Before、@After等注解),生成**Advisor(切点+通知)**。
  2. 通过@EnableAspectJAutoProxy注解,spring会创建一个AnnotationAwareAspectJAutoProxyCreator,它会通过**后置处理器(BeanPostProcessor)**来创建动态代理。
  3. 在被代理bean初始化完成之后,AnnotationAwareAspectJAutoProxyCreator后置处理器的postProcessAfterInitialization方法会被调用。并根据Advisor和被代理bean创建一个代理bean。创建代理bean的过程中,会为其创建一个拦截器(MethodInterceptor)。拦截器中会持有一个Advisor列表,这个列表中包含了切点和通知信息。
  4. 最后在运行时,业务调用了被代理的bean的方法,代理bean会用拦截器对其进行拦截,拦截器会根据Advisor列表找到对应的切点和通知,然后执行相应的增强操作。无论是CGLIB代理还是JDK动态代理,都有拦截器链,拦截器链中的拦截器可以在方法调用前后进行相应的操作,从而实现对目标方法调用的增强或修改逻辑。

手动创建代理实现的AOP

使用ProxyFactoryBean,结合Advice的各种实现类,实现aop

不怎么用没有深入研究,但是ProxyFactoryBeanAnnotationAwareAspectJAutoProxyCreator有共同的间接父类ProxyCreatorSupport

image-20230927161501759

image-20230927161534777

具体看下面代码示例

代码示例

自动创建代理方式

输入请求:

http://localhost:8081/aop/test?u=12345

控制台输出:

请求参数为:12345
执行方法之前执行。。。。。
aop测试
执行方法之后执行。。。。。

1.pom
<!-- AOP的pom-->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.7</version>
</dependency>
2.切面类
package com.example.springboot.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
//设置注解执行的顺序
@Order(1)
public class AspectTest {
 
    /**
     * 定义切点,切点为对应controller
     */
    @Pointcut("execution(public * com.example.springboot.controller.*.*(..))")
    public void aopPointCut(){
 
    }
 
    @Before("aopPointCut()")
    public void testbefor(JoinPoint joinPoint) {
        illegalParam(joinPoint);
        System.out.println("执行方法之前执行。。。。。");
    }
    
 
    @After("aopPointCut()")
    public void testAfter(JoinPoint joinPoint) {
        //illegalParam(joinPoint);
        System.out.println("执行方法之后执行。。。。。");
    }
 
    /**
     *获取请求参数
     * @param joinPoint
     * @return
     */
    private static void  illegalParam(JoinPoint joinPoint) {
        
        //获取request和response
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        
        if(joinPoint == null){
            return;
        }
        boolean flag = false;
        try{
            // 参数值
            Object[] args = joinPoint.getArgs();
            if (args != null) {
                for (Object o : args) {
                    System.out.println("请求参数为:"+o);
 
                }
            }
        }catch(Exception e){
        }
    }
    
}
3.调用类controller
package com.example.springboot.controller;

import com.example.springboot.annotation.TestAnnotation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("aop")
public class AopController {
 
    @RequestMapping("test")
    @ResponseBody
    public String aopTest(@RequestParam(name = "u")String user) {
       // System.out.println(user);
        System.out.println("aop测试");
        return "success";

    }
}

手动创建代理方式

使用ProxyFactoryBean,结合Advice的实现类MethodBeforeAdvice

写一个类,作为被代理类,即目标类

public class Service1 {
 
    public void m1() {
        System.out.println("我是 m1 方法");
    }
 
    public void m2() {
        System.out.println("我是 m2 方法");
    }
}

手动配置动态代理

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
 
 
@Configuration
public class MainConfig1 {

    //注册目标对象
    @Bean
    public Service1 service1() {
        return new Service1();
    }
 
    //注册一个前置通知
    @Bean
    public MethodBeforeAdvice beforeAdvice() {
        MethodBeforeAdvice advice = new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("准备调用:" + method);
            }
        };
        return advice;
    }
 
    //注册一个后置通知
    @Bean
    public MethodInterceptor costTimeInterceptor() {
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                long starTime = System.nanoTime();
                Object result = invocation.proceed();
                long endTime = System.nanoTime();
                System.out.println(invocation.getMethod() + ",耗时(纳秒):" + (endTime - starTime));
                return result;
            }
        };
        return methodInterceptor;
    }
 
    //注册ProxyFactoryBean
    @Bean
    public ProxyFactoryBean service1Proxy() {
        //1.创建ProxyFactoryBean
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        //2.设置目标对象的bean名称
        proxyFactoryBean.setTargetName("service1");
         /**
         * 3.设置拦截器的bean名称列表,此处2个(advice1和advice2)
         *      这里就相当于设置advise通知了,并且由拦截器的类型 MethodBeforeAdvice 与 MethodInterceptor 可知,
         *      切点就是被代理类的方法
         * */
        proxyFactoryBean.setInterceptorNames("beforeAdvice", "costTimeInterceptor");
        return proxyFactoryBean;
    }
}
  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值