java 设计模式-代理模式

目录

概述

一. 什么是代理模式

1. 举例说明

二. 代理模式作用

1. 保护代理

2. 增强功能

3. 代理交互

4. 远程代理:

三. 代理模式3个角色

四. 静态代理

1. 代码示例:

五. JDK动态代理

1. 代码示例:

六. CGLIB 动态代理

1.代码示例

 七. JDK动态代理和CGLIB动态代理区别

八. 两种在spring的使用


概述

        代理模式(Proxy Pattern)是设计模式中的一种结构型模式,其核心思想是通过创建一个代理对象来间接访问实际对象(也称为主题对象),从而在不改变实际对象的前提下,为实际对象添加额外的功能或控制。

代理模式根据实现方式可分:

  1. 接口实现:某个类必须有实现的接口,如果某个类没有实现接口,那么这个类就不能通过 JDK 产生动态代理了
    1. 静态代理
    2. 动态代理
  2. 继承实现:是 CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,(使用 Java 反射技术创建代理类的实例)CGLIB 是一个第三方库

一. 什么是代理模式

        代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。

        代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。

1. 举例说明

        以卖房为例:房东 A 要卖房,客户 B 要买房,

没有代理模式情况:

  1. 房东 A 要卖房(可能要贴小广告卖房)
  2. 带买家 B 来看房
  3. 买家 B 非常满意,交易达成(出售成功)

使用代理模式:

  1. 房东 A 要卖房
  2. 找到代理 C ,由 C 去做卖房信息发布
  3. 代理 C 带有购买意向的客户看房
  4. 买家 B 很满意,交易达成

在这个例子中房东只需找到代理即可,其他发布房源信息和看房都由代理去做。

二. 代理模式作用

1. 保护代理

        代理可以为实际对象提供一层保护,确保对象不会被不信任的调用者直接访问。

2. 增强功能

        在程序中,需要给某个对象的功能进行功能增强的时候,可以考虑找一个代理进行增强,例如:Spring 框架中的 AOP 切面编程就是对代理模式的经典应用。

3. 代理交互

        在程序中,A对象无法和B对象直接交互时,也可以使用代理模式来解决。

4. 远程代理

        代理可以作为远程对象的本地代表,隐藏对象位于不同地址空间的事实,使得远程调用就像本地调用一样。

三. 代理模式3个角色

  1. 目标对象:目标对象也就是被代理对象(对应着被代理类)
  2. 代理对象:代理对象对应着代理类
  3. 公共接口:目标对象和代理对象的公共接口(目标对象和代理对象都需要实现这个接口)注意:在JDK中是通过接口实现代理的,但是在cglib中使用的是继承来实现动态代理的

UML图

四. 静态代理

        静态代理实现相对来比较简单,静态代理是代理模式中较简单的一种模式,它在编译时就已经确定代理类和目标类的关系。

1. 代码示例:

  1. 创建公共接口
    package com.demo.proxy;
    
    /**
     * 文件名:Subject
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:公共接口
     */
    public interface Subject {
        void info();
    }
    
  2. 创建目标类
    package com.demo.proxy;
    
    /**
     * 文件名:RealSubject
     * 创建者:
     * 创建时间:2024-09-05
     * 描述: 目标类
     */
    public class RealSubject implements Subject{
        @Override
        public void info() {
            System.out.println("被代理对象方法执行。。。");
        }
    }
    
  3. 创建代理类
    package com.demo.proxy;
    
    /**
     * 文件名:ProxySubject
     * 创建者:
     * 创建时间:2024-09-05
     * 描述: 代理类
     */
    public class ProxySubject implements Subject{
        private Subject subject;
        public ProxySubject(Subject subject) {
            this.subject = subject;
        }
        @Override
        public void info() {
            perReqs();
            subject.info();
            postReqs();
        }
    
        private void perReqs(){
            System.out.println("记录请求日志");
        }
        private void postReqs(){
            System.out.println("记录响应日志");
        }
    
    }
    
  4. 测试类
    package com.demo.proxy;
    
    /**
     * 文件名:Main
     * 创建者:
     * 创建时间:2024-09-05
     * 描述: 测试类
     */
    public class Main {
        public static void main(String[] args) {
            //1.创建目标对象
            Subject subject = new RealSubject();
    
            //2.创建代理对象(将目标对象传入代理对象构造中)
            Subject sub = new ProxySubject(subject);
            sub.info();
    
        }
    }
    
  5. 测试结果

