代理模式:JAVA静态代理和动态代理的实例和实现详解

 更新时间:2021年08月24日 10:17:37   作者:长臂人猿  
这篇文章主要给大家介绍了关于Java静态代理和动态代理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

代理模式,我们这里结合JAVA的静态代理和动态代理来说明,类比Spring AOP面向切面编程:增强消息,也是代理模式。

而我们的静态代理和动态代理,与(service)接口和(serviceImpl)接口实现类有什么区别呢?静态代理的概念与其在理解上类似,可以说静态代理是实现类的增强消息。而且,静态代理针对的是所以实现接口的实现类(通过向上转型实现)。

静态代理

实现简述

本质上是一接口一代理,对该接口的所有实现类进行"增强"(额外)操作:

如下例:human接口有两个实现类:man和woman,我们需要在每个human接口被实现的时候说他是god创造的(增强操作)。那么我们针对human接口手动实现一个GodProxy静态代理类。那么我们通过这个代理执行human接口的实现对象的方法时就可以引入增强操作。

创建human接口

public interface Human {
    public void sex();
}

创建接口实现类

public class Man implements Human{
    @Override
    public void sex() {
        System.out.println( "this is Man" );
    }
}
public class Women implements Human{
    @Override
    public void sex() {
        System.out.println( "this is Women" );
    }
}

创建针对接口实现增强操作的代理

public class GodProxy implements Human{
    Human huamnGenarator;
    public GodProxy(Human huamnGenarator){
        this.huamnGenarator = huamnGenarator;
    }
    @Override
    public void sex() {
        System.out.println( "God begin to make human" );
        huamnGenarator.sex();
        System.out.println(" End of work ");
    }
}

代理实现效果

public class 静态代理 {
    public static void main(String[] args) {
        GodProxy proxy_1 = new GodProxy(new Man());
        GodProxy proxy_2 = new GodProxy(new Women());
        proxy_1.sex();
        System.out.println("\n\n");
        proxy_2.sex();
    }
}

在这里插入图片描述

动态代理

实现简述

相对与静态代理,一个代理类proxy只能针对一个接口实现操作,众所周知类可以实现多个接口,那能不能对所有的实现类的接口实现增强操作呢?可以的,我们需要通过反射的方式,通过向上转型使得传入参数为Object对象,但是Object里面的getClass方法获取Class对象,实际上是实现类的Class(由Java的向上转型特性可知)。 获取实现类对象的class对象且获得其实现的接口数组。我们可以达到这个目的:这就是动态代理:对多个接口实现静态代理。

动态代理类主要实现InvocationHandler这个接口,接口实现invoke方法来实现增强操作。并通过一个自定义的方法来创建和绑定动态代理类和入参(向上转型为obj的实现类对象),从而实现实现类对象方法的增强操作。

即我们通过动态代理传入实例类对象,在自己写的newProxyInstance方法中通过Proxy类的newProxyInstance方法代理生成一个新的实例类对象,这个新的对象不仅包含所有的入参实力类对象信息,且在通过代理类生成新的实例类对象过程中注入了invoke方法(我们实现InvocationHandler接口的核心方法)的逻辑。

这里我们有两个疑问:

1、如何实现增强的invoke方法;——通过java.lang.reflect.Proxy.newProxyInstance方法中复原向上转型的obj对象为原对象(具体实现类对象),并绑定增强的invoke方法。

2、代理对象如何复现对象方法; ——利用向上转型的复原不变性。

要点:向上转型

向上转型:子类实例赋值给父类引用。 无法调用子类拓展方法,但是她的实现确确实实存在:

        Object obj = new Girl();
        Girl girl = (Girl) obj;
        System.err.println(obj.getClass().getName());
        System.err.println(obj.getClass().getInterfaces().length);
        girl.nickName();

输出:

src.代理模式.Girl
2

少女

创建YoungMan接口

这里还将用到Human接口,两个接口。

public interface YoungMan {
    public void nickName();
}

创建两个接口实现类

public class Boy implements Human,YoungMan{
    @Override
    public void sex() {
        System.out.println( "this is Man" );
    }
    @Override
    public void nickName() {
        System.out.println( "少年" );
    }
}

public class Girl implements Human,YoungMan{
    @Override
    public void sex() {
        System.out.println( "this is Women" );
    }
    @Override
    public void nickName() {
        System.out.println( "少女" );
    }
}

创建动态代理实例对象

这里我们主要通过Proxy.newProxyInstance方法创建一个代理类,传参:

   public static Object newProxyInstance(ClassLoader loader, 
                                            Class<?>[] interfaces, 
                                            InvocationHandler h)

类加载器:指定代理类由哪个classloader加载;代理类需要代理实现的接口方法;InvocationHandler对象:表示的是当动态代理对象调用方法的时候实际执行的会是该InvocationHandler对象上的invoke方法(即增强方法)。在invoke方法中通过反射方法名称去执行实际要执行的方法和增强操作。(实例说明可以看文末补充)

注意返回的这里自定义的newProxyInstance是Object。

invoke方法中的args为方法传入的参数们;

