动态代理的几种方式

内容纲要

动态代理在Java中是一个常用的设计模式,它允许在运行时创建代理对象并指定代理行为。动态代理有几种实现方式,常见的有以下几种:

1. JDK 动态代理(基于接口)

  • 原理:JDK动态代理利用java.lang.reflect.Proxy类和InvocationHandler接口,要求目标对象实现一个接口,代理对象通过Proxy.newProxyInstance方法动态生成。
  • 使用场景:适用于接口驱动的代理,不需要显式编写代理类。

示例代码

   public interface MyService {
       void sayHello();
   }

   public class MyServiceImpl implements MyService {
       public void sayHello() {
           System.out.println("Hello, world!");
       }
   }

   public class MyServiceProxy implements InvocationHandler {
       private Object target;

       public MyServiceProxy(Object target) {
           this.target = target;
       }

       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           System.out.println("Before invoking " + method.getName());
           Object result = method.invoke(target, args);
           System.out.println("After invoking " + method.getName());
           return result;
       }
   }

   public class Main {
       public static void main(String[] args) {
           MyService target = new MyServiceImpl();
           MyService proxy = (MyService) Proxy.newProxyInstance(
                   MyService.class.getClassLoader(),
                   new Class[]{MyService.class},
                   new MyServiceProxy(target)
           );
           proxy.sayHello();
       }
   }
   ```
   **输出**:
   ```
   Before invoking sayHello
   Hello, world!
   After invoking sayHello

2. CGLIB 动态代理(基于继承)

  • 原理:CGLIB(Code Generation Library)通过继承目标类,生成目标类的子类来进行代理。它不要求目标对象实现接口。CGLIB 通过字节码技术生成目标类的子类,并重写方法实现代理。
  • 使用场景:适用于目标对象没有接口时,或者你需要对类的非接口方法进行代理。

示例代码

   public class MyService {
       public void sayHello() {
           System.out.println("Hello, world!");
       }
   }

   public class MyServiceInterceptor implements MethodInterceptor {
       @Override
       public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
           System.out.println("Before invoking " + method.getName());
           Object result = proxy.invokeSuper(obj, args);
           System.out.println("After invoking " + method.getName());
           return result;
       }
   }

   public class Main {
       public static void main(String[] args) {
           Enhancer enhancer = new Enhancer();
           enhancer.setSuperclass(MyService.class);
           enhancer.setCallback(new MyServiceInterceptor());
           MyService proxy = (MyService) enhancer.create();
           proxy.sayHello();
       }
   }
   ```
   **输出**:
   ```
   Before invoking sayHello
   Hello, world!
   After invoking sayHello

3. Spring AOP 动态代理(基于 JDK 或 CGLIB)

  • 原理:Spring框架提供了AOP(面向切面编程)功能,支持基于JDK动态代理和CGLIB动态代理。Spring会根据目标对象是否实现接口来决定使用JDK代理还是CGLIB代理。
  • 使用场景:广泛应用于事务管理、日志记录、权限控制等场景,适用于开发者在应用层做切面编程。

示例代码(JDK动态代理示例)

   public class MyServiceImpl implements MyService {
       public void sayHello() {
           System.out.println("Hello, Spring AOP!");
       }
   }

   @Component
   public class MyServiceAspect {
       @Before("execution(* MyService.sayHello(..))")
       public void beforeAdvice() {
           System.out.println("Before method execution.");
       }
   }

配置Spring容器后,AOP会自动为MyServiceImpl创建代理对象,并在调用sayHello方法前触发beforeAdvice

4. ASM 动态代理(字节码操作)

  • 原理:ASM是一个字节码操作框架,它能够直接操作字节码来动态生成代理类。ASM提供了比CGLIB更底层的字节码生成控制,适合在性能要求特别高的场景下使用。
  • 使用场景:主要用于性能敏感型的应用,或需要更复杂字节码操作的场景。

总结

  • JDK动态代理:适用于接口驱动的代理。
  • CGLIB动态代理:适用于没有接口的类的代理。
  • Spring AOP:基于JDK或CGLIB,根据目标对象的情况自动选择代理方式。
  • ASM:适用于字节码级的操作,性能要求较高时使用。

不同的动态代理方式适用于不同的场景,根据需求选择合适的方式。

Leave a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注

close
arrow_upward