java之代理技术

作者: Java面试官 | 来源:发表于2017-02-05 15:49 被阅读65次

    java之代理模式

    直接聊技术!

    描述

    代理模式是常用的java设计模式,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

    代理类可以分为两种。
    静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    动态代理:在程序运行时,运用反射机制动态创建而成。

    总之,意思就是 你不用去做,让别人代替你去做!


    先讲讲如何通过静态代理实现具体业务


    业务描述
    在吃午饭之前洗手,在吃午饭之后擦嘴

    package staticProxy; 
    /**
     * @author:稀饭
     * @time:下午12:23:19
     * @filename:EatLaunch.java
     */
    
    public interface EatLunch {
        public void eat();
    }   
    
    package staticProxy; 
    /**
     * @author:稀饭
     * @time:下午12:21:31
     * @filename:EatLaunch.java
     */
    
    public class EatLaunchImpl implements EatLunch{
    
        @Override
        public void eat() {
            // TODO Auto-generated method stub
            System.out.println("吃午饭了!");
        }   
    }
    
    //代理类
    package staticProxy; 
    /**
     * @author:稀饭
     * @time:下午12:27:27
     * @filename:EatLunchProxy.java
     */
    public class EatLunchProxy implements EatLunch {
    
        private EatLunch eatLunch;
    
        public EatLunchProxy(){
            eatLunch = new EatLaunchImpl();
        }
    
        public void before()
        {
            System.out.println("洗手洗手!");
        }
    
        public void after()
        {
            System.out.println("擦嘴擦嘴!");
        }
    
        /**
         * @Title: eat
         * @Description: TODO
         */
        @Override
        public void eat() {
            // TODO Auto-generated method stub
            before();
            eatLunch.eat();
            after();
        }
    }
    
    package staticProxy;
    /**
     * @Author 稀饭
     * @DATE 2017/2/512:40
     */
    public class TestDemo {
    public static void main(String[] args)
    {
    EatLunch eatLunch = new EatLunchProxy();
    eatLunch.eat();
    }
    }
    

    从以上的源码可以看出,如果业务场景是吃早饭、吃完饭也要洗手、擦嘴的话,则需要多提供几个代理类,这样会导致代码不美观并且多,那么如何解决呢?学完动态代理之后你就知道了啦!


    动态代理可以分为两种:JDK动态代理和CGLIB动态代理

    如何区分
    JDK动态代理无法代理一个没有接口的类,而这也是CGLIB动态代理出现的原因了。


    JDK动态代理的demo,源码如下

    package dynamicProxy;
    /**
     * @author:稀饭
     * @time:上午11:33:09
     * @filename:Hello.java
     */
    
    public interface Hello {
        public void say(String name);
    }
    package dynamicProxy;
    
    /**
     * @author:稀饭
     * @time:上午11:48:43
     * @filename:HelloImpl.java
     */
    
    public class HelloImpl implements Hello {
    
        /** @Title: say 
         * @Description: TODO
         * @param name  
         */
        @Override
        public void say(String name) {
            // TODO Auto-generated method stub
            System.out.println("dynamicProxy.Hello "+name+"!");
        }
    
    }
    package dynamicProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @author:稀饭
     * @time:上午11:26:04
     * @filename:DynamicPrxy.java
     */
    
    public class DynamicProxy implements InvocationHandler {
    
        private Object target;
    
        public DynamicProxy(Object target) {
            this.target = target;
        }
    
        /**
         * @Title: invoke
         * @Description: TODO
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            // TODO Auto-generated method stub
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
        
        //同样的业务操作
        public void before() {
            System.out.println("this is before function");
        }
        
        //同样的业务操作
        public void after() {
            System.out.println("this is after function");
        }
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    }
    package dynamicProxy;
    
    /**
     * @author:稀饭
     * @time:上午11:32:56
     * @filename:TestDemo.java
     */
    
    public class TestDemo {
    
        /** @描述:
         *  @时间:上午11:32:56
         *  @开发者:稀饭
         *  @测试:
         *  @param
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            DynamicProxy dy = new DynamicProxy(new HelloImpl());
            Hello hello = dy.getProxy();
            hello.say("proxy");
        }
    }
    

    源码解析
    DynamicProxy实现了InvocationHandler接口,必须实现invoke方法,在该方法中直接通过反射invoke method,在调用前后分别处理before和after,最后将result返回。而在TestDemo中hello调用say的时候,由于Hello已经被“代理”了,所以在调用say函数的时候其实是调用invoke函数,而在invoke函数中先是实现了before函数才实现Object result = method.invoke(target, args),这一句其实是调用say函数,而后才实现after函数,用吃饭前洗手、吃饭后擦嘴的业务来套的话用了动态代理是不是只需要一个代理类就可以了?对的!


    CGlib动态代理的demo,源码如下


    /**
     * @author:稀饭
     * @time:上午11:48:43
     * @filename:HelloImpl.java
     */
        
    public class Hello {
    
        /** @Title: say 
         * @Description: TODO
         * @param name  
         */
        public void say(String name) {
            // TODO Auto-generated method stub
            System.out.println("dynamicProxy.Hello "+name+"!");
        }
    
    }
    package CGlibProxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    /**
     * @Author 稀饭
     * @DATE 2017/2/515:16
     */
    public class CGlibProxy implements MethodInterceptor {
    
        private static CGlibProxy instance = new CGlibProxy();
    
        // 单例模式
        private CGlibProxy() {
            // TODO Auto-generated constructor stub
        }
    
        public static CGlibProxy getInstance() {
            return instance;
        }
    
        // 同样的业务操作
        public void before() {
            System.out.println("this is before function");
        }
    
        // 同样的业务操作
        public void after() {
            System.out.println("this is after function");
        }
        
        //核心代码
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            // TODO Auto-generated method stub
            before(); 
            Object result = proxy.invokeSuper(obj, args);
            after();
            return result;
        }
        
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Class<T> cls){
            return (T) Enhancer.create(cls, this);
        }
    }
    package CGlibProxy;
    /**
     * @author:稀饭
     * @time:上午11:32:56
     * @filename:TestDemo.java
     */
    
    public class TestDemo {
    
        /** @描述:
         *  @时间:上午11:32:56
         *  @开发者:稀饭
         *  @测试:
         *  @param
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Hello helloProxy = CGlibProxy.getInstance().getProxy(Hello.class);
            helloProxy.say("Jack");
        }
    
    }
    

    CGlib实现的是方法级别的代理,也可以理解成对方法拦截。

    tip:代理实现aop的重要技术!


    Note:发布的这些文章全都是自己边学边总结的,难免有纰漏,如果发现有不足的地方,希望可以指出来,一起学习咯,么么哒。
    开源爱好者,相信开源的力量必将改变世界:
    ** osc :** https://git.oschina.net/xi_fan
    github: https://github.com/wiatingpub

    相关文章

      网友评论

        本文标题:java之代理技术

        本文链接:https://www.haomeiwen.com/subject/hpxeittx.html