IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java代理模式 -> 正文阅读

[Java知识库]java代理模式

代理模式

1.静态代理

a. 角色分析

  • 真实角色:被代理的角色;
  • 代理角色:代理真实角色,代理真实角色后,一般我们会有附属操作,用来对真实角色的增强;
  • 客户端:访问代理对象的人;

b.静态代理的好处

  • 可以是真实对象的操作更加简单,不用去关注公共业务;
  • 在不修改目标对象的前提下,可以通过代理对象对目标对象功能扩展;

c.静态代理的缺点

  • 一个真实角色就需要一个代理角色;代码量翻倍;导致开发效率下降;
  • 代理角色和真实角色实现了相同的接口,代理角色通过真实角色实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理角色也需要实现此方法。增加了代码维护的复杂度;

d.静态代理代码实现

  1. 先创建一个公共的接口Animal,提供一个sleep方法;

    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 21:53
     */
    public interface Animal {
        void sleep();
    }
    
  2. 创建一个Dog类真实角色实现Animal接口,重写sleep方法;

    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 21:53
     */
    public class Dog implements Animal {
        @Override
        public void sleep() {
            System.out.println("小狗狗睡觉!!!");
        }
    }
    
  3. 创建一个Dog的代理角色DogProxy类;

    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 21:54
     */
    public class DogProxy implements Animal {
        private Dog dog;
    
        public void setDog(Dog dog) {
            this.dog = dog;
        }
    
        @Override
        public void sleep() {
            System.out.println("小狗狗睡觉前执行的代码");
            dog.sleep();
            System.out.println("小狗狗睡觉后执行的代码");
        }
    }
    

4.测试

?

/**
 * @author tsc
 * @project_name spring
 * @date 2021/9/13 22:11
 */
public class App {
    public static void main(String[] args) {
        DogProxy dogProxy = new DogProxy();
        dogProxy.setDog(new Dog());
        dogProxy.sleep();
    }
}

测试结果

D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA 
小狗狗睡觉前执行的代码
小狗狗睡觉!!!
小狗狗睡觉后执行的代码

Process finished with exit code 0

2. 动态代理

① jdk代理(接口代理)

? 前提条件:代理角色不需要实现接口,但是真实角色一定要实现接口,否则不能用动态代理

? 所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:java.lang.reflect InvocationHandler和java.lang.reflect Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可;

  • Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
    
    
    • ClassLoader loader:指定当前真实对象使用类加载器,获取加载器的方法是固定的
    • Class<?>[] interfaces:真实对象实现的接口的类型,使用泛型方式确认类型(接口字节码对象的数组)
    • InvocationHandler h:调度方法,执行真实角色的方法时,会触发事件处理器的方法,会把当前执行真实角色的方法作为参数传入
  • InvocationHandler是由代理实例的调用处理程序实现的接口

    每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

    Object invoke(Object proxy,
                  Method method,
                  Object[] args)
    
    
    • Object proxy:代理角色实例;
    • Method method:真实角色执行的方法对象;
    • Object[] args:真实角色执行的参数列表;

    a.创建一个Ihadoop接口;

    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 10:57
     */
    public interface Ihadoop {
        void eating(String name);
    }
    

    b.创建一个真实角色并实现Ihadoop接口;

    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 10:58
     */
    public class Hadoop implements Ihadoop {
        @Override
        public void eating(String name) {
            System.out.println("大象再吃"+name);
            try {
                Thread.sleep(3000);//给点延迟方便测试
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    c.测试

    import com.gec.targect.Ihadoop;
    import com.gec.targect.impl.Hadoop;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 10:58
     */
    
    /**
     * 利用jdk动态代理实现对真实对象执行一个方法的耗时情况;
     * 本身Hdoop的eat()方法是没有计算耗时情况的;
     */
    public class MainApp {
        public static void main(String[] args) {
            Ihadoop hadoopProxy = (Ihadoop) Proxy.newProxyInstance(Hadoop.class.getClassLoader()//获取类加载器
                    ,new Class[]{Ihadoop.class}//获取真实对象实现的接口的类型
                    , new InvocationHandler() {//匿名内部类实现调度方法
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            long startTime = System.currentTimeMillis();
                            Object res = method.invoke(new Hadoop(), args);
                            long endTime = System.currentTimeMillis();
                            long time = endTime - startTime;
                            System.out.println("耗时" + time);
                            return res;
                        }
                    }
            );
            hadoopProxy.eating("香蕉");
        }
    }
    

    lambda表达式等价写法

    import com.gec.targect.Ihadoop;
    import com.gec.targect.impl.Hadoop;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
     * @author tsc
     * @project_name spring
     * @date 2021/9/13 10:58
     */
    
    /**
     * 利用jdk动态代理实现对真实对象执行一个方法的耗时情况;
     * 本身Hdoop的eat()方法是没有计算耗时情况的;
     */
    public class MainApp {
        public static void main(String[] args) {
            //匿名内部类实现调度方法
            Ihadoop hadoopProxy = (Ihadoop) Proxy.newProxyInstance(Hadoop.class.getClassLoader()//获取类加载器
                    ,new Class[]{Ihadoop.class}//获取真实对象实现的接口的类型
                    , (proxy, method, args1) -> {
                        long startTime = System.currentTimeMillis();
                        Object res = method.invoke(new Hadoop(), args1);
                        long endTime = System.currentTimeMillis();
                        long time = endTime - startTime;
                        System.out.println("耗时" + time);
                        return res;
                    }
            );
            hadoopProxy.eating("香蕉");
        }
    }
    

    jdk动态代理后的结果

    D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA 
    大象再吃香蕉
    耗时3003
    
    Process finished with exit code 0
    

    输出的耗时时间就是对真实角色的eat()方法的增强;本身真实角色Hadoop只是普通的吃方法没有计算耗时功能;

② Cglib代理(子类代理)

a.前提条件

1.需要引入cglib的jar文件;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9CyjlPwq-1631546773528)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\image-20210913225858614.png)]