五. JDK动态代理

        java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。

        代理类在程序运行时利用反射机制动态创建而成,主要分为jdk动态代理cglib动态代理

        JDK 动态代理类的字节码在程序运行时由 Java 反射机制动态生成,无需手工编写它的源代码。
        动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为 Java 反射机制可以生成任意类型的动态代理类。

1. 代码示例:

参数说明:

Proxy类的静态方法 newProxyInstance() 参数:

  • loader:类加载器,使用本类的类加载器即可,或者 target 对象的类加载器也行。
  • interfaces:接口数组,表示被代理类实现的接口,因可能会实现多个接口,所以这里是数组的形式。
  • InvocationHandler:核心,在这里创建一个内部实现类实现InvocationHandler接口,重写invoke方法
    invoke 里的参数 proxy 是代理类的实例,method是代理类的实例执行的方法,args则是执行的方法里面的参数,
    我们可以在这里对执行核心业务逻辑前后增加代码。method.invoke(target, args)这里是用了反射的原理让target对象去执行method方法。
  1. 定义接口
    package com.demo.proxy.dynamic;
    
    /**
     * 文件名:UserService
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:业务服务接口
     */
    public interface IUserService {
        boolean login(String name,String pwd);
        void signOut(String name);
    }
    
  2. 定义接口实现类(也就是业务实现类)
    package com.demo.proxy.dynamic;
    
    /**
     * 文件名:UserService
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:业务实现类
     */
    public class UserService implements IUserService{
        /**
         * 登录
         * @param name
         * @param pwd
         * @return
         */
        @Override
        public boolean login(String name,String pwd) {
            System.out.println(name+":登录成功");
            return true;
        }
    
        /**
         * 退出
         * @param name
         */
        @Override
        public void signOut(String name) {
            System.out.println(name +": 退出成功");
        }
    }
    
  3. 定义代理类(无需实现接口)
    package com.demo.proxy.dynamic;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 文件名:DefineProxy
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:动态代理类
     */
    public class DefineProxy {
        private Object target;
        public DefineProxy(Object target){
            this.target = target;
        }
        public Object createProxy(){
            return Proxy.newProxyInstance(DefineProxy.class.getClassLoader(), this.target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    perName(method.getName());
                    Object invoke = method.invoke(target, args);
                    postName(method.getName());
                    return invoke;
                }
            });
        }
    
        /**
         * 前置方法
         * @param name
         */
        private void perName(String name){
            System.out.println(name+"方法-前置执行");
        }
        /**
         * 后置方法
         * @param name
         */
        private void postName(String name){
            System.out.println(name+"方法-后置执行");
        }
    }
    
  4. 定义测试类
    package com.demo.proxy.dynamic;
    
    /**
     * 文件名:Main
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:测试类
     */
    public class Main {
        public static void main(String[] args) {
            //1.创建目标对象
            UserService userService = new UserService();
    
            //2.创建代理对象
            DefineProxy defineProxy = new DefineProxy(userService);
    
            //3.调用生成代理方法
            IUserService proxy = (IUserService) defineProxy.createProxy();
    
            //4.代理执行方法
            boolean flg = proxy.login("小明","12345");
            System.out.println("返回值:"+flg);
            System.out.println("=============================");
            proxy.signOut("小明");
    
        }
    }
    
  5. 测试结果截图
  6. 修改动态代理类(通过实现 InvocationHandler 接口来实现代理 )
    package com.demo.proxy.dynamic;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 文件名:DefineProxyTwo
     * 创建者:宁贝贝
     * 创建时间:2024-09-05
     * 描述:动态代理类,通过实现接口
     */
    public class DefineProxyTwo implements InvocationHandler {
        private Object target;
        public DefineProxyTwo(Object target){
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            perName(method.getName());
            Object res = method.invoke(target,args);
            postName(method.getName());
            return res;
        }
    
        /**
         * 前置方法
         * @param name
         */
        private void perName(String name){
            System.out.println(name+"方法-前置执行");
        }
        /**
         * 后置方法
         * @param name
         */
        private void postName(String name){
            System.out.println(name+"方法-后置执行");
        }
    }
    
  7. 修改测试类
    package com.demo.proxy.dynamic;
    
    import java.lang.reflect.Proxy;
    
    /**
     * 文件名:MainTwo
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:测试类
     */
    public class MainTwo {
    
        public static void main(String[] args) {
            //1.创建被代理对象
            UserService userService = new UserService();
    
            //2.获取被代理对象的类加载器,用作生成代理对象的必要参数
            ClassLoader classLoader = userService.getClass().getClassLoader();
    
            //3.获取被代理对象的实现接口,
            // 用作生成代理对象的必要参数
            // 方法映射就是基于这个参数实现的
            Class<?>[] interfaces = userService.getClass().getInterfaces();
    
            //4.获取被代理对象的增强主题类,用作生成代理对象的必要参数
            DefineProxyTwo defineProxyTwo = new DefineProxyTwo(userService);
    
            //5.生成代理对象的核心代码
            IUserService proxyInstance = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, defineProxyTwo);
    
            // 使用代理对象执行方法
            proxyInstance.login("星星","765432");
            System.out.println("==========================");
            proxyInstance.signOut("星星");
        }
    }
    

