Spring-1-透彻理解Spring XML的Bean创建--IOC

这篇具有很好参考价值的文章主要介绍了Spring-1-透彻理解Spring XML的Bean创建--IOC。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习目标

上一篇文章我们介绍了什么是Spring,以及Spring的一些核心概念,并且快速快发一个Spring项目,实现IOC和DI,今天具体来讲解IOC

能够说出IOC的基础配置和Bean作用域

了解Bean的生命周期

能够说出Bean的实例化方式

一、Bean的基础配置

问题导入

问题1:在<bean>标签上如何配置别名?

问题2:Bean的默认作用范围是什么?如何修改?

1 Bean基础配置【重点】

配置说明

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

2 Bean别名配置

配置说明

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

注意事项:

获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No bean named 'studentDaoImpl' available
代码演示

【第0步】创建项目名称为10_2_IOC_Bean的maven项目

【第一步】导入Spring坐标

  <dependencies>
      <!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.15</version>
      </dependency>

      <!-- 导入junit的测试包 -->
      <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter</artifactId>
          <version>5.8.2</version>
          <scope>test</scope>
      </dependency>

      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.28</version>
      </dependency>
  </dependencies>

【第二步】导入Student实体类

@Data
@ToString
@AllArgsConstructor
public class Student {
    private String name;
    private String address;
    private Integer age;

    private Integer status;
}

【第三步】定义Spring管理的类(接口)

  • StudentDao接口和StudentDaoImpl实现类

package com.zbbmeta.dao;

public interface StudentDao {
    /**
     * 添加学生
     */
    void save();
}
package com.zbbmeta.dao.impl;

import com.zbbmeta.dao.StudentDao;

public class StudentDaoImpl implements StudentDao {
    @Override
    public void save() {
        System.out.println("DAO: 添加学生信息到数据库...");
    }
}

  • StudentService接口和StudentServiceImpl实现类

package com.zbbmeta.service;

public interface StudentService {
    /**
     * 添加学生
     */
    void save();
}

package com.zbbmeta.service.impl;

import com.zbbmeta.dao.StudentDao;
import com.zbbmeta.dao.impl.StudentDaoImpl;
import com.zbbmeta.service.StudentService;

public class StudentServiceImpl implements StudentService {

    //创建成员对象
    private StudentDao studentDao = new StudentDaoImpl();
    @Override
    public void save() {

    }
}

【第四步】创建Spring配置文件在resources目录下,配置对应类作为Spring管理的bean对象

  • 定义application.xml配置文件并配置StudentDaoImpl

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    目标:熟悉bean标签详细的属性应用
    -->
    <!--
    name属性:可以设置多个别名,别名之间使用逗号,空格,分号等分隔
    -->
    <bean name="studentDao2,abc studentDao3" class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao"></bean>

</beans>

注意事项:bean定义时id属性和name中名称不能有重复的在同一个上下文中(IOC容器中)不能重复

【第四步】根据容器别名获取Bean对象

package com.zbbmeta;

import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class NameApplication {
    public static void main(String[] args) {
        /**
         * 从IOC容器里面根据别名获取对象执行
         */
        //1.根据配置文件application.xml创建IOC容器
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        //2.从IOC容器里面获取id="abc"对象
        StudentDao studentDao = (StudentDao) ac.getBean("abc");
        //3.执行对象方法
        studentDao.save();
        //4.关闭容器
        ac.close();
    }
}
打印结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

3 Bean作用范围配置【重点】

配置说明

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

扩展: scope的取值不仅仅只有singleton和prototype,还有request、session、application、 websocket ,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request表示保存到request域中。

代码演示

在application.xml中配置prototype格式

  • 定义application.xml配置文件并配置StudentDaoImpl

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
    目标:熟悉bean标签详细的属性应用
    -->
    <!--
    scope属性:定义bean的作用范围,一共有5个
         singleton: 设置单例创建对象(推荐,也是默认值),好处:节省资源
         prototype: 设置多例创建对象,每次从IOC容器获取的时候都会创建对象,获取多次创建多次。
         request: 在web开发环境中,IOC容器会将对象放到request请求域中,对象存活的范围在一次请求内。
                  请求开始创建对象,请求结束销毁对象
         session: 在web开发环境中,IOC容器会将对象放到session会话域中,对象存活的范围在一次会话内。
                  会话开始开始创建对象,会话销毁对象销毁。
         global-session: 是多台服务器共享同一个会话存储的数据。
    -->
    <bean class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao4" scope="prototype"></bean>

</beans>

根据容器别名获取Bean对象

package com.zbbmeta;

