前置要点:Spring 内置了 repackaged CGLIB(
org.springframework.cglib),不需要额外引入 cglib 依赖Spring 规则:
目标 Bean没有实现任何接口:Spring AOP 自动走 CGLIB
目标 Bean 有接口:默认优先 JDK;设置
proxyTargetClass=true强制 CGLIB
方式 1:注解开启(@EnableAspectJAutoProxy,最常用)
@Configuration
// proxyTargetClass = true → 强制CGLIB代理,哪怕类实现了接口
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
SpringBoot application.yml 配置(等价上面注解)
spring:
aop:
proxy-target-class: true # true=CGLIB;false=优先JDK
SpringBoot2+ 默认
proxy-target-class=true,也就是默认 CGLIBSpring Fra...
XML 老式配置(SSM)
<aop:aspectj-autoproxy proxy-target-class="true"/>
方式 2:手动用 ProxyFactory(原生 Spring API,不依赖 @Aspect 注解)
适合安全增强(鉴权、审计、参数校验)场景,自己写拦截逻辑,不用 AspectJ 注解:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
// 目标类:无接口,普通Bean
public class UserSecurityService {
public void deleteUser(Long uid) {
System.out.println("执行删除用户:" + uid);
}
}
// 安全增强拦截器(AOP联盟标准MethodInterceptor)
class SecurityInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// ========== 安全增强逻辑:权限校验、审计日志 ==========
System.out.println("[安全校验] 前置:校验操作权限");
// 放行原方法
Object ret = invocation.proceed();
System.out.println("[安全审计] 后置:记录操作日志");
return ret;
}
}
// 生成CGLIB代理
public class TestProxyFactory {
public static void main(String[] args) {
UserSecurityService target = new UserSecurityService();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.setProxyTargetClass(true); // ✅ 关键:强制CGLIB(继承类代理)
proxyFactory.addAdvice(new SecurityInterceptor());
UserSecurityService proxy = (UserSecurityService) proxyFactory.getProxy();
proxy.deleteUser(1001L);
}
}
方式 3:ProxyFactoryBean(XML 配置 Bean)
<bean id="userServiceTarget" class="com.demo.UserSecurityService"/>
<bean id="securityAdvice" class="com.demo.SecurityInterceptor"/>
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userServiceTarget"/>
<property name="proxyTargetClass" value="true"/> <!-- CGLIB开关 -->
<property name="interceptorNames" value="securityAdvice"/>
</bean>
CGLIB 硬性限制(踩坑重点)
目标类不能是 final:CGLIB 靠生成子类继承目标类,final 类无法继承,直接报错Spring
被拦截方法不能是 final /private:无法被子类重写,不会进入拦截器
同类内部 this.xxx () 调用,不会触发 AOP 增强
public void methodA(){ this.methodB(); // ❌ this指向原对象,不是代理对象,methodB切面失效 }解决:从 Spring 容器拿代理对象调用,或者
AopContext.currentProxy()((UserSecurityService)AopContext.currentProxy()).methodB();注意:需要开启
@EnableAspectJAutoProxy(exposeProxy = true)Java17+ 模块化强封装:部分版本会报非法访问 JVM 启动参数(开发环境使用,生产尽量规避)
--add-opens java.base/java.lang=ALL-UNNAMED
和【安全增强】场景结合的选型建议
统一面向接口业务层:优先 JDK 代理,更稳定,无继承限制
第三方 SDK、遗留代码、没有接口的业务类:使用 CGLIB 代理做权限、参数校验、防篡改、审计日志
事务 @Transactional 也是 AOP,同样遵守这套代理规则(很多人遇到事务失效就是 CGLIB this 自调用问题)
补充:@Configuration 的 CGLIB 代理
@Configuration 默认 proxyBeanMethods=true,也是 CGLIB 子类代理,目的是保证 @Bean 方法之间互相调用拿到单例 Bean;如果不需要可以关闭:
@Configuration(proxyBeanMethods = false)
快速区分当前 Bean 是哪种代理
AopUtils.isJdkDynamicProxy(proxyObj); // true=JDK
AopUtils.isCglibProxy(proxyObj); // true=CGLIB本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问