commons-proxy的使用
代理可以分为两种。
- 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
- 动态代理:在程序运行时,运用反射机制动态创建而成。
代理主要用途有懒加载,安全,事务,日志,行为监控等对个方面。commons-proxy中提供了代理的多种生成工具,比如用JDK动态代理,CGLIB和Javassist。首先,先介绍工具的实现原理,然后再介绍对应的commons-proxy中对工具的运用[list]
JDK 动态代理 JDK动态代理主要涉及两个类 Proxy,InvocationHandler.Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类.
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
InvocationHandler接口中只有一个方法,一般我们会实现这个类,重写这个唯一的方法invoke(),当用Proxy创建代理对象(代理类的名字是不可知的,这也是动态代理与静态代理的区别)时,传入了一个InvocationHandler参数。当调用代理对象的任何方法放入时候,会自动跳转到调用InvocationHandler中的invoke方法(你可以在invoke()方法内设置断点跟踪)
Object invoke(Object proxy, Method method, Object[] args)
使用JDK动态代理步骤:
首先,必须有一个实现了接口的对象(即要代理的对象)
其次,先实现InvocationHandler接口,并且在此实现中得到代理的真正对象(一般通过构造函数或setter方法注入,由成员变量接受);然后调用Proxy的newProxyInstance,创建代理对象。 接口Animal,实现类Dog,InvocationHandler的实现MyHandler(如无特别说明,下面举例以此为据)
package com.bstek.dylan;
public interface Animal {
public void eat();
}
package com.bstek.dylan;
public class Dog implements Animal {
public void eat() {
System.out.println("I am eating ");
}
}
package com.bstek.dylan;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyHandler implements InvocationHandler {
private Object target ;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("--------ok---------");
return method.invoke(target, args);
}
public static void main(String[] args) {
Animal dog = new Dog();
dog = (Animal) new MyHandler().bind(dog);
dog.eat();
}
}
结果
--------ok---------
I am eating
Proxy.newProxyInstance()方法内部运行机制:
代理类名:把接口类加载到JVM,放到内部Set里保存,把接口的完整名字保存,带包名的接口名字,并以把这组接口名称数组转换成List作为key,用于下面生成代理类后保存到内部Map的key.也就是相当于这一组的接口名称对应的一个生成的代理类.主要是从内存里找是否之前已经生成好了这同一组接口的代理类,如果有就直接拿出。这里第一次是需要新建立的,所以开始创建代理,首先检查代理目标接口的访问控制符是否是默认包级别的,如果是就需要给生成的代理类设置目标接口同样的包名,才能默认访问这种级别下的接口。如果这种有默认访问控制标识符的目标接口,又有不同包名的目标接口,则会报出错误。否则其它情况,是给的无包名的代理类,生成的代理类的默认名称是$Proxy开头加Proxy里标识唯一类名的数字,是静态long型变量,每次生成一次代理类会累加
字节码类: 动态生成class字节码类,该类相当于是Proxy的子类,实现了需要代理的接口方法,并在每个方法里调用了InvocationHandler的invoke方法,而我们自己实现的InvocationHandler接口类里完成了以反射方式最终对目标业务类的接口方法进行调用。所以此种方式实现的动态代理只能代理接口方法,对具体类的代理不能实现。
ProxyGenerator.generateProxyClass(proxyName, interfaces)
根据指定接口和代理类名生成class字节数据,再用这代码生成最终类
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
commons-proxy中有一个类叫ProxyFactory,它底层用JDK中Proxy.newProxyInstance来创建代理对象的.在以下创建方法中,只看形参,主要有两点区别
1.有无ClassLoader,意思是可以自己指定;如果没有指定,默认是当前线程的ClassLoader(通过Thread.currentThread().getContextClassLoader()获取),传入的ClassLoader就是传入到Proxy.newProxyInstance的ClassLoader形参中。2. 有三种不同类型ObjectProvider,Interceptor ,Invoker
ProxyFactory创建代理的方法如下
Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider delegateProvider, Class[] proxyClasses)
Object createDelegatorProxy(ObjectProvider delegateProvider, Class[] proxyClasses)
Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor, Class[] proxyClasses)
Object createInterceptorProxy(Object target, Interceptor interceptor, Class[] proxyClasses)
Object createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class[] proxyClasses)
Object createInvokerProxy(Invoker invoker, Class[] proxyClasses)
ObjectProvider作为参数构造了DelegatorInvocationHandler ,这个类实现了InvocationHandler,所以,当目标对象被调用的时候,该类的invoke()方法中就通过ObjectProvider拿到被代理的目标对象,然后调用目标对象上的方法。很显然,由于DelegatorInvocationHandler 在内部构造,invoke()方法根本不可能被重写,所以假如你想在方法前后打印日志,是根本不可能实现的。故ObjectProvider只被简单的作为一个代理实现。同样DelegatorInvocationHandler传入到Proxy.newProxyInstance方法的形参中。
private static class DelegatorInvocationHandler implements InvocationHandler
{
private final ObjectProvider delegateProvider;
protected DelegatorInvocationHandler( ObjectProvider delegateProvider )
{
this.delegateProvider = delegateProvider;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
try
{
return method.invoke( delegateProvider.getObject(), args );
}
catch( InvocationTargetException e )
{
throw e.getTargetException();
}
}
}
举例
BeanProvider provider = new BeanProvider();
provider.setBeanClass(Dog.class);
ProxyFactory factory = new ProxyFactory();
Animal proxy = (Animal)factory.createDelegatorProxy(provider, provider.getObject().getClass().getInterfaces());
proxy.eat();
结果
I am eating
Interceptor和Target(目标对象)作为形参传入InterceptorInvocationHandler,
同样,InterceptorInvocationHandler实现了InvocationHandler.当调用代理对象方法时,会调用invoke(),其中ReflectionInvocation实现了Invocation
private static class InterceptorInvocationHandler implements InvocationHandler
{
private final Object target;
private final Interceptor methodInterceptor;
public InterceptorInvocationHandler( Object target, Interceptor methodInterceptor )
{
this.target = target;
this.methodInterceptor = methodInterceptor;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
final ReflectionInvocation invocation = new ReflectionInvocation( target, method, args );
return methodInterceptor.intercept( invocation );
}
}
看以上代码methodInterceptor.intercept( invocation )可知只需要重写 intercept( invocation ),在方法中只需要调用invocation.proceed()即可实现目标对象的方法调用,你可以在invocation.proceed()前后打印日志.ReflectionInvocation的proceed()方法实现(仍然用反射)
public Object proceed() throws Throwable
{
try
{
return method.invoke( target, arguments );
}
catch( InvocationTargetException e )
{
throw e.getTargetException();
}
}
举例
public class MyInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
String method = invocation.getMethod().getName();
if(method.equals("eat")) {
System.out.println("吃饭");
}
return invocation.proceed();
}
}
Interceptor interceptor = new MyInterceptor();
ProxyFactory factory = new ProxyFactory();
Animal animal = new Dog();
Animal proxy = (Animal)factory.createInterceptorProxy(animal, interceptor, animal.getClass().getInterfaces());
proxy.eat();
结果
吃饭
I am eating
Invoker传入InvokerInvocationHandler,可见你只需要重写invoke()。
private static class InvokerInvocationHandler implements InvocationHandler
{
private final Invoker invoker;
public InvokerInvocationHandler( Invoker invoker )
{
this.invoker = invoker;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
return invoker.invoke( proxy, method, args );
}
}
举例
public class MyInvoker implements Invoker {
private Object target;
public MyInvoker(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] arguments)
throws Throwable {
String methodName = method.getName();
if(methodName.equals("eat")) {
System.out.println("吃饭");
}else if(methodName.equals("defecate")) {
System.out.println("排便");
}
return method.invoke(target, arguments);
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
Animal animal = new Dog();
Invoker invoker = new MyInvoker(animal);
ProxyFactory factory = new ProxyFactory();
Animal proxy = (Animal)factory.createInvokerProxy(invoker, animal.getClass().getInterfaces());
proxy.eat();
结果
吃饭
I am eating
总结:ProxyFactory创建代理有三种方式(不考虑ClassLoader):ObjectProvider,
Interceptor,Invoker.
- ObjectProvider接口只有一个Object getObject(),只是提供目标对象ProxyFactory创建代理对象,不能够手动控制方法调用;
- Interceptor接口只有一个 Object intercept(Invocation invocation)Invocation类中封装了一些信息,比如代理对象,方法,方法参数,方法调用(前面讲的Invocation.process()),可以在intercept中做打印日志,控制方法是否调用等操作;
- Invoker接口只有一个Object invoke(Object proxy, Method method, Object[] arguments),与intercept不同的是它并没有把proxy,method,arguments和method.invoke()封装为
Invocation.process()。
CGLIB
Javassist
[list]