import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScopeApplication {
    public static void main(String[] args) {
        /**
         * Bean的作用域范围演示
         */
        //1.根据配置文件application.xml创建IOC容器
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        //2.从IOC容器里面获取id="studentService"对象
        System.out.println("=========singleton(单例)模式=========");
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
        System.out.println("studentDao = " + studentDao);
        System.out.println("studentDao1 = " + studentDao1);
        System.out.println("=========prototype模式=========");
        StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao4");
        StudentDao studentDao3 = (StudentDao) ac.getBean("studentDao4");
        System.out.println("studentDao2 = " + studentDao2);
        System.out.println("studentDao3 = " + studentDao3);
        //4.关闭容器
        ac.close();
    }
}

打印结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

注意:在我们的实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性

二、Bean的实例化

思考:Bean的实例化方式有几种?

2 实例化Bean的三种方式

2.1 构造方法方式【重点】
  • BookDaoImpl实现类

public class StudentDaoImpl implements StudentDao {
    public StudentDaoImpl() {
        System.out.println("Student dao constructor is running ....");
    }

    @Override
    public void save() {
        System.out.println("DAO: 添加学生信息到数据库...");
    }
}
  • application.xml配置

    <!--
    目标:讲解bean创建方式
    -->
    <!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
    <bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>
  • AppForInstanceBook测试类

public class OneApplication {
    public static void main(String[] args) {
        /**
         * 无参构造方式创建Bean
         */
        //1.根据配置文件application.xml创建IOC容器
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        //2.从IOC容器里面获取id="studentService"对象
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
        //3.执行对象方法
        studentDao.save();
        //4.关闭容器
        ac.close();
    }
}
  • 运行结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

注意:无参构造方法如果不存在,将抛出异常BeanCreationException

2.2 静态工厂方式
  • StudentDaoFactory工厂类

package com.zbbmeta.factory;

import com.zbbmeta.dao.StudentDao;
import com.zbbmeta.dao.impl.StudentDaoImpl;

public class StudentDaoFactory {
//    静态工厂创建对象
    public static StudentDao getStudentDao(){
        System.out.println("Student static factory setup....");
        return new StudentDaoImpl();
    }
}
  • applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    目标:讲解bean创建方式
    -->
    <!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
<!--    <bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>-->

    <!--创建StudentDaoImpl对象方式2:调用静态工厂方法创建对象加入IOC容器
    class="com.zbbmeta.factory.StudentDaoFactory" 设置工厂类全名
    factory-method="getStudentDao" 调用工厂的静态方法
-->
    <bean class="com.zbbmeta.factory.StudentDaoFactory" factory-method="getStudentDao" id="studentDao2"></bean>


</beans>

注意:测试前最好把之前使用Bean标签创建的对象进行注释

  • TwoApplication测试类

public class TwoApplication {
    public static void main(String[] args) {
        /**
         * 无参构造方式创建Bean
         */
        //1.根据配置文件application.xml创建IOC容器
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        //2.从IOC容器里面获取id="studentService"对象
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao2");
        //3.执行对象方法
        studentDao.save();
        //4.关闭容器
        ac.close();
    }
}
  • 运行结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

2.3 实例工厂方式
  • UserDao接口和UserDaoImpl实现类

    //利用实例方法创建StudentDao对象
    public StudentDao getStudentDao2(){
        System.out.println("调用了实例工厂方法");
        return new StudentDaoImpl();
    }
  • StudentDaoFactory工厂类添加方法

//实例工厂创建对象
public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}
  • applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    目标:讲解bean创建方式
    -->
    <!--创建StudentDaoImpl对象方式1:默认调用类的无参构造函数创建-->
<!--    <bean id="studentDao" class="com.zbbmeta.dao.impl.StudentDaoImpl"/>-->

    <!--创建StudentDaoImpl对象方式2:调用静态工厂方法创建对象加入IOC容器
    class="com.zbbmeta.factory.StudentDaoFactory" 设置工厂类全名
    factory-method="getStudentDao" 调用工厂的静态方法
-->
<!--    <bean class="com.zbbmeta.factory.StudentDaoFactory" factory-method="getStudentDao" id="studentDao2"></bean>-->


    <!--创建BookDaoImpl对象方式3:调用实例工厂方法创建对象加入IOC容器
    class="com.itheima.factory.BookDaoFactory" 设置工厂类全名
    factory-method="getBookDao" 调用工厂的静态方法
-->
    <!--第一步:创建工厂StudentDaoFactory对象-->
    <bean class="com.zbbmeta.factory.StudentDaoFactory" id="studentDaoFactory"></bean>
    <!--第一步:调用工厂对象的getStudentDao2()实例方法创建StudentDaoImpl对象加入IOC容器
        factory-bean="studentDaoFactory" 获取IOC容器中指定id值的对象
        factory-method="getStudentDao2" 如果配置了factory-bean,那么这里设置的就是实例方法名
    -->
    <bean factory-bean="studentDaoFactory" factory-method="getStudentDao2" id="studentDao3"></bean>


