动态代理模式

 动态代理的意义在于生成一个占位(又称代理对象),来代理真实对象,从而控制真实对象的访问。

 先来谈谈什么是代理模式。假设这样一个场景,你的公司是一家软件公司,你是一位软件工程师。客户带着需求去找公司显然不会直接和你谈,而是去找商务谈,此时客户会认为商务就代表公司。

  显然客户是通过商务去访问软件工程师的,那么商务(代理对象)的意义是什么呢?商务可以进行谈判,比如项目启动前的商务谈判,项目的资金,交付,进度等。或者项目完成之后结尾款等。因此,代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他规则控制是否使用真实对象,显然在这个例子里商务控制了客户对软件工程师的访问。

经过上面的论述。代理可分为两个步骤:

● 代理对象和真实对象建立代理关系。

● 实现代理对象的代理逻辑方法。

在java中有多种动态代理技术。如JDK、CGLIB、javassist、ASM。其中最常用的动态代理技术有两种:一种是JDK动态代理,这是JDK自带的功能;另一种是CGLIB,这是第三方提供的一个技术,目前,spring常用JDK和CGLIB,而MyBatis还使用了Javassist,无论哪种代理其技术,他们的理念都是相似的。下面通过代码讲解一下JDK动态代理技术。

JDK动态代理技术是java.lang.reflect.*包提供的方式,塔必须借助一个接口才能产生代理对象,所以先定义接口,如下:

public interface Helloworld {
    public void sayHelloword(String a);
}

 然后提供实现类Helloworld来实现接口,代码如下:

public class HelloworldImpl implements Helloworld{
    @Override
    public void sayHelloword(String a){
        System.out.println("hello world"+a);
    }
}

  这是最简单的java接口和实现类的关系,此时可以开始动态代理了。按照我们之前的分析,先要建立起代理对象和真实服务对象的关系。然后实现代理逻辑,所以一共分为两个步骤。

在JDK动态代理中,要实现代理逻辑类必须去实现java.lang.reflect.InvocationHandler接口,它里面定义了一个invoke方法,并提供接口数组用于下挂代理对象,代码如下:

public class JdkProxyExample implements InvocationHandler {
    //真实对象
    private Object target  = null;

    /**
     * 建立代理对象和真实对象的代理关系并返回代理对象
     * @param target
     * @return 代理对象
     */
    public Object bind(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    @Override
    public  Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("进入代理逻辑方法");
        System.out.println("在调度真实对象之前的服务");
        Object obj = method.invoke(target,args);//相当于调用sayhelloworld方法
        System.out.println("在调度真实对象之后的服务");
        return obj;
    }
}

第一步,建立代理对象和真实对象的关系。这里使用了bind方法去完成的,方法里面首先用类的属性target保存了真实对象,然后通过如下代码建立并生成代理对象。

Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

其中newProxyInstance方法包含3个参数。

● 第一个是类加载器,我们采用了target本身的类加载器。

● 第二个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在target实现的接口下。HelloWorldImpl对象的接口显然就是HelloWorld,代理对象可以这样生命:HelloWorld proxy = xxxxx;

● 第三个是定义实现方法逻辑的代理类,this表示当前对象,他必须实现InvocationHandler接口的Invoke方法,他就是代理逻辑方法的实现方法。

第二步,实现代理逻辑方法。invoke方法可以实现代理逻辑,invoke方法的3个参数的含义如下:

● proxy,代理对象,就是bind方法生成的对象

● method,当前调度的方法

● args,调度方法的参数。

 当我们使用了代理对象调度之后,他就会进入到invoke方法里。

 Object obj = method.invoke(target,args);

这行代码相当于调度真实对象的方法,只是通过反射实现而已。

  类比前面的例子,proxy相当于商务对象,target相当于软件工程师对象,bind方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,他将控制软件工程师的访问。

测试JDK动态代理,代码如下:

public static void main(String[] args) {
        Helloword proxy = (Helloword) lanjieqi_InterceptorJdkProxy.bind(new HelloworldImpl(),
                "main.java.dongtaidaili.lanjieqi_MyInterceptor");
        proxy.sayHelloword("hongwei");
    }
反射方法前逻辑
hello worldhongwei
反射方法后逻辑

  此时,在调度打印hello world之前和之后都可以加入相关的逻辑,甚至可以不调度hello world的打印。

  这就是JDK动态代理,它是一种最常用的动态代理。

 

posted @ 2018-10-29 16:22  hongwei_jia  阅读(134)  评论(0)    收藏  举报