六. CGLIB 动态代理

        JDK 中提供的生成动态代理类的机制有个鲜明的特点是:某个类必须有实现的接口,如果某个类没有实现接口,那么这个类就不能通过 JDK 产生动态代理了。

        CGLIB(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
        CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。

CGLib 创建某个类 A 的动态代理类的模式是:

  1. 查找 A 上的所有非 final 的 public 类型的方法定义;
  2. 将这些方法的定义转换成字节码;
  3. 将组成的字节码转换成相应的代理的 class 对象;
  4. 实现 MethodInterceptor 接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)

1.代码示例

  1. 定义普通业务类
    package com.demo.proxy.cglib;
    
    /**
     * 文件名:UserService
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:一个普通的业务类
     */
    public class UserService{
        /**
         * 登录
         * @param name
         * @param pwd
         * @return
         */
        public boolean login(String name,String pwd) {
            System.out.println(name+":登录成功");
            return true;
        }
    
        /**
         * 退出
         * @param name
         */
        public void signOut(String name) {
            System.out.println(name +": 退出成功");
        }
    }
    
  2. 定义CGLIB动态代理类
    package com.demo.proxy.cglib;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * 文件名:CGLibProxy
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:cglib动态代理类
     */
    public class CGLibProxy implements MethodInterceptor {
    
        private Object target;
    
        public CGLibProxy(Object target) {
            this.target = target;
        }
    
        public Object createProxy(){
            //cglib中的增强器,用来创建动态代理
            Enhancer enhancer = new Enhancer();
            //设置要创建动态代理的类
            enhancer.setSuperclass(target.getClass());
            //设置回调,这里相当于是对于代理类上所有方法的调用,都会调用callback,而callback则需要实现intercept()方法进行拦截。
            enhancer.setCallback(this);
            //创建代理类
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("cglib 代理开始...");
            Object object = proxy.invokeSuper(obj,args);
            System.out.println("cglib 代理结束...");
            return object;
        }
    }
    
  3. 定义测试类
    package com.demo.proxy.cglib;
    
    /**
     * 文件名:Main
     * 创建者:
     * 创建时间:2024-09-05
     * 描述:测试类
     */
    public class Main {
        public static void main(String[] args) {
            //1.创建被代理对象
            UserService userService = new UserService();
            //2.创建CGLibProxy对象
            CGLibProxy cgLibProxy = new CGLibProxy(userService);
            //3.调用createProxy创建代理对象
            UserService proxy = (UserService)cgLibProxy.createProxy();
            //4.代理执行方法
            boolean res = proxy.login("晓月","5676543");
            System.out.println("登录结果:"+res);
            System.out.println("--------------------------------");
            proxy.signOut("晓月");
    
            System.out.println("=================================");
    
            //其实上面那几个步骤可以一行代码全搞定
            UserService us = (UserService)new CGLibProxy(new UserService()).createProxy();
            boolean result = us.login("小红","13123123");
            System.out.println("登录结果:"+result);
            System.out.println("--------------------------------");
            us.signOut("小红");
        }
    }
    
  4. 测试结果

 七. JDK动态代理和CGLIB动态代理区别

  1. 实现原理:JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的,它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。
  2. 性能表现:JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛,适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它不能代理final类和final方法,以及一些无法生成子类的类。
  3. 应用场景:JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适用于代理类的场景,例如Spring中的AOP切面编程等。

