Spring学习笔记:Spring IoC基于XML的DI详细配置全解

发布于:2025-06-12 ⋅ 阅读:(28) ⋅ 点赞:(0)

这个是学习刘Java的Spring学习的第三个笔记,确实一个文章一两万字,看着比较累,但是看完以后感觉有点意思。一点点来,把这个专栏看完,还有很多欠缺的地方,这个Spring是目前最基本要巩固的一个知识点。

前面知道了IoC容器,和DI依赖注入的DI的两种方式(构造器和setter方法)。

value字面量

对于基本类型,String,包装类型的属性,直接使用value属性的字符串来描述具体的值。在注入的时候,会把这些做一个转换,转换成属性和参数的具体属性。对应的Spring的有value标签。
(这个基本类型,字符串,以及个基本类型对应的包装类型比较特殊,和那种其他的对象类型。)

<bean id="simpleSetterBased" class="com.spring.core.SimpleSetterBased">
    <constructor-arg name="property1">
        <value>xxx</value>
    </constructor-arg>
    <constructor-arg name="property2">
        <value>yyy</value>
    </constructor-arg>
    
    <!--setter方法 name表示属性名 value 表示属性值-->
    <property name="property3">
        <value>123</value>
    </property>
    <property name="property4">
        <value>false</value>
    </property>
</bean>

properties的快捷转换
Spring容器支持通过propertyEditor直接解析value中的特定格式字符串的字面量,转换为一个Propertise集合。(就是读取配置文件,只不过这个和我们后面工程里面读取配置文件那种.propertise不太一样,他这个是读取xml文件配置文件。)

/**
 * @author lx
 */
public class PropertiesDI {

    private Hashtable properties;

    /**
     * setter
     */
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "PropertiesDI{" +
                "properties=" + properties +
                '}';
    }
}

xml文件

<!--properties-->
<bean class="com.spring.core.PropertiesDI" id="propertiesDI">
    <property name="properties">
        <!--直接写配置即可,自动转换为Properties-->
        <value>
            ! 注释
            # 注释
            # “#”“!”开头的一行被算作注释不会解析。
            # key和value可以使用 “=”、“:”、“ ”等符号分割,详见properties说明


            key=value
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
            ccc:ddd
            aaa bbb
            eee    fff
        </value>
    </property>
</bean>

引用其他Bean

ref引用
在< constructor-arg >、< property >、< entry >标签中有一个ref属性,用于将bean的指定属性的值设置为对容器管理的另一个bean的引用。

被引用的bean是要设置其属性的bean的依赖项,在设置该属性之前,需要对其进行初始化。ref属性的值需要与引用的目标bean的id或者name属性中的一个值相同。

<!--ref-->
<!--定义一个Bean-->
<bean name="helloSpring3" class="com.spring.core.HelloSpring"/>

<bean class="com.spring.core.RefDI" id="refDI">
    <!--使用ref属性引用helloSpring3的bean-->
    <constructor-arg name="helloSpring1" ref="helloSpring3"/>

    <!--使用ref标签引用helloSpring3的bean-->
    <constructor-arg name="helloSpring2">
        <ref bean="helloSpring3"/>
    </constructor-arg>
</bean>

parent继承
< bean >,< ref >等标签中还有一个parent属性,这个属性用于指定bean将使用父bean的属性和配置,除了Autowire,scope和lazy init属性,parent属性用于相同属性以及值的复用。

对于parent继承,会有以下几种情况:
(1)如果父bean有class属性,而子bean没有class属性,那么子bean就是和父bean同一个class类型,相当于创建两个相同的对象。

(2)如果父bean有class属性,而子bean也有class属性,那么允许它们是不同的类型,但是子bean必须含有父bean中定义的所有的注入方式。

(3)如果父bean没有class属性,那么子bean必须定义class属性

(4)这里的父bean和子bean 以及“继承”,并不是Java中的继承关系,仅仅是复用了注入方式,精简了代码。(他这个parent不是什么继承关系,就是复用代码。)

(5)如果子bean和父bean中注入对相同依赖同时注入的值的话,那么可能会相互覆盖对方的值。这根据依赖注入的先后顺序:父bean的构造器注入->子bean的构造器注入->父bean的setter注入->子bean的setter注入,排序在后面的对相同依赖的注入值将会覆盖之前注入的值!

idfer引用校验值

< idfer >标签是<.construtor-arg >,< property >以及某些集合的子标签。
< idfer >标签用于将一个bean的id或者name的字符串值(并不是引用),传递给< construct-arg >,< property >这些集合。
这个标签用的比较少。

