代理

作者: 小马蛋 | 来源:发表于2019-11-08 09:34 被阅读0次

    一、概念

    代理模式是设计模式的一种,提供了对目标对象的另外访问模式,即通过代理对象访问目标对象,特点是,可以在目标对象实现的基础上增强额外功能操作,扩展目标对象功能或者改变目标对象的功能操作,代理模式中,用户不直接操作目标对象,而是通过访问代理对象,在代理方法中,由代理对象进行相关处理。

    proxy.jpeg

    二、分类

    Java代理可以分为静态代理和动态代理。

    动态代理又细分为两个不同的实现类库,一是jdk动态代理。二是cglib动态代理。

    1)静态代理

    静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。

    
    // 枪接口
    interface Gun {
        void fire();
    }
    
    

    目标类(被代理类)

    
    // 定义手枪类(被代理目标)
    class Pistol implements Gun{
        @Override
        public void fire(){
            System.out.println("biu~");
        }
    }
    
    

    代理类:

    
    //代理类(代理对象)
    class ProxyFactory implements Gun{
        //接收保存目标对象
        Gun gun;
    
        //创建代理类的对象时,实际传入一个被代理类的对象
        public ProxyFactory(Gun gun){
            this.gun = gun;
        }
    
        @Override
        public void fire() {
            System.out.println("开火之前需要先进行瞄准");
            gun.fire();
            System.out.println("验枪,退子弹");
        }
    }
    

    测试:

    public class Test {
        public static void main(String[] args) {
            Pistol pistol = new Pistol();
            ProxyFactory pro = new ProxyFactory(pistol);
            pro.fire();
        }
    }
    
    

    输出:

    开火之前需要先进行瞄准
    biu~
    验枪,退子弹
    

    特点:

    ① 在不修改目标对象的情况下,对目标功能进行扩展。
    ② 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要增加,随着项目的变迁,会导致代码极为臃肿且难以维护。

    2)jdk动态代理

    不需要代理类实现接口或者继承类,通过Proxy类的静态方法newProxyInstance生成代理类实例,static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler ); 方法接收三个参数:

    1、代理类的类加载器
    2、代理类实现的接口列表
    3、代理类(implements InvocationHandler)实例

    接口:

    public interface Gun {
        void fire();
        void firefire();
    }
    

    目标类,实现Gun接口:

    public class Pistol implements Gun {
        @Override
        public void fire() {
            System.out.println("biu~");
        }
        @Override
        public void firefire() {
            System.out.println("biu~biu~");
        }
    }
    

    代理类,实现InvocationHandler接口:

    public class GunProxy implements InvocationHandler {
        private Gun gun;
    
        public GunProxy(Gun gun) {
            this.gun = gun;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             System.out.println("开火之前需要先进行瞄准");
             Object result = method.invoke(gun, args);
             System.out.println("验枪,退子弹");
             return result;
        }
    }
    

    测试:

    @Test
    public void test0() {
        Gun gun = new Pistol();
        GunProxy gunProxy = new GunProxy(gun);
        gun = (Gun)(Proxy.newProxyInstance(GunProxy.class.getClassLoader(), new Class[]{Gun.class}, gunProxy));
        gun.fire();
        gun.firefire();
    }
    

    输出:

    开火之前需要先进行瞄准
    biu~
    验枪,退子弹
    
    开火之前需要先进行瞄准
    biu~biu~
    验枪,退子弹
    

    两个方法fire()和firefire() 代理类无需增加方法,即可实现代理操作。

    jdk动态代理特点:

    ① 无惧接口实现类的增多而导致的代理类跟进维护。jdk动态代理帮我们做了代理类和被代理类的绑定操作。
    ② 代理类无需继承和实现目标类继承和实现的类、接口,代理类只需实现InvocationHandler接口即可。
    ③ 在静态代理中,目标代理类的方法一旦增加或者修改,需要我们手动维护代理类,太过繁琐。而动态代理则无须手工干预。

    3)cglib动态代理

    使用cglib代理有几个前提:

    ① 目标类不能为final
    ② 目标对象的方法如果为final/static,不会被拦截
    ③ 需要额外的jar包

    手枪类:

    public class Pistol {
        public void fire() {
            System.out.println("biu~");
        }
    
        public void firefire() {
            System.out.println("biu~biu~");
        }
    }
    

    代理类或拦截器类:

    public class GunProxy implements MethodInterceptor {
        private Object target;
    
        public GunProxy(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance(){
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("开火之前需要先进行瞄准");
            //执行目标对象的方法
            Object returnValue = method.invoke(target, objects);
            System.out.println("验枪,退子弹");
            return returnValue;
        }
    }
    

    测试:

    @Test
    public void test0() {
        Pistol pistol = new Pistol();
        Pistol gunProxy = (Pistol) new GunProxy(pistol).getProxyInstance();
        gunProxy.fire();
        gunProxy.firefire();
    }
    

    输出:

    开火之前需要先进行瞄准
    biu~
    验枪,退子弹
    
    开火之前需要先进行瞄准
    biu~biu~
    验枪,退子弹
    

    两个方法fire()和firefire() 代理类无需增加方法,即可实现代理操作。

    cglib动态代理特点:

    ① 目标对象可以是类,使用字节码技术,类不能被final关键字修饰,因为产生的代理对象是继承目标类的,而被final修饰的类,是不能被继承的。
    ② 目标对象的方法如果为final/static,不会被拦截
    ③ 目标类无需实现接口,爽!

    三、结论

    正常情况下推荐使用jdk动态代理和cglib动态代理,减少手工干预,降低代码复杂度。另外提一嘴,Spring 同时利用jdk动态代理和cglib动态代理,MyBatis核心Dao实例返回的就是通过jdk动态代理生成的代理类实例。

    相关文章

      网友评论

          本文标题:代理

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