涉及到 Spring 框架中的动态代理时,主要是指 Spring AOP(面向切面编程)中的代理机制。Spring AOP 使用动态代理来实现切面功能,通过在运行时动态地创建代理对象来增强原始对象的行为。
Spring 动态代理概述
- 代理模式简介:
- 代理模式是一种结构型设计模式,允许在不改变原始类(被代理类)代码的情况下,通过引入代理类来控制对原始类的访问。
- Spring 中的代理机制:
- Spring AOP 提供两种代理方式:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理。–默认的实现是 JDK 动态代理
- JDK 动态代理:
- 如果目标对象实现了接口,Spring AOP 将使用 JDK 动态代理来创建 AOP 代理。在运行时,Spring AOP 使用 Java 标准库 java.lang.reflect.Proxy 来创建代理对象,该代理对象实现了目标对象的所有接口,并在方法调用前后执行切面逻辑。
javapublic interface UserService {
void saveUser(User user);
}
javapublic class UserServiceImpl implements UserService {
public void saveUser(User user) {
System.out.println("我是impl");
}
}
/**
* MyAspect 类实现了 MethodInterceptor 接口,用于实现面向切面编程(AOP)的切面逻辑。
* 该类的主要作用是在目标方法执行前、执行后或出现异常时注入自定义的处理逻辑。
*/
public class MyAspect implements MethodInterceptor {
/**
* 方法拦截器的核心方法,用于拦截目标对象的方法调用。
*
* @param invocation 方法调用的拦截对象,包含目标对象、目标方法等信息。
* @return 返回目标方法的执行结果,可以根据需要进行处理或修改。
* @throws Throwable 如果目标方法执行过程中抛出异常,该方法可以重新抛出异常或进行异常处理。
*/
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
System.out.println("我是MyAspect");
System.out.println("--------------------");
// 调用 invocation.proceed() 来执行目标方法,这是 AOP 中心点调用目标方法的入口。
// 在此之前或之后可以添加额外的逻辑,例如日志记录、性能监控、权限检查等。
// 在目标方法执行前后添加额外逻辑
Object result = invocation.proceed();
return result;
}
public static void main(String[] args) {
// 创建一个代理工厂对象
// 创建 AOP 代理
ProxyFactory proxyFactory = new ProxyFactory();
// 设置代理的目标对象为一个实现了UserService接口的具体服务实现
proxyFactory.setTarget(new UserServiceImpl());
// 添加一个切面建议,该切面将被织入到目标对象的方法调用中
proxyFactory.addAdvice(new MyAspect());
// 通过代理工厂获取代理对象,该代理对象实现了UserService接口
// 在调用其方法时,会先执行MyAspect中的逻辑
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.saveUser(new User());
}
}
执行结果显示,在调用saveUser之前,执行了invoke方法中的逻辑
- CGLIB 动态代理:
- 如果目标对象没有实现接口,Spring AOP 将使用 CGLIB 动态代理。CGLIB(Code Generation Library)通过继承目标类来创建代理对象,因此可以对类的方法进行增强。
public class UserService {
public void saveUser(String username) {
System.out.println("Saving user: " + username);
}
}
/**
* 日志切面类,用于实现方法执行前后的日志记录。
* 通过实现MethodInterceptor接口,可以在方法执行前、执行后和发生异常时插入自定义逻辑。
*/
public class LogAspect implements MethodInterceptor {
/**
* 在目标方法执行前后的拦截器方法。
*
* @param o 目标对象,即被拦截的对象。
* @param method 目标方法,即被拦截的方法。
* @param objects 方法参数。
* @param proxy 方法代理,用于调用目标方法。
* @return 目标方法的返回值。
* @throws Throwable 如果目标方法执行过程中抛出异常。
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("执行目标方法之前");
Object result = proxy.invokeSuper(o, objects);
System.out.println("执行目标方法之后");
return result;
}
public static void main(String[] args) {
// 创建Enhancer对象,用于生成动态代理类
// 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置代理类的超类
enhancer.setSuperclass(UserService.class);
// 设置回调函数,即拦截器
enhancer.setCallback(new LogAspect());
// 创建动态代理类实例
// 创建代理对象
UserService proxy = (UserService) enhancer.create();
// 调用代理对象的方法,此时会触发拦截器中的intercept方法
// 调用代理对象的方法
proxy.saveUser("Alice");
}
}
- 代理选择:
- Spring AOP 默认情况下优先使用 JDK 动态代理,只有当目标类没有实现接口时才会使用 CGLIB 动态代理。可以通过配置强制使用 CGLIB 或者禁用 CGLIB 来控制代理方式。
- 应用场景:
- 动态代理在 Spring 中广泛应用于事务管理、日志记录、性能监控等方面,通过声明式的 AOP 配置将切面逻辑与业务逻辑分离,提高了代码的可维护性和可测试性。