动态代理


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是代理以及代理模式的作用?

代理就好比我们的代购,比如我们想要在美国的商家买一双球鞋,但是我们不能够直接去美国买,于是我就可以向代购买,代购将从美国买回来的鞋子卖给我们,并且获得利益.

在java中就是类A想要调用类C的方法,但是没有权限来访问类C,但是B有权限访问类C,我们就可以通过调用类B的方法来访问类C,那么类B就是我们所说的代理类.
代理模式的作用就是:

   1.控制访问  即类B有权限访问类C, 例子中就是代购可以去美国买球鞋,但是我们不能
   2.增强功能   即类B调用了类C的方法,在此基础之上还能增加自己想要的功能,例子中就是代购买了球鞋,可以增加球鞋的价格
               来卖给我们,进而获取利益

二、静态代理

1. 定义业务接口

定义业务接口 ShoeSell(目标接口),其中含有抽象方法 sell(int amount), sell 是目标方法

代码如下(示例):

package jmu.czx.service;

public interface ShoeShell {

    //定义方法 参数 amount:表示一次购买的数量,暂时不用
    //返回值表示一双球鞋的价格。
    float sell(int amount);
    
}

2.定义接口实现类

代码如下(示例):

package jmu.czx.factory;

import jmu.czx.service.ShoeShell;

public class USAFactory implements ShoeShell {
    @Override
    public float sell(int amount) {
        //可以根据 购买的数量 amount有其他优惠
        return 1000;
    }
}

3.创建代理类

创建A代购

package jmu.czx.daigou;

import jmu.czx.factory.USAFactory;
import jmu.czx.service.ShoeShell;

public class A implements ShoeShell {
    private USAFactory usaFactory=new USAFactory();

    @Override
    public float sell(int amount) {
        float price = usaFactory.sell(amount);
        //在单价之上增加100元作为利润
        return price+100;
    }
}

创建B代购

package jmu.czx.daigou;

import jmu.czx.factory.USAFactory;
import jmu.czx.service.ShoeShell;

public class B implements ShoeShell {
    private USAFactory usaFactory=new USAFactory();
    @Override
    public float sell(int amount) {
        float price=usaFactory.sell(amount);
        //在单价之上增加80元作为利润
        return price+80;
    }
}

4.实现代理模式

package jmu.czx;

import jmu.czx.daigou.A;
import jmu.czx.daigou.B;

public class Test {
    public static void main(String[] args) {
        float price=0.0f;
        A a=new A();
        price=a.sell(1);
        System.out.println("A代购的price为:"+price);
        System.out.println("===========================");
        B b=new B();
        price=b.sell(1);
        System.out.println("B代购的price为:"+price);
      
    }
}

5.静态代理的缺点

(1) 代码复杂,难于管理
代理类和目标类实现了相同的接口,每个代理都需要实现目标类的方法,这样就出现了大量的代码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
(2) 代理类依赖目标类,代理类过多代理类只服务于一种类型的目标类,如果要服务多个类型。势要为每一种目标类都进行代理,静态代理在程序规模稍大时就无法胜任了,代理类数量过多。
所以这时候就需要我们用到动态代理。

三、动态代理

1. 概念

动态代理是指代理类对象在程序运行时由 JVM 根据反射机制动态生成的。动态代理不需要定义代理类的.java 源文件。动态代理其实就是 jdk 运行期间,动态创建 class 字节码并加载到 JVM。动态代理的实现方式常用的有两种:使用 JDK 代理代理,与通过 CGLIB 动态代理。

而本文主要讲的是JDK动态代理,需要用到 InvocationHandler 接口 ,Method 类 ,
Proxy

接下来对静态代理进行改写,来实现动态代理,可以先记住语法,背下来也行,以后再慢慢理解

2.InvocationHandler接口 ,Method类 ,Proxy类作用

(1) InvocationHandler 接口
InvocationHandler 接口叫做调用处理器,负责完调用目标方法,并增强功能。通 过 代 理 对 象 执 行 目 标 接 口 中 的 方 法 , 会 把 方 法 的 调 用 分 派 给 调 用 处 理 器 (InvocationHandler)的实现类,执行实现类中的 invoke()方法,我们需要把功能代理写在 invoke()方法中

接口中只有一个方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    proxy:代表生成的代理对象
    method:代表目标方法
    args:代表目标方法的参数

(2) Method 类
invoke()方法的第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用
目标方法。这两个 invoke()方法,虽然同名,但无关。

public Object invoke ( Object obj, Object... args) 
obj:表示目标对象
args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数

该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对
象确定。
在代码中,一般的写法为
method.invoke(target, args);
其中,method 为上一层 invoke 方法的第二个参数。这样,即可调用了目标类的目标
方法
(3) Proxy 类
通 过 JDK 的 java.lang.reflect.Proxy 类 实 现 动 态 代 理 , 会 使 用 其 静 态 方 法
newProxyInstance(),依据目标对象、业务接口及调用处理器三者,自动生成一个动态代理对

public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler handler) 
loader:目标类的类加载器,通过目标对象的反射可获取
interfaces:目标类实现的接口数组,通过目标对象的反射可获取
handler:调用处理器。

3.步骤

jdk 动态代理是代理模式的一种实现方式,其只能代理接口。
实现步骤
1、新建一个接口,作为目标接口
2、为接口创建一个实现类,是目标类 (到这边都和之前的代码步骤是一样的)

3、创建类实现 java.lang.reflect.InvocationHandler 接口,调用目标方法并增加其他功能代码

package jmu.czx.daigou;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MySellHandler implements InvocationHandler {
    private  Object target=null;  //定义目标对象

    public MySellHandler(Object target) {
        //使用构造方法传入目标对象,给目标对象提供代理功能
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        res = method.invoke(target,args);    //执行目标方法,进行访问控制
        if(res!=null)
        {
            float price =(float)res;
            res=price+25; //在目标方法执行之后,增强功能,即提高价格

        }
        return res;
    }
    
}

4、创建动态代理对象,使用 Proxy.newProxyInstance()方法,并把返回值强制转为接口类型

package jmu.czx;

import com.bjpowernode.service.UsbSell;
import jmu.czx.daigou.A;
import jmu.czx.daigou.B;
import jmu.czx.daigou.MySellHandler;
import jmu.czx.factory.USAFactory;
import jmu.czx.service.ShoeShell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
//        float price=0.0f;
//        A a=new A();
//        price=a.sell(1);
//        System.out.println("A代购的price为:"+price);
//        System.out.println("===========================");
//        B b=new B();
//        price=b.sell(1);
//        System.out.println("B代购的price为:"+price);
          //创建代理
        ShoeShell target= new  USAFactory();
          //创建调用处理器
        InvocationHandler handler= new MySellHandler(target);
        //创建jdk动态代理
         ShoeShell usa=(ShoeShell) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),handler);
         //通过代理对象执行业务方法,实现利润增加
        float price=usa.sell(1);
        System.out.println("A代购的price为:"+price);


    }
}

4.总结

动态代理在spring,mybatis等框架中都会用到需要好好掌握
我的第一篇博客,有很多不足的地方,希望大家能够提意见进行改进。

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhixuChen200

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值