Java EE3-我独自整合(第四章:Spring bean标签的常见配置)

bean 标签的别名配置

  • bean 标签有属性 name,用于定义 bean 的别名。别名可定义多个,使用逗号(,)、分号(😉 或空格( )分隔

Spring配置

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"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="default">

        <bean id="student" name="stu1 stu2" class="com.tianshi.domain.Student" >
                <property name="id" value="1"/>
                <property name="name" value="张三"/>
        </bean>

        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

测试(自动转换)

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    public static void main(String[] args) {
//        //耦合方式管理Student对象
//        Student student = new Student();
//        student.setId(1);
//        student.setName("张三");
//        System.out.println(student);

//        //通过Spring容器创建Student的对象
//        //创建Spring容器,创建的容器指定读取配置文件为类型的构造参数
//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//        Student student=(Student)applicationContext.getBean("student");
//        student.setId(2);
//        student.setName("李四");
//        System.out.println(student);

        //自动转换+别名
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //原始名称
        Student student1 = context.getBean("student", Student.class);
        System.out.println(student1);
        //别名1
        Student student2 =  context.getBean("stu1",Student.class);
        System.out.println(student2);
        //别名2
        Student student3 = context.getBean("stu2",Student.class);
        System.out.println(student3);
        System.out.println(student1 == student2);

    }
}

bean 标签的所用范围配置--scope 属性

Spring 中 bean 标签有属性 scope,用于定义 bean 的作用范围,常用可选范围如下:

  • singleton:单例(默认)
  • prototype:非单例

Spring配置

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"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="default">

        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >
                <property name="id" value="1"/>
                <property name="name" value="单例学生"/>
        </bean>

        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">
                <property name="id" value="1"/>
                <property name="name" value="多例学生"/>
        </bean>

        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class StudentTest {
    public static void main(String[] args) {
//        //耦合方式管理Student对象
//        Student student = new Student();
//        student.setId(1);
//        student.setName("张三");
//        System.out.println(student);

//        //通过Spring容器创建Student的对象
//        //创建Spring容器,创建的容器指定读取配置文件为类型的构造参数
//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//        Student student=(Student)applicationContext.getBean("student");
//        student.setId(2);
//        student.setName("李四");
//        System.out.println(student);

        //自动转换+别名
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //原始名称
        Student student1 = context.getBean("student1", Student.class);
        System.out.println(student1);
        //别名1
        Student student2 =  context.getBean("stu1",Student.class);
        System.out.println(student2);
        //别名2
        Student student3 = context.getBean("stu2",Student.class);
        System.out.println(student3);
        System.out.println(student1 == student2);
        System.out.println(student2 == student3);

        //非单例
        Student student4 =  context.getBean("student",Student.class);
        System.out.println(student4);
        //非单例别名
        Student student5 =  context.getBean("student_",Student.class);
        System.out.println(student5);
        System.out.println(student4 == student5);
    }
}

Spring 为什么 bean 默认是单例的?

  • 适合交给容器进行管理的 bean
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 不适合交给容器进行管理的 bean
    • 封装实体的域对象

bean 的生命周期

  • 生命周期:从创建到消亡的完整过程
  • bean 生命周期:bean 从创建到销毁的整体过程
  • bean 生命周期控制:在 bean 创建后到销毁前做一些事情
  • Spring 中 bean 的生命周期具体如下:
    1. 初始化容器
      1. 创建对象(内存分配)
      2. 执行构造方法
      3. 执行属性注入(set 操作)
      4. 执行 bean 初始化方法
    2. 使用 bean
      • 执行业务操作
    3. 关闭/销毁容器
      • 执行 bean 销毁方法

上述所有步骤中,还未接触到的初始化和销毁两个过程(即 bean 的生命周期控制方法)Spring 中的生命周期控制方法也是有两种实现方案,分别是:自定义、实现与标准接口实现

自定义实现声明周期控制方法

实体类

java 复制代码
package com.tianshi.domain;

public class Book {
    private Integer id;
    private String name;

    public Book() {
    }

    public Book(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer id() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String name() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 初始化方法
     * 不要有返回值,不要有参数表。
     */
    public void init(){
        System.out.println("Book类型初始化方法运行。。。。。");
    }

    /**
     * 销毁方法|回收资源方法
     * 不要有返回值,不要有参数表。
     */
    public void destroy(){
        System.out.println("Book类型销毁方法运行。。。。。。");
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Spring配置

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="default">

    <bean id="book" init-method="init" destroy-method="destroy" class="com.tianshi.domain.Book">
        <property name="id" value="100"/>
        <property name="name" value="spring in action"/>
    </bean>

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Book;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        Book book = context.getBean("book", Book.class);
        System.out.println(book);

    }
}

经测试发现,销毁方法并没有执行,这是 Spring 框架机制的问题,原因是 Spring 定义了,只有在容器关闭前才会触发 bean 的销毁。那么关闭容器的方式有:

