深入理解java代理

来源:互联网 发布:mac air可以换电池吗 编辑:程序博客网 时间:2024/05/17 08:02

最近接触到了java的代理。但是在core java上死活看不懂他在做啥。被迫无奈只能自己在网络上找资料看什么是代理。

0首先代理是一种设计模式。《大话设计模式》上用一个追女孩的例子生动的描述了什么是代理。

代理模式的定义:Provide a surrogate or placeholder for another object to controlaccess to it(为其他对象提供一种代理以控制对这个对象的访问)。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能

UML视图


代理的一种直观描述。这个平易近人一点。动态代理其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组);然后再利用您指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员。 初始化之后将对象返回给调用的客户端。这样客户端拿到的就是一个实现你所有的接口的Proxy对象。

应用 参考文献【2】

现实世界中,秘书就相当于一个代理,老板开会,那么通知员工开会时间、布置会场、会后整理会场等等开会相关工作就可以交给秘书做,老板就只需要开会就行了,不需要亲自做那些事。同理,在我们程序设计中也可使用代理模式来将由一系列无关逻辑组合在一起的代码进行解耦合,比如业务代码中的日志代码就可以在代理中进行。Spring的AOP就是典型的动态代理应用。

举例子 参考文献【2】

(1)远程代理(Remote Proxy) -可以隐藏一个对象存在于不同地址空间的事实。也使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。

(2)虚拟代理(Virtual Proxy) – 允许内存开销较大的对象在需要的时候创建。只有我们真正需要这个对象的时候才创建。

(3)写入时复制代理(Copy-On-Write Proxy) – 用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。是虚拟代理的一个变体。

(4)保护代理(Protection (Access)Proxy) – 为不同的客户提供不同级别的目标对象访问权限

(5)缓存代理(Cache Proxy) – 为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。

(6)防火墙代理(Firewall Proxy) – 控制网络资源的访问,保护主题免于恶意客户的侵害。

(7)同步代理(SynchronizationProxy) – 在多线程的情况下为主题提供安全的访问。

(8)智能引用代理(Smart ReferenceProxy) - 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

(9)复杂隐藏代理(Complexity HidingProxy) – 用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Façade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。

 


1代码实现(由参考文献【3】提取)

定义一个HelloWorld接口

package com.ljq.test; /** * 定义一个HelloWorld接口 *  * @author jiqinlin * */ public interface HelloWorld {    public void sayHelloWorld();}
类HelloWorldImpl是HelloWorld接口的实现
package com.ljq.test; /** * 类HelloWorldImpl是HelloWorld接口的实现 *  * @author jiqinlin * */ public class HelloWorldImpl implements HelloWorld{    public void sayHelloWorld() {        System.out.println("HelloWorld!");    }}

HelloWorldHandler是 InvocationHandler接口实现

