###代码示例java
Java原生API中,动态代理经常使用的API有两个:InvocationHandler接口和Proxy类数据库
首先上代码StaffLoggerAspect.javaexpress
public class StaffLoggerAspect implements InvocationHandler { Object target; public Object getObject(Object object) { target = object; return Proxy.newProxyInstance(Staff.class.getClassLoader(), Staff.class.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(proxy.getClass().getName()); return method.invoke(target, args); } }
Main类的main方法bootstrap
public static void main(String[] args) { StaffLoggerAspect aspect = new StaffLoggerAspect(); Staff staff1 = new Staff(); staff1.setName(""); System.out.println(staff1); Object staff2 = aspect.getObject(staff1); System.out.println(staff2); }
输出结果安全
Staff{name='', age=0} com.sun.proxy.$Proxy0 Staff{name='', age=0}
###注意事项网络
先来解释getObject方法,不少网上的教程都有某种函数用来作相似的事情,这个函数用来返回一个与被代理对象实现了相同接口的代理对象dom
注意它返回的是代理对象,而不是原对象。代理对象是Proxy类的子类(API文档),实现了被代理对象的全部接口,因此对这个函数结果进行强制转换的话必须转换成对应的接口类型而不是对象类型,对于没有接口的对象怎么办呢?只能转换成Object对象了,固然你可以经过代理使用的方法也只有Object自带的equals, toString之类的了,一些Object方法不会触发invoke方法,详见后边Proxy对象特征最后一条。代理对象的类名是预留类名详见Java API对于Proxy类的解释ide
###Java API Proxy 部分原文及解释函数
A proxy class has the following properties:性能
- Proxy classes are public, final, and not abstract. (即不可继承)
- 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. (代理对象类名是没有明肯定义的,可是以$Proxy开头的类名要给代理对象保留着,因此标准状况下若是发现某个class的类名以$Proxy开头,那它确定是代理对象,具体可见输出结果)
- A proxy class extends java.lang.reflect.Proxy. (代理对象全是这个类的子类,因此能够用instanceof来判断是不是代理对象)
- A proxy class implements exactly the interfaces specified at its creation, in the same order.(代理对象和被代理对象所实现接口彻底一致,连顺序也一致,顺序是作什么用的呢?原文档后边有一节标题是Methods Duplicated in Multiple Proxy Interfaces,这个顺序便是用来处理多接口具备相同函数声明的状况的,这里不详述)
- 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, invoking getInterfaces on its Class object will return an array containing the same list of interfaces (in the order specified at its creation), invoking getMethods on its Class object will return an array of Method objects that include all of the methods in those interfaces, and invoking getMethod will find methods in the proxy interfaces as would be expected.(由于以前说的一些原理,因此代理对象的getInterfaces返回被代理对象全部接口,顺序一致,代理对象的getMethod返回那些接口的方法)
- The Proxy.isProxyClass method will return true if it is passed a proxy class-- a class returned by Proxy.getProxyClass or the class of an object returned by Proxy.newProxyInstance-- and false otherwise.(代理对象两种获取方式:Proxy.getProxyClass或者Proxy.newProxyInstance其它方式获取的Proxy.isProxyClass会返回false)
- The java.security.ProtectionDomain of a proxy class is the same as that of system classes loaded by the bootstrap class loader, such as java.lang.Object, because the code for a proxy class is generated by trusted system code. This protection domain will typically be granted java.security.AllPermission.(代理对象权限极高,具备java.security.AllPermission,和java.lang.Object及其它的启动对象一致)
- Each proxy class has one public constructor that takes one argument, an implementation of the interface InvocationHandler, 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 the Proxy.newProxyInstance method, which combines the actions of calling Proxy.getProxyClass with invoking the constructor with an invocation handler.(代理类具备一个public单参数构造函数,须要InvocationHandler对象做为输入参数)
- Given a proxy instance proxy and one of the interfaces implemented by its proxy class Foo, the following expression will return true:
proxy instanceof Foo
and the following cast operation will succeed (rather than throwing a ClassCastException):
(Foo) proxy
(首先要注意断句,看英文的话implemented by后边很容易搞错,应该是its proxy class / Foo, 总体上是这样的: Given a proxy instance proxy and one of its interfaces, for example Foo, ... Foo是接口,不是代理类的对象!!!根据以前proxy对象的说明,这一点自己没什么难理解的,它一加说明反而容易弄错)
- Each proxy instance has an associated invocation handler, the one that was passed to its constructor. The static Proxy.getInvocationHandler method will return the invocation handler associated with the proxy instance passed as its argument.(废话,就是说想要拿到某个代理对象的InvocationHandler的话调用Proxy.getInvocationHandler方法)
- An interface method invocation on a proxy instance will be encoded and dispatched to the invocation handler's invoke method as described in the documentation for that method. (代理对象上方法调用会发送到与之关联的InvocationHandler对象的invoke方法,注意全部方法调用都会被转发,须要在invoke里判断是否是你要弄的那个方法)
- An invocation of the hashCode, equals, or toString methods declared in java.lang.Object on a proxy instance will be encoded and dispatched to the invocation handler's invoke method in the same manner as interface method invocations are encoded and dispatched, as described above. The declaring class of the Method object passed to invoke will be java.lang.Object. Other public methods of a proxy instance inherited from java.lang.Object are not overridden by a proxy class, so invocations of those methods behave like they do for instances of java.lang.Object.(hashCode, equals, toString方法也会转发到proxy对象InvocationHandler的invoke方法,其它没有转发!!!重要!!!要注意!!!)
文档重要部分解释完了,这里补充一些实际使用时候用的上的包括思考方式等。
最开始我看到那个getObject里target设置的不是Proxy对象有点转不过来,其实要想清楚,过程是这样的:
使用Java API动态代理的注意事项就简单说到这里,对于Method对象的使用和注意事项请自行查找相关API或教程,CGLib动态代理实现原理有所不一样,类似性确定有,可是请注意和Java原生API动态代理的区别(Java原生经过反射直接生成getInterfaces()获得的那些接口的一个对象,并无论被代理对象extends的部分,因此构造很是快,可是执行的时候性能低,由于须要各类转发;CGLib经过反射直接生成被代理对象的子类,因此不可用于final类的代理,由于这种类不可被继承,同时会把invoke的内容写入被生成的代理对象里,因此生成的时候会很慢,可是这种对象直接就是被代理对象相同类型的对象,毕竟是生成被代理类的子类,因此使用方便,被代理对象能够不须要接口,并且执行方法速度很快。由于原理区别比较大,CGLib确定还有其它与Java API不一样的特征,笔者暂时没有时间研究CGLib的特色,暂不详述)。在选择上,对于单例模式的对象,或者说相比于对象方法调用的次数,构造次数不多的对象建议用CGLib,对于须要大量构造(好比数据库的一条记录,网络服务的一个Request对象),相对而言每一个对象方法调用次数不是不少的对象,建议使用Java原生API。
发现文中有不当之处,但愿指正,尤为CGLib和Java API比较部分,是从原理出发的一些推断,并不肯定,建议你们去看CGLib的相关文档。
笔者撰文不易,转载请注明出处,拜谢