public class GodForYoungProxy implements InvocationHandler {
    private Object godForYoungProxy;
    //参数为Object设计一个向上转型
    public Object newProxyInstance(Object godForYoungProxy) {
        this.godForYoungProxy = godForYoungProxy;
        //this指的是GodForYoungProxy这个InvocationHandler实现类
        System.err.println( godForYoungProxy.getClass().getName() );
        return Proxy.newProxyInstance(godForYoungProxy.getClass().getClassLoader(), godForYoungProxy.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(" God for Young begin to work ... ");
        proxy = method.invoke(godForYoungProxy, args);
        System.out.println(" End of work ");
        return proxy;
    }
}

代理实现效果

public class 动态代理 {
    public static void main(String[] args) {
        GodForYoungProxy godForYoungProxy = new GodForYoungProxy();
        Human human = (Human) godForYoungProxy.newProxyInstance(new Boy());
        YoungMan youngMan = (YoungMan) godForYoungProxy.newProxyInstance(new Boy());
        human.sex();
        youngMan.nickName();
        //向上转型测试
//        Object obj = new Girl();
//        Girl girl = (Girl) obj;
//        System.err.println(obj.getClass().getName());
//        System.err.println(obj.getClass().getInterfaces().length);
//        girl.nickName();
    }
}

在这里插入图片描述

代理生成对象中的obj向上转型对象的.getClass().getName()打印:

在这里插入图片描述

要点:InvocationHandler补充

结合创建动态代理实例对象目录内容补充说明如下:官网的Proxy.newProxyInstance中的入参说明

loader – the class loader to define the proxy class
interfaces – the list of interfaces for the proxy class to implement
h – the invocation handler to dispatch method invocations to =>将方法调用分派到的调用处理程序

同样的上述代码实现效果,我们将实现的GodForYoungProxy implements InvocationHandler中的invoke方法注释一行:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(" God for Young begin to work ... ");
       // proxy = method.invoke(godForYoungProxy, args);
        System.out.println(" End of work ");
        return proxy;
    }

明显的,我们发现少了实际的sex和nickName方法的输出内容,因为没有实现对应调用方法:

在这里插入图片描述

通过这样的特性,其实我们在invoke方法里面拥有很强的操作性,比如说让指定方法执行,对不同方法执行不同策略等。

代理模式和修饰模式的区别

代理模式和装饰者模式很相似,但是他们的区别在于:

1、代理模式是在类编译的时候,增强方法就已经确定的,有些动态代理不支持多层嵌套;装饰者则可以不断递归被构造装饰;

2、代理模式:强调对对象的访问控制,方法的使用都是通过反射实现;代理模式则是强调功能的增加,相当于在原基础的被修饰者上不断套娃。

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • SpringBoot统计接口调用耗时的三种方式

    SpringBoot统计接口调用耗时的三种方式

    在实际开发中,了解项目中接口的响应时间是必不可少的事情,SpringBoot 项目支持监听接口的功能也不止一个,接下来我们分别以 AOP、ApplicationListener、Tomcat 三个方面去实现三种不同的监听接口响应时间的操作,需要的朋友可以参考下
    2024-06-06
  • Java类加载器层次结构原理解析

    Java类加载器层次结构原理解析

    这篇文章主要介绍了Java类加载器层次结构原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 阿里云主机上安装jdk 某库出现问题的解决方法

    阿里云主机上安装jdk 某库出现问题的解决方法

    今天安装jdk到阿里云服务上,首先看下阿里云是32位还是64位的,如果是32位下载32位的包,如果是64位的下载64位的包,下面与大家分享下安装过程中遇到问题的解决方法
    2013-06-06
  • springboot+log4j.yml配置日志文件的方法

    springboot+log4j.yml配置日志文件的方法

    这篇文章主要介绍了springboot+log4j.yml配置日志文件的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • Java集合-HashMap

    Java集合-HashMap

    这篇文章主要介绍了Java集合HashMap,也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,下面来看看文章的具体内容吧,需要的小伙伴也可参考一下
    2022-01-01
  • Java中的Redis是什么意思

    Java中的Redis是什么意思

    Redis是一个非常强大的工具,它可以用来实现很多有趣的应用,还可以使用Redis来实现分布式锁,这样你就可以在多线程或多进程的环境下同步代码,这篇文章主要介绍了Java中的Redis是什么意思,需要的朋友可以参考下
    2023-08-08
  • 一文详解gRPC快速整合SpringCloud

    一文详解gRPC快速整合SpringCloud

    这篇文章主要为大家介绍gRPC快速整合SpringCloud的实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Spring Boot2.0实现静态资源版本控制详解

    Spring Boot2.0实现静态资源版本控制详解

    这篇文章主要给大家介绍了关于Spring Boot2.0实现静态资源版本控制的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • 详解Spring Boot中Controller用法

    详解Spring Boot中Controller用法

    Controller是SpringBoot里最基本的组件,他的作用是把用户提交来的请求通过对URL的匹配,分配个不同的接收器,再进行处理,然后向用户返回结果。下面通过本文给大家介绍Spring Boot中Controller用法,需要的朋友参考下
    2017-05-05
  • Java内部类和异常类的概念以及使用

    Java内部类和异常类的概念以及使用

    这篇文章主要介绍了Java内部类和异常类的概念以及使用,文中有非常详细的代码以及注释,适合正在学习java基础的同学们使用,需要的朋友可以参考下
    2021-04-04

最新评论