关于java:秒懂-Java-的三种代理模式

29次阅读

共计 6009 个字符,预计需要花费 16 分钟才能阅读完成。

前言

代理 (Proxy) 模式是一种 结构型 设计模式,提供了对指标对象另外的拜访形式;即通过代理对象拜访指标对象。

这样做的益处是:能够在指标对象实现的根底上,加强额定的性能操作,即扩大指标对象的性能。

这里应用到编程中的一个思维:不要随便去批改他人曾经写好的代码或者办法,如果须要批改,能够通过代理的形式来扩大该办法。

代理模式大抵有三种角色:

  • Real Subject:实在类,也就是被代理类、委托类。用来真正实现业务服务性能;
  • Proxy:代理类,将本身的申请用 Real Subject 对应的性能来实现,代理类对象并不真正的去实现其业务性能;
  • Subject:定义 RealSubject 和 Proxy 角色都应该实现的接口。

代理模式有三种类型,动态代理,动静代理(JDK 代理,接口代理)、Cglib 代理(在内存中动静的创立指标对象的子类)

注释

动态代理

动态代理须要先定义接口,被代理对象与代理对象一起实现雷同的接口,而后 通过调用雷同的办法来调用指标对象的办法

能够看见,代理类无非是在调用委托类办法的前后减少了一些操作。委托类的不同,也就导致代理类的不同。

某公司生产电视机,在当地销售须要找到一个代理销售商。那么客户须要购买电视机的时候,就间接通过代理商购买就能够。

代码示例:

电视机:

public class TV {

    private String name;// 名称

    private String address;// 生产地

    public TV(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public String getAddress() {return address;}

    public void setAddress(String address) {this.address = address;}

    @Override
    public String toString() {
        return "TV{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

创立公司接口:

public interface TVCompany {

    /**
     * 生产电视机
     * @return 电视机
     */
    public TV produceTV();}

公司的工厂生产电视机:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV() {System.out.println("TV factory produce TV...");
        return new TV("小米电视机","合肥");
    }
}

代理商去下单拿货(动态代理类):

public class TVProxy implements TVCompany{

    private TVCompany tvCompany;

    public TVProxy(){}

    @Override
    public TV produceTV() {System.out.println("TV proxy get order ....");
        System.out.println("TV proxy start produce ....");
        if(Objects.isNull(tvCompany)){System.out.println("machine proxy find factory ....");
            tvCompany = new TVFactory();}
        return tvCompany.produceTV();}
}

消费者通过代理商拿货(代理类的应用):

public class TVConsumer {public static void main(String[] args) {TVProxy tvProxy = new TVProxy();
        TV tv = tvProxy.produceTV();
        System.out.println(tv);
    }
}

输入后果:

TV proxy get order .... 
TV proxy start produce .... 
machine proxy find factory .... 
TV factory produce TV...
TV{name='小米电视机', address='合肥'}

Process finished with exit code 0

小结:

  • 长处:动态代理模式在不扭转指标对象的前提下,实现了对指标对象的性能扩大。
  • 毛病:动态代理实现了指标对象的所有办法,一旦指标接口减少办法,代理对象和指标对象都要进行相应的批改,减少保护老本。

如何解决动态代理中的毛病呢?答案是能够应用动静代理形式

动静代理

动静代理具备如下特点:

  1. JDK 动静代理对象不须要实现接口,只有指标对象须要实现接口。
  2. 实现基于接口的动静代理须要利用 JDK 中的 API,在 JVM 内存中动静的构建Proxy 对象
  3. 须要应用到 java.lang.reflect.Proxy,和其 newProxyInstance 办法,然而该办法须要接管三个参数。

留神该办法是在 Proxy 类中是静态方法,且接管的三个参数顺次为:

  • ClassLoader loader:指定以后指标对象应用类加载器,获取加载器的办法是固定的。
  • Class<?>[] interfaces:指标对象实现的接口的类型,应用泛型形式确认类型。
  • InvocationHandler h:事件处理,执行指标对象的办法时,会触发事件处理器的办法,会把以后执行指标对象的办法作为参数传入。

有一天公司减少了业务,发售的商品越来越多,售后也须要更上。然而公司发现原来的代理商,还要再培训能力实现全副的业务,于是就找了另外的 动静代理商 B 代理商 B 承诺无缝对接公司所有的业务,不论新增什么业务,均不须要额定的培训即可实现。

代码示例:

公司减少了培修业务:

public interface TVCompany {

    /**
     * 生产电视机
     * @return 电视机
     */
    public TV produceTV();

    /**
     * 培修电视机
     * @param tv 电视机
     * @return 电视机
     */
    public TV repair(TV tv);
}

工厂也得把培修业务搞起来:

public class TVFactory implements TVCompany {
    @Override
    public TV produceTV() {System.out.println("TV factory produce TV...");
        return new TV("小米电视机","合肥");
    }

    @Override
    public TV repair(TV tv) {System.out.println("tv is repair finished...");
        return new TV("小米电视机","合肥");
    }
}

B 代理商 全面代理公司所有的业务。应用Proxy.newProxyInstance 办法生成代理对象,实现 InvocationHandler 中的 invoke办法,在 invoke 办法中通过反射调用代理类的办法,并提供加强办法。

public class TVProxyFactory {

    private Object target;

    public TVProxyFactory(Object o){this.target = o;}

    public Object getProxy(){return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("TV proxy find factory for tv....");
                Object invoke = method.invoke(target, args);
                return invoke;
            }
        });
    }
}

