zoukankan      html  css  js  c++  java
  • 动态代理

           上次总结了一下静态代理,代理的思想很容易理解,但是静态代理的能力毕竟有限。假如我有很多类都需要使用相同的代理,那么我们必须为其编写代理类,而这些代理类做的工作大都一样,在关键时刻会调用真实对象,在调用真实对象的方法前后会有所增强处理。不同之处就是调用的对象不同,那么是否有这样一个类,只需我们给它一个真实对象,给它一些必要的参数,它就能帮我们生成代理类呢。

           当然可以,JDK自带的就有,它们就是java.lang.reflect包中的InvocationHandler接口和Proxy类,这种动态代理依赖于接口。还有一种是cglib的动态代理,它不需要实现特定的接口,基于字节码技术实现。


    一.JDK动态代理

    还是上次的接口和真实对象:

    //专业迎宾团队
    public interface IHelloWorld {
    	//规定了做什么
    	public void sayhello();
    }
    //第二小队
    public class HelloWorldImpl2 implements IHelloWorld {
    	//同样是致欢迎   用标准普通话
    	@Override
    	public void sayhello() {
    		System.out.println("您好 !");
    	}
    }

    创建一个自己的调用处理器,实现InvocationHandler接口:

    /**
     * @author Lucare
     * JDK动态代理
     * 2014年12月16日
     */
    
    //自定义调用处理器
    public class MyInvocationHandler implements InvocationHandler{
    	//使用了Object类型   可代理的类更多  
    	private Object target;
    	
    	public MyInvocationHandler(Object target) {
    		this.target = target;
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args)
    			throws Throwable {
    		System.out.println("doSomething before the method");
    		Object result = method.invoke(target, args);
    		System.out.println("doSomething after the method");
    		return result;
    	}
    	/**
    	 * 获取代理类的实例  实际上是jdk动态帮我们生成了一个代理类  该类继承了Proxy并实现了我们传入的真实类所实现的接口
    	 * 这个过程有些复杂,可参见Proxy.newProxyInstance方法源码
    	 */
    	public Object getProxy(){
    		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
    	}
    
    }

    客户端测试:

    public class Client {
    	public static void main(String[] args) {
    		IHelloWorld helloword = new HelloWorldImpl2();
    		MyInvocationHandler mih = new MyInvocationHandler(helloword);
    		IHelloWorld proxy = (IHelloWorld) mih.getProxy();
    		proxy.sayhello();
    	}
    }
    
    输出:

    doSomething before the method
    您好 !
    doSomething after the method

            刚开始我也会好奇:那个接口中的invoke方法被谁调用?当我们获得了代理类的实例proxy,它实现了IHelloWord接口,必然也实现了接口中的sayhello方法,而我们并没有明显调用invoke方法,那么只可能是在sayhello方法中被调用。


            现在假如我们需要再使用代理,我们要更改的只是Client类的代码,或者我们在另一个地方直接重新来个Client,只要和前面的相关,再来一个真实类。也可以再重新定义一个接口,再来若干实现。总之代码可以最大限度得到复用,我们可以共用调用处理器前后的增强。


    二.Cglib动态代理

            这个例子需要下载相关jar包。在这里我也只是简单了解了下,但是在框架中得到了很多运用,因为这种方式可以达到非侵入式的效果,你只需要写好真实类,它会帮你在合适的地方加入方法增强,如下所示。

    一个真实类:

    public class HelloWorld {
    	public void sayhello(){
    		System.out.println("Hello world!");
    	}
    }

    一个创建代理的类:

    //使用第三方cglib的动态代理,基于字节码实现,拦截实际方法
    public class CglibProxy implements MethodInterceptor{
    	//要代理的原始对象
    	private Object obj;
    	
    	public Object createProxy(Object target){
    		this.obj = target;
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(this.obj.getClass());//设置代理目标
    		enhancer.setCallback(this);//设置回调
    //		enhancer.setClassLoader(target.getClass().getClassLoader());
    		//通过字节码技术动态创建子类实例
    		return enhancer.create();
    	}
    	
    	@Override
    	public Object intercept(Object proxy, Method method, Object[] params,
    			MethodProxy methodProxy) throws Throwable {
    		Object result = null;
    		
    		System.out.println("before this method invoke");
    		//调用原始对象的方法
    		result = methodProxy.invokeSuper(proxy, params);
    		
    		System.out.println("after this method invoke");
    		
    		return result;
    	}
    
    }


    测试客户端:

    package com.fcs.cglib;
    
    public class Client {
    	public static void main(String[] args) {
    		HelloWorld helloworld = new HelloWorld();
    		CglibProxy cglibProxy = new CglibProxy();
    		HelloWorld hw = (HelloWorld) cglibProxy.createProxy(helloworld);
    		hw.sayhello();
    	}
    }
    

    输出:

    before this method invoke
    Hello world!
    after this method invoke

    是不是感觉清爽了很多,是不是有了春天(spring)的感觉。任何类都可以使用这个代理了,只要它是我们需要的,我们就把真实对象传给这个代理生成类的createProxy方法,然后一切高枕无忧,生成的代理类就是真实类的子类,方法sayhello得到了重写,我们向上转型,然后调用sayhello。


    不足之处敬请指正。

       

    ================================== 赵客缦胡缨,吴钩霜雪明。 银鞍照白马,飒沓如流星。 ==================================
  • 相关阅读:
    权限控制
    包(package)
    this和super关键字
    成员变量的隐藏和方法重写(覆盖)
    转--htaccess语法教程 apache服务器伪静态规则教程
    转---高并发Web服务的演变——节约系统内存和CPU
    MYSQL 分表实践
    MySql主从配置实践及其优势浅谈
    一位IT牛人的十年经验之谈
    最近对Memcache的一些学习
  • 原文地址:https://www.cnblogs.com/lucare/p/9312681.html
Copyright © 2011-2022 走看看