美文网首页
基于接口的动态代理和基于子类的动态代理

基于接口的动态代理和基于子类的动态代理

作者: 吃伏冒有礼貌 | 来源:发表于2019-11-27 15:01 被阅读0次

    代理的分析

    举个例子,以前我们购买电脑的时候,我们直接找到厂家购买电脑,厂家也会对我购买的电脑进行售后,在那个时候,购买与售后都由厂家负责.


    一开始我们和厂家有直接的联系

    但是后来,生产厂家随着时间的发展和推移,各种销量和生产的提升,生产厂家发现既要生产,又要销售和售后压力很大


    这个时候生产厂家运营成本主要为两个功能 1 生产 2销售和售后,l
    那么作为生产厂家来说,不希望销售和售后给他带来很大的生产压力,代理商就因应而生
    我们再购买电脑时就不能直接与厂家联系了,而是跟代理商联系

    当电脑出现了质量问题,我们也是去找经销商.经销商再去找厂家帮我们维修.


    不管是销售和售后,我们都不需要再直接与生产厂家保持联系了,生产厂家的压力也被缓解了
    我们使用代理机制,可以实现对业务流程的增强,同时代理商和生产厂家之间也会些要求,比如找谁作为代理商,或者说代理商找什么样的生产厂家
    代理分析
    我们如何再程序中体现刚刚分析的

    基于接口的动态代理

    代理者想要销售产品时,对选择生产厂家有必要的准则,1是提供产品 2.是提供售后
    在Java程序中,我们需要接口来提供这个规范
    创建一个生产者的接口,这个生产者有两个功能:1,销售商品 2,售后服务,我们为它写两个方法,saleProduct 和
    afterService,参数是销售的金额

    IProductor

    package proxy;
    
    public interface IProductor {
        void saleProduct(Float money);
        void afterService(Float money);
    }
    

    ProductorImpl

    package proxy;
    
    public class ProductorImpl implements IProductor {
    
        public void saleProduct(Float money) {
            System.out.println("销售产品,并拿到钱:"+money);
        }
        public void afterService(Float money) {
            System.out.println("提供产品售后,并拿到钱:"+money);
        }
    }
    

    作为一个消费者,就要去购买了,我们创建一个Client类,模拟一个消费者.
    原来购买产品的时候,通过生产厂家直接买,直接找到生产厂家,付款.

    package proxy;
    
    import java.lang.reflect.Proxy;
    
    public class Client {
        public static void main(String[] args) {
           IProductor productor = new ProductorImpl();
            productor.saleProduct(10000f);   
        }
    }
    

    而现在,我们需要代理来完成这个功能,不再需要直接与生产厂家接触了.
    当代理出现后,我们如何去连接这个代理呢?
    动态代理

    • 特点:随用随创建,随用随加载
    • 作用:不修改代码的基础上,对代码进行增强

    动态代理分两类:

    • 基于接口的动态代理
    • 基于子类的动态代理

    基于接口的动态代理

    • 涉及的类:Proxy
    • 提供者:JDK官方
    • 如何创建代理对象?
      使用Proxy类中的newProxyInstance()方法
    • 创建代理对象的要求
      被代理对象最少实现一个接口,否则不能使用
    • newProxyInstance方法的参数
      • ClassLoader
        用于加载代理对象字节码的,写的是被代理对象的类加载器,和被加载对象使用相同的类加载器
        代理谁就写谁的ClassLoader,是固定写法
      • Class [ ]
        用于代理对象和被代理对象有相同的方法.
        (怎样拥有相同的方法?只要两个都实现同一个接口,那么这两个方法就拥有了相同的方法),也是固定写法
      • InvoationHandler
        用于提供增强代码,让我们写如何代理
        我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的.
        接口的实现类一般都是谁用谁写


        newProxyInstance方法的参数
        new InvoationHandler()

        这个接口的实现类就是只有一个方法就是invoke,当我们实现了 InvoationHandler 的接口,代理对象就写完了,它返回的是Object类型,我们用IProductor接受,强转成Productor类型


        InvoationHandler

    invoke
    * 作用:执行被代理对象的任何接口方法都会经过此方法(也就是该方法有一个拦截个功能)
    * proxy:代理对象的引用
    * method:当前执行的方法
    * args:当前执行方法所需要的参数
    * 和被代理的对象有相同的返回值

    image.png
    当我们把这些东西都分析完之后,其实只需要在return这个地方return method.invoke,第一个参数Object指的是谁的方法,这个方法就是被代理对象的方法,第二个args就是被代理对象的参数.
    image.png
    当匿名内部类访问外部成员方法时,外部成员方法要用final修饰
    这里我们被代理对象时productor,当写上productor时,因为匿名内部类访问外部成员时要用final修饰,之前定义productor的地方用上final修饰
    final IProdctor productor
    写到这里其实只是写出了一个代理对象,并没有增强方法,要实现代码增强,还要写代码.
    我们的思路就是消费者购买产品的时候给了10000,但是作为代理商,需要提取20%作为利润,生产厂家只能拿到8000块
    那么如何去做这个操作呢?
    第一步,获取方法执行的参数
    第二步,判断当前方法是不是销售
    增强的代码

    提醒money * 0.8f的时候记得带f,否则会报错
    Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch

    money * 0.8 * f

    这段代码写完以后,我们并没有对saleProduct进行改变,但我们已经实现了方法的增强,这就是基于接口的动态代理,但是它有一个问题如果我们的类不实现任何一个接口的时候,它是无法执行的

    基于子类的动态代理

    针对于我们的类不实现任何接口的情况,我们怎么解决我们如何代理一个普通的java类呢?
    这时候就要用到基于子类的动态代理
    之前有略微提到,基于子类的动态代理,提供者为第三方,叫cglib.
    所以在pom.xml导入cglib的依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.itheima</groupId>
        <artifactId>day03_eesy_02proxy</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <maven.compiler.source>12</maven.compiler.source>
            <maven.compiler.target>12</maven.compiler.target>
        </properties>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/cglib/cglib -->
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2.2</version>
            </dependency>
        </dependencies>
    </project>
    

    就在基于接口的动态代理的工程中,新建一个名叫cglib的pacage,复制ProductorImpl和Client进去,并修改ProductorImpl文件名为Productor,Productor不再实现任何接口


    ,复制ProductorImpl和Client进去,并修改ProductorImpl文件名为Productor

    Productor 类如下:

    package cglib;
    
    public class Productor{
    
        public void saleProduct(Float money) {
            System.out.println("销售产品,并拿到钱:"+money);
        }
    
        public void afterService(Float money) {
            System.out.println("提供产品售后,并拿到钱:"+money);
        }
    }
    
    • 基于子类的动态代理
      • 如何创建代理对象:Enhancer
        提供者:第三方 cglib库
      • 如何创建代理对象
        使用Enhancer的create方法
      • 被创建代理对象的要求
        被代理类不能是最终类
      • create方法的参数
        Class:字节码
        他是固定写法,被代理对象的字节码(只要有了被代理对象的字节码,
        (不管是想用它的类加载器或者查看它有哪些内容,都可以得到,要想代理谁,就写谁的.class)
        (Class[ ]:字节码数组 在基于子类的动态代理中,子类是可以不实现任何接口的,所以这个方法没有了)
        Callback:用于提供增强的代码
        我们一般写的都是该接口的子接口实现类,MethodInterceptor,new一个MethodInterceptor,它是Callback的子接口
        Enhance.crate()方法,可以看到一个是Class类型,一个是Callback类型
        我们一般写的都是该接口的子接口实现类,MethodInterceptor,new一个MethodInterceptor
    MethodInterceptor可以看到是继承了Callback类
    • MethodInterceptor
      执行被代理对象的任何方法都经过该方法
      @param proxy
      @param method
      @param args
      此时把参数名改成一样.以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
      @param methodProxy 当前执行方法的代理对象(用不上)
      @return
      @throws Throwable
      在Client中,还是和跟基于子类的动态代理一样,写上增强的代码,思路仍然一致 1.获取方法执行的参数,2.判断当前方法是不是销售,这里就直接copy基于子类的动态代理的增强代码拿来使用,然后Enhancer.crate()方法拿Productor类的对象CglibP来接收,然后代理对象执行saleProduct方法
    Client

    完整代码如下

    package cglib;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import proxy.IProductor;
    
    import java.lang.reflect.Method;
    
    public class Client {
    
    
        public static void main(String[] args) {
            final Productor productor = new Productor();
    
            Productor CglibP = (Productor) Enhancer.create(productor.getClass(), new MethodInterceptor() {
    
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    Object invoke = null;
                    Float money = (Float) args[0];
                    if ("saleProduct".equals(method.getName())) {
                        invoke = method.invoke(productor, money * 0.8f);
                    }
                    return invoke;
                }
            });
            CglibP.saleProduct(20000f);
        }
    }
    
    

    相关文章

      网友评论

          本文标题:基于接口的动态代理和基于子类的动态代理

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