秒懂动态代理的三种实现方法 - 极悦
首页 课程 师资 教程 报名

秒懂动态代理的三种实现方法

  • 2022-04-20 10:43:05
  • 749次 极悦

代理模式是一种结构类型的设计模式,它提供了另一种方式是通过代理访问目标对象的对象。

这样做的优点是:实现目标对象的基础上,额外的功能操作可以提高,也就是说,目标对象可以扩展的功能。

这里使用一个想法在编程:不要修改代码或别人写的方法。如果你需要修改它,您可以扩展代理的方法。

代理模型有三个角色:

真正的主题:真正的类,也就是说,代理类和委托类。用来真正完整的业务服务功能;

代理:代理类实现自己的请求的函数对应于真正的主题,和代理类对象并没有真正实现其业务功能;

主题:定义RealSubject和代理的角色应该实现的接口。

有三种类型的代理模式,静态代理,动态代理(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

优点:静态代理模式实现的功能扩张目标对象不改变目标对象。

缺点:静态代理实现所有目标对象的方法。一旦方法被添加到目标接口,代理对象和目标对象必须做出相应调整,从而增加维护成本。

如何解决静态代理的缺点吗?答案是,您可以使用动态代理

动态代理

动态代理有以下特点:

JDK动态代理对象不需要实现接口,只有目标对象需要实现的接口。

实现动态代理基于接口,您需要使用JDK动态构建的API在JVM内存代理对象。

需要使用数组。代理,其newProxyInstance方法,但这种方法需要接收三个参数。

注意,这个方法在代理类是一个静态方法,和接收三个参数:

类加载器加载程序:指定当前目标对象使用类加载器,并获得的方法加载程序是固定的。

类< ?>[]接口:接口实现的目标对象的类型,和通用方法用于确定类型。

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代理全面代理公司的业务。使用代理。newProxyInstance方法生成一个代理对象在调用方法,实现InvocationHandler反映在代理类的方法调用通过调用方法,并提供一个增强的方法。

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

代理对象不需要实现该接口,但目标对象必须实现的接口,否则不能使用动态代理。

动态代理模式,所有函数调用最终会由调用函数,所以我们可以做一些操作,如日志系统、交易,拦截,权限控制,等等。

JDK动态代理的最致命的问题之一是,它只能代理实现类实现一个接口,代理和代理类只能实现接口的方法。如果实现类都有自己的私有方法,但接口不如果是这样的话,这个方法不能用于代理调用。

解决这个问题?我们可以使用CGLIB动态代理机制。

Cglib代理

静态代理和JDK代理需要一个对象来实现一个接口。有时只是一个单一对象的代理对象。在这种情况下,可以使用Cglib代理。

Cglib代理代理,可以被称为子类构造一个子类对象在内存中实现扩张目标对象的函数。

剂C不仅想为公司行为,但也想从多个产品的工厂。

Cglib增强器生成一个代理类,通过实现MethodInterceptor接口,并在截距法实现的,在这个过程中可以添加增强方法和可以使用反射方法或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;
    }
}

新代理的工厂

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

在SpringAOP使用代理

有两种实现AOP在Spring,JDK和Cglib,如下所示:

如果目标对象需要实现一个接口,使用JDK代理。

如果目标对象不需要实现的接口,使用Cglib代理。

总结

静态代理代理类与目标类需要实现接口方法,以便代理可以增强其功能。

JDK动态代理:需要一个代理类实现一个接口,使用代理。newProxyInstance生成代理类方法和实现InvocationHandler调用方式来达到增强功能。

Cglib动态代理:代理类实现接口不使用Cblib增强器生成子代理对象类和实现MethodInterceptor拦截方法,这种方法可以达到增强功能。

选你想看

你适合学Java吗?4大专业测评方法

代码逻辑 吸收能力 技术学习能力 综合素质

先测评确定适合在学习

在线申请免费测试名额
价值1998元实验班免费学
姓名
手机
提交