Spring03

一、代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它的概念很简单,它通过创建一个代理对象来控制对原始对象的访问。代理模式主要涉及两个角色:代理角色真实角色 。代理类负责代理真实类,为真实类提供控制访问的功能,真实类 则完成具体的业务逻辑 。这样,当我们不方便或者不能直接访问真实对象时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象

1.1、代理模式的实现方式

实现代理模式有两种方案:静态代理动态代理静态代理 是指代理类在编译期 就已经确定,即需要事先手动编写一个代理类动态代理则是在运行时动态生成代理类

动态代理方案两种实现:其一,通过Java本身自带java.lang.reflect.Proxy 类 来实现动态代理功能。其二,通过额外引入一个开源的高性能代码生成包CGlib来动态生成代理类。二者底层实现上都应用了反射和操作字节码技术

JDK动态代理是基于接口实现的代理,只能代理实现了接口的类

CGlib方式是基于继承实现的代理,它不是指真实类需要继承某个父类,而是生成的代理类 作为真实类的子类去代理父类,即代理类继承自真实类 。这种方式不需实现接口,可以作为JDK代理方式的补充方案。

二、静态代理的实现

抽象接口:定义视频播放器接口Player

java 复制代码
public interface Player {
    void loadVideo(String filename);
    void playVideo(String filename);
}

真实类 :定义接口实现类VPlayer

java 复制代码
public class VPlayer implements Player {
    @Override
    public void loadVideo(String filename) {
        System.out.println("加载MP4视频文件:"+filename);
    }

    @Override
    public void playVideo(String filename) {
        System.out.println("播放MP4视频:"+filename);
    }
}

代理类:定义代理类VPlayerProxy,实现同样的接口

java 复制代码
public class VPlayerProxy implements Player {

    private Player player;

    public VPlayerProxy(Player player) {
        this.player = player;
    }

    @Override
    public void loadVideo(String filename) {
        player.loadVideo(filename);
    }

    @Override
    public void playVideo(String filename) {
        player.playVideo(filename);
    }
}

客户端调用

java 复制代码
public class Client1 {
    public static void main(String[] args) {
        //直连方式
        Player vplay=new VPlayer();
        vplay.playVideo("aaa.mp4");
        System.out.println();

        //代理方式
        Player proxy=new VPlayerProxy(vplay);
        proxy.loadVideo("aaa.mp4");
        proxy.playVideo("aaa.mp4");

    }
}

最终得到的结果:

2.1、分析

客户端调用中,采用代理方式时,Player proxy=new VPlayerProxy(vplay);采用多态 的形式创建了代理类的对象 proxy,此时通过有参构造,传入的参数是真实类 的对象vplay。

通过上图可以知道,传入的真实类 的对象vplay赋值给了成员变量player。

再去调用代理对象的方法时,实际是使用真实类的对象去调用真实类的方法

三、JDK 实现动态代理

JDK动态代理是Java标准库中提供的一种代理方式,它可以在运行时动态生成一个代理对象,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理。JDK动态代理通过反射机制实现代理功能。

抽象接口:定义明星接口Star

java 复制代码
package com.itheima.proxy;

public interface Star {
    String sing(String name);

    void dance();
}

真实类 :定义接口实现类BigStar

java 复制代码
package com.itheima.proxy;

public class BigStar implements Star {
    private String name;

    public BigStar(String name) {
        this.name = name;
    }

    public String sing(String name) {
        System.out.println(this.name + "正在唱:" + name);
        return "谢谢!";
    }

    public void dance() {
        System.out.println(this.name + "正在优美的跳舞~~");
    }
}

工具类 :定义生成代理类的工具类ProxyUtil,此时的代理类是在运行时动态生成的。

java 复制代码
package com.itheima.proxy;

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

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar) {
        //ClassLoader loader,
        //Class<?>[] interfaces,
        //InvocationHandler h)

        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("sing")) {
                            System.out.println("准备话筒,收钱20万");
                        } else if (method.getName().equals("dance")) {
                            System.out.println("准备场地,收钱100万");
                        }
                        return method.invoke(bigStar, args);
                    }
                });
        return starProxy;
    }
}

客户端调用

java 复制代码
package com.itheima.proxy;

import org.springframework.cglib.proxy.Enhancer;


public class Test1 {
    public static void main(String[] args) {
        BigStar star = new BigStar("大明星");
        Star starProxy = ProxyUtil.createProxy(star);

        String rs = starProxy.sing("七里香");
        System.out.println(rs);





    }
}

最终得到的结果:

3.1、分析

JDK动态代理是基于反射 实现的,所以导入的包是import java.lang.reflect.Proxy;

采用的是Proxy 的静态方法newProxyInstance去创建代理对象。

Proxy.newProxyInstance中的参数含义:

