Java浅剖动态代理

一、什么是代理?

中间商很多同学应该知道,就是赚差价的。比如我下面代码的例子,生产商和代理商。

代理商帮生产商销售,消费者给代理商10000元购买物品,代理商则给生产商约定价格8000元,

赚了2000元。

以上的描述是生活中的场景,那代码中代理的作用是怎么体现的呢?

在这里插入图片描述

1、首先整个对象流通是:消费者——>代理商——>生产商

2、代理商在调用生产商的sale函数之前对money参数动了手脚,money *= 0.8,自己收缴了2000块

3、所以生产商自然而然的最终得到是8000块

简单一句话概括:在代理商调用生产商函数的前后对本应传入生产商函数的参数进行数据处理

二、什么是静态代理?

静态代理你可以理解为很呆板、死板的一个代理商。这家代理商可以为很多家生产商代理,但业务呢就

比较死板,帮生产商代理的业务都是相同的,多一个业务不行,少一个也不行,如果你非要多或者少,

则需要全部生产商都同意,否则一个不同意,则总的代理业务不变。

代码中的静态代理如果要增加、减少代理业务就必须要修改代理类中的方法,依赖性非常强,

不具备左右逢源的代理是非常不经用的。

你读完上面这段话你不需要真正了解我所说的,你只要知道一句话:静态代理,呆就完事了!

静态代理的简单Demo:

1、生产商的业务接口(可理解为代理商和生产商的业务规约)

public interface IProducer {
    public void sale(Float money);
}

2、生产商的业务(sale方法是被代理商承包了的业务)

public class Producer implements IProducer{
    @Override
    public void sale(Float money) {
        System.out.println("生产商赚钱了:" + money);
    }
}

3、代理商

implements IProducer:代表代理商要获悉代理的业务方法

public class StaticProxy implements IProducer{

    private IProducer beAgent;

    public StaticProxy(IProducer beAgent) {
        this.beAgent = beAgent;
    }

    @Override
    public void sale(Float money) {
        beAgent.sale(money*0.8f);
    }
}

4、程序入口

public class Boot {
    public static void main(String[] args) {
        //被代理对象————生产商
        IProducer producer = new Producer();

        //静态代理————staticProxy,并传入被代理对象producer
        StaticProxy staticProxy = new StaticProxy(producer);

        //静态代理对象执行代理方法,期间对被代理对象的方法的数据进行处理
        staticProxy.sale(10000f);
    }
}

5、程序输出
在这里插入图片描述
6、问题

此时如果生产商增加了业务数量,那此时代理商也要相应增加代理业务的方法代码,

这样耦合性非常的强,生产商模块和代理模块不能独立开,修改一个模块即会影响

另一个模块的运行,不利于维护代码,更不利于代码的扩展


三、什么是动态代理?

固定的代码,能处理你所有要被增强的方法和逻辑处理

简单来说:不修改源码的基础上,增强方法

动态代理分两种:

第一种:基于接口的动态代理

这里我要很重要的声明一点:(很重要)

基于接口的动态代理是代理谁?

请不要搞错,动态代理代理的是方法接口!不要错以为代理的是该接口的实现类哦!

你们可以理解为代理类就是该接口的 新的实现类

可能有人奇怪了,如果动态代理代理的是接口,那接口的原本实现类是必须的吗?

明显不是的,因为上面已经说了代理类就是该接口的 新的实现类

此时该代理类拥有接口的所有方法,而方法的具体执行内容在于invoke函数之中~~~

此时代理类是可以独立运行的!

所以代理类的接口是不一定需要实现类的,需要的仅仅是接口而已(提供方法)!

但是不实现接口的情况下,创建代理类和普通情况下创建代理类有所不同

在这里插入图片描述
注意了,第二个参数是 new Class[ ]{ IProducer.class },而不是IProducer.class.getInterfaces()

这里我只是想说明,JDK动态代理代理的接口不一定要实现。(但Mybasti好像就是这种实现)

但通常情况下我们还是会去实现这个接口,接下来就是讲这种通常情况。


通过Proxy.newProxyInstance创建代理类

Proxy.newProxyInstance有三个参数

1、ClassLoader loader:被代理接口的类加载器

2、Class<?>[] interfaces:提供被代理的接口,作用是使代理对象和被代理接口有共同的方法

3、InvocationHandler h:(核心)当代理对象调用代理方法时就会调用invoke方法

invoke方法: 调用代理对象的方法时,都会经过该方法。此方法有拦截的功能

动态代理简单Demo