  • 手工关闭容器
    • ConfigurableApplicationContext 接口 close()操作
  • 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
    • ConfigurableApplicationContext 接口 registerShutdownHook()操作

修改测试代码再次测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Book;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    public static void main(String[] args) {
        // ConfigurableApplicationContext是ApplicationContext接口的子接口
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        Book book = context.getBean("book", Book.class);
        System.out.println(book);
        // 手工关闭容器。关闭容器的时候,Spring会先销毁bean对象。如果bean配置了销毁方法,先运行销毁方法,后销毁对象
//         context.close();
        // 注册关闭任务。通知Spring框架,当JVM关闭的时候,先关闭容器
        context.registerShutdownHook();
    }
}

两种方式均能够成功

标准接口实现生命周期控制方法

实体类

java 复制代码
package com.tianshi.domain;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import java.io.Serializable;

public class Book implements Serializable, InitializingBean, DisposableBean {
    private Integer id;
    private String name;

    public Book() {
    }

    public Book(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer id() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String name() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 标准接口:初始化在属性完成后要做什么
     * @throws Exception
     */
    public void afterPropertiesSet() throws Exception {
        System.out.println("基于Spring接口完成初始化。。。");
    }

    /**
     * 标准接口:销毁
     * @throws Exception
     */
    public void destroy() throws Exception {
        System.out.println("基于Spring接口完成销毁。。。");
    }

    /**
     * 初始化方法
     * 不要有返回值,不要有参数表。
     */
//    public void init(){
//        System.out.println("Book类型初始化方法运行。。。。。");
//    }

    /**
     * 销毁方法|回收资源方法
     * 不要有返回值,不要有参数表。
     */
//    public void destroy(){
//        System.out.println("Book类型销毁方法运行。。。。。。");
//    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Spring配置

  • 采用 Spring 框架提供的标准接口来完成生命周期的控制,可以省略对应的配置内容
  • 虽然配置省略了,但是代码和 Spring 框架耦合在一起,不利于后续代码的维护,不推 荐使用
java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd"
        default-autowire="default">

    <bean id="book" class="com.tianshi.domain.Book">
<!--        init-method="init" destroy-method="destroy"-->
        <property name="id" value="100"/>
        <property name="name" value="spring in action"/>
    </bean>

<!--        <bean id="student1" name="stu1 stu2" class="com.tianshi.domain.Student" >-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="单例学生"/>-->
<!--        </bean>-->

<!--        <bean id="student" name="student_" class="com.tianshi.domain.Student" scope="prototype">-->
<!--                <property name="id" value="1"/>-->
<!--                <property name="name" value="多例学生"/>-->
<!--        </bean>-->

        <!--
    标签常用属性:
      name: 别名,给bean增加别名。功能和id相同。一个标签id唯一。name可以定义若干个。
             别名的值要求全局唯一。定义多个别名,可以使用符合分割。常用 ',' ' '。
             现在使用的不多。曾经struts框架流行的时候常用。
      scope : 作用域。可以赋予的值只有 singleton 和 prototype
             singleton, 单例的。Spring IoC容器启动的时候自动创建bean对象,且仅创建唯一一次。默认
             prototype, 多例的。IoC容器启动时不创建对象,每次调用geBean方法时,创建一个对象。
      init-method : 初始化方法。在对象创建完成,设置注入完成后,getBean获取对象前,运行。
             初始化逻辑,会在容器创建完成时,就已完成。
      destroy-method : 销毁方法。在容器关闭|销毁前执行。
        -->



        <!--        &lt;!&ndash;Spring框架的核心配置文件&ndash;&gt;-->
<!--        &lt;!&ndash;一个bean标签就是一个domain对象,让Spring框架帮忙创建管理这个对象-->
<!--            id      唯一标识-->
<!--            class   类型的具体名称,必须写包名.类名-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        <bean id="student" class="com.tianshi.domain.Student"/>&ndash;&gt;-->

<!--        &lt;!&ndash;配置静态工厂&ndash;&gt;-->
<!--&lt;!&ndash;        //唯一一个静态工厂(构造注入)&ndash;&gt;-->
<!--        <bean id="studentWithStaticFactory" class="com.tianshi.factory.StudentStaticFactory"-->
<!--              factory-method="getStudent">-->
<!--                &lt;!&ndash;DI构造注入-->
<!--                    用来确定给哪个属性赋值(可同时使用):-->
<!--                        index:参数在构造参数表中的下标,从0开始计数-->
<!--                        name:构造方法参数名称-->
<!--                        type:构造方法参数类型-->
<!--                    设置属性的值(只用一个):-->
<!--                        value:要传入的构造方法参数值(简单类型)-->
<!--                        ref:要传入的构造方法参数(引用类型)-->
<!--                    建议用index+name+value/ref完成配置-->
<!--                &ndash;&gt;-->
<!--                <constructor-arg index="0" name="id" value="100"/>-->
<!--                <constructor-arg index="1" name="name" value="小明"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;配置实例工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;C名称空间注入-->
<!--            基于参数索引的配置:-->
<!--                c:_索引数="值" 注入简单类型数据值-->
<!--                c:_索引数-ref="bean的id" 注入Spring容器中的其他的bean对象-->
<!--            基于参数名的配置:-->
<!--                c:参数名="值" 注入简单类型的数据值-->
<!--                c:参数名="bean的id" 注入Spring容器中其他的bean对象-->
<!--&ndash;&gt;-->
<!--&lt;!&ndash;        //第一个实例工厂(C名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentFactory1" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        <bean id="studentWithInstanceFactory1" class="com.tianshi.domain.Student"-->
<!--        factory-bean="studentFactory1" factory-method="getStudent" c:_0="101" c:name="李四"/>-->
<!--&lt;!&ndash;        //Book的实例工厂&ndash;&gt;-->
<!--        <bean id="bookFactory" class="com.tianshi.factory.BookInstanceFactory"/>-->
<!--        <bean id="bookWithInstanceFactory" class="com.tianshi.domain.Book" p:id="1" p:name="空"/>-->
<!--&lt;!&ndash;        //第二个实例工厂(各种类型的属性注入的配置)&ndash;&gt;-->
<!--        <bean id="studentFactory2" class="com.tianshi.factory.StudentInstanceFactory"/>-->
<!--        &lt;!&ndash;自动装配&ndash;&gt;-->
<!--        &lt;!&ndash;局部的依赖注入自动装配-->
<!--            在bean标签中,有属性 autowire,代表是否采用自动装配逻辑。-->
<!--            可以设置的值:-->
<!--              default : 默认的,此是默认值。默认策略就是全局策略-->
<!--              no : 不使用自动装配逻辑。-->
<!--              byName : 根据名字完成自动装配。当前bean中的property的名字和当前配置文件中bean的id如果一样,则自动注入-->
<!--                有可能,类型不匹配-->
<!--              byType : 根据类型完成自动装配。当前bean中的property的类型和当前配置文件中bean的类型如有一样,则自动注入-->
<!--                有可能,当前配置文件中有多个bean的类型和同一个property的类型相同,抛出异常。-->
<!--                最常用。一般情况下,一个配置文件,不会配置多个同类型bean-->
<!--              constructor : 构造器注入。扫描当前bean类型中的有参数构造方法,扫描构造方法的参数表,-->
<!--                基于先byType,再byName的顺序逻辑,在当前配置文件中匹配bean对象,调用构造方法注入。-->
<!--        &ndash;&gt;-->
<!--        <bean id="studentWithInstanceFactory2" class="com.tianshi.domain.Student"-->
<!--              factory-bean="studentFactory2" factory-method="getStudent" autowire="byType">-->
<!--                <property name="id" value="105"/>-->
<!--                <property name="name" value="王"/>-->
<!--                &lt;!&ndash;数组array标签,一个标签代表一个数组&ndash;&gt;-->
<!--                <property name="hobbies">-->
<!--                        <array>-->
<!--                                &lt;!&ndash;数组中的简单数据,按照属性依次从0开始赋值&ndash;&gt;-->
<!--                                <value>0下标</value>-->
<!--                                <value>1下标</value>-->
<!--                                <value>2下标</value>-->
<!--                                &lt;!&ndash;ref,数组中的一个引用类型数据,按照配置属性依次从0下标开始赋值&ndash;&gt;-->
<!--                                &lt;!&ndash;<ref bean="beanId"></ref>&ndash;&gt;-->
<!--                        </array>-->
<!--                </property>-->
<!--                &lt;!&ndash;List集合,list标签,一个标签代表一个集合对象&ndash;&gt;-->
<!--                <property name="books">-->
<!--                        <list>-->
<!--                                &lt;!&ndash;0下标位置对象&ndash;&gt;-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                                &lt;!&ndash;配置一个局部的bean对象,不需要命名(不写id),局部有效&ndash;&gt;-->
<!--                                <bean class="com.tianshi.domain.Book">-->
<!--                                        <property name="id" value="2"></property>-->
<!--                                        <property name="name" value="算法导论"/>-->
<!--                                </bean>-->
<!--                                <ref bean="bookWithInstanceFactory"></ref>-->
<!--                        </list>-->
<!--                </property>-->
<!--                &lt;!&ndash;Set集合,set标签&ndash;&gt;-->
<!--                <property name="set">-->
<!--                        <set>-->
<!--                                <value>set值1</value>-->
<!--                                <value>set值2</value>-->
<!--                                <value>set值3</value>-->
<!--                        </set>-->
<!--                </property>-->
<!--                &lt;!&ndash;Map集合,map标签和entry子标签,一个map标签代表一个Map集合一个entry标签代表map中的一个键值对&ndash;&gt;-->
<!--                <property name="map">-->
<!--                        <map>-->
<!--                                &lt;!&ndash;entry有属性 key:简单类型键值,key-ref:引用类型键值-->
<!--                    value:简单类型value值,value-ref:引用类型value值&ndash;&gt;-->
<!--                                <entry key="1" value="value1"></entry>-->
<!--                                <entry key="2" value="value2"></entry>-->
<!--                                <entry key="3" value="value3"></entry>-->
<!--                        </map>-->
<!--                </property>-->
<!--                &lt;!&ndash;引用类型属性,使用ref注入&ndash;&gt;-->
<!--&lt;!&ndash;                <property name="book" ref="bookWithInstanceFactory"/>&ndash;&gt;-->
<!--        </bean>-->
<!--        &lt;!&ndash;配置FactoryBean工厂两个&ndash;&gt;-->
<!--        &lt;!&ndash;设值注入-->
<!--            标签属性:-->
<!--                name:要注入数据的property属性名-->
<!--                value:为属性赋值(简单数据类型)-->
<!--                ref:为属性赋值(引用数据类型)-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第一个FactoryBean工厂(设值注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean1" class="com.tianshi.factory.StudentFactoryBean">-->
<!--                <property name="id" value="102"/>-->
<!--                <property name="name" value="王五"/>-->
<!--        </bean>-->

<!--        &lt;!&ndash;P名称空间注入-->
<!--            仅基于参数名,无基于索引:-->
<!--                p:属性名 = "简单数据"-->
<!--                p:属性名-ref = "引用数据,其他bean标签的id"-->
<!--        &ndash;&gt;-->
<!--&lt;!&ndash;        //第二个FactoryBean工厂(P名称空间注入)&ndash;&gt;-->
<!--        <bean id="studentWithFactoryBean2" class="com.tianshi.factory.StudentFactoryBean" p:id="103" p:name="赵六"/>-->
</beans>

测试

java 复制代码
package com.tianshi.test;

import com.tianshi.domain.Book;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BookTest {
    public static void main(String[] args) {
        // ConfigurableApplicationContext是ApplicationContext接口的子接口
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        Book book = context.getBean("book", Book.class);
        System.out.println(book);
        // 手工关闭容器。关闭容器的时候,Spring会先销毁bean对象。如果bean配置了销毁方法,先运行销毁方法,后销毁对象
         context.close();
        // 注册关闭任务。通知Spring框架,当JVM关闭的时候,先关闭容器
//        context.registerShutdownHook();
    }
}
相关推荐
overmind2 小时前
oeasy Python 121[专业选修]列表_多维列表运算_列表相加_列表相乘
java·windows·python
资深数据库专家2 小时前
总账EBS 应用服务器1 的监控分析
java·网络·数据库
房开民2 小时前
可变参数模板
java·开发语言·算法
t***5442 小时前
如何在现代C++中更有效地应用这些模式
java·开发语言·c++
_深海凉_2 小时前
LeetCode热题100-最小栈
java·数据结构·leetcode
不知名的忻2 小时前
Morris遍历(力扣第99题)
java·算法·leetcode·morris遍历
daidaidaiyu3 小时前
一文学习入门 ThingsBoard 开源物联网平台
java·mqtt·spring
状元岐3 小时前
C#反射从入门到精通
java·javascript·算法
亚历克斯神3 小时前
Elasticsearch 全文搜索实战:构建企业级搜索引擎
java·spring·微服务