购买、培修这两个业务 B 代理 就能够间接搞定了。前面公司再减少业务,B 代理也能够一样搞定。

public class TVConsumer {public static void main(String[] args) {TVCompany target = new TVFactory();
        TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();
        TV tv = tvCompany.produceTV();
        tvCompany.repair(tv);
    }
}

输入后果:

TV proxy find factory for tv.... 
TV factory produce TV...
TV proxy find factory for tv.... 
tv is repair finished...

Process finished with exit code 0

小结:

  1. 代理对象不须要实现接口,然而指标对象肯定要实现接口,否则不能用动静代理。
  2. 动静代理的形式中,所有的函数调用最终都会通过 invoke 函数的转发,因而咱们就能够在这里做一些本人想做的操作,比方日志零碎、事务、拦截器、权限管制等。

JDK 动静代理有一个最致命的问题是它只能代理实现了某个接口的实现类,并且代理类也只能代理接口中实现的办法,要是实现类中有本人公有的办法,而接口中没有的话,该办法不能进行代理调用。

怎么解决这个问题呢?咱们能够用 CGLIB 动静代理机制。

Cglib 代理

动态代理和 JDK 代理都须要某个对象实现一个接口,有时候代理对象只是一个独自对象,此时能够应用 Cglib 代理。

Cglib 代理能够称为子类代理,是在内存中构建一个子类对象,从而实现对指标对象性能的扩大。

C 代理商不仅想代理公司,而且还想代理多个工厂的产品。

Cglib 通过 Enhancer 来生成代理类,通过实现MethodInterceptor 接口,并实现其中的 intercept 办法,在此办法中能够增加加强办法,并能够利用反射 Method 或者 MethodProxy 继承类 来调用原办法。

看到 B 代理商 承接了公司(接口)的多种业务,那么此时 C 代理商 又从中发现新的商机,B 只能代理某个公司的产品,而我不仅想要代理公司产品,而且对接不同的工厂,拿货渠道更广,赚钱更痛快。于是 Cglib 就用上了。

代码示例:

public class TVProxyCglib implements MethodInterceptor {

    // 给指标对象创立一个代理对象
    public Object getProxyInstance(Class c){
        //1. 工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(c);
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创立子类(代理对象)return enhancer.create();}

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("TVProxyFactory enhancement.....");
        Object object = methodProxy.invokeSuper(o, objects);
        return object;
    }
}

新代理的 B 工厂

public class TVFactoryB {public TV produceTVB() {System.out.println("tv factory B producing tv....");
        return new TV("华为电视机", "南京");
    }

    public TV repairB(TV tv) {System.out.println("tv B is repair finished....");
        return tv;
    }
}

C 代理 能够间接和公司单干,也能够和工厂打交道。并且能够代理任何工厂的产品。

public class TVConsumer {public static void main(String[] args) {TVCompany tvCompany = (TVCompany) new TVProxyCglib().getProxyInstance(TVFactory.class);
        TV tv = tvCompany.produceTV();
        tvCompany.repair(tv);
        System.out.println("==============================");

        TVFactoryB tvFactoryB = (TVFactoryB) new TVProxyCglib().getProxyInstance(TVFactoryB.class);
        TV tv = tvFactoryB.produceTVB();
        tvFactoryB.repairB(tv);
    }
}

输入后果:

TVProxyFactory enhancement.....
TV factory produce TV...
TVProxyFactory enhancement.....
tv is repair finished...
==============================
TVProxyFactory enhancement.....
tv factory B producing tv.... 
TVProxyFactory enhancement.....
tv B is repair finished.... 

Process finished with exit code 0

Spring 中 AOP 应用代理

Spring 中 AOP 的实现有 JDK 和 Cglib 两种,如下图:

如果指标对象须要实现接口,则应用 JDK 代理。

如果指标对象不须要实现接口,则应用 Cglib 代理。

总结

  1. 动态代理:须要代理类和指标类都实现接口的办法,从而达到代理加强其性能。
  2. JDK 动静代理 :须要代理类实现某个接口,应用Proxy.newProxyInstance 办法生成代理类,并实现 InvocationHandler 中的 invoke 办法,实现加强性能。
  3. Cglib 动静代理 :无需代理类实现接口,应用Cblib 中的 Enhancer 来生成代理对象子类,并实现 MethodInterceptor 中的 intercept 办法,在此办法中能够实现加强性能。

最初

我是一个正在被打击还在致力后退的码农。如果文章对你有帮忙,记得点赞、关注哟,谢谢!

正文完
 0