第二天 Spring AOP:@annotation 与 execution 并存
Spring AOP:@annotation 与 execution 并存
自定义注解
package com.ranchuan.ai.comm.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义切面注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Action {
String name();
}
目标类
package com.ranchuan.ai.comm.aspect;
import org.springframework.stereotype.Service;
/**
* 拦截注解
*/
@Service
public class DemoAnnotationService {
@Action(name = "注解式拦截的add操作")
public void add() {}
}
package com.ranchuan.ai.comm.aspect;
import org.springframework.stereotype.Service;
/**
* 拦截方法
*/
@Service
public class DemoMethodService {
public void add() {};
}
切面
package com.ranchuan.ai.comm.aspect;
import lombok.extern.log4j.Log4j2;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 切面类
*/
@Aspect //通过 @Aspect 注解声明为一个切面
@Component //通过 @Compotent 让这个切面成为Spring容器管理的Bean
@Log4j2
public class DemoLogAspect {
@Pointcut("@annotation(com.ranchuan.ai.comm.aspect.Action)") //声明有 注解 Action 的都为切点,切点名为 annotationPointcut
public void annotationPointcut() {};
@After("annotationPointcut()") //声明一个建言,并使用 @PointCut定义的切点,After,在切点之后执行
public void after (JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
log.info("AFTER 注解式拦截: {}", action.name());
}
@Before("execution(* com.ranchuan.ai.comm.aspect.DemoMethodService.*(..))") // 声明一个建言,直接使用拦截规则作为参数
public void before (JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
log.info("BEFORE 方法规则拦截: {}", method.getName());
}
}
配置
package com.ranchuan.ai.comm.aspect;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @Date 2026/11/11 22:30
* @Version 1.0
* @Author tfj
*/
@Configuration // 让这个类等价于xml配置文件,Spring启动的时候会加载这个类,根据它的配置创建容器
@ComponentScan("com.ranchuan.ai.comm.aspect") // 告诉Spring:扫描这个包及其子包,把所有带有 @Component、@Service、@Aspect等注解的类注册成Bean
@EnableAspectJAutoProxy // 开始Spring对AspectJ的支持,它会Spring容器在启动时注册一个核心组件 AnnotationAwareAspectJAutoProxyCreator
/**
* 这个组件会做三件事:
* 1.扫描容器里所有带@Aspect的Bean
* 2.解析里面的 @Pointcut、@Before、@After等规则
* 3.对符合条件的Bean创建代理对象
* 也就是说,只有加上 @EnableAspectJAutoProxy,Spring 才会真正给目标类生成代理,切面逻辑才会被执行。
*/
public class DemoAopConfig {
}
要点
两种切点的本质区别:@annotation 是白名单——业务方法主动标记,精确到单个方法,需侵入代码;execution 是通配——按签名规则批量匹配,业务零改动,粒度粗。
execution(* com.ranchuan.ai.comm.aspect.DemoMethodService.*(..)) 四个位置依次是:返回值、类全限定名、方法名、参数。
@EnableAspectJAutoProxy 注册 AnnotationAwareAspectJAutoProxyCreator,负责扫 @Aspect、解析切点、生成代理。缺它则切面静默失效。有接口默认 JDK 代理,无接口走 CGLIB,proxyTargetClass=true 可强制后者。
三个失效场景:@Retention 非 RUNTIME(反射读不到注解);切面类未注册为 Bean;同类内 this.method() 自调用绕过代理——需注入自身或 AopContext.currentProxy() 配合 exposeProxy=true。
浙公网安备 33010602011771号