2.代理的类不能为final,否则报错;
3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

b.Cglib概述

? Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象(真实角色)实现某个接口的问题。,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写;它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

c.代码实现Cglib代理

a.创建一个真实角色(被代理对象)Hadoop1;

/**
 * @author tsc
 * @project_name spring
 * @date 2021/9/13 11:38
 */
public class Hadoop1 {
    public static void eating(String name) {
        System.out.println("大象再吃"+name);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

b.编写cglib动态代理并测试

import com.gec.cglib.targect.Hadoop1;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @author tsc
 * @project_name spring
 * @date 2021/9/13 11:39
 */

/**
 * 利用jdk动态代理实现对真实对象执行一个方法的耗时情况;
 * 本身Hdoop1的eat()方法是没有计算耗时情况的;
 */
public class MainApp {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();//创建一个Cglib的执行对象
        enhancer.setSuperclass(Hadoop1.class);//设置真实角色的字节码对象
        enhancer.setCallback(new MethodInterceptor() {//设置方法拦截器对象
            //Object o 真实角色的子类(代理角色)
            //Method method  代理角色的方法对象
            // Object[] objects  共用的方法参数对象
            //MethodProxy methodProxy 真实对象的方法对象
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                long startTime = System.currentTimeMillis();
                //方法一 method.invoke 实际上是调用代理对象的方法,此时我们需要传入一个Hadoop1对象
                //Object res = method.invoke(new Hadoop1(), objects);
                //方法二 methodProxy.invokeSuper 实际上调用的是传入对象的父类的方法 ,此时我们传入代理对象(因为代理对象就是真实对象的子类)这种效率高且节省内存(推荐这一种)
                Object res = methodProxy.invokeSuper(o, objects);
                long endTime = System.currentTimeMillis();
                long time = endTime - startTime;
                System.out.println("耗时" + time);
                return res;
            }
        });
        Hadoop1 o = (Hadoop1) enhancer.create();
        o.eating("苹果");
    }
}

结果展示

D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA 
大象再吃苹果
耗时3010

Process finished with exit code 0

动态代理优缺点:

  • 优点:灵活易用,不用手动创建代理类。
  • 缺点:如果频繁创建代理类,可能会让metaspace空间不足,从而触发频繁FullGC,因此如果我们需要自己用到动态代理时,一般将创建出来的代理对象进行缓存复用
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-14 13:11:30  更:2021-09-14 13:13:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:14:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码