Java 中动态代理(基于接口和基于子类

2021/4/12 20:25:58

本文主要是介绍Java 中动态代理(基于接口和基于子类,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Java 中动态代理(基于接口和基于子类)

参考:https://www.cnblogs.com/gonjan-blog/p/6685611.html

零:前提知识

0.1、代理模式

​ 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

0.2、静态代理

​ 静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

代码实现:

首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

/**
 * 创建Person接口
 * @author Gonjan
 */
public interface Person {
    //上交班费
    void giveMoney();
}

Student类实现Person接口。Student可以具体实施上交班费的动作。

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    
    @Override
    public void giveMoney() {
       System.out.println(name + "上交班费50元");
    }
}

StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,由于实现了Peson接口,同时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
 * @author Gonjan
 *
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;
    
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    
    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        stu.giveMoney();
    }
}

main方法

public static void main(String[] args) {
        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
        Person zhangsan = new Student("张三");
        
        //生成代理对象,并将张三传给代理对象
        Person monitor = new StudentsProxy(zhangsan);
        
        //班长代理上交班费
        monitor.giveMoney();
    }

这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。

代理模式最主要的就是有一个公共接口(Person),一个具体的类(Student),一个代理类(StudentsProxy),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途(扩展性)。就这个例子来说,加入班长在帮张三上交班费之前想要先反映一下张三最近学习有很大进步,通过代理模式很轻松就能办到:

public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;
    
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    
    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        System.out.println("张三最近学习有进步!");
        stu.giveMoney();
    }
}

一、概述:

​ 所谓的动态代理,需要一个代理类,这个代理类是动态生成的,那么这个任务就需要交给Java虚拟机来做了,由Java虚拟机来去动态的生成代理类,也就是动态代理,动态代理分为:基于接口的动态代理和基于子类的动态代理。

二、基于接口的动态代理

​ 基于接口的动态代理是使用JDK官方提供的 Proxy 类,被代理的类至少实现一个接口,创建代理使用的方法:

  • newProxyInstance():创建代理对象
    • ClassLoader参数:类加载器,用于加载代理对象字节码和被代理对象使用相同的类加载器
    • Class[]参数:字节码数组,用于让代理对象和被代理对象有相同的方法
    • InvocationHandler:用于提供增强的代码,让开发人员写如何代理,一般都是写一个该接口的实现类,通常都是匿名内部类,此接口的实现类都是谁用谁写

2.1、代码实例

​ 以生产厂家销售商品为例,现如今生产厂家都不直接销售商品,而是通过代理商来销售,代理商从中赚取差价

  • 先创建一个销售商品的接口

    public interface IProducer {
        //销售
        public void saleProduct(float money);
    }再创建生产厂家
    
  • 再创建生产厂家

    public class Producer implements IProducer{
        //销售
        public void saleProduct(float money){
            System.out.println("销售产品,赚钱:" + money);
        }
    }
    
    
  • 通过代理商后进行销售

    public class Consumer {
        public static void main(String[] args) {
            final Producer producer = new Producer();
            IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                    producer.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 作用:执行被代理对象的任何接口方法都会经过该方法
                         * @param proxy:代理对象的引用
                         * @param method:当前执行的方法
                         * @param args:当前执行方法所需的参数
                         * @return:和被代理对象方法有相同的返回值
                         * @throws Throwable
                         */
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //增强的代码
                            Object returnValue = null;
                            //1.获取方法执行的参数
                            Float money = (Float) args[0];
                            //2.判断方式是不是销售
                            if("saleProduct".equals(method.getName())){
                                returnValue = method.invoke(producer,money*0.8f);
                            }
                            return returnValue;
                        }
                    });
            proxyProducer.saleProduct(10000f);
        }
    }
    
    

    使用接口代理,达到了不改变源码,对方法进行了增强,厂家本应该赚取10000,经过代理商后,只赚取到8000,还有2000代理商赚走了

三、基于子类的动态代理

1、介绍

​ 基于子类的动态代理是使用第三方 cglib库,所以要先导入jar包,被代理类不能是最终类,使用 Enhance 类中的 create 方法:

  • create():创建代理对象
    • 参数Class:字节码,用于指定被代理对象的字节码
    • 参数Callback:用于提供增强的代码,让开发人员写如何代理,一般都是写一个该接口的实现类,通常都是匿名内部类,此接口的实现类都是谁用谁写,一般都是该接口的子接口实现类:MethInterceptor

2、代码实例

还是以生产厂家销售商品为例

  • 销售厂家

    public class Producer{
        //销售
        public void saleProduct(float money){
            System.out.println("销售产品,赚钱:" + money);
        }
    }
    
  • 通过代理商后进行销售

    public class Consumer {
        public static void main(String[] args) {
            final Producer producer = new Producer();
            Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor(){
                /**
                 * 作用:执行被代理对象的任何接口方法都会经过该方法
                 * @param proxy:代理对象的引用
                 * @param method:当前执行的方法
                 * @param args:当前执行方法所需的参数
                 * @return:和被代理对象方法有相同的返回值
                 * @throws Throwable
                 */
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    //增强的代码
                    Object returnValue = null;
                    //1.获取方法执行的参数
                    Float money = (Float) args[0];
                    //2.判断方式是不是销售
                    if("saleProduct".equals(method.getName())){
                        returnValue = method.invoke(producer,money*0.8f);
                    }
                    return returnValue;
                }
            });
            cglibProducer.saleProduct(10000f);
        }
    }
    
    

    使用子类进行代理,不需要创建接口,而是使用第三方 cglib库



这篇关于Java 中动态代理(基于接口和基于子类的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程