乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

@EnableAspectJAutoProxy

定义与作用

@EnableAspectJAutoProxy 是 Spring 提供的配置类级别注解,用于开启 Spring 对 @AspectJ 风格切面的自动代理支持。没有此注解,Spring 容器不会扫描 @Aspect 标注的类,也不会为匹配的目标 Bean 创建 AOP 代理对象。

在 Spring Boot 2.x 项目中,虽然 Spring Boot 的自动配置已经隐式开启了 AOP 支持,但在纯 Spring Framework 5.x 项目或自定义配置类中,必须显式添加此注解才能使 AOP 生效。

适用位置与常用属性

适用位置

@EnableAspectJAutoProxy 只能标注在配置类上,即带有 @Configuration 注解的类。它通过 @Import(AspectJAutoProxyRegistrar.class) 向 Spring 容器注册一个 AnnotationAwareAspectJAutoProxyCreator,该后置处理器负责在 Bean 初始化后检测是否需要创建 AOP 代理。

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.feixiang.student")
public class AppConfig {
}

常用属性

属性类型默认值说明
proxyTargetClassbooleanfalse是否强制使用 CGLIB 代理。true 时优先使用 CGLIB(即使目标实现了接口);false 时优先使用 JDK 动态代理(目标有接口时)
exposeProxybooleanfalse是否将代理对象暴露到 AopContext 中。设置为 true 后,可在目标方法内通过 AopContext.currentProxy() 获取当前代理对象,解决同类内部 this 调用不触发 AOP 的问题

核心原理

AOP代理创建流程

当 Spring 容器启动时,@EnableAspectJAutoProxy 注册的 AnnotationAwareAspectJAutoProxyCreator 作为 BeanPostProcessor 介入每个 Bean 的初始化过程:

proxyTargetClass 属性对代理方式的影响

关键理解:

  • proxyTargetClass = false 时,Spring 遵循"有接口用 JDK,无接口用 CGLIB"的策略
  • proxyTargetClass = true 时,Spring 统一使用 CGLIB,代理对象是目标类的子类
  • Spring Boot 2.x 在 application.properties 中默认配置了 spring.aop.proxy-target-class=true,因此即使不显式设置,也倾向于使用 CGLIB

完整示例:飞翔科技学生管理系统开启 AOP 代理

场景简述

广州飞翔科技公司的后端开发小崔正在搭建学生成绩管理系统的 Spring 配置。架构师白歌要求:

  1. 系统必须支持 AOP 切面编程,用于后续接入日志和权限模块
  2. 大翔(CTO)明确要求所有代理统一使用 CGLIB,避免 JDK 代理带来的接口方法限制
  3. 部分 Service 内部需要同类方法调用,必须支持 AopContext.currentProxy() 获取代理对象

操作前代码/配置

在未添加 @EnableAspectJAutoProxy 之前,小崔的配置类如下:

@Configuration
@ComponentScan("com.feixiang.student")
public class AppConfig {
}

此时即使编写了 @Aspect 切面类,Spring 容器也不会为其创建代理:

@Aspect
@Component
public class LogAspect {
    @Before("execution(* com.feixiang.student.service.*.*(..))")
    public void logBefore() {
        System.out.println("[LOG] 方法即将执行");
    }
}

运行结果:调用 StudentScoreService.queryScore() 时,控制台没有任何日志输出。切面完全失效。

原因:Spring 容器没有注册 AnnotationAwareAspectJAutoProxyCreator,不知道需要为 Bean 创建 AOP 代理。

使用该注解的完整代码

第一步:配置类开启 AOP 代理

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@ComponentScan("com.feixiang.student")
public class AppConfig {
}

第二步:定义切面类(此时可正常生效)

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.feixiang.student.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.printf("[飞翔科技-日志] %s 方法开始执行%n", methodName);
    }
}

第三步:目标 Service 类

@Service
public class StudentScoreService {

    public Score queryScore(Long studentId) {
        System.out.println("执行业务:查询学生 " + studentId + " 的成绩");
        return new Score(studentId, 88.5);
    }

    public void updateScore(Long studentId, Double newScore) {
        System.out.println("执行业务:更新学生 " + studentId + " 的成绩为 " + newScore);
    }
}

第四步:验证代理类型