内部Bean

< bean >标签内部可以使用< construct-arg >,< property >以及某些集合标签,表示依赖注入。
在< construct-arg >,< property >这些集合也会可以使用<.bean >子标签,表示一个内部bean。(原因很简单,如果我们注入的是一个对象,并且我们不想要通过ref引用其他已存在的bean,那么只有定义自己的内部的bean。)

和“外部”bean区别,内部bean不需要定义id或者name属性,因为这个对象一个外部bean对象。
就算指定了,容器也不会使用这些值作为bean的名字,我们也不能通过IoC容器获取。
容器在创建时也会忽略内部bean的scope作用域属性(后面会讲),因为内部 bean 始终是匿名的,并且始终使用外 bean 创建。无法独立访问内部bean,也无法将它们注入其他外部bean中。

/**
 * 内部bean
 *
 * @author lx
 */
public class InnerBean {
    private InnerBeanInner innerBeanInner1;
    private InnerBeanInner innerBeanInner2;

    public void setInnerBeanInner1(InnerBeanInner innerBeanInner1) {
        this.innerBeanInner1 = innerBeanInner1;
    }

    public void setInnerBeanInner2(InnerBeanInner innerBeanInner2) {
        this.innerBeanInner2 = innerBeanInner2;
    }


    @Override
    public String toString() {
        return "InnerBean{" +
                "innerBeanInner1=" + innerBeanInner1 +
                ", innerBeanInner2=" + innerBeanInner2 +
                '}';
    }

    public static class InnerBeanInner {
        private String property1;
        private int property2;


        public void setProperty1(String property1) {
            this.property1 = property1;
        }

        public void setProperty2(int property2) {
            this.property2 = property2;
        }

        @Override
        public String toString() {
            return "InnerBeanInner{" +
                    "property1='" + property1 + '\'' +
                    ", property2=" + property2 +
                    '}';
        }
    }
}

<!--内部bean-->
<bean id="innerBean" class="com.spring.core.InnerBean">
    <property name="innerBeanInner1">
        <!--内部bean 不需要指定id或者name-->
        <bean class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="aaa"/>
            <property name="property2" value="111"/>
        </bean>
    </property>
    <property name="innerBeanInner2">
        <!--内部bean 指定id或者name也没用,不能通过容器获取到-->
        <bean id="innerBeanInner" class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="bbb"/>
            <property name="property2" value="222"/>
        </bean>
    </property>
</bean>

集中注入

注入方式

Spring提供了详细的集合注入方式,< list >,< set >,<.map >,< array >标签分别注入对应的Java的list,set,map,array的接口。

因为集合的元素既可以是基本类型也可以是对象甚至集合,因此集合注入非常灵活。另外集合注入支持泛型转。

/**
 * 集合注入
 *
 * @author lx
 */
public class CollectionDI {

    //集合属性注入

    private List list;
    private Set set;
    private Map map;
    private Properties properties;
    private Object[] array;

    public CollectionDI(List list, Set set, Map map, Properties properties, Object[] array) {
        this.list = list;
        this.set = set;
        this.map = map;
        this.properties = properties;
        this.array = array;
    }

    static class CollectionInner {
        private String property1;
        private int property2;


        public void setProperty1(String property1) {
            this.property1 = property1;
        }

        public void setProperty2(int property2) {
            this.property2 = property2;
        }

        @Override
        public String toString() {
            return "CollectionInner{" +
                    "property1='" + property1 + '\'' +
                    ", property2=" + property2 +
                    '}';
        }
    }

    @Override
    public String toString() {
        return "CollectionDI{" +
                "\n" + "list=" + list +
                "\n" + ", set=" + set +
                "\n" + ", map=" + map +
                "\n" + ", properties=" + properties +
                "\n" + ", array=" + Arrays.toString(array) +
                '}';
    }
}

<!--Collection注入-->
<bean id="collectionInner" class="com.spring.core.CollectionDI.CollectionInner">
    <property name="property1" value="refs"/>
    <property name="property2" value="111"/>
</bean>

<bean id="collectionDI" class="com.spring.core.CollectionDI">
    <!--list只有一个元素时,可以使用value属性赋值或者ref引用就行了-->
    <constructor-arg name="list">
        <!--list标签表示list集合,用于定义多个元素-->
        <list>
            <!--value标签用于定义字面量的值作为集合元素-->
            <value>111</value>
            <!--也可以使用Bean标签定义一个bean(对象)作为集合元素-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="list"/>
                <property name="property2" value="1"/>
            </bean>
            <!--也可以引用外部bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--当然集合元素也可以定义集合-->

        </list>
    </constructor-arg>
    <!--set只有一个元素时,可以使用value属性赋值或者ref引用就行了-->
    <constructor-arg name="set">
        <!--set标签表示set集合,用于定义多个元素-->
        <set>
            <!--value标签用于定义字面量的值作为集合元素-->
            <value>111</value>
            <!--也可以使用Bean标签定义一个bean(对象)作为集合元素-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="set"/>
                <property name="property2" value="2"/>
            </bean>
            <!--也可以引用外部bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--也可以使用idref,仅作为字符串-->
            <idref bean="collectionInner"/>
            <!--当然集合元素也可以定义集合-->
        </set>
    </constructor-arg>
    <constructor-arg name="map">
        <!--map标签表示map集合-->
        <map>
            <!--map标签中首先需要定义entry标签,表示一个键值对-->
            <entry key="key" value="value"/>
            <!--key和value都可以使用标签-->
            <entry key-ref="collectionInner" value-ref="collectionInner"/>
            <!--key和value都可以引用外部bean-->
            <entry>
                <!--key可以使用内部bean,或者集合等等-->
                <key>
                    <bean class="com.spring.core.CollectionDI.CollectionInner">
                        <property name="property1" value="mapkey"/>
                        <property name="property2" value="3"/>
                    </bean>
                </key>
                <!--注意value标签只能是注入字面量值,如果想要对象类型的value,那么直接使用Bean标签就行了-->
                <bean class="com.spring.core.CollectionDI.CollectionInner">
                    <property name="property1" value="mapvalue"/>
                    <property name="property2" value="3"/>
                </bean>

                <!--value也可以是集合等等类型,但是只能有一个大标签-->
                <!--<map>-->
                <!--    <entry key="innermap" value="innermap"/>-->
                <!--    <entry key="inner2map" value="inner2map"/>-->
                <!--</map>-->
                <!--也可以使用idref,仅作为字符串-->
                <!--<idref bean="collectionInner"/>-->
            </entry>
        </map>
    </constructor-arg>
    <constructor-arg name="properties">
        <!--props标签表示properties集合-->
        <props>
            <!--props标签中首先需要定义prop标签,表示一个String键值对-->
            <prop key="111">111</prop>
            <prop key="111">222</prop>
            <prop key="222">222</prop>
            <prop key="null">null</prop>
        </props>

        <!--实际上也可以放map,但是要求String类型的key和value-->
        <!--<map>-->
        <!--    <entry key="key" value="value"/>-->
        <!--</map>-->
    </constructor-arg>
    <!--数组只有一个元素时,可以使用value属性赋值或者ref引用就行了-->
    <constructor-arg name="array">
        <!--array标签表示array数组,用于定义多个元素-->
        <array>
            <!--value标签用于定义字面量的值作为集合元素-->
            <value>111</value>
            <!--也可以使用Bean标签定义一个bean(对象)作为集合元素-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="array"/>
                <property name="property2" value="4"/>
            </bean>
            <!--也可以引用外部bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--也可以使用idref,仅作为字符串-->
            <idref bean="collectionInner"/>
            <!--当然集合元素也可以定义为集合-->
        </array>
    </constructor-arg>
</bean>

集合的继承与合并

Spring容器还支持集合的继承和合并。我们可以定义父< list >、< map >、< set >、< props >、< array >集合bean,并且支持子< list >、< map >、< set >、< props >、< array >集合bean从父集合bean继承值,当然子集合bean也可以重写值。

null注入

Spring将默认字符串为空的视为空字符串,就算value的字面量设置为null,但实际也是变成一个“null”字符串而不是null,真的想设置null,可以使用< null >标签,这个标签能设置null值。

p-namespace

用的不多,就是允许你使< bean >元素的属性而不是嵌套的< property >子标签来描述依赖,实际上是简化setter方式注入的配置。

c-namespace

和p-namespace一样用的不多,Spring 3.1继续引入了c-namespace快捷注入,允许你使< bean >元素的属性而不是嵌套的< constructor-arg >子标签来描述依赖,实际上是简化构造器方式注入的配置。

name复合属性注入
用的不多

depends-on

这个可以和刘Java我认为有不同的地方,这个注解挺有用处的,尤其是使他对应的注解dependOn。
适用场景就是Bean之间有依赖关系,A依赖B先实例化。

dependOn属性可以显示强制在初始化,可以在使用了这个元素的Bean在这个Bean之前先初始化一个或者多个Bean。depends-on的值是bean的id或者name,用到多个bean时可以用逗号分号来作为分隔符(“,” “;”)按照这个顺序来初始化bean。

depends-on可以指定初始化bean的顺序,也可以指定依赖bean的销毁的顺序,最先初始化的bean是最后一个被销毁的。
无论是指定初始化顺序还是销毁顺序,都要求设置该属性的bean的作用范围是singleton,否则depends-on属性无效。(必须是要单例,depends-on属性才起作用)

<!--depends-on  如果不是scope=singleton(默认),那么depends-on属性无效-->
<bean class="com.spring.core.DependsOnDI.DependsOnDIA" depends-on="dependsOnDIC dependsOnDIB" id="dependsOnDIA" destroy-method="destroy" init-method="init"/>
<bean class="com.spring.core.DependsOnDI.DependsOnDIB" id="dependsOnDIB" destroy-method="destroy" init-method="init"/>
<bean class="com.spring.core.DependsOnDI.DependsOnDIC" id="dependsOnDIC" destroy-method="destroy" init-method="init"/>

lazy-init延迟初始化

ApplicationContext在默认情况下,初始化创建所有定义好的singleton的bean以及他们的依赖项。(这样设置,可在初始化就能发现创建bean的问题,而不是getBean方法才出问题。)

< bean >的lazy-init属性可以设置bean为延迟初始化,使用了这个属性的话,bean不会一开始被容器初始化,而是在使用时用再初始化。

这时候会有一个问题,如果一个bean A是延迟初始化的,另一个bean B是非延迟初始化的,B依赖A,那容器初始化B怎么办?实际上容器初始化B时会把这个延迟加载的Bean A也给初始化。
这个lazy-init属性默认是false,但有一个问题就是这个你要把这个scope属性设置“singleton”,才可以生效。如果你设置的scope是prototype,哪怕你设置的lazy-init是false也会延迟初始化,因为prototype是getBean就要生成一个实例。
(scope作用域,singleton是全局,容器启动生成唯一单例,容器会管理实例的销毁,容器销毁就会销毁。
prototype原型模式,对比singleton单例模式,他是每一次getBean将会创建一个实例,但是容器不会管理销毁这些实例。)

< beans >也可以使用这个lazy-init属性

XML依赖自动注入

这个“自动注入”和我们之前的DI“依赖注入”的那种自动,不一样不是通过依赖的方式来自动依赖注入。

“自动注入”,这个你可以理解为,你配置一种特殊的配置文件的“省略写法”,这个文件上的bean会被主动的去自动注入。容器根据这个文件自动去注入,这里和之前的配置文件相比,内容很精简,里面的关系是让容器自己去解析,不需要我们指定特别多属性,也就是不让我们手动写配置文件。

自动注入的优点如下:
(1)自动注入,可以显著减少手动指定属性或构造参数的需要
(2)自动注入,可以自动手动更新对象的配置(如果你想添加一个新的依赖项,无需修改文件就自动装配该依赖项。)

使用基于XML的配置元数据时,可以通过设置bean标签的autwire属性自动设置自动注入方式。

(1)no: 默认值,不启动自动装配,bean必须引用ref定义元素,配置多一些,这样就可以让系统结构和bean之间关系更清晰。

(2)byName:通过属性名字的方式查找bean依赖的对象并为其注入,使用这个注入方式,IoC容器会在配置文件中找id/name值包含对应的name的bean。setter方式为注入方式,这个自动setter和之前说的手动setter不一样(静态工厂方法,工厂方法,或者XML直接set方法),这里有限制就是,这个自动装配的bean必须包含这个名字(毕竟是byName)。

(3)byType:通过属性的类型来查找bean来依赖注入,设置byType依赖注入IoC容器来查找对应的class属性来注入这个bean,也是setter方法注入的方式。

(4)constructor:同byType一样,通过类型来查找依赖对象,但是和byType不一样的是,就是constructor是通过构造器来自动注入,而不是byType的setter方法注入。但是有多个同类型的bean,constructor会报错。

XML自动注入的方式

自动注入节省配置文件的编写方式
对于byName注入,没有该name的依赖就不会注入
对于byType注入,容器有多个同类型bean的,找不到合适的依赖,也会报错
对于constructor注入,容器有多个同类型的bean,找不到合适的依赖,会报错。
使用自动注入之后,不容易明确之间的依赖关系。
实际他们的依赖规则非常复杂,同样是多个同类型的bean,现在constructor会根据参数名进一步匹配,setter注入时不会根据参数名匹配的。

对于这些问题,解决的方式有:
(1)最常见的是放弃自动注入,多配置一些代码,保证不会出问题。以下是手动注入和自动注入做一个对比:

xml 手动注入
<bean id="userService" class="com.example.UserService"> <property name="orderService" ref="orderService"/> </bean>
xml自动注入autowire
xml <bean id="userService" class="com.example.UserService" autowire="byType"/>

(2)< bean >的autowire-candicate属性设置为false,该bean自动注入候选bean中排除。
(3)< bean >primary属性设置为true,该bean主动设置候选bean。
(4)使用注解方式注入

这个就是XML的autowire的自动注入,节省了属性,但是还是有问题会,中间的依赖关系不清晰,同样这个对应的autowire注解方式也要考虑这些。

选择自动注入候选bean
bean标签有一个default-autowire-candidate(默认的自动注入候选)属性,用于统一筛选当前容器的bean主动注入候选bean,default-autowire-candidate的值可以模式匹配。

将bean标签的属性autowire-candidate设置为false(默认为true)也会将这个bean排除候选bean。
将< bean >标签的primary属性设置为true(默认为fasle),将该bean指定为主要候选对象,一般指定一个就行了,可以指定多个。

方法注入

IoC容器中的bean之间依赖关系通过属性确定,这样可能由于bean的声明周期不同而造成某些问题,比如某个bean a是单例只会在容器启动的时候创建一次,bean a 依赖bean b,但是这时候会要求这个bean不能是单例,希望每访问一次bean需要注入一个新的bean的实例。

查找方法注入
是指IoC容器重新编写配置文件的指定方法,注入方法的返回结果将返回容器另一个命名的bean。

原理也简单,解析<.bean >标签,具有< lookup-method >标签的利用cglib的动态代理技术,生成该类的动态子类。该代理类将会代理< lookup-method >的name属性的指定方法,最终返回bean属性指定名称的bean对象,这个返回是向容器要对象。那么如果这个bean对象是prototype类型,必然每一次生成一个新的对象并返回,如果这个bean对象是singleton类型,必然每一次返回同一个对象。

<bean class="com.spring.core.LookupMethodIn.LookupMethodInA" 
id="lookupMethodInA">
    <!--普通setter注入-->
    <property name="lookupMethodInB" ref="lookupMethodInB"/>
    <!--查找方法注入 name表示要被动态替换的方法名,bean表示容器中的一个bean的名字,没有过i嗯都会从容器返回该bean的实例-->
    <lookup-method name="createLookupMethodInC" bean="lookupMethodInC"/>
</bean>
<!--需要被注入的bean-->
<bean class="com.spring.core.LookupMethodIn.LookupMethodInB" id="lookupMethodInB" scope="prototype"/>
<bean class="com.spring.core.LookupMethodIn.LookupMethodInC" id="lookupMethodInC" scope="prototype"/>

这个要注意一点是,用lookup-method因为有覆盖方法的要求实现依赖注入逻辑(或者注解@lookup)通过cglib来完成反射,生成代理类。
除此之外Spring是通过反射来完成(反射构造器来创建实例,但是不会像cglib生成代理类。)

任意方法替换
任意方法替换(Arbitrary Method Replacement):可以实现方法主体和返回结果的替换,相当于运行时使用一个方法替换另一个方法。

scope作用域
作用域分类

<.bean >标签scope属性用于定义该bean的作用域,这是一个非常强大的属性。

所谓作用域,实际应该是“用来声明容器中的对象应该处于限定的场景,或者说对象的特定存活时间,即对象在对象进入其相应的scope之前生成并装配这些对象,这些对象不再处于这些scope的限定之后,容器经常会销毁这些对象。”

Spring 5.x 支持6个scope作用域(这6个作用域,后面的4个是指web开发的ApplicationContext中可用。)

1 singleton
(1)默认值,单例。
单个bean的定义绑定到单个bean的实例,每个实例仅保存单个对象实例,随后的请求和对应单个名为bean的请求都返回缓存的对象。
(2)应用加载时,创建容器时,对象就被创建,只要容器在,对象一直活着。并且这个对象不会再被创建,直到销毁容器时,对象被销毁了。

2 prototype
(1)原型,单个bean的定义绑定到多个bean的实例(对比前面的singleton),具有prototype作用域的bean,被注入到一个要初始化的bean中。或者getBean获取这个bean的时候,都会获得一个新的实例。

通常来说,这个有状态的bean时用的是prototype作用域的bean,使用的singletion的作用域的bean是无状态的。
(有状态和无状态的bean,对应指的是对象的可变数据,无状态的对应的是bean是线程安全的,有状态线程不完全)
(2)Spring不负责prototype作用域的bean完整生命周期,容器实例化,配置,以及其他对象生成的对象交给客户端,就不再记录这个实例。

3 request
单个bean定义绑定到单个http请求生命周期,每一次http请求会产生一个新的bean,这个bean在当前http请求是有效的。

4 session
单个bean定义绑定到这个session的生命周期,每个独立的session产生一个新的bean,比上一个request的存活时间长一点。

5 application
单个bean定义绑定到ServletContext生命周期(一个web应用可以有多个IoC容器)

6 websocket
单个bean绑定到websocket周期

实际后面4个根web应用的作用域,实际在项目中都没有人怎么用,主要还是用singleton。

Singletion依赖prototype

当一个prototype的bean是一个singleton bean的依赖时,ApplicationContext在创建singleton是会立即创建一个bean,如果希望singleton bean在运行时重复获取prototype bean的新实例。则不能将prototype bean通过传统方式(构造器或者setter)注入到singleton bean中,因为当Spring容器实例化singleton bean并解析和注入其依赖项时,该注入只发生一次。如果在运行时需要多个原型bean的新实例,应该使用前面讲的方法注入。

bean的回调扩展

Spring框架提供了很多bean相关的回调接口,可以使用很多功能:

(1)bean的生命周期回调
生命周期接口是与容器对bean对生命周期的管理进行的交互接口,这样的接口两个:初始化之后和销毁之前。

对于自己创建的bean,即容器不能管理的bean的生命周期接口无效,比如prototype类型的bean,他创建之后他的生命周期不归容器来管理,所以只能调用初始化调回接口。(但是销毁调回接口就用不上了)

与bean的生命周期Spring管理,最原始的方式时Spring的InitializingBean和DisposiableBean接口。前者对应的是afterPropertiseSet方法,后者destory方法,来做到这个bean的初始化和销毁的一些操作。

JSR-250标准注解接口,对于Spring来说对应标准注解的@PostConstruct和@PreDestory,来作为bean的创建初始化回调和销毁回调,这个是最佳实践。

统一默认回调:

bean的回调总结:
new 容器的时候,refresh方法销毁已经存在的bean,生成新的bean
close方法,是销毁已存在bean。

Spring 2.5开始三种方式周期回调:
1 initalizingBean接口和DisposeableBean接口
2 自定义的init()和destory()方法,使用XML配置的init-method和destory- method属性,自定义init和destroy方法。
3 使用标准的注解,@PostConstruct 和 @PreDestroy注解来完成整个Bean周期的回调

如果同时配置了这么多生命周期回调的方法,那他的执行顺序是怎么样的?
(1)第一个是标准注解的@PostConstruct和@PreDestroy
(2)Spring自己的生命周期的接口
(3)XML定义的自定义的生命周期方法

容器状态回调

ApplicationContext容器有自己的状态,Spring提供一些接口,可以注册监听不同的状态变化,并调用不同的回调方法。

Lifecycle回调,一般用于一些后台组件的活动的开启和关闭,ApplicationCOntCOntext收到一个start或者stop信号,将这些信号传递给Lifecycle实现的接口。

非web容器应用的优雅关闭的容器

上面很多都是ApplicaionContext容器,也就是web应用的比较多,如果不是web应用怎么办?怎么去做生命周期管理?

web应用容器的关闭会随着JVM的结束而结束,不是web容器怎么优雅关闭。可以使用钩子方法,在JVM里面注册一个shutdownHook到JVM中,JVM关闭也就会安全的把容器关闭掉,让资源释放。

@Test
public void shutdownHook() {
    System.out.println("new容器的代码内部实际上会调用一次refresh操作,因此会自动启动");
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    //添加回调方法,在JVM关闭时会调用容器close方法的逻辑,如果不加那么容器并不会正常关闭
    //ac.registerShutdownHook();
    System.out.println("start调用");
    ac.start();
}

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.refresh();
    context.registerShutdownHook(); // 手动注册
}

(别说我项目中还真见过,就是redission的,如果连接不上就会调用shutdownHook方法来关闭容器)

总结
这个XML是比较老的工程,所以用的少,但是有很多东西可以通用,为后面的部分打基础。


网站公告

今日签到

点亮在社区的每一天
去签到