代理

来源:互联网 发布:中国律师事务所 知乎 编辑:程序博客网 时间:2024/04/27 17:56


什么是代理?

生活中的代理:比如我们要买一部华为手机,我们可以去华为深圳总部购买原厂出厂的手机,但是我在武汉,要去深圳买手机,路费很昂贵。于是武汉就有一个代理商,我们可以去代理商那里去买不用搭火车去深圳,而且武汉的代理商还可以提供一些华为非官方的服务,比如购买一部华为手机送一个保护壳。这就是生活中的代理。

程序中的代理:为已存在的多个具有相同接口的目标类 的各个方法增加一些系统功能,这就是程序中的代理。程序中的代理分为静态代理和动态代理。

为什么要有代理?

主要的目的是为了对目标类,进行精确的控制,比如:编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码

eg:我们在调用某个类的某个方法时,不仅要的到方法调用后的结果,还想再调用方法前后加上自己定义的一段程序并执行,这时使用代理技术就很方便了。

Collection proxy2 = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class},new InvocationHandler(){ArrayList al = new ArrayList();@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {long startTime = System.currentTimeMillis(); //[1]Object retval = method.invoke(al,args);<span style="white-space:pre"></span>long endTime = System.currentTimeMillis();   //[2]<span style="white-space:pre"></span><span style="white-space:pre"></span>System.out.println(method.getName()+" using time:"+(endTime-startTime));return retval;}});

上面两一代码,就是在调用某个方法时想知道调用这个方法所用的时间,则可以在invoke中加上[1] [2]代码。

为什么我们一般使用的都是动态类?

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情

动态代理类是怎么生成的?

JVM在运行期间动态生成类的字节码,这个字节码加载进内存得到了可以实例化对象的动态代理类。具体代码如下:

Object proxy = Proxy.newProxyInstance(loader, interfaces, h)
1,必须有个参数--类加载器,一般为要生成的代理类的类加载器
作用:产生的字节码文件必须有一个相关的类加载器才能加载到内存。

2,必须有个参数--接口,代理类所实现的一个或多个接口
作用:我们要生成的代理类只有知道了他实现的接口我们才知道他有哪些方法,才能生成这个有价值的类

3,必须有个参数--对象,实现了InvocationHandler接口的类的对象
作用:生成的类中的方法的代码是怎样的,得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是这个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了 

eg:

private static Object getProxy(final ArrayList target,final Advice advice) {Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //1target.getClass().getInterfaces(),  //2new InvocationHandler(){<span style="white-space:pre"></span>    //3@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {advice.beforMethod(method);   Object retval = method.invoke(target,args);advice.afterMethod(method);  return retval;}});return proxy;}

分析InvocationHandler

很明显这个单词的字面意思就是调用处理者,顾名思义invocationhandler对象就是用来处理代理对象调用的方法的。怎么处理?
这个对象中有个方法invoke(object proxy,Method method,Object[] args)
proxy:本代理对象
method:本代理对象调用的方法名
args:本代理对象调用的方法的参数
这个方法中处理的具体方法:method.invoke(target,args);
eg:

new InvocationHandler(){<span></span>@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {advice.beforMethod(method);   Object retval = method.invoke(target,args);advice.afterMethod(method);  return retval;}}

注意:
在代理实例上java.long.object中声明的方法中,只有调用的是toString() hashCode()  equals()中的一个才会转到invoke方法中执行。所以proxy1.getClass()是获得的proxy代理类的字节码,而不是target的字节码
eg:
proxy1.toString();转到invoke中,使用的是targe对应类中的方法
proxy1.getClass();不转到invoke,使用的是代理类中的方法,得到代理类的字节码


动态代理的工作原理:

要产生一个代理,代理的构造方法根据传入的参数,知道了要生成的代理类的类加载器,实现的接口和调用处理者,生成字节码。而调用处理者(Invocationhandler)创建了一个目标,即代理类代理的目标,并定义了目标的方法,和系统自己添加的代码。客户端调用代理的各个方法,代理的各个方法会把调用请求转发给刚才通过构造方法传进去的handler对象,这个handler对象又把各个请求分发给目标的相应方法;handler的invoke方法中可以加入日志功能,还可以调用目标的对应方法;


0 0
原创粉丝点击