public class ProxyTypeDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        StudentScoreService service = ctx.getBean(StudentScoreService.class);
        
        // 打印实际类名,观察代理方式
        System.out.println("Bean 实际类型:" + service.getClass().getName());
        
        service.queryScore(2024001L);
        
        ctx.close();
    }
}

操作后运行结果及分析

控制台输出:

Bean 实际类型:com.feixiang.student.service.StudentScoreService$$EnhancerBySpringCGLIB$$3a2f1b9c
[飞翔科技-日志] queryScore 方法开始执行
执行业务:查询学生 2024001 的成绩

分析:

  • 类名中包含 $$EnhancerBySpringCGLIB$$,证明确实使用了 CGLIB 代理
  • @Before 日志正常输出,证明 @EnableAspectJAutoProxy 已成功开启 AOP 支持
  • 由于设置了 exposeProxy = true,后续可在 StudentScoreService 内部通过 AopContext.currentProxy() 获取代理对象,解决同类内部调用不触发 AOP 的问题

对比实验:将 proxyTargetClass 改为 false,且确保 StudentScoreService 实现了接口:

public interface ScoreService {
    Score queryScore(Long studentId);
    void updateScore(Long studentId, Double newScore);
}

@Service
public class StudentScoreService implements ScoreService {
    // ...
}

此时运行输出:

Bean 实际类型:com.sun.proxy.$Proxy42

类名以 $Proxy 开头,证明使用了 JDK 动态代理。此时只能通过接口类型获取 Bean:ctx.getBean(ScoreService.class),若通过实现类类型获取会抛出 NoSuchBeanDefinitionException。

易错场景与面试考点

易错场景一:忘记添加 @EnableAspectJAutoProxy 导致切面不生效

反例:

@Configuration
@ComponentScan("com.feixiang.student")
// 错误!缺少 @EnableAspectJAutoProxy
public class AppConfig {
}
@Aspect
@Component
public class SecurityAspect {
    @Before("execution(* com.feixiang.student.service.*.*(..))")
    public void checkPermission() {
        System.out.println("权限校验通过");
    }
}

现象:应用正常启动,没有任何报错,但调用 Service 方法时权限校验始终不执行。

排查思路:

  1. 检查配置类是否有 @EnableAspectJAutoProxy
  2. 检查切面类是否有 @Aspect 和 @Component(或任意 Spring 组件注解)
  3. 检查 Pointcut 表达式是否匹配正确包路径
  4. 检查目标类是否被 Spring 容器管理(是否有 @Service 等注解且在扫描包下)

易错场景二:JDK 代理下通过实现类类型获取 Bean 失败

反例:

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)  // 使用 JDK 代理
public class AppConfig {
}

@Service
public class StudentScoreService implements ScoreService {
}
// 错误!JDK 代理下不能通过实现类获取
StudentScoreService service = ctx.getBean(StudentScoreService.class);
// 抛出 NoSuchBeanDefinitionException

正确做法:

// JDK 代理下必须通过接口获取
ScoreService service = ctx.getBean(ScoreService.class);

或统一设置 proxyTargetClass = true,允许通过实现类获取。

面试高频考点

考点一:@EnableAspectJAutoProxy 的作用是什么?不添加会怎样?

该注解向 Spring 容器注册 AnnotationAwareAspectJAutoProxyCreator,用于在 Bean 初始化后检测是否需要创建 AOP 代理。不添加此注解,Spring 不会识别 @Aspect 切面类,也不会为 Bean 创建代理,所有 Advice 失效。

考点二:proxyTargetClass = true 和 false 的区别?

false(Spring 默认)时,目标类实现接口则使用 JDK 动态代理,未实现接口则使用 CGLIB。true(Spring Boot 2.x 默认)时,统一使用 CGLIB 代理,无论目标是否实现接口。CGLIB 通过生成子类实现代理,无法代理 final 类和方法;JDK 代理基于接口,只能代理接口中声明的方法。

考点三:exposeProxy = true 有什么作用?

将当前代理对象暴露到 AopContext 的 ThreadLocal 中,使得目标方法内部可以通过 AopContext.currentProxy() 获取代理对象。这是解决同类内部 this.method() 调用不触发 AOP 问题的关键手段之一。

上一页
AOP核心概念
下一页
@Aspect