第二天 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。

posted @ 2026-11-12 00:27  染川  阅读(0)  评论(0)    收藏  举报