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

代理的分析

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


13543313-6f8b13696f57fc14.png
一开始我们和厂家有直接的联系

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


13543313-d2adce1458b11ad6.png
这个时候生产厂家运营成本主要为两个功能 1 生产 2销售和售后,l

那么作为生产厂家来说,不希望销售和售后给他带来很大的生产压力,代理商就因应而生
13543313-56e8417cd3032a3b.png
我们再购买电脑时就不能直接与厂家联系了,而是跟代理商联系

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


13543313-8654900983dc1b4f.png
不管是销售和售后,我们都不需要再直接与生产厂家保持联系了,生产厂家的压力也被缓解了

我们使用代理机制,可以实现对业务流程的增强,同时代理商和生产厂家之间也会些要求,比如找谁作为代理商,或者说代理商找什么样的生产厂家
13543313-69b7ed737a6ed706.png
代理分析

我们如何再程序中体现刚刚分析的
基于接口的动态代理

代理者想要销售产品时,对选择生产厂家有必要的准则,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
      用于提供增强代码,让我们写如何代理
      我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的.
      接口的实现类一般都是谁用谁写


      13543313-5cccc82cd3246657.png
      newProxyInstance方法的参数

      13543313-0850677d072259c6.png
      new InvoationHandler()

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


      13543313-127b7dface773ca9.png
      InvoationHandler

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

13543313-b3f10f67b16fdd2c.png
image.png

当我们把这些东西都分析完之后,其实只需要在return这个地方return method.invoke,第一个参数Object指的是谁的方法,这个方法就是被代理对象的方法,第二个args就是被代理对象的参数.
13543313-a3e14eb613e2352c.png
image.png

当匿名内部类访问外部成员方法时,外部成员方法要用final修饰
这里我们被代理对象时productor,当写上productor时,因为匿名内部类访问外部成员时要用final修饰,之前定义productor的地方用上final修饰
13543313-98da99d63f791a0f.png
final IProdctor productor

写到这里其实只是写出了一个代理对象,并没有增强方法,要实现代码增强,还要写代码.
我们的思路就是消费者购买产品的时候给了10000,但是作为代理商,需要提取20%作为利润,生产厂家只能拿到8000块
那么如何去做这个操作呢?
第一步,获取方法执行的参数
第二步,判断当前方法是不是销售
13543313-24fb5f0462744809.png
增强的代码

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

13543313-dd58bb66a3e0f8f3.png
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不再实现任何接口


13543313-adb569eedc0a1f9c.png
,复制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的子接口
      13543313-0214cf04153a35e1.png
      Enhance.crate()方法,可以看到一个是Class类型,一个是Callback类型

      13543313-d24b9a3335c475c1.png
      我们一般写的都是该接口的子接口实现类,MethodInterceptor,new一个MethodInterceptor
13543313-967a88b951d7ede7.png
MethodInterceptor可以看到是继承了Callback类
  • MethodInterceptor
    执行被代理对象的任何方法都经过该方法
    @param proxy
    @param method
    @param args
    此时把参数名改成一样.以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
    @param methodProxy 当前执行方法的代理对象(用不上)
    @return
    @throws Throwable
    在Client中,还是和跟基于子类的动态代理一样,写上增强的代码,思路仍然一致 1.获取方法执行的参数,2.判断当前方法是不是销售,这里就直接copy基于子类的动态代理的增强代码拿来使用,然后Enhancer.crate()方法拿Productor类的对象CglibP来接收,然后代理对象执行saleProduct方法
13543313-27734b7f155ee58a.png
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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值