commons-proxy的使用

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]

    你可能感兴趣的:(jvm,jdk,thread)