核心思想:使用代理对象来替代原始对象完成对象访问,这样就可以在不修改原始对象代码的前提下,拓展原始对象的额外功能。

1. 静态代理

静态代理实现步骤:

  1. 定义一个接口和实现类。
  2. 定义一个代理类也实现该接口。
  3. 将目标对象注入到代理类中,然后在代理类的方法中调用对应目标类中的方法。

例子:

  1. 定义发短信的接口及其实现类

    public interface SmsService {
     String send(String message);
    }
    
    public class SmsServiceImpl implements SmsService {
     @Override
     public String send(String message) {
         System.out.println("send message:" + message);
         return message;
     }
    }
  2. 创建代理类

    public class SmsProxy implements SmsService {
     private final SmsServiceImpl smsServiceImpl;
    
     public SmsProxy(SmsService smsService) {
         this.smsService = smsService;
     }
    
     @Override
     public String send(String message) {
         //调用方法之前,我们可以添加自己的操作
         System.out.println("before method send()");
         smsService.send(message);
         //调用方法之后,我们同样可以添加自己的操作
         System.out.println("after method send()");
         return null;
     }
    }
  3. 测试

    public class Main {
     public static void main(String[] args) {
         SmsService smsService = new SmsServiceImpl();
         SmsProxy smsProxy = new SmsProxy(smsService);
         smsProxy.send("java");
     }
    }
    
    // 输出
    // before method send()
    // send message:java
    // after method send()

    缺点

  4. 接口每新增一个接口,对应的目标类和代理类都需要修改。
  5. 需要对每个目标类都单独写一个代理类。

2. 动态代理

在运行时利用反射机制动态生成类字节码,并加载到JVM中的。

2.1 JDK动态代理

核心: InvocationHandler接口和Proxy类
JDK动态代理实现步骤:

  1. 定义一个接口及其实现类。
  2. 自定义InvocationHandler并重写invoke方法,在invoke方法中会调用原生方法并进行拓展逻辑处理。
  3. 通过Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler h) 方法创建代理对象。

例子:

  1. 定义一个接口及其实现类。

    public interface SmsService {
     String send(String message);
    }
    
    public class SmsServiceImpl implements SmsService {
     @Override
     public String send(String message) {
         System.out.println("send message:" + message);
         return message;
     }
    }
  2. 定义JDK动态代理类

    public class DebugInvocationHandler implements InvocationHandler {
     /**
      * 代理类中的真实对象
      */
     private final Object target;
    
     public DebugInvocationHandler(Object target) {
         this.target = target;
     }
    
     // invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
         //调用方法之前,我们可以添加自己的操作
         System.out.println("before method " + method.getName());
         Object result = method.invoke(target, args);
         //调用方法之后,我们同样可以添加自己的操作
         System.out.println("after method " + method.getName());
         return result;
     }
    }
  3. 获取代理对象的工厂,使用Proxy.newProxyInstance()方法创建代理对象

    public class JdkProxyFactory {
     public static Object getProxy(Object target) {
         return Proxy.newProxyInstance(
                 target.getClass().getClassLoader(), // 目标类的类加载
                 target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                 new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
         );
     }
    }
  4. 使用Proxy.newProxyInstance()方法创建代理对象

    SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
    smsService.send("java");
    
    // 输出
    // before method send
    // send message:java
    // after method send

缺点:

  1. 只能代理实现了接口的类。

2.2 cglib动态代理

**核心:通过继承的方式实现代理,MethodIntercept接口和Enhancer类。
cglib动态代理实现步骤:

  1. 定义一个类。
  2. 自定义MethodIntercept并重写intercept方法,intercept用于拦截原生类的方法,类似于JDK动态代理中的invoke
  3. 通过Enhancercreate()创建代理对象。

例子:

  1. 定义一个类

    public class AliSmsService {
     public String send(String message) {
         System.out.println("send message:" + message);
         return message;
     }
    }
  2. 自定义MethodIntercept并重写intercept方法

    public class DebugMethodInterceptor implements MethodInterceptor {
    
    
     /**
      * @param o           被代理的对象(需要增强的对象)
      * @param method      被拦截的方法(需要增强的方法)
      * @param args        方法入参
      * @param methodProxy 用于调用原始方法
      */
     @Override
     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
         //调用方法之前,我们可以添加自己的操作
         System.out.println("before method " + method.getName());
         Object object = methodProxy.invokeSuper(o, args);
         //调用方法之后,我们同样可以添加自己的操作
         System.out.println("after method " + method.getName());
         return object;
     }
    }
  3. 通过Enhancer获取代理类

    public class CglibProxyFactory {
     public static Object getProxy(Class<?> clazz) {
         // 创建动态代理增强类
         Enhancer enhancer = new Enhancer();
         // 设置类加载器
         enhancer.setClassLoader(clazz.getClassLoader());
         // 设置被代理类
         enhancer.setSuperclass(clazz);
         // 设置方法拦截器
         enhancer.setCallback(new DebugMethodInterceptor());
         // 创建代理类
         return enhancer.create();
     }
    }
  4. 实际使用

    AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
    aliSmsService.send("java");
    
    // 输出
    // before method send
    // send message:java
    // after method send

2.3 JDK动态代理和cglib动态代理区别

  • JDK动态代理只能代理实现了接口的类,而cglib可以代理未实现任何接口的类。cglib通过生成原生类的子类来拦截被代理类的的方法调用,因此cglib不能代理被final修饰的类和方法。
  • jdk效率更高。SpringAOP中如果原生类实现了接口,默认使用JDK动态代理,否则使用cglib。
参考文献:
https://snailclimb.gitee.io/j...

th2009yu
1 声望0 粉丝