jdk动态代理

目录

1 前言

2 代理方式

        2.1 静态代理

         2.1.1 静态代理实现

        2.2 动态代理

         2.2.1 动态代理实现的几种方式

         2.2.2 实现动态代理


1 前言

        代理模式:是指为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一对象不适合或者不能直接引用另外一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

        使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。客户类真正想要访问的对象是目标对象,但客户真正能够访问的是代理对象,客户类对目标对象的访问是通过访问代理类来实现的。

作用:

1 功能增强:在你原有的功能上,增加了额外的功能,新增加的功能叫做功能增强

2 控制访问:代理类不让你访问目标

2 代理方式

        2.1 静态代理

1)代理类是自己手工实现的

2)自己所需要代理的对象是确定的

         2.1.1 静态代理实现

步骤1 :定义一个卖u盘的接口

步骤2:创建一个厂家的类,实现步骤一中卖u盘的方法

步骤3:创建商家(代理),实现步骤一中的接口

步骤4:创建客户端类,调用商家方法买u盘

代码实现:

步骤一:定义一个卖u盘的接口

public interface UsbSell {

    /**
     * 定义一个方法 厂家,商家都要用完成的功能
     * @param amount 购买的数量
     * @return
     */
    float sell(int amount);
}

步骤2:创建一个厂家的类,实现步骤一中卖u盘的方法

//目标类 厂家只向中间商供货,不会想个体小商户供货
public class UsbFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        //定义一个U盘的价格是65元
        float price = 65.0f;
        return amount*price;
    }
}

步骤3:创建商家(代理),实现步骤一中的接口

//代理类-----中间商
public class TaoBao implements UsbSell {

    //代理的是金士顿 定义目标厂家类
    private UsbFactory usbFactory =  new UsbFactory();

    @Override
    public float sell(int amount) {
        //调用目标方法
        float price = usbFactory.sell(amount);

        price =  (price + 15)*amount;
        return price;
    }
}

步骤4:创建客户端类,调用商家方法买u盘

    @Test
    public void test7(){
        TaoBao taoBao = new TaoBao();
        float sell = taoBao.sell(80);
        System.out.println("***总金额****"+sell); //***总金额****417200.0
    }

可见,我们通过代理中间商去购买u盘的时候,被中间商每个u盘都赚了15元的差价。

我们可以发现,这个加价格的这个功能时我们在调用了目标类之后进行的,其实这一步就相当于我们一个功能的增强(代理类在完成功能时会进行一个增强),同时我们也可进行一些其他的功能增强,比如:返回优惠券或者红包,只要是在我们原有功能完成之后进行的功能都属于功能增强。

静态代理优缺点:

优点:实现方便简单,容易理解

缺点:目标类增加时,代理会增加。接口功能修改了商家和代理类都有改动

        2.2 动态代理

        动态代理:在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理的目标类。(动态代理是一种创建Java对象的能力,省去了我们自己创建代理类对象)

Spring Aop底层实现,是通过jdk动态代理或者CGlib代理在运行时期在对象初始化阶段织入代码的

jdk动态代理是基于接口实现的

CGlib是基于类的集成实现的

2.2.1 动态代理实现的几种方式

1 cglib动态代理:cglib是第三方的工具库,创建代理对象,cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中重名的方法,实现功能的修改

因为cglib是继承,重写方法,所以需要要继承的类不能是final的,重写的方法也不能是final的。cglib在mybatis,spring中的框架中都有使用

2 jdk的动态代理:使用Java反射包中的类和接口实现动态代理的功能。反射包:Java lang reflect 三个类:InvocationHandler ,Method,Proxy

1)InvocationHandler接口(调用处理器):中一个方法invoke().表示代理对象要执行的功能代码,代理类功能要完成的功能就写在invoke()方法中。

代理类完成的功能:

        调用目标方法,执行目标方法的功能

        功能增强,在目标方法调用时,增强功能

方法原型:

参数:

Object proxy:jdk创建的代理对象,无需赋值

Method method:目标类中的方法,jdk提供method对象

Object[] args:目标类中方法的参数

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

2)Method:表示方法的(目标类中的方法)

作用:通过Method可以执行某个目标类的方法 Method.invoke(目标对象,目标参数);

注意:此处的invoke方法和InvocationHandler类中的invoke方法不是同一个

3)Proxy类:创建代理对象,不在通过之前那种new 类的构造方法来创建对象了,而是使用Proxy类中的方法,代替new的使用

方法;静态方法 newProxyInstance()

参数:

ClassLoader loader 类加载器:负责向内存中加载对象的。使用反射获取对象的ClassLoader(比如:类B>>B.getCalss().getClassLoader())目标对象的类加载器

Class<?> interface:目标对象实现的接口,也是通过反射获取的

InvocationHandler h:代理类要完成的功能

public static Object newProxyInstance(ClassLoader loader,

Class<?> interface,InvocationHandler h)

2.2.2 实现动态代理

步骤一:创建接口,定义目标类要完成的功能

public interface UsbSell {

    float sell(int amount);
}

步骤二:创建目标类实现接口

public class UsbSellFactory implements UsbSell{

    @Override
    public float sell(int amount) {
        System.out.println("进入目标类中执行的方法");
        return 65.0f*amount;
    }
}

步骤三:创建InvocationHandler接口的实现类,在invoke方法中完成代理的功能(1 调用目标方法2增强功能)

public class SellHandle implements InvocationHandler {

    /**
     * 定义一个target 表示要传入的代理对象
     */
    private Object taeget = null;

    public SellHandle(Object taeget) {
        //目标对象
        this.taeget = taeget;
    }

    /**
     * 调用处理器
     * 1调用目标方法 2 功能增强
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


        /**
         * 1 执行目标方法 jdk会自动将参数args代入
         */
        Object invoke = method.invoke(taeget, args);

        /**
         * 2 进行功能增强 将每个优盘进行加价25元卖出
         */
        if (invoke != null ){
           Float price =  (Float)invoke;
           int num =  Integer.parseInt(args[0].toString());
           price = (price + 25.0f)*num;
           invoke = price;
        }


        return invoke;
    }
}

步骤四:使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型

public class MainShop {

    public static void main(String[] args) {

        //1 创建目标对象
        UsbSell factory = new UsbSellFactory();

        //2 创建InvocationHandler对象
        InvocationHandler handle = new SellHandle(factory);

        //3 创建代理对象
        UsbSell proxy = (UsbSell)Proxy.newProxyInstance(
                factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handle);

        //4 通过代理执行方法
        float sell = proxy.sell(10);
        System.out.println("通过动态代理,调用方法:"+sell);

    }
}

执行结果:

 总结:jdk动态代理通过创建代理对象执行方法,其实是在InvocationHandler类中调用目标方法,调用目标方法之后进行一个功能的增强,然后再进行一个结果的返回。

posted @ 2021-12-12 19:05  小猪不会叫  阅读(34)  评论(0)    收藏  举报  来源