1、还是一个接口

public interface IProducer {
    public void getMoney(Float money);
}

2、还是一个实现了业务接口的生厂商

public class Producer implements IProducer {

    @Override
    public void getMoney(Float money) {
        System.out.println("生产商赚了:" + money);
    }
}

3、创建实现了InvocationHandler接口的ProxyInvocation类

public class ProxyInvocation implements InvocationHandler {

    //要被增强的对象
    Object beAagent;
	
    public ProxyInvocation(Object beAagent) {
        this.beAagent = beAagent;
    }

    //被代理接口的方法处理器(对被代理对象方法被调用前的数据进行预处理和代理对象方法被调用后的数据处理)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取方法的第一个参数
        Float money = (Float) args[0];
        System.out.println("方法处理前:");
        method.invoke(beAagent, money);
        System.out.println("方法处理后:");
        method.invoke(beAagent, money*0.8f);
        return null;
    }
}

以上一直在讲动态代理的创建过程,但并没有说明动态代理在此代码中彰显的作用。

所以我们讲解一下动态代理在此代码中彰显的作用

通过动态代理实现增强方法(很重要)——在InvocationHandler接口实现类ProxyInvocation中关联一

要被增强的对象(对象里含有要被增强的方法)。

当调用代理对象的方法时,可以在其中插入利用反射技术实现调用被增强对象的方法,

最终实现增强方法的目的!

4、程序入口

注意:

请注意这里的Proxy.newProxyInstance返回值是接口类,你要记着,JDK动态代理的是接口而不是实现类!

public class Boot {
    public static void main(String[] args) {
        //生产商————被代理对象
        IProducer producer = new Producer();
        //创建生产商的动态代理对象————proxyOfProducer
        IProducer proxyOfProducer = (IProducer) Proxy.newProxyInstance(IProducer.class().getClassLoader(),
                producer.getClass().getInterfaces(),
                new ProxyInvocation(producer));
        proxyOfProducer.getMoney(10000f);
    }
}

5、程序输出
在这里插入图片描述

还有一个点为什么基于接口的动态代理需要接口?(引用自CSDN博主XyGoodCode–侵删)
在这里插入图片描述

第二种:基于子类的动态代理

这回,我们这个被代理对象不需要实现接口了,仅仅需要被代理类即可,相比于基于接口代理的方法

应该有所优化很和简化。不过要引入第三方jar包:cglib

使用的是Enhancer类中的静态方法create,

其中create方法只有两个参数:

1、Class type:被代理对象的字节码(字节码中含有该类的所有信息)

2、Callback接口:和基于接口动态代理的InvocationHandler接口作用一样,也是写如何代理的逻辑代

码,我们一般使用Callback接口的子接口MethodInterceptor。

然后除此之外与基于接口的动态代理方法应该是别无二致了

1、导入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
   <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib</artifactId>
       <version>3.1</version>
   </dependency>

2、还是一个Producer类,不需要实现接口

public class Producer {

    public void saleProduct(Float money){
        System.out.println("销售产品,并拿到钱:" + money);
    }
}

3、程序入口

public class Client {
    public static void main(String[] args) {
        Producer producer = new Producer();
        /**
         * 动态代理
         * 特点:字节码随用随创建,随用随加载
         * 作用:不修改源码基础上进行增强
         * 分类:
         *     1、基于接口的动态代理
         *     2、基于子类的动态代理
         * 基于子类的动态代理:
         *     涉及的类:Enhancer
         *     提供者:第三方cglib库
         * 如何创建代理对象:
         *     使用Enhancer类中的create方法
         * 创建代理对象的要求:
         *     被代理类不能是最终类(最终类:无法创建子类)
         * create方法的参数:
         *     Class:字节码
         *            它是用于指定被代理对象的字节码
         *     Callback(接口):用于提供增强的代码
         *            它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
         *            此接口的实现类都是谁用谁写
         *            我们一般写的都是该接口的子接口实现类:MethodInterceptor
         */
        Producer proxyProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             * @param proxy
             * @param method
             * @param args
             *以上三个参数和基于接口的动态代理中的invoke方法的参数是一样的
             * @param methodProxy:当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            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 null;
            }
        });

        proxyProducer.saleProduct(12000f);
    }
}

总结:

感觉将自己获取的知识输出真的太不容易了,无法对自己已有的认识进行具象化,但或许有部分原因是

自己对动态代理还了解得不够透彻,以后如果对这方面知识有了更全面的了解,我会及时补缺的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值