基于注解配置的AOP
# 1、注解方式AOP基本使用
Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:
<!-- 配置目标对象 -->
<bean id="target" class="com.itheima.aop.TargetImpl" />
<!-- 配置通知对象 -->
<bean id="advices" class="com.itheima.aop.Advices" />
<!-- 配置 AOP 切面 -->
<aop:config proxy-target-class="true">
<!-- 声明一个切面,引用通知对象 -->
<aop:aspect ref="advices">
<!-- 配置环绕通知 -->
<aop:around
method="around"
pointcut="execution(* com.itheima.aop.*.*(..))" />
</aop:aspect>
</aop:config>
目标类被Spring容器管理、通知类被Spring管理
// 目标类,实现了 Target 接口
@Component("target")
public class TargetImpl implements Target {
@Override
public void show() {
System.out.println("show Target running...");
}
}
// 通知类,包含一个环绕通知方法
@Component
public class AnnoAdvice {
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知...");
// 调用目标方法
joinPoint.proceed();
System.out.println("环绕后通知...");
}
}
配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
// 第一步:声明该类是一个切面组件
@Component
@Aspect
public class AnnoAdvice {
// 第二步:定义环绕通知,拦截 com.itheima.aop 包下所有方法
@Around("execution(* com.itheima.aop.*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知...");
// 执行目标方法
joinPoint.proceed();
System.out.println("环绕后通知...");
}
}
注解@Aspect、@Around需要被Spring解析,所以在Spring核心配置文件中需要配置aspectj的自动代理
<aop:aspectj-autoproxy/>
如果核心配置使用的是配置类的话,需要配置注解方式的aop自动代理
@Configuration
@ComponentScan("com.itheima.aop")
@EnableAspectJAutoProxy //第三步
public class ApplicationContextConfig {
}
# 2、注解方式AOP配置详解
各种注解方式通知类型
//前置通知
@Before("execution(* com.itheima.aop.*.*(..))")
public void before(JoinPoint joinPoint){}
//后置通知
@AfterReturning("execution(* com.itheima.aop.*.*(..))")
public void AfterReturning(JoinPoint joinPoint){}
//环绕通知
@Around("execution(* com.itheima.aop.*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {}
//异常通知
@AfterThrowing("execution(* com.itheima.aop.*.*(..))")
public void AfterThrowing(JoinPoint joinPoint){}
//最终通知
@After("execution(* com.itheima.aop.*.*(..))")
public void After(JoinPoint joinPoint){}
切点表达式的抽取,使用一个空方法,将切点表达式标注在空方法上,其他通知方法引用即可
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AnnoAdvice {
// 切点表达式抽取:匹配 com.itheima.aop 包下的所有方法
@Pointcut("execution(* com.itheima.aop.*.*(..))")
public void pointcut() {}
// 前置通知:在目标方法执行前执行
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知...");
// 可使用 joinPoint.getSignature() 获取方法签名信息
}
// 后置通知:在目标方法正常返回后执行(不包括抛异常)
@AfterReturning("AnnoAdvice.pointcut()")
public void afterReturning(JoinPoint joinPoint) {
System.out.println("后置通知...");
}
// 其他通知可继续添加,例如 @After、@AfterThrowing、@Around 等
}
# 3、注解方式AOP原理剖析
之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了 <aop:config>标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor ,最终,在该BeanPostProcessor中完成了代理对象的生成。
同样,从aspectj-autoproxy标签的解析器入手
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了<aop:config>标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor ,最终,在该BeanPostProcessor中完成了代理对象的生成。
<!--开启aop aspectj的自动代理-->
<aop:aspectj-autoproxy/>
同样,从aspectj-autoproxy标签的解析器入手
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
而AspectJAutoProxyBeanDefinitionParser代码内部,最终也是执行了和xml方式AOP一样的代码
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source)
如果使用的是核心配置类的话
@Configuration
@ComponentScan("com.itheima.aop")
@EnableAspectJAutoProxy
public class ApplicationContextConfig {
}
查看@EnableAspectJAutoProxy源码,使用的也是@Import导入相关解析类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
使用@Import导入的AspectJAutoProxyRegistrar源码,一路追踪下去,最终还是注册了AnnotationAwareAspectJAutoProxyCreator 这个类
// 方法 1:AOP 导入类中触发注册自动代理创建器
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 注册 AspectJ 注解自动代理创建器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
}
// 方法 2:重载方法,提供默认的 source 参数为 null
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
// 方法 3:最终调用注册或升级自动代理创建器
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
// 注册或升级 AnnotationAwareAspectJAutoProxyCreator 到容器中
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