八. 两种在spring的使用

        如果被代理对象实现了需要被代理的接口,则使用 JDK 的动态代理,否则便使用 CGLIB 代理,在spring中它会自动根据使用情况进行切换,但是如果你缺少CGLIB依赖肯定是会报错。需要添加 cglib 依赖包。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/872340.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

P1.8COB小间距成本下降,增大COB超微小间距LED显示屏市场份额

随着P1.8 COB&#xff08;Chip On Board&#xff09;小间距技术的不断成熟与成本的有效控制&#xff0c;其在LED显示屏市场中的竞争力日益凸显。这一趋势不仅激发了行业内对超微小间距LED显示屏的浓厚兴趣&#xff0c;更推动了市场的快速扩张。 随着成本的进一步下降&#xff…

支持图片和视频分割,SAM2最新分割一切大模型分享

Segment Anything Model 2&#xff08;简称SAM 2&#xff09;是由Meta&#xff08;Facebook AI&#xff09;开发的最新一代图像和视频分割模型。 SAM2能够实现对静态图像和动态视频中的对象进行实时、可提示的分割&#xff0c;将图像与视频分割功能整合到了同一个系统中。 SA…

在Centos中的mysql的备份与恢复

1.物理备份 冷备份&#xff1a;关闭数据库时进行热备份&#xff1a;数据库运行时进行&#xff0c;依赖于数据库日志文件温备份&#xff1a;数据库不可写入但可读的状态下进行 2.逻辑备份 对数据库的表或者对象进行备份 3.备份策略 完全备份&#xff1a;每次都备份完整的数…

【运维监控】influxdb 2.0+telegraf 监控tomcat 8.5运行情况(1)

关于java应用的监控本系列有文章如下&#xff1a; 【运维监控】influxdb 2.0telegraf 监控tomcat 8.5运行情况 【运维监控】influxdb 2.0grafana 监控java 虚拟机以及方法耗时情况 【运维监控】Prometheusgrafana监控tomcat运行情况 【运维监控】Prometheusgrafana监控spring b…

数学建模强化宝典(13)M-K检验法

前言 M-K检验法&#xff0c;全称为Mann-Kendall检验法&#xff0c;是一种非参数的假设检验方法&#xff0c;广泛应用于时间序列数据的趋势性变化检验&#xff0c;特别是气候序列中的趋势分析和突变点检测。以下是对M-K检验法的详细介绍&#xff1a; 一、定义与背景 M-K检验法由…

Leetcode面试经典150题-83.删除链表中的重复元素

解法都在代码里&#xff0c;不懂就留言或者私信 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val …

服务器监控工具都是监控服务器的哪些性能和指标

服务器监控工具通常用于确保服务器及其相关服务的正常运行。这些工具可以帮助管理员快速识别并解决问题&#xff0c;从而减少停机时间和性能下降的风险。以下是服务器监控工具通常会监控的一些主要内容&#xff1a; 系统健康状态&#xff1a; CPU使用率 内存&#xff08;RAM&…

油猴插件录制请求,封装接口自动化参数

参考&#xff1a;如何使用油猴插件提高测试工作效率 一、背景 在酷家乐设计工具测试中&#xff0c;总会有许多高频且较繁琐的工作&#xff0c;比如&#xff1a; 查询插件版本&#xff1a;需要打开Chrome控制台&#xff0c;输入好几个命令然后过滤出版本信息。 查询模型商品&…

JAVA—单元测试

单元测试&#xff1a;就是针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;编写测试代码对其进行正确性测试 之前是使用main函数调用来进行检测&#xff0c;无法实现自动化测试 也会影响其他方法的测试 目录 1.junit框架概述 2.junit框架的常见注解 1.junit框架…

基于C++实现(MFC界面)家谱管理系统

