Spring AOP与代理模式
一、Spring AOP与代理模式
** 1.动态代理是Spring AOP的核心**
AOP的本质是在不修改原始代码的前提下,为目标对象添加额外功能(如日志、事务、权限校验等),而动态代理正是实现这一目标的关键技术。它能在运行时动态生成代理对象,拦截对目标对象的方法调用,并在调用前后插入自定义逻辑。
2.Spring AOP支持的动态代理方式
Spring根据目标对象的类型,会自动选择不同的代理实现:
-
JDK动态代理
- 原理:基于Java原生的
java.lang.reflect.Proxy
类,要求目标对象必须实现接口。 - 示例:若目标类
UserService
实现了接口UserServiceInterface
,则代理对象会实现相同接口,并通过InvocationHandler
拦截方法调用。 - 限制:无法代理没有接口的类。
- 原理:基于Java原生的
-
CGLIB代理
- 原理:通过字节码生成库CGLIB,生成目标类的子类作为代理对象(基于继承)。
- 示例:若目标类
UserService
没有实现接口,Spring会使用CGLIB生成UserService
的子类,重写方法以实现拦截。 - 优势:无需接口即可代理,适用于普通类。
-
ByteBuddy(Spring 5+新增)
- 原理:性能比CGLIB更优的字节码生成库,通过生成子类或动态修改类字节码实现代理。
- 应用场景:当需要更高性能的代理时,Spring会自动选择ByteBuddy(需引入相关依赖)。
3.Spring AOP代理的选择机制
Spring通过ProxyFactory
决定使用哪种代理方式:
- 若目标对象实现了接口,优先使用JDK动态代理。
- 若目标对象是普通类(无接口),则使用CGLIB或ByteBuddy代理。
- 可通过配置
proxy-target-class=true
强制使用CGLIB/ByteBuddy代理(即使有接口)。
二、示例:JDK动态代理
1.JDK动态代理的流程如下:
-
定义切面(Aspect)
通过@Aspect
注解声明切面类,使用@Before
、@After
等注解定义通知(Advice)。 -
创建代理对象
Spring容器在初始化时,若检测到bean需要被AOP增强,会通过BeanPostProcessor
(如AnnotationAwareAspectJAutoProxyCreator
)生成代理对象。 -
方法拦截与通知执行
- 当调用代理对象的方法时,JDK动态代理会将调用转发给
InvocationHandler.invoke()
方法。 - 在
invoke()
中,Spring会根据切点(Pointcut)判断是否需要执行切面逻辑,按顺序调用前置、后置、环绕等通知。
- 当调用代理对象的方法时,JDK动态代理会将调用转发给
-
返回结果或处理异常
通知执行完毕后,将结果返回给调用方,或处理可能的异常。
2.代码示例
// 1. 定义接口
public interface UserService {void saveUser(String name);
}// 2. 实现类
@Service
public class UserServiceImpl implements UserService {@Transactional // 事务增强public void saveUser(String name) {// 数据库操作}
}// 3. 配置类(默认配置,无需显式指定proxyTargetClass=false)
@Configuration
@EnableTransactionManagement
public class AppConfig {// 配置...
}
// 4.Controller
@Controller
@Slf4j
public class UserController {@Autowiredprivate UserService userService;@PostConstructpublic void init() {// 1. 直接打印类名log.info("代理类型: " + userService.getClass().getName());// 2. 使用AopUtils判断log.info("是否为JDK代理: " + AopUtils.isJdkDynamicProxy(userService));log.info("是否为CGLIB代理: " + AopUtils.isCglibProxy(userService));// 3. 获取目标类(非代理类)Class<?> targetClass = AopProxyUtils.ultimateTargetClass(userService);log.info("目标类: " + targetClass