乐趣区

关于设计模式:代理模式

核心思想:应用代理对象来代替原始对象实现对象拜访,这样就能够在不批改原始对象代码的前提下,拓展原始对象的额定性能。

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…

退出移动版