</beans>
  • ThreeApplication测试类

package com.zbbmeta;

import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ThreeApplication {
    public static void main(String[] args) {
        /**
         * 无参构造方式创建Bean
         */
        //1.根据配置文件application.xml创建IOC容器
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        //2.从IOC容器里面获取id="studentService"对象
        StudentDao studentDao = (StudentDao) ac.getBean("studentDao3");
        //3.执行对象方法
        studentDao.save();
        //4.关闭容器
        ac.close();
    }
}

  • 运行结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

三、Bean的生命周期【了解】

问题导入

问题1:多例的Bean能够配置并执行销毁的方法?

问题2:如何做才执行Bean销毁的方法?

1 生命周期相关概念介绍

  • 生命周期:从创建到消亡的完整过程

  • bean生命周期:bean从创建到销毁的整体过程

  • bean生命周期控制:在bean创建后到销毁前做一些事情

1.1生命周期过程
  • 初始化容器
    • 创建对象(内存分配)

    • 执行构造方法

    • 执行属性注入(set操作)

    • 执行bean初始化方法

  • 使用bean
    • 执行业务操作

  • 关闭/销毁容器
    • 执行bean销毁方法

2 代码演示

2.1 Bean生命周期控制

【第0步】创建项目名称为10_4_IOC_BeanLifeCycle的maven项目

【第一步】导入Spring坐标

  <dependencies>
      <!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.15</version>
      </dependency>

      <!-- 导入junit的测试包 -->
      <dependency>
          <groupId>org.junit.jupiter</groupId>
          <artifactId>junit-jupiter</artifactId>
          <version>5.8.2</version>
          <scope>test</scope>
      </dependency>

      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.28</version>
      </dependency>
  </dependencies>

【第二步】导入Student实体类

@Data
@ToString
@AllArgsConstructor
public class Student {
    private String name;
    private String address;
    private Integer age;

    private Integer status;
}

【第三步】定义Spring管理的类(接口)

  • StudentDao接口和StudentDaoImpl实现类

package com.zbbmeta.dao;

public interface StudentDao {
    /**
     * 添加学生
     */
    void save();
}
package com.zbbmeta.dao.impl;

import com.zbbmeta.dao.StudentDao;

public class StudentDaoImpl implements StudentDao {

    public StudentDaoImpl(){
        System.out.println("Student Dao 的无参构造");
    }

    @Override
    public void save() {
        System.out.println("DAO: 添加学生信息到数据库...");
    }

    public void init(){
        System.out.println("Student Dao 的初始化方法");
    }

    public void destroy(){
        System.out.println("Student Dao 的销毁方法");
    }
}

【第四步】创建Spring配置文件在resources目录下,配置对应类作为Spring管理的bean对象

  • 定义application.xml配置文件并配置StudentDaoImpl

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--目标:创建StudentDaoImpl对象:设置生命周期方法
        init-method="init" 在对象创建后立即调用初始化方法
        destroy-method="destroy":在容器执行销毁前立即调用销毁的方法
        注意:只有单例对象才会运行销毁生命周期方法
-->
    <bean class="com.zbbmeta.dao.impl.StudentDaoImpl" id="studentDao" init-method="init" destroy-method="destroy"></bean>
</beans>

【第四步】根据容器别名获取Bean对象

package com.zbbmeta;

import com.zbbmeta.dao.StudentDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LifeCycleApplication {
    public static void main(String[] args) {


            //1.根据配置文件application.xml创建IOC容器
            ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
            //2.从IOC容器里面获取id="studentService"对象
            StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
            //3.执行对象方法
            studentDao.save();
            //4.关闭容器
            ac.close();
        
    }
}

打印结果

Spring-1-透彻理解Spring XML的Bean创建--IOC,JavaWeb,spring,xml,数据库

3 Bean销毁时机

  • 容器关闭前触发bean的销毁

  • 关闭容器方式:
    • 手工关闭容器 调用容器的close()操作

    • 注册关闭钩子(类似于注册一个事件),在虚拟机退出前先关闭容器再退出虚拟机 调用容器的registerShutdownHook()操作文章来源地址https://www.toymoban.com/news/detail-635980.html

public class LifeCycleApplication {
    public static void main(String[] args) {


            //1.根据配置文件application.xml创建IOC容器
            ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
            //2.从IOC容器里面获取id="studentService"对象
            StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
            //3.执行对象方法
            studentDao.save();
            //4.关闭容器
//            ac.close();
        //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
        ac.registerShutdownHook();
    }
}

