zoukankan      html  css  js  c++  java
  • JAVA基础知识之JVM-——动态代理(AOP)

    代理模式简介

    在很多场景下,我们想使用一个类(通常是接口)A时,并不是直接使用这个类,而是通过另外一个类B去调用A的方法,这里的类B就是一个代理类。

    有很多场景都会用到这种方法,例如假如创建类A需要很大的开销,我们会直接使用类B来代表类A。 又或者类A在远程主机上,我们没有权限直接调用A的方法,而代理类B却有权限,我们可以调用B从而访问到A。 又或者我们需要批量给A的方法加强一些功能,而我们没有权限修改A,或者修改A会导致其他关联的类因为耦合性需要修改,这时候我们也同样可以通过代理类B来增加功能。 这种间接访问A的方式就是一种非常常见的代理模式——通常我们只需要知道一个接口里提供了哪些方法,但并不需要方法细节,我们可以通过代理类来控制这些方法的调用,并满足例如前面提到的各种场景的功能要求。

    动态代理

    JAVA的代理模式大体上分为静态代理(编译期间就已经明确代理类及委托类的细节)和动态代理(在JVM中动态生成一个代理类,通过动态生成的代理类去访问委托类)。

    动态代理也称为AOP模式,即面向切面编程。 实现AOP的方式有很多,目前主要有两种,一种是JDK自己实现的基于接口的动态代理,另一种是cglib方式,不需要强制实现接口。

    本篇只简单介绍JDK自带的动态代理。

    JDK动态代理

    java动态代理主要涉及几个角色,目标类(接口-target),代理对象(proxy),处理器类(接口-InvocationHandler)。

    实现原理大致是这样的,通过代理类proxy访问目标类target时,在JDK底层,会动态生成一个proxy类(其实是子类),同时让会让这个proxy类实现target接口,这个Proxy类包含一个InvocationHandler类型的成员变量,后面就会用处理器对象去初始化这个Proxy类对象。proxy中对target类所有的方法实现都交给InvocationHandler的实现类去处理,通过在proxy中重写target方法的实现,而在实现中直接调用InvocationHandler的invoke方法,这样就变成了我们调用代理proxy类执行target方法的时候,实际上是执行了InvocationHandler的invoke方法,而在invoke方法中,我们可以加入我们自己的逻辑,然后才调用真正的target中的方法。

    整个过程可以用下面两段伪代码来模拟,

    首先根据我们为proxy传入的参数(target所有接口方法,InvocationHandler实例),JDK会在底层动态地为我们创建一个代理类,代理类如下,

     1 class Proxy0 extends Proxy  implements target {
     2 
     3   private Method m1;
     4 
     5   //用InvocationHandler去初始化proxy, 后期就可以将proxy的代理的方法交给InvocationHandler去处理
     6   public Proxy0(InvocationHandler handler) {
     7 
     8     supper(hander)
     9   }
    10 
    11   public void setM1(Method m1) {
    12 
    13     this.m1 = m1;
    14   }
    15 
    16   private void method1(Object para) {
    17 
    18     this.hander.invoke(this, m1, new object[] {para});
    19   }   
    20 
    21 }

    这段代理类的动态生成过程都由JVM控制,我们不可见(当然可以通过特殊方式拿到)。可以看到代理类会实现目标类target的所有方法,但是实现类中是调用了InvocationHandler的invoke方法的,这个invoke方法是需要我们自己去实现的,一般类似这样,

     1 class InvocationHandlerImpl implements InvocationHandler {
     2 private Object target; 3 @Override
     4 public Object invoke(Object proxy, Method method, Object[] args) {
     5 // 加入我们自己的代码
     6 ....
     7 //调用目标类的真正方法
     8 method.invoke(target, args);
     9 //加入我们自己的代码
    10 .....
    11 }

    关键点在于第2行和第8行,使用了反射执行了目标类的真正需要被代理的方法。 

    以上整个过程,就是proxy代理的实现原理。

    下面这张图片借用了别人的博客

    下面是一个简单的例子,演示JDK动态代理的用法,

    定义一个Dog接口, JDK的动态代理是基于接口的,其他动态代理可以基于继承的方式。

    1 package aop;
    2 
    3 public interface Dog {
    4     void info();
    5     void run();
    6 }

    为接口写个实现类,

     1 package aop;
     2 
     3 public class GunDog implements Dog {
     4 
     5     @Override
     6     public void info() {
     7         // TODO Auto-generated method stub
     8         System.out.println("我是一只猎狗");
     9     }
    10 
    11     @Override
    12     public void run() {
    13         // TODO Auto-generated method stub
    14         System.out.println("我奔跑迅速");
    15     }
    16 
    17 }

    写个工具类,

     1 package aop;
     2 
     3 public class DogUtil {
     4     public void method1() {
     5         System.out.println("====this is method1====");
     6     }
     7     
     8     public void method2() {
     9         System.out.println("====this is method2====");
    10     }
    11 }

    关键点,写一个处理器,实现InvocationHandler接口,

    在invoke中加上我们自己的逻辑,再调用真正的target的方法。 

    后续代理类在处理target的所有方法时,都会在底层调用invoke方法,即将调用交给invoke了

     1 package aop;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 
     6 public class MyInvocationHandler implements InvocationHandler {
     7 
     8     private Object target;
     9 
    10     public void setTarget(Object target) {
    11         this.target = target;
    12     }
    13 
    14     @Override
    15     public Object invoke(Object proxy, Method method, Object[] args)
    16             throws Throwable {
    17         // TODO Auto-generated method stub
    18         DogUtil du = new DogUtil();
    19         du.method1();
    20         Object result = method.invoke(target, args);
    21         du.method2();
    22         return result;
    23     }
    24 
    25 }

    写一个工厂类获取proxy类实例,简化编程

     1 package aop;
     2 
     3 import java.lang.reflect.Proxy;
     4 
     5 public class MyProxyFactory {
     6     public static Object getProxy(Object target) {
     7         MyInvocationHandler handler = new MyInvocationHandler();
     8         handler.setTarget(target);
     9         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    10     }
    11 }

    下面是测试类,

     1 package aop;
     2 
     3 public class Test {
     4     public static void main(String[] args) {
     5         Dog target = new GunDog();
     6         Dog dog = (Dog)MyProxyFactory.getProxy(target);
     7         dog.info();
     8         dog.run();
     9     }
    10 }

    执行结果如下,

    1 代理开始。。。
    2 我是一只猎狗
    3 代理结束。。。
    4 代理开始。。。
    5 我奔跑迅速
    6 代理结束。。。

    reference

    http://blog.csdn.net/luanlouis/article/details/24589193 

  • 相关阅读:
    UI是一个状态机
    WPF : Binding的3个属性: Source, RelativeSource, ElementName
    业务驱动设计
    WPF : 对Collection要注意重用子控件
    WPF : UserControl的Initialized事件不会触发
    mvc3上传图片
    MVC3.0自定义视图引擎(切换皮肤)
    ASP.NET MVC 多语言解决方案
    键盘键位表
    Silverlight之IsolatedStorageSettings对象
  • 原文地址:https://www.cnblogs.com/fysola/p/6111697.html
Copyright © 2011-2022 走看看