Java动态代理的实现

1.什么是代理?

       代理本身是一种设计模式(Proxy Pattern),它的具体表现形式是,当你想调用类A的方法时,实际上并不直接调用类A,而是调用类A的代理类B,它们具有相同的方法名。换句话说,类B持有类A的对象,并且可以通过同名的方法来实现对类A的访问控制。那么问题就产生了:为什么要用这种看似多此一举,画蛇添足的方式呢?

2.为什么要用代理?

       代理模式有它相应的业务场景:

1)解决直接访问对象带来的问题:比如由于安全问题,类A不能直接访问,那么此时就需要一个代理类B来供外部调用,类B提供类A允许的功能函数;

2)调用对象方法时,提供对方法的控制,比如在方法调用前,执行某些动作,即我们通常所谓的AOP。这样可以在不影响原有类的情况下,对方法执行更精确的控制。

 

3.代理的实现方式

 

代理的基本逻辑关系如下:

 

       在实际开发中,我们往往需要实现的部分是代理类的实现,和客户端调用,那么代理类的实现方式有如下几种,为了描述方便,我们将Class A称为被代理类,ClassB称为代理类:

1)静态代理:顾名思义,自己写一个固定的代理类即图中的ClassB ,实用性不高,不推荐;

2)java本身的动态代理:即ClassB的生成是在运行时动态生成,这样的灵活性最高,代理类所代理的类可以动态变化(通过构造函数或者getter方法),局限是被代理类必须是接口实现,通过使用java 的代理类生成模板代码(java的一大特色就是代码模板多);

3)Cglib库,解除了被代理类必须是接口实现的限制,也属于动态代理。

 

4. java的动态代理实现

       1)首先要有被代理类和它的接口。

              接口如下:

public interface Person {
    public void eat();
    public void drink();
    public void sleep();

   public void work();
}

创建一个类Programer(程序员也是人嘛),实现接口Person

public class Programer implements Person {

    @Override

    public void eat() {

        System.out.println("Programmer eat");

    }



    @Override

    public void drink() {

        System.out.println("programer drink");

    }



    @Override

    public void sleep() {

        System.out.println("programmer sleep");

    }



    @Override

    public void work() {

        System.out.println("coding some code");

    }

}

 

2)被代理类有了,然后创建一个代理句柄类PersonHandler,它要实现java的InvocationHandler,代码如下:

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;



public class PersonHandler implements InvocationHandler {



    private Object proxyObject;



    public PersonHandler(Object object) {

        this.proxyObject = object;

    }



    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {



        System.out.println("调用方法为:" + method.getName());

        method.invoke(proxyObject);

        return null;

    }

}

PersonHandler的构造函数的参数,就是要被代理的类,我们这里用的是Object类型,目的是兼容各种类型的被代理类,这样设计具有一定的通用性。

下面的invoke方法,就是在生成动态代理对象之后,调用每个方法,最终都要走这个invoke方法,它的参数Method,就是被代理类的方法对象。

 

3)写一个客户端ProgramerProxy调用动态代理。

import java.lang.reflect.Proxy;



public class ProgramerProxy {



    public static void main(String[] args) {



        Person programer = new Programer();

        PersonHandler personHandler = new PersonHandler(programer);

        Person programmerProxy =

                (Person)Proxy.newProxyInstance(programer.getClass().getClassLoader(),

                        programer.getClass().getInterfaces(), personHandler);

        programmerProxy.eat();

    }



}

其中的关键代码是:

Person programmerProxy =
                (Person)Proxy.newProxyInstance(programer.getClass().getClassLoader(),
                        programer.getClass().getInterfaces(), personHandler);

 

这句代码,就是通过Proxy生成了一个代理类的实例,指向了Person接口的一个具体实现类programmerProxy

下来就可以愉快的调用programmerProxy中的方法了,由于它和Programer类都是Person接口的实现类,因此方法名是一样的。

 

4)总结

我们都干了什么?做了四件事:

  1. 写了一个接口A
  2. 写了一个类B实现了接口A
  3. 写了一个类C实现了InvocationHandler接口,并且类C持有类B
  4. 写了一个类D,通过javaProxy类,以BC为参数,创造了一个动态代理类E(也是A的对象),E可以调用类B的所有方法。

   那么整个过程中,被代理类是A,代理类是E,其余的对象都是必要的参数和过程中的工具类。

 

4. 代理类是如何工作的?

       programmerProxy在调用eat方法时,实际上调用的是PersonHandlerinvoke方法,eat的方法作为Method对象当做了invoke的参数,那么要执行eat,必须在invoke方法中执行method.invoke方法,即反射调用eat方法,被代理对象此时做为invoke参数才真正被代理了。

   由此看来,PersonHandler作为中间层对象,实现了对代理类的访问,某种程度上也可以将它看成一个代理类,只不过这个代理类有一定的通用性(可以代理各种类型的对象,而不是某一个类)。

时序图:

5. 扩展:

      我们可以看到,所有的代理类必须要经过InvocationHandlerinvoke方法,可以对方法进行拦截,加入自己的拦截代码,每个类的调用的公共代码可以在这里进行实现,比如日志打印等等。

   InvocationHandler是不是必须一定要有一个被代理的对象呢,未必,我们其实想做的只是拦截方法,不一定要调用method.invoke,我们可以利用这个特性,加入公共方法,比如mybatis就的实现就利用了这个特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值