package com.ljq.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 实现在方法调用前后向控制台输出两句字符串 *  * @author jiqinlin * */ public class HelloWorldHandler implements InvocationHandler{    //要代理的原始对象     private Object obj;        public HelloWorldHandler(Object obj) {        super();        this.obj = obj;    }    /**     * 在代理实例上处理方法调用并返回结果     *      * @param proxy 代理类     * @param method 被代理的方法     * @param args 该方法的参数数组     */    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        Object result = null;        //调用之前         doBefore();        //调用原始对象的方法        result=method.invoke(obj, args);        //调用之后        doAfter();        return result;    }        private void doBefore(){        System.out.println("before method invoke");    }        private void doAfter(){        System.out.println("after method invoke");    }    }


测试类

package com.ljq.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class HelloWorldTest {    public static void main(String[] args) {        HelloWorld helloWorld=new HelloWorldImpl();        InvocationHandler handler=new HelloWorldHandler(helloWorld);                //创建动态代理对象        HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(                helloWorld.getClass().getClassLoader(),                 helloWorld.getClass().getInterfaces(),                 handler);        proxy.sayHelloWorld();    }}

运行结果


至此。已经成功运用了java的代理了。但是要想深入探究这些还是远远不够。

2深入一点

根据参考文献【1】。他提取了代理对象proxy的源码。就是我们通过下面代码获得的那个对象

HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(                helloWorld.getClass().getClassLoader(),                 helloWorld.getClass().getInterfaces(),                 handler);

proxy的下面类似的代码

$Proxy0 extends java.lang.reflect.Proxy implements <span style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px; font-family: Arial, Helvetica, sans-serif;">com.ljq.test.</span><span style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px; font-family: Arial, Helvetica, sans-serif;">HelloWorld</span><span style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px; font-family: Arial, Helvetica, sans-serif;"></span>{    java.lang.reflect.Method m4;    java.lang.reflect.Method m2;    java.lang.reflect.Method m0;    java.lang.reflect.Method m3;    java.lang.reflect.Method m1;    void <span style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px; font-family: Arial, Helvetica, sans-serif;">sayHelloWorld</span><span style="color: rgb(51, 51, 51); line-height: 26px; font-family: Arial, Helvetica, sans-serif;">();</span>    int hashCode();    boolean equals(java.lang.Object);    java.lang.String toString();}

这是一个很奇怪的东西。看看他的类名就知道。


2再深入一点

我查阅了java的API文档【4】看看里面对proxy是怎么描述的。也尝试着把他们简要翻译过来。

Proxy提供了一个建立动态代理类和实例的静态方法。它也是所有用这个方法建立的动态代理类的超类。
一个简单的建立方法就是
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class<?>[] { Foo.class },handler);上面代码用的方法就是这个方法。
动态代理类是在运行时在创建实现特定的接口的类。每一个代理实例都有一个实现了InvocationHandler的处理类。在代理类调用的方法将会被发送到InvocationHandler处理类的invoke方法。


代理类有很多性质
1如果代理接口都是public那么代理类也是public
2如果任意一个接口不是public代理类就不是public
3代理类的名字不是合法的
4代理类都是Proxy的子类
5代理类在创建的时候精确实现了特定的接口。而且实现的顺序也一样。
6如果代理类实现了一个非public接口,他将和接口定义在一个包里面。如果不是代理类的包没有定义。
包密封不会阻止代理类定义在特定的包里面。
7对应Class类的getInterfaces方法返回与interfaces一样的结果
8Proxy.isProxyClass方法用于检测是不是代理类。
9prox instanceof Foo会返回true    
10(Foo) proxy可以执行。不会抛出ClassCastException异常。
并且代理里面还有其他的问题。有兴趣可以参看API原文



英文参上

public class Proxyextends Objectimplements Serializable
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

To create a proxy for some interface Foo:

     InvocationHandler handler = new MyInvocationHandler(...);     Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);     Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).                     newInstance(handler); 
or more simply:
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),                                          new Class<?>[] { Foo.class },                                          handler); 

A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created, with behavior as described below. Aproxy interface is such an interface that is implemented by a proxy class. Aproxy instance is an instance of a proxy class. Each proxy instance has an associatedinvocation handler object, which implements the interface InvocationHandler. A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to theinvoke method of the instance's invocation handler, passing the proxy instance, ajava.lang.reflect.Method object identifying the method that was invoked, and an array of typeObject containing the arguments. The invocation handler processes the encoded method invocation as appropriate and the result that it returns will be returned as the result of the method invocation on the proxy instance.