一、题目&#xff1a;家谱管理系统 二、内容&#xff1a; 2.1 概述 2.1.1 选题原因 做此题的原因是因为可以比较方便的记录家族历代成员的情况与关系&#xff0c;能很好的保存家族每一代的信息&#xff0c;而不用人工纸质的方式来存取家谱&#xff0c;更便于人们保存和使用…

基于STELLA系统动态模拟技术及在农业、生态环境等科学领域中的实践应用

STELLA是一种用户友好的计算机软件。通过绘画出一个系统的形象图形&#xff0c;并给这个系统提供数学公式和输入数据&#xff0c;从而建立模型。依据专业兴趣&#xff0c;STELLA可以用来建立各种各样的农业、生态、环境等方面的系统动态模型&#xff0c;为科研、教学、管理服务…

Day16_0.1基础学习MATLAB学习小技巧总结(16)——元胞数组

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

linux top命令介绍以及使用

文章目录 介绍 top 命令1. top 的基本功能2. 如何启动 top3. top 的输出解释系统概况任务和 CPU 使用情况内存和交换空间进程信息 4. 常用操作 总结查看逻辑CPU的个数查看系统运行时间 介绍 top 命令 top 是一个在类 Unix 系统中广泛使用的命令行工具&#xff0c;用于实时显示…

pikachu文件包含漏洞靶场攻略

1.File inclusion(local)&#xff08;本地文件包含&#xff09; 提交一个球员信息 filename后面输入../../../../../1.php访问php文件 2.File Inclusion(remote)&#xff08;远程文件包含&#xff09; 修改配置 远程包含漏洞的前提&#xff1a;需要php.ini配置如下&#…

深入理解 Babel - 微内核架构与 ECMAScript 标准化|得物技术

随着浏览器版本的持续更新&#xff0c;浏览器对JavaScript的支持越来越强大&#xff0c;Babel的重要性显得较低了。但Babel的设计思路、背后依赖的ECMAScript标准化思想仍然值得借鉴。 本文涉及的Babel版本主要是V7.16及以下&#xff0c;截至发文时&#xff0c;Babel最新发布的…

利用SSH加密实现的HTTP隧道分析与检测

1.隧道介绍 Chisel是一个快速稳定的TCP/UDP隧道工具&#xff0c;该工具基于HTTP实现&#xff0c;并通过SSH加密保证通信安全。Chisel可以进行端口转发、反向端口转发以及SOCKS流量代理&#xff0c;使用GO语言编写&#xff0c;具备较好的跨平台特性。该工具的主要用于绕过防火墙…

Hive数据库与表操作全指南

目录 Hive数据库操作详解 创建数据库 1&#xff09;语法 2&#xff09;案例 查询数据库 1&#xff09;展示所有数据库 &#xff08;1&#xff09;语法 &#xff08;2&#xff09;案例 2&#xff09;查看数据库信息 &#xff08;1&#xff09;语法 &#xff08;2&#…

Spring之整合Mybatis底层源码解析

整合核心思路 由很多框架都需要和Spring进行整合&#xff0c;而整合的核心思想就是把其他框架所产生的对象放到Spring容器中&#xff0c;让其成为Bean。 ​ 比如Mybatis&#xff0c;Mybatis框架可以单独使用&#xff0c;而单独使用Mybatis框架就需要用到Mybatis所提供的一些类…

学习笔记八:基于Jenkins+k8s+Git+DockerHub等技术链构建企业级DevOps容器云平台

基于Jenkinsk8sGitDockerHub等技术链构建企业级DevOps容器云平台 测试jenkins的CI/CD在Jenkins中安装kubernetes插件安装blueocean插件配置jenkins连接到我们存在的k8s集群配置pod-template添加自己的dockerhub凭据测试通过Jenkins部署应用发布到k8s开发环境、测试环境、生产环…

手机玩机常识-----小米系列机型 Android 15 更新计划 那些机型将会更新安卓15

小米机型是很多米粉最喜欢把玩的&#xff0c;其中解锁bl root 刷写twrp以及刷第三方系统资源相对其他品牌机型来说比较丰富。目前安卓15快要更新到很多机型。我们来了解下小米系列机型的更新计划是咋样的 小米会定期更新有关 Redmi红米 设备的支持日期的数据&#xff0c;包括可…