1、ClassLoader loader:指定一个类加载器,把生成的代码对象加载到内存中。

一般采用 当前类.class**.getClassLoader()**

2、Class<?>[] interfaces:指定真实类实现的接口 ,用于指定生成的代理类有哪些方法

3、**InvocationHandler h:**这是一个接口,所以直接new出来的是它的匿名内部类。

4、整个流程如下:

4.1、首先创建代理对象starProxy **:**Star starProxy = ProxyUtil.createProxy(star);

4.2、调用代理对象的sing方法:String rs = starProxy.sing("七里香");

4.3、调用了代理对象的任何方法都会调用代理对象的invoke 方法,因此invoke 方法被称为回调方法

4.4、进入了invoke方法后就会进行判断:

if (method.getName().equals("sing")) {

System.out.println("准备话筒,收钱20万");

}

就会打印:准备话筒,收钱20万

4.5、调用真实对象的方法:

method.invoke(bigStar, args);会去调用真实对象的方法,其中bigStar是传入的真实对象,

method 是真实对象中的方法,args 是传入的参数,会传到真实对象的方法中。

四、cglib实现动态代理

CGLIB(Code Generation Library)是一个基于ASM(Java字节码操作框架)实现的代码生成库,它可以在运行时动态生成目标类的子类作为代理类并覆盖其中的方法来实现代理功能 。与Java自带的JDK动态代理不同,CGlib动态代理可以代理没有实现接口的类

真实类: 没有实现任何接口

java 复制代码
//音频播放器
public class APlayer {
    public void loadAudio(String filename) {
        System.out.println("加载MP3音频文件:"+filename);
    }

    public void playAudio(String filename) {
        System.out.println("播放MP3:"+filename);
    }
}

工具类 :定义生成代理类的工具类CglibProxyFactory,此时的代理类是在运行时动态生成的。

java 复制代码
public class CglibProxyFactory implements MethodInterceptor {

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        Enhancer en = new Enhancer();
        //设置代理的父类
        en.setSuperclass(clazz);
        //设置方法回调
        en.setCallback(this);
        //创建代理实例
        return (T)en.create();
    }

    @Override
    //参数中的object是目标对象,method和args是目标对象的方法和参数,methodProxy是方法代理
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = null;

        if ("loadAudio".equals(method.getName())) {
            //通过继承的方法实现代理,因此这里调用invokeSuper
            result = methodProxy.invokeSuper(object, args);
        }
        if ("playAudio".equals(method.getName())) {
            result = methodProxy.invokeSuper(object, args);
        }
        return result;
    }
}

客户端调用

java 复制代码
public class Client3 {
    public static void main(String[] args) {
        APlayer aplayer=new APlayer();
        APlayer proxy = new CglibProxyFactory().getProxy(aplayer.getClass());
        //验证代理类的父类
        System.out.println("代理类的父类:"+proxy.getClass().getSuperclass().getSimpleName());
        System.out.println();

        proxy.loadAudio("荷塘月色.mp3");
        proxy.playAudio("荷塘月色.mp3");
    }
}

最终的结果:

4.1、分析

整个流程如下:

1、

APlayer proxy = new CglibProxyFactory().getProxy(aplayer.getClass());

通过CglibProxyFactory工具类生成代理对象proxy。

2、

proxy.loadAudio("荷塘月色.mp3");

此时调用了代理对象中的loadAudio方法, 就会被代理对象中intercept方法拦截。

所有调用代理对象中的方法都会被intercept方法拦截。

3、 if ("loadAudio".equals(method.getName())) {
//通过继承的方法实现代理,因此这里调用invokeSuper
result = methodProxy.invokeSuper(object, args);
}

判断为true,就会进行result = methodProxy.invokeSuper(object, args);方法

result = methodProxy.invokeSuper(object, args);调用的是真实对象中的方法。

object表示真实对象,

methodProxy表示真实对象中的方法,

args表示传过去的参数

五、总结

1、 实现代理模式有两种方案:静态代理动态代理。

2、静态代理 是指代理类在编译期 就已经确定,即需要事先手动编写一个代理类

3、动态代理则是在运行时动态生成代理类

4、JDK动态代理是基于接口实现的代理,只能代理实现了接口的类。

5、****CGlib方式是基于继承实现的代理,它不是指真实类需要继承某个父类,而是生成的代理类 作为真实类的子类去代理父类,即代理类继承自真实类 。这种方式不需实现接口。

参考文章:
代理模式:一文彻底搞懂静态代理和动态代理-CSDN博客

相关推荐
_oP_i34 分钟前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx37 分钟前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康1 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘2 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意2 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上3 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人4 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.4 小时前
Mybatis-Plus
java·开发语言
不良人天码星4 小时前
lombok插件不生效
java·开发语言·intellij-idea