A proxy class has the following properties:

  • Proxy classes are public, final, and not abstract if all proxy interfaces are public.
  • Proxy classes are non-public, final, and not abstract if any of the proxy interfaces is non-public.
  • The unqualified name of a proxy class is unspecified. The space of class names that begin with the string"$Proxy" should be, however, reserved for proxy classes.
  • A proxy class extends java.lang.reflect.Proxy.
  • A proxy class implements exactly the interfaces specified at its creation, in the same order.
  • If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. Otherwise, the package of a proxy class is also unspecified. Note that package sealing will not prevent a proxy class from being successfully defined in a particular package at runtime, and neither will classes already defined by the same class loader and the same package with particular signers.
  • Since a proxy class implements all of the interfaces specified at its creation, invokinggetInterfaces on its Class object will return an array containing the same list of interfaces (in the order specified at its creation), invokinggetMethods on its Class object will return an array of Method objects that include all of the methods in those interfaces, and invokinggetMethod will find methods in the proxy interfaces as would be expected.
  • The Proxy.isProxyClass method will return true if it is passed a proxy class-- a class returned byProxy.getProxyClass or the class of an object returned by Proxy.newProxyInstance-- and false otherwise.
  • The java.security.ProtectionDomain of a proxy class is the same as that of system classes loaded by the bootstrap class loader, such asjava.lang.Object, because the code for a proxy class is generated by trusted system code. This protection domain will typically be grantedjava.security.AllPermission.
  • Each proxy class has one public constructor that takes one argument, an implementation of the interfaceInvocationHandler, to set the invocation handler for a proxy instance. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling theProxy.newProxyInstance method, which combines the actions of callingProxy.getProxyClass with invoking the constructor with an invocation handler.

A proxy instance has the following properties:

  • Given a proxy instance proxy and one of the interfaces implemented by its proxy classFoo, the following expression will return true:
          proxy instanceof Foo 
    and the following cast operation will succeed (rather than throwing a ClassCastException):
          (Foo) proxy 
  • Each proxy instance has an associated invocation handler, the one that was passed to its constructor. The staticProxy.getInvocationHandler method will return the invocation handler associated with the proxy instance passed as its argument.
  • An interface method invocation on a proxy instance will be encoded and dispatched to the invocation handler'sinvoke method as described in the documentation for that method.
  • An invocation of the hashCode, equals, or toString methods declared injava.lang.Object on a proxy instance will be encoded and dispatched to the invocation handler'sinvoke method in the same manner as interface method invocations are encoded and dispatched, as described above. The declaring class of theMethod object passed to invoke will be java.lang.Object. Other public methods of a proxy instance inherited fromjava.lang.Object are not overridden by a proxy class, so invocations of those methods behave like they do for instances ofjava.lang.Object.

Methods Duplicated in Multiple Proxy Interfaces

When two or more interfaces of a proxy class contain a method with the same name and parameter signature, the order of the proxy class's interfaces becomes significant. When such aduplicate method is invoked on a proxy instance, the Method object passed to the invocation handler will not necessarily be the one whose declaring class is assignable from the reference type of the interface that the proxy's method was invoked through. This limitation exists because the corresponding method implementation in the generated proxy class cannot determine which interface it was invoked through. Therefore, when a duplicate method is invoked on a proxy instance, theMethod object for the method in the foremost interface that contains the method (either directly or inherited through a superinterface) in the proxy class's list of interfaces is passed to the invocation handler'sinvoke method, regardless of the reference type through which the method invocation occurred.

If a proxy interface contains a method with the same name and parameter signature as thehashCode, equals, or toString methods of java.lang.Object, when such a method is invoked on a proxy instance, the Method object passed to the invocation handler will have java.lang.Object as its declaring class. In other words, the public, non-final methods ofjava.lang.Object logically precede all of the proxy interfaces for the determination of whichMethod object to pass to the invocation handler.

Note also that when a duplicate method is dispatched to an invocation handler, theinvoke method may only throw checked exception types that are assignable to one of the exception types in thethrows clause of the method in all of the proxy interfaces that it can be invoked through. If theinvoke method throws a checked exception that is not assignable to any of the exception types declared by the method in one of the proxy interfaces that it can be invoked through, then an uncheckedUndeclaredThrowableException will be thrown by the invocation on the proxy instance. This restriction means that not all of the exception types returned by invokinggetExceptionTypes on the Method object passed to the invoke method can necessarily be thrown successfully by the invoke method.


参考文献

【1】http://blog.csdn.net/rokii/article/details/4046098

【2】http://blog.csdn.net/jackiehff/article/details/8621517

【3】http://www.cnblogs.com/linjiqin/archive/2011/02/18/1957600.html

【4】java8 api文档

0 0
原创粉丝点击