到了这里,关于Spring-1-透彻理解Spring XML的Bean创建--IOC的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

    我们要关注一个接口 BeanFactory ,它是Spring IOC容器的根接口,也是容器的入口。 类的描述中已经清楚的说明了: 我们来看一下这个接口里面的方法。 我们可以看到有各种各样的 getBean 方法,让我们可以从容器中获取到各种各样的Bean对象。 BeanFactory 有一个实现类 DefaultListab

    2024年02月05日
    浏览(38)
  • Spring IOC - Bean的扫描

            @Component及其衍生注解:@Configuration、@Controller、@Service、@Repository标记的类,被Spring IOC扫描到后,即可被容器管理起来。其原理基本涵盖在AnnotationConfigApplicationContext构造函数体的三行代码里。         这个无参构造方法做了三件事情: 首先创建bean工厂,实际上

    2024年02月16日
    浏览(33)
  • 【Spring篇】IOC/DI配置管理第三方bean

    🍓系列专栏:Spring系列专栏 🍉个人主页:个人主页 目录 一、案例:数据源对象管理 1.环境准备 2.实现Druid管理 3.实现C3P0管理 二、加载properties文件 1.第三方bean属性优化 2.读取单个属性 3.注意事项   三、核心容器 1.环境准备 2.容器   1.容器的创建方式 2.Bean的三种获取方式 3.容器

    2024年02月02日
    浏览(53)
  • 【Spring专题】Spring之Bean的生命周期源码解析——阶段二(IOC之实例化)

    由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解的,所以不了解前置知识的情况下,大概率没办法看懂当前的内容。所以,特别推荐看看我前面的文章(自上而下次序): Spring底层核心原理解析——引导篇【学习难度: ★★☆☆☆ 】

    2024年02月13日
    浏览(53)
  • 【仿写spring之ioc篇】四、实现bean的初始化阶段

    在Bean的初始化阶段有前置和后置方法,这个方法是通过BeanPostProcessor来管理的,下面我们对原有的项目结构做小小的更改。 对启动类作出修改,先检查有没有BeanPostProcessor的实现类,有的话就使用,没有就使用默认的。 第二次循环先检查是不是postProcessor,是的话就跳过就行

    2024年02月10日
    浏览(43)
  • 11Spring IoC注解式开发(上)(元注解/声明Bean的注解/注解的使用/负责实例化Bean的注解)

    注解的存在主要是为了简化XML的配置。Spring6倡导全注解开发。 注解开发的优点 :提高开发效率 注解开发的缺点 :在一定程度上违背了OCP原则,使用注解的开发的前提是需求比较固定,变动较小。 自定义一个注解: 该注解上面修饰的注解包括:Target注解和Retention注解,这两个注

    2024年01月21日
    浏览(44)
  • 【Spring专题】Spring之Bean的生命周期源码解析——阶段二(二)(IOC之属性填充/依赖注入)

    由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解的,所以不了解前置知识的情况下,大概率没办法看懂当前的内容。所以,特别推荐看看我前面的文章(自上而下次序): Spring底层核心原理解析【学习难度: ★★☆☆☆ 】 手写简易

    2024年02月12日
    浏览(39)
  • 【Spring教程11】Spring框架实战:IOC/DI注解开发管理第三方bean的全面深入详解

    欢迎大家回到《 Java教程之Spring30天快速入门》,本教程所有示例均基于Maven实现,如果您对Maven还很陌生,请移步本人的博文《 如何在windows11下安装Maven并配置以及 IDEA配置Maven环境》,本文的上一篇为《 纯注解开发模式下的依赖注入和读取properties配置文件》 前面定义bean的时

    2024年02月04日
    浏览(59)
  • 从入门到精通:掌握Spring IOC/DI配置管理第三方bean的技巧

    以后我们会用到很多第三方的bean,我们以数据源是 Druid(德鲁伊) 和 C3P0 来配置举个例子。 1.1.1 环境准备 先来准备下案例环境: 1.1.2 思路分析 需求:使用Spring的IOC容器来管理Druid连接池对象 1.使用第三方的技术,需要在pom.xml添加依赖 2.在配置文件中将【第三方的类】制作成一个

    2024年02月02日
    浏览(46)
  • Spring系列二:基于XML配置bean

    上文中, 我们学习到了 Spring系列一:spring的安装与使用 接下来我们学习, 通过XML配置bean Bean管理包括两方面: 创建bean对象, 给bean注入属性 案例: 通过spring的ioc容器, 获取一个bean对象, 获取方式: 按类型. 演示通过bean的类型获取对象 细节 按照类型获取bean, 要求ioc容器中的同一个

    2024年02月14日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包