Spring IoC XML based DI detailed configuration solution [20000 words]

Keywords: Java Spring xml ioc

This paper introduces in detail the complete solution of DI configuration based on XML for Spring IoC.

Previous articles The concept of Spring IOC container and IoC assembly based on XML In, we learned two methods of dependency injection. Now let's take a look at some more detailed configurations. This article is based on spring 5.2.8.

1 value literal

For the properties of basic type, String and wrapper type, we can directly use the String value of value property to describe the specific value, which is more readable. At the time of final injection, Spring's transformation service will convert these values from String to the actual type of property or parameter.

Spring supports the use of < value > tags to represent specific literal values:

<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 method name Represents the property name value Represents the attribute value-->
    <property name="property3">
        <value>123</value>
    </property>
    <property name="property4">
        <value>false</value>
    </property>
</bean>

1.1 Properties quick conversion

The Spring container supports directly parsing string literal values in a specific format in value through the property editor and converting them into a Properties collection. Later, we will also learn the injection method of collection, but this is a very easy shortcut!

Let's test it. First, there is a PropertiesDI class with a Properties (Hashtable is the parent class of Properties) attribute inside:

/**
 * @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 +
                '}';
    }
}

The configuration is as follows:

<!--properties-->
<bean class="com.spring.core.PropertiesDI" id="propertiesDI">
    <property name="properties">
        <!--Write configuration directly,Automatic conversion to Properties-->
        <value>
            ! notes
            # notes
            # "#”The line beginning with "!" is counted as a comment and will not be parsed.
            # key and value can be separated by "=", ":" and "" symbols. See the properties description for details


            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>

Test:

@Test
public void properties() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("propertiesDI", PropertiesDI.class));
}

The results are as follows:

[propertiesDI]
PropertiesDI{properties={jdbc.url=jdbc:mysql://localhost:3306/mydb, jdbc.driver.className=com.mysql.jdbc.Driver, eee=fff, key=value, aaa=bbb, ccc=ddd}}

2 reference other bean s

2.1 ref reference

There is a ref attribute in the < constructor Arg >, < property >, < entry > tags, which is used to set the value of the specified attribute of the bean as a reference to another bean managed by the container. This is how reference type property dependencies are set. The referenced bean is a dependency of the bean whose property is to be set. It needs to be initialized before setting the property. The value of the ref attribute needs to be the same as one of the id or name attributes of the referenced target bean.

Of course, there is also a < ref > tag, which can be used as sub tags of < constructor Arg >, < property > and some collection tags. The bean attribute of the < ref > tag can also be used to specify the referenced target bean< The ref > tag allows you to create a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file or not. The value of the bean attribute needs to be the same as one of the id or name attributes of the referenced target bean.

In the following example, there is a RefDI class for ref test:

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

    private HelloSpring helloSpring1;
    private HelloSpring helloSpring2;

    public RefDI(HelloSpring helloSpring1, HelloSpring helloSpring2) {
        this.helloSpring1 = helloSpring1;
        this.helloSpring2 = helloSpring2;
    }

    @Override
    public String toString() {
        return "RefDI{" +
                "helloSpring1=" + helloSpring1 +
                ", helloSpring2=" + helloSpring2 +
                '}';
    }
}

Profile:

<!--ref-->
<!--Define a Bean-->
<bean name="helloSpring3" class="com.spring.core.HelloSpring"/>

<bean class="com.spring.core.RefDI" id="refDI">
    <!--use ref Attribute reference helloSpring3 of bean-->
    <constructor-arg name="helloSpring1" ref="helloSpring3"/>

    <!--use ref Label reference helloSpring3 of bean-->
    <constructor-arg name="helloSpring2">
        <ref bean="helloSpring3"/>
    </constructor-arg>
</bean>

Test:

@Test
public void ref() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("refDI", RefDI.class));
}

Successfully injected other bean s:

initialization
[helloSpring3, refDI]
RefDI{helloSpring1=HelloSpring{hello='hello'}, helloSpring2=HelloSpring{hello='hello'}}

2.2 parent inheritance

There is also a parent attribute in the < bean >, < ref >, etc. this attribute is used to specify that the target bean will use the properties and configuration of the parent bean. In addition to the autowire, scope and lazy init attributes, the parent attribute is used for the reuse of the same attributes and values. The value of the parent attribute is the same as one of the id attribute or name attribute of the target parent bean.

There are several cases of parent inheritance:

  1. If the parent bean has a class attribute and the child bean has no class attribute, the child bean is the same class type as the parent bean, which is equivalent to creating two identical objects.
  2. If the parent bean has a class attribute and the child bean also has a class attribute, they are allowed to be of different types, but the child bean must contain all the injection methods defined in the parent bean.
  3. If the parent bean does not have a class attribute, the child bean must define a class attribute. The parent bean is actually similar to a template of attributes and values. It is only referenced by the value bean to realize configuration reuse and cannot be instantiated. (at this time, the parent bean must add abstract="true" Property, indicating that the parent bean will not be created, which is similar to an abstract class. Otherwise, the startup container will try the parent bean, but throw an exception because the parent bean has no class: No bean class specified on bean definition). In this case, the child bean must also contain all injection methods defined in the parent bean.
  4. The parent bean, child bean and "inheritance" here are not the inheritance relationship in Java. They just reuse the injection method and simplify the code!
  5. If the child bean and the parent bean inject values for the same dependency at the same time, they may overwrite each other's values. This depends on the order of dependency injection: constructor injection of parent bean - > constructor injection of child bean - > setter injection of parent bean - > setter injection of child bean. The injection values of the same dependency in the later order will overwrite the previously injected values!

In the following cases, there are three classes:

/**
 * @author lx
 */
public class ParentOne {
    private String property1;

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

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

An interesting thing is that although ParentTwo inherits ParentOne, it does not inherit the private property property1. However, it still works normally because it inherits the setProperty1 method. This is the meaning of "the child bean must contain all injection methods defined in the parent bean". For setter method injection, it doesn't matter if you don't have this property, As long as there is a method with the same name, the parameter type can be compatible (from String to parameter type), there will be no error, which has nothing to do with the return value (see ParentThree)!

3 idref reference check value

The < idref > tag can usually be used as a sub tag of < constructor Arg >, < property > and some collection tags to pass the string value (not a reference) of the id or name of another bean in the container to < constructor Arg >, < property > and some collection tags. At the same time, the idref container will also verify whether the bean with this name really exists during deployment (defined), this is a way to prevent errors. The label is currently used less.

In the following case, there is an IdrefCheck class to verify whether the bean is defined:

public class IdrefCheck {

    private String targetName;

    public void setTargetName(String targetName) {
        this.targetName = targetName;
    }

    @Override
    public String toString() {
        return "IdrefDI{" +
                "targetName='" + targetName + '\'' +
                '}';
    }
}

Profile:

<!--idref-->
<!--Define a Bean-->
<bean name="helloSpring3" class="com.spring.core.HelloSpring" />

<!--idrefCheck check bean-->
<bean class="com.spring.core.IdrefCheck" name="idrefCheck">
    <!--It's actually equal to<property name="targetName" value="helloSpring3">-->
    <!--But more bean Verification function-->
    <property name="targetName">
        <idref bean="helloSpring3"/>
    </property>
</bean>

In fact, the value referenced by the bean attribute of < Idref > is equal to a value of String type, all of which are strings, but < Idref > has an additional function to verify the existence of the bean with the corresponding name!

In the idea, if the bean name specified by the bean property of idref does not exist in the container, it will be directly red. If it runs, it will throw: invalid bean name 'hellospprin3' in bean reference for bean property 'targetname'.

@Test
public void idref() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("idrefDI", IdrefCheck.class));
}

The results are as follows:

org.springframework.beans.factory.BeanCreationException: Error creating 
bean with name 'idrefDI' defined in class path resource [DI.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean name 'helloSpring3' in bean reference for bean property 'targetName'

A long time ago (before spring 2.0), a common function of the < Idref > tag was to use it in the AOP interceptor defined in ProxyFactoryBean. Using the < Idref > element when specifying the interceptor name can prevent you from misspelling the interceptor ID. however, it is rarely used at present!

4 internal bean

The < bean > tag can use < constructor Arg >, < property > and some collection tags to represent dependency injection. Similarly, the < bean > sub tag can also be used in < constructor Arg >, < property > and some collection tags to represent an internal bean. The reason is very simple. If we inject an object and we don't want to reference other existing beans through ref, we have to define our own internal beans.

The difference from "external" beans is that internal beans do not need to define id or name attributes, because this object is equivalent to an external bean's own object. Even if specified, the container will not use these values as the bean name, and we cannot get them through the IoC container. The container will also ignore the scope scope attribute of the internal bean when it is created (which will be described later), because the internal bean is always anonymous and always created using the external bean. Internal beans cannot be accessed independently or injected into other external beans.

In the following case, there is an InnerBean class for internal bean testing:

/**
 * inner 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 +
                    '}';
        }
    }
}

Profile:

<!--inside bean-->
<bean id="innerBean" class="com.spring.core.InnerBean">
    <property name="innerBeanInner1">
        <!--inside bean No need to specify id perhaps name-->
        <bean class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="aaa"/>
            <property name="property2" value="111"/>
        </bean>
    </property>
    <property name="innerBeanInner2">
        <!--inside bean appoint id perhaps name It's no use,Cannot get from container-->
        <bean id="innerBeanInner" class="com.spring.core.InnerBean.InnerBeanInner">
            <property name="property1" value="bbb"/>
            <property name="property2" value="222"/>
        </bean>
    </property>
</bean>

Test:

@Test
public void innerBean() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    InnerBean innerBean = ac.getBean("innerBean", InnerBean.class);
    System.out.println(innerBean);
}

result:

[innerBean]
InnerBean{innerBeanInner1=InnerBeanInner{property1='aaa', property2=111}, innerBeanInner2=InnerBeanInner{property1='bbb', property2=222}}

5 set injection

5.1 injection mode

Spring provides a detailed collection injection method< List >, < Set >, < Map >, < props >, < array > tags are used to inject lists, sets, maps, Properties and arrays in Java, mainly for the injection of dependencies of collection types. Because the elements of a collection can be either basic types, objects or even collections, collection injection is very flexible. In addition, collection injection supports generic conversion. During injection, the string value of value will be automatically converted to the corresponding generic type!

In the following example, there is a CollectionDI class for collection injection test:

/**
 * Set injection
 *
 * @author lx
 */
public class CollectionDI {

    //Collection attribute injection

    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) +
                '}';
    }
}

Profile:

<!--Collection injection-->
<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 When there is only one element, you can use value Attribute assignment or ref Just reference-->
    <constructor-arg name="list">
        <!--list Label representation list Collection that defines multiple elements-->
        <list>
            <!--value The tag is used to define the literal value as a collection element-->
            <value>111</value>
            <!--You can also use Bean Label defines a bean(object)As a collection element-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="list"/>
                <property name="property2" value="1"/>
            </bean>
            <!--You can also reference external bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--Of course, collection elements can also define collections-->

        </list>
    </constructor-arg>
    <!--set When there is only one element, you can use value Attribute assignment or ref Just reference-->
    <constructor-arg name="set">
        <!--set Label representation set Collection that defines multiple elements-->
        <set>
            <!--value The tag is used to define the literal value as a collection element-->
            <value>111</value>
            <!--You can also use Bean Label defines a bean(object)As a collection element-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="set"/>
                <property name="property2" value="2"/>
            </bean>
            <!--You can also reference external bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--You can also use idref,As string only-->
            <idref bean="collectionInner"/>
            <!--Of course, collection elements can also define collections-->
        </set>
    </constructor-arg>
    <constructor-arg name="map">
        <!--map Label representation map aggregate-->
        <map>
            <!--map The label needs to be defined first entry Label that represents a key value pair-->
            <entry key="key" value="value"/>
            <!--key and value You can use labels-->
            <entry key-ref="collectionInner" value-ref="collectionInner"/>
            <!--key and value Can reference external bean-->
            <entry>
                <!--key Internal can be used bean,Or assemble, etc-->
                <key>
                    <bean class="com.spring.core.CollectionDI.CollectionInner">
                        <property name="property1" value="mapkey"/>
                        <property name="property2" value="3"/>
                    </bean>
                </key>
                <!--be careful value Tags can only be injected literal values, if you want the object type value,Then use it directly Bean Just label it-->
                <bean class="com.spring.core.CollectionDI.CollectionInner">
                    <property name="property1" value="mapvalue"/>
                    <property name="property2" value="3"/>
                </bean>

                <!--value It can also be a collection type, etc., but there can only be one large label-->
                <!--<map>-->
                <!--    <entry key="innermap" value="innermap"/>-->
                <!--    <entry key="inner2map" value="inner2map"/>-->
                <!--</map>-->
                <!--You can also use idref,As string only-->
                <!--<idref bean="collectionInner"/>-->
            </entry>
        </map>
    </constructor-arg>
    <constructor-arg name="properties">
        <!--props Label representation properties aggregate-->
        <props>
            <!--props The label needs to be defined first prop Label that represents a String Key value pair-->
            <prop key="111">111</prop>
            <prop key="111">222</prop>
            <prop key="222">222</prop>
            <prop key="null">null</prop>
        </props>

        <!--You can actually put it map,But request String Type key and value-->
        <!--<map>-->
        <!--    <entry key="key" value="value"/>-->
        <!--</map>-->
    </constructor-arg>
    <!--When the array has only one element, you can use value Attribute assignment or ref Just reference-->
    <constructor-arg name="array">
        <!--array Label representation array Array that defines multiple elements-->
        <array>
            <!--value The tag is used to define the literal value as a collection element-->
            <value>111</value>
            <!--You can also use Bean Label defines a bean(object)As a collection element-->
            <bean class="com.spring.core.CollectionDI.CollectionInner">
                <property name="property1" value="array"/>
                <property name="property2" value="4"/>
            </bean>
            <!--You can also reference external bean-->
            <ref bean="collectionInner"/>
            <value>null</value>
            <!--You can also use idref,As string only-->
            <idref bean="collectionInner"/>
            <!--Of course, set elements can also be defined as sets-->
        </array>
    </constructor-arg>
</bean>

Test:

@Test
public void collectionDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("collectionDI", CollectionDI.class));
}

The results are as follows:

[collectionInner, collectionDI]
CollectionDI{
list=[111, CollectionInner{property1='list', property2=1}, CollectionInner{property1='refs', property2=111}, null]
, set=[111, CollectionInner{property1='set', property2=2}, CollectionInner{property1='refs', property2=111}, null]
, map={key=value, CollectionInner{property1='refs', property2=111}=CollectionInner{property1='refs', property2=111}, CollectionInner{property1='mapkey', property2=3}=CollectionInner{property1='mapvalue', property2=3}}
, properties={null=null, 222=222, 111=222}
, array=[111, CollectionInner{property1='array', property2=4}, CollectionInner{property1='refs', property2=111}, null]}

5.2 inheritance and merging of sets

The Spring container also supports inheritance and merging of collections. We can define parent < list >, < Map >, < set >, < props >, < array > collection beans, and support child < list >, < Map >, < set >, < props >, < array > collection beans to inherit values from parent collection beans. Of course, child collection beans can also override values. In fact, the collection inheritance here is the extension of the parent bean and child bean in the parent inheritance mentioned above. The collection is also a bean. The usage is the same. Both use the parent attribute, but the collection has more merging options!

The dependency value defined by the subset bean will override the dependency value of the parent collection bean by default, but the < list >, < Map >, < set >, < props >, < array > tags support the merging of collection elements. By setting the attribute merge=true in the subcollection bean tag (invalid in the parent collection bean tag), the value of the subcollection bean is ultimately the result of merging the elements of the parent collection bean and the subcollection bean. According to the nature of the collection, the map will replace the value of the same key, the set will be de duplicated, they are all out of order, and the list and array will be accumulated and ordered (the elements of the sub collection bean will be merged after the elements of the parent collection bean).

When merge is adopted, the collection elements injected by the child and parent collection beans may also overwrite each other rather than merge. Secondly, the constructor injection method will be completely replaced by the setter injection method, and the setter injection of the parent set bean will be completely replaced by the setter injection of the child set bean, because setter injection is equivalent to resetting a new set. Therefore, the collection consolidation mentioned here is only for two child and parent set beans. It is the constructor injection method!

Note: when merge is used, even if the parent collection bean injects only one value into the same dependency, value or ref quick injection cannot be used!

In the following case, there is a CollectionDI class for collection inheritance merge test:

/**
 * Collection inheritance merge
 *
 * @author lx
 */
public class CollectionMerging {

    //Collection inheritance merge

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

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

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

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

Profile:

<!--    Collection inheritance merge-->
<bean name="collectionMergingParent" abstract="true">
    <constructor-arg name="list">
        <list>
            <value>1111</value>
        </list>
    </constructor-arg>
    <constructor-arg name="set">
        <set>
            <value>2222</value>
            <value>3333</value>
        </set>
    </constructor-arg>
    <constructor-arg name="properties">
        <props>
            <prop key="1">1</prop>
            <prop key="2">2</prop>
            <prop key="3">3</prop>
            <prop key="5">5</prop>
        </props>
    </constructor-arg>
    <property name="properties">
        <props>
            <prop key="1">1</prop>
            <prop key="2">10</prop>
            <prop key="3">3</prop>
        </props>
    </property>
    <constructor-arg name="map">
        <map>
            <entry key="1" value="1"/>
            <entry key="2" value="1"/>
            <entry key="3" value="1"/>
        </map>
    </constructor-arg>
    <!--Same dependency usage merge Cannot be used when merging value perhaps ref Simple injection-->
    <!--<constructor-arg name="array" value="111"/>-->
    <constructor-arg name="array">
        <array merge="true">
            <value>111</value>
            <value>121</value>
        </array>
    </constructor-arg>
</bean>


<bean id="collectionMergingChild" class="com.spring.core.CollectionMerging" parent="collectionMergingParent">
    <!--list No weight loss-->
    <constructor-arg name="list">
        <list merge="true">
            <value>1111</value>
            <value>2222</value>
        </list>
    </constructor-arg>
    <!--set Will go heavy-->
    <constructor-arg name="set">
        <set merge="true">
            <value>1111</value>
            <value>2222</value>
        </set>
    </constructor-arg>
    <!--KV The collection is replaced value,Because for properties Dependency ultimately has a parent bean of setter Injection method,
    So the ultimate parent bean of setter Injection overwrites all previous injections  -->
    <constructor-arg name="properties">
        <props merge="true">
            <prop key="1">0</prop>
            <prop key="2">2</prop>
            <prop key="3">4</prop>
            <prop key="4">4</prop>
        </props>
    </constructor-arg>
    <!--<property name="properties">-->
    <!--    <props merge="true">-->
    <!--        <prop key="1">0</prop>-->
    <!--        <prop key="2">2</prop>-->
    <!--        <prop key="3">4</prop>-->
    <!--        <prop key="4">4</prop>-->
    <!--    </props>-->
    <!--</property>-->
    <constructor-arg name="array">
        <array merge="true">
            <value>111</value>
            <value>111</value>
        </array>
    </constructor-arg>
</bean>

Test:

@Test
public void collectionMerging() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("collectionMergingChild", CollectionMerging.class));
}

The results are as follows:

[collectionMergingParent, collectionMergingChild]
CollectionMerging{list=[1111, 1111, 2222], set=[2222, 3333, 1111], properties={3=3, 2=10, 1=1}, map={1=1, 2=1, 3=1}, array=[111, 121, 111, 111]}

6 null injection

Spring treats the value of an attribute whose default value is null as an empty string. In addition, even if the literal value of value is set to null, it is regarded as a "null" string. If you really want to set to null instead of an empty string value, you should use the < null / > tag, which represents the real injection of null value.

In the following case, there is a NullDI class for null injection test:

/**
 * null injection
 *
 * @author lx
 */
public class NullDI {

    //null injection

    public String property1;
    public String property2;
    public NullDI nullDI;

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

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

    public void setNullDI(NullDI nullDI) {
        this.nullDI = nullDI;
    }

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

Profile:

<bean class="com.spring.core.NullDI" id="nullDI">
    <!--Empty value Property is treated as an empty string-->
    <property name="property1" value=""/>
    <!--only null The tag represents the real injection null-->
    <property name="property2">
        <null/>
    </property>
    <property name="nullDI">
        <null/>
    </property>
</bean>

Test:

@Test
public void nullDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    NullDI nullDI = ac.getBean("nullDI", NullDI.class);
    System.out.println(nullDI.property1 == null);
    System.out.println(nullDI.property2 == null);
    System.out.println(nullDI.nullDI == null);
    System.out.println(nullDI);
}

The results are as follows:

[nullDI]
false
true
true
NullDI{property1='', property2='null', nullDI=null}

7 p-namespace quick injection

This knowledge point can be understood.

P-namespace (p-namespace) allows you to use the < bean > element's attributes instead of nested < property > sub tags to describe dependencies. In fact, it simplifies the configuration of setter injection! This knowledge point can be understood.

In the following example, there is a PNameSpaceDI class for p-namespace injection test:

/**
 * p-namespace Attribute injection
 *
 * @author lx
 */
public class PNameSpaceDI {

    //p-namespace attribute injection

    private String property1;
    private boolean property2;
    private PNameSpaceDIInner pNameSpaceDIInner;

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

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

    public void setpNameSpaceDIInner(PNameSpaceDIInner pNameSpaceDIInner) {
        this.pNameSpaceDIInner = pNameSpaceDIInner;
    }



    public static class PNameSpaceDIInner {
        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 +
                    '}';
        }
    }

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

Profile:
P-namespace is available after spring 2. X. The p-namespace is not defined by default in the xsd file, so we need to import it manually:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

bean configuration:

<!--p-namespace injection-->
<!--ordinary setter injection-->
<bean id="pNameSpaceDI1" class="com.spring.core.PNameSpaceDI">
    <property name="property1" value="vv"/>
    <property name="property2" value="true"/>
    <property name="pNameSpaceDIInner">
        <bean class="com.spring.core.PNameSpaceDI.PNameSpaceDIInner">
            <property name="property1" value="p1"/>
            <property name="property2" value="11"/>
        </bean>
    </property>
</bean>

<!--p-namespace injection-->
<bean id="pNameSpaceDI2" class="com.spring.core.PNameSpaceDI" p:property1="xxx" p:property2="true"
      p:pNameSpaceDIInner-ref="pNameSpaceDIInner"/>
<!--p-namespace Referenced objects can only be external objects-->
<bean id="pNameSpaceDIInner" class="com.spring.core.PNameSpaceDI.PNameSpaceDIInner" p:property1="p1" p:property2="11"/>

After introducing p-namespace, you can use it. It can be seen that the usage is very simple: for String, basic type and its wrapper class, we can use p: attribute name = "xxx"; For object types, we should use the P: attribute name - ref = "xxx" to indicate that the reference is to be given to a bean.

p-namespace simplifies the way of setter injection to a certain extent, but it also has a disadvantage, that is, for the injection of reference type, it can only refer to the beans of the external container, and cannot define the internal beans.

Test:

@Test
public void pNameSpaceDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    PNameSpaceDI pNameSpaceDI = ac.getBean("pNameSpaceDI2", PNameSpaceDI.class);
    System.out.println(pNameSpaceDI);
}

The results are as follows:

[pNameSpaceDI1, pNameSpaceDI2, pNameSpaceDIInner]
PNameSpaceDI{property1='xxx', property2=true, pNameSpaceDIInner=InnerBeanInner{property1='p1', property2=11}}

8 c-namespace quick injection

This knowledge point can be understood.

Similar to the previous p-namespace, Spring 3.1 continues to introduce c-namespace quick injection, which allows you to use the attributes of < bean > elements instead of nested < constructor Arg > sub tags to describe dependencies. In fact, it simplifies the configuration of constructor injection!

In the following example, there is a PNameSpaceDI class for c-namespace injection test:

/**
 * c-namespace Attribute injection
 *
 * @author lx
 */
public class CNameSpaceDI {

    //c-namespace attribute injection

    private String property1;
    private boolean property2;
    private CNameSpaceDIInner cNameSpaceDIInner;

    /**
     * Constructor required
     */
    public CNameSpaceDI(String property1, boolean property2, CNameSpaceDIInner cNameSpaceDIInner) {
        this.property1 = property1;
        this.property2 = property2;
        this.cNameSpaceDIInner = cNameSpaceDIInner;
    }

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

        public CNameSpaceDIInner(String property1, int property2) {
            this.property1 = property1;
            this.property2 = property2;
        }

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

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

Profile:
c-namespace is not defined by default in the xsd file, and we also need to manually import it:

xmlns:c="http://www.springframework.org/schema/c"

bean configuration:

<!--c-namespace injection-->
<!--Common constructor injection-->
<bean id="cNameSpaceDI" class="com.spring.core.CNameSpaceDI">
    <constructor-arg name="property1" value="yy"/>
    <constructor-arg name="property2" value="true"/>
    <constructor-arg name="cNameSpaceDIInner">
        <bean class="com.spring.core.CNameSpaceDI.CNameSpaceDIInner">
            <constructor-arg name="property1" value="c1"/>
            <constructor-arg name="property2" value="22"/>
        </bean>
    </constructor-arg>
</bean>


<!--c-namespace injection-->

<!--By parameter name-->
<bean id="cNameSpaceDI2" class="com.spring.core.CNameSpaceDI" c:property1="xxx" c:property2="true"
      c:cNameSpaceDIInner-ref="cNameSpaceDIInner"/>
<!--By parameter location index-->
<bean id="cNameSpaceDI3" class="com.spring.core.CNameSpaceDI" c:_0="xxxx" c:_1="true" c:_2-ref="cNameSpaceDIInner"/>

<!--c-namespace Referenced objects can only be external objects-->
<bean id="cNameSpaceDIInner" class="com.spring.core.CNameSpaceDI.CNameSpaceDIInner">
    <constructor-arg name="property1" value="c1"/>
    <constructor-arg name="property2" value="22"/>
</bean>

After introducing c-namespace, you can use it. It can be seen that the usage is very simple: for String, basic type and its wrapper class, we can use C: attribute name = "XXX"; For object types, we should use the C: attribute name - ref = "XXX" to refer to an external bean. In addition, c-namespace can use parameter index instead of parameter name. The index starts from 0 and the format is c:_index = "XXX" or c:_index-ref=“xxx”.

c-namespace simplifies the way of constructor injection to a certain extent, but it also has a disadvantage, that is, for the injection of reference types, it can only refer to the beans of the external container, and cannot define the internal beans.

Test:

@Test
public void cNameSpaceDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("cNameSpaceDI2", CNameSpaceDI.class));
    System.out.println(ac.getBean("cNameSpaceDI3", CNameSpaceDI.class));
}

The results are as follows:

[cNameSpaceDI, cNameSpaceDI2, cNameSpaceDI3, cNameSpaceDIInner]
CNameSpaceDI{property1='xxx', property2=true, cNameSpaceDIInner=CNameSpaceDIInner{property1='c1', property2=22}}
CNameSpaceDI{property1='xxxx', property2=true, cNameSpaceDIInner=CNameSpaceDIInner{property1='c1', property2=22}}

9 name compound attribute name injection

This knowledge point can be understood.

When setting the < property > dependency of a bean, you can use the composite property name or nested property name for the name property to set the dependency for the inner property, which requires that all dependent components on the path are not null except the final property name. If there is a null dependency in the middle, a NullPointerException is thrown. Note: in addition to the setter method, we must have getter methods for the components that depend on the path, otherwise we cannot implement composite attribute injection!

  1. For object attributes, attribute names are separated by ".";
  2. For a collection or array index, use "[index]" after the attribute name;
  3. For Map, use "[key]" after the attribute name;
  4. Multiple sets nesting is supported, such as < property name = "property5[0][1]" value = "33" / >, which means that the value of the second element in the first set element in the property5 property set is 33;
  5. Of course, it supports more complex object attribute + collection nesting, which can be written according to the syntax.

In the following case, there is a composundpropertydi class with multi-layer nested inner classes for composite property name injection test:

/**
 * Composite attribute injection
 *
 * @author lx
 */
public class CompoundPropertyDI {

    //Composite attribute injection

    public String property1;
    public boolean property2;
    public CompoundPropertyDIInner compoundPropertyDIInner;

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

    }

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

    public void setCompoundPropertyDIInner(CompoundPropertyDIInner compoundPropertyDIInner) {
        this.compoundPropertyDIInner = compoundPropertyDIInner;
    }
    
    /**
     * getter Is a necessary method for compound attribute name injection
     */
    public CompoundPropertyDIInner getCompoundPropertyDIInner() {
        return compoundPropertyDIInner;
    }

    public static class CompoundPropertyDIInner {
        public String property1;
        public int property2;
        public int property3;
        public CompoundPropertyDIInnerInner compoundPropertyDIInnerInner;

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

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

        public void setProperty3(int property3) {
            this.property3 = property3;
        }

        public void setCompoundPropertyDIInnerInner(CompoundPropertyDIInnerInner compoundPropertyDIInnerInner) {
            this.compoundPropertyDIInnerInner = compoundPropertyDIInnerInner;
        }

        /**
         * getter Is a necessary method for compound attribute name injection
         */
        public CompoundPropertyDIInnerInner getCompoundPropertyDIInnerInner() {
            return compoundPropertyDIInnerInner;
        }

        public static class CompoundPropertyDIInnerInner {
            public String property1;
            public int property2;


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

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

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

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

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

Profile:

<!--Composite attribute injection-->
<!--Multilayer nesting bean-->
<bean id="compoundPropertyDI" class="com.spring.core.CompoundPropertyDI">
    <property name="property1" value="com"/>
    <property name="property2" value="false"/>
    <property name="compoundPropertyDIInner">
        <bean class="com.spring.core.CompoundPropertyDI.CompoundPropertyDIInner">
            <property name="property1" value="111"/>
            <property name="property2" value="222"/>
            <property name="compoundPropertyDIInnerInner">
                <!--More than two levels of inner class nesting need to be used $separate-->
                <bean class="com.spring.core.CompoundPropertyDI$CompoundPropertyDIInner$CompoundPropertyDIInnerInner">
                    <property name="property1" value="111"/>
                    <property name="property2" value="222"/>
                </bean>
            </property>
        </bean>
    </property>


    <!--Composite attributes are injected externally bean Directly for internal bean Attribute assignment of-->
    <!--Note: except setter In addition to the method, we must have information about the components that depend on the path getter Method, otherwise composite attribute injection cannot be implemented-->
    <property name="compoundPropertyDIInner.property3" value="333"/>
    <property name="compoundPropertyDIInner.compoundPropertyDIInnerInner.property1" value="444"/>
</bean>

Test:

@Test
public void compoundPropertyDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("compoundPropertyDI", CompoundPropertyDI.class));
}

The results are as follows: Compound attribute name injection is successfully implemented:

[compoundPropertyDI]
CompoundPropertyDI{property1='com', property2=false, compoundPropertyDIInner=CompoundPropertyDIInner{property1='111', property2=222, property3=333, compoundPropertyDIInnerInner=CompoundPropertyDIInnerInner{property1='444', property2=222}}}

10 dependencies on forced order

This knowledge point can be understood.

If a bean is a dependency of another bean, it usually means that the bean is set as a property of another bean. However, sometimes the dependencies between beans are not direct, and there is no direct reference relationship between them. For example, Database beans in web applications must be initialized before DAO beans, but DAO beans do not need to hold Database bean instances.

The dependencies on attribute of the < bean > tag can explicitly force one or more specified beans to be initialized before initializing beans that use this element. The values of dependencies on are the id or name of the bean initialized first. When multiple dependencies on beans need to be specified, they can be separated by ",", ";", and Spring will initialize the beans in the order defined in dependency on.

In addition, the dependencies on attribute can also specify the destruction time order of the corresponding dependencies, and the bean initialized first will be destroyed last. Whether the initialization order or destruction order is specified, the scope of the bean setting this property is required to be singleton, otherwise the dependencies on property is invalid!

In the following example, there is a DependsOnDI class for the dependencies on injection test:

/**
 * depends-on
 *
 * @author lx
 */
public class DependsOnDI {

    public static class DependsOnDIA {
        public DependsOnDIA() {
            System.out.println("DependsOnDIA initialization");
        }

        public void init() {
            System.out.println("DependsOnDIA Initialize callback");
        }

        public void destroy() {
            System.out.println("DependsOnDIA Destroy callback");
        }

    }

    public static class DependsOnDIB {
        public DependsOnDIB() {
            System.out.println("DependsOnDIB initialization");
        }

        public void init() {
            System.out.println("DependsOnDIB Initialize callback");
        }

        public void destroy() {
            System.out.println("DependsOnDIB Destroy callback");
        }
    }

    public static class DependsOnDIC {
        public DependsOnDIC() {
            System.out.println("DependsOnDIC initialization");
        }

        public void init() {
            System.out.println("DependsOnDIC Initialize callback");
        }

        public void destroy() {
            System.out.println("DependsOnDIC Destroy callback");
        }
    }
}

Configuration file. It is required to initialize dependsOnDIC and dependsOnDIB in sequence before dependsOnDIA initialization:

<!--depends-on  If not scope=singleton(default),that depends-on attribute invalid-->
<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"/>

Test:

@Test
public void dependsOnDI() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    ac.close();
}

The results are as follows. They are indeed initialized and destroyed in order:

DependsOnDIC initialization
DependsOnDIC Initialize callback
DependsOnDIB initialization
DependsOnDIB Initialize callback
DependsOnDIA initialization
DependsOnDIA Initialize callback
[dependsOnDIA, dependsOnDIB, dependsOnDIC]
com.spring.core.DependsOnDI$DependsOnDIA@7a9273a8
DependsOnDIA Destroy callback
DependsOnDIB Destroy callback
DependsOnDIC Destroy callback

11 lazy init delay initialization

This knowledge point can be understood.

By default, when ApplicationContext is initialized, it will create all bean s with a defined scope of singleton and their dependencies. The advantage of this is that errors in the configuration can be found immediately, rather than waiting for use.

The lazy init attribute of the < bean > tag can set the bean to delay initialization. The bean instance is created after the first request for the bean, rather than when the container is started.
In addition, when a delayed initialization bean is a dependency of a non delayed initialization singleton bean, ApplicationContext will create the delayed initialization bean at startup, because it must meet the requirements of the dependency of the singleton bean. Deferred initialization beans are immediately initialized and injected into other singleton beans that are not deferred initialized.

The lazy init attribute is valid only when scope="singleton". If scope = "potype", initialization will be delayed even if lazy init = "false" (false by default).

We can also set default lazy init = "true" on the < beans > tag to uniformly set whether to delay initialization for all beans under the tag. The default is false and non delay initialization!

In the following example, there is a lazyinitia class for lazyinitia injection test:

/**
 * lazy-init
 *
 * @author lx
 */
public class LazyInitDI {

    public static class LazyInitDIA {
        public LazyInitDIA() {
            System.out.println("LazyInitDIA initialization");
        }
    }

    /**
     * Rely on lazyinitiadic
     */
    public static class LazyInitDIB {
        private LazyInitDIC lazyInitDIC;

        public LazyInitDIB(LazyInitDIC lazyInitDIC) {
            this.lazyInitDIC = lazyInitDIC;
            System.out.println("LazyInitDIB initialization");

        }
    }

    public static class LazyInitDIC {
        public LazyInitDIC() {
            System.out.println("LazyInitDIC initialization");
        }
    }
}

Profile:

<!--lazy-init Delay initialization-->

<!--lazyInitDIA Delay initialization-->
<bean class="com.spring.core.LazyInitDI.LazyInitDIA" lazy-init="true" id="lazyInitDIA"/>
<!--lazyInitDIB belong to singleton Non delayed initialization of-->
<bean class="com.spring.core.LazyInitDI.LazyInitDIB" id="lazyInitDIB">
    <!--rely on lazyInitDIC-->
    <constructor-arg name="lazyInitDIC" ref="lazyInitDIC"/>
</bean>
<!--lazyInitDIC It belongs to delayed initialization, but is singleton of lazyInitDIB Dependency, so it is initialized immediately-->
<bean class="com.spring.core.LazyInitDI.LazyInitDIC" lazy-init="true" id="lazyInitDIC"/>

Test:

@Test
public void lazyInit() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    //It is initialized only when the bean instance of lazyinitia is used
    //System.out.println(ac.getBean("lazyInitDIA", LazyInitDI.LazyInitDIA.class));
}

The results are as follows:

LazyInitDIC initialization
LazyInitDIB initialization
[lazyInitDIA, lazyInitDIB, lazyInitDIC]

12 automatic XML dependency injection

This knowledge point can be understood.

The "automatic injection" here is not the "dependency injection DI" mentioned earlier, but an omitted writing method for XML configuration files. In the previous case, we need to write the configuration file to indicate the dependency and injection methods. Spring supports automatic dependency injection in some cases. By checking the contents of ApplicationContext, spring can automatically resolve and configure bean dependencies. Let's not need handwritten configuration files.

The advantages of automatic injection are as follows:

  1. Automatic injection can significantly reduce the need to manually specify properties or constructor parameters. (the parent inheritance mentioned earlier also has this function).
  2. Automatic injection can automatically update the configuration of objects. For example, if you need to add a dependency to a class, you can automatically assemble the dependency without modifying the configuration file.

When using XML based configuration metadata, you can set the automatic injection mode by setting the autowire attribute of the < bean > tag. There are four types:

  1. no: default value. Auto assembly is not enabled. The bean must reference an element defined by ref. Although the configuration is a little more, it can make the relationship between the system structure and beans clearer.

  2. byName: find and inject the bean dependent object by the name of the attribute. For example, if the autowire property of a bean is byName, a property is named a, and there is a setA method, the IoC container will find the bean whose id/name value contains a in the configuration file, and then inject it using the setter method mode. Note that the automatic setter here is limited compared with the manual setter injection mentioned earlier, that is, the XXX of setXXX method must correspond to the name of an instance in a container, otherwise it will not be injected.

  3. byType: finds and injects the bean dependent object by the type of the attribute. For example, if the autowire property of a bean is byType, a property is named a, and a setA method is used, and the type of this property a is com.A, the IoC container will find the bean with class com.A in the configuration file, and then inject it using the setter method pattern. Note that the automatic setter injection here is limited compared with the manual setter injection, that is, the XXX of the setXXX method must be the property name (the case of the first letter can be changed). If the container has multiple beans of the same type, an exception is thrown: expected single matching bean but found x: xxx.

  4. Constructor: like byType, it also finds dependent objects by type. The difference from byType is that it uses constructor injection instead of setter method injection. If the container has multiple beans of the same type and cannot find the most appropriate dependency, an exception will also be thrown: expected single matching bean but found x: xxx.

In addition, the default autowire attribute of < beans > tag also has the above options. You can specify the general automatic injection method of all < beans >, but if some < beans > actively set their own automatic injection method, the setting method of < beans > tag will be overwritten!

In the following example, there is an AutowireDI class for autowire injection test:

/**
 * autowire injection
 *
 * @author lx
 */
public class AutowireDI {

    /**
     * Rely on AutowireDIB and AutowireDIC
     */
    public static class AutowireDIA {
        private AutowireDIB autowireDIB;
        private AutowireDIC autowireDIC;

        public void setAutowireDIC(AutowireDIC autowireDIC) {
            this.autowireDIC = autowireDIC;
        }

        public AutowireDIA() {
        }

        public AutowireDIA(AutowireDIB autowireDIB) {
            this.autowireDIB = autowireDIB;
        }

        @Override
        public String toString() {
            return "AutowireDIA{" +
                    "autowireDIB=" + autowireDIB +
                    ", autowireDIC=" + autowireDIC +
                    '}';
        }
    }

    public static class AutowireDIB {
        private String property1;
    }

    public static class AutowireDIC {
        private String property1;
    }
}

Profile:

<!--autowire-->
<!--Dependencies for constructor injection-->
<bean id="autowireDIB" class="com.spring.core.AutowireDI.AutowireDIB"/>
<bean id="autowireDIB2" class="com.spring.core.AutowireDI.AutowireDIB"/>
<!--be used for setter Injected dependency-->
<bean id="autowireDIC" class="com.spring.core.AutowireDI.AutowireDIC"/>
<!--<bean id="autowireDIC2" class="com.spring.core.AutowireDI.AutowireDIC"/>-->

<!--By default, automatic injection is not allowed-->
<bean id="autowireDIA0" class="com.spring.core.AutowireDI.AutowireDIA" />

<!--byType Will automatically setter injection autowireDIC If the container has more than one of the same type bean,And can't find the most suitable one bean,Then an exception is thrown:-->
<!--expected single matching bean but found 2: autowireDIC,autowireDIC2-->
<bean id="autowireDIA1" class="com.spring.core.AutowireDI.AutowireDIA" autowire="byType"/>

<!--byName Will automatically setter injection autowireDIC-->
<bean id="autowireDIA2" class="com.spring.core.AutowireDI.AutowireDIA" autowire="byName"/>

<!--constructor Inject automatic constructor into autowireDIC If the container has more than one of the same type bean,And can't find the most suitable one bean,Then an exception is thrown:-->
<bean id="autowireDIA3" class="com.spring.core.AutowireDI.AutowireDIA" autowire="constructor"/>

Test:

@Test
public void autowire() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    System.out.println(ac.getBean("autowireDIA0", AutowireDI.AutowireDIA.class));
    System.out.println(ac.getBean("autowireDIA1", AutowireDI.AutowireDIA.class));
    System.out.println(ac.getBean("autowireDIA2", AutowireDI.AutowireDIA.class));
    System.out.println(ac.getBean("autowireDIA3", AutowireDI.AutowireDIA.class));
}

The results are as follows: successful automatic injection:

[autowireDIB, autowireDIB2, autowireDIC, autowireDIA0, autowireDIA1, autowireDIA2, autowireDIA3]
AutowireDIA{autowireDIB=null, autowireDIC=null}
AutowireDIA{autowireDIB=null, autowireDIC=com.spring.core.AutowireDI$AutowireDIC@782663d3}
AutowireDIA{autowireDIB=null, autowireDIC=com.spring.core.AutowireDI$AutowireDIC@782663d3}
AutowireDIA{autowireDIB=com.spring.core.AutowireDI$AutowireDIB@1990a65e, autowireDIC=null}

12.1 automatic XML injection

Automatic injection can save the preparation of configuration files to some extent, but it also has its own problems:

  1. For byName injection, if the dependency of the name is not found, it will not be injected.
  2. For byType injection, if the container has multiple beans of the same type and cannot find the most appropriate dependency, an exception is thrown: expected single matching bean but found x: xxx.
  3. For constructor injection, if the container has multiple beans of the same type and cannot find the most appropriate dependency, an exception will also be thrown. expected single matching bean but found x: xxx.
  4. After using automatic injection, it is not easy to clarify the dependencies between bean s!
  5. In fact, their dependency injection rules are complex, and their specific rules will be explained later when talking about the source code. For example, if there are multiple instances of the same type during constructor injection, further matching can be performed according to the parameter name, but setter injection will not match according to the parameter name.

Common solutions to these problems are:

  1. The most common choice is to give up automatic injection (which is not enabled by default). Although there is more configuration code, there is no problem, and the relationship between bean s is more obvious.
  2. Set the autowire candidate property of < bean > to false, and the bean is excluded from the automatic injection candidate bean.
  3. Set the primary attribute of < bean > to true, and the bean is set as the main candidate bean for automatic injection.
  4. Use annotation injection (as will be described later, annotation is actually an automatic injection).

12.2 selecting candidate bean s for automatic injection

First, the < beans > tag has a default autowire candidates attribute, which is used to uniformly filter which beans with id/name under the current container can be used as candidate beans for active injection. The value of default autowire candidates can use pattern matching. For example, to limit the names of candidate beans to any bean that ends in Repository, you can use * Repository. Multiple patterns can be provided, separated by commas. The default autowire candidates property will only affect the automatic injection of byName. It will not affect the explicit automatic injection of byName.

In addition, setting the autowire candidate attribute of the < bean > tag to false (true by default) will also exclude the bean from the automatic injection candidate bean, and the bean will not be available during automatic injection (including annotation style injection, such as @ Autowired). If the autowire candidates property is set to true or false, the default autowire candidates property will invalidate the filtering of the bean! It should be noted that the autowire candidate attribute will only affect the automatic injection through type, and it will not affect the explicit automatic injection of byName.

Of course, you can also set the primary attribute of the < bean > tag to true (fasle by default) and specify the bean as the main candidate. Generally, you can specify one or more, but in that case, you can't determine which bean to inject. Note that even if the value of the primary property is set to true or false, the default autowire candidates property will still be valid for filtering the bean! And the priority is less than the display defined attribute of autowire candidate!

Profile:

<!--default-autowire-candidates Set the mode to filter the specified rules name,id of bean-->
<!--It only affects automatic injection through types, and it does not affect explicit injection byName Automatic injection of.-->
<beans default-autowire-candidates="*DIB1*">
    <!--Dependency for constructor injection, even if set primary="true",Still affected default-autowire-candidates Influence of-->
    <bean id="autowireDIB" class="com.spring.core.AutowireDI.AutowireDIB" primary="true"/>
    <!--autowire-candidate The properties of the display settings are not affected default-autowire-candidates And the priority is greater than primary Set properties for-->
    <bean id="autowireDIB2" class="com.spring.core.AutowireDI.AutowireDIB" primary="false"
          autowire-candidate="true"/>
    <!--be used for setter Injected dependency  autowire-candidate="false"It will not affect the passage byName injection  -->
    <bean id="autowireDIC" class="com.spring.core.AutowireDI.AutowireDIC" autowire-candidate="false"/>
    <!--<bean id="autowireDIC2" class="com.spring.core.AutowireDI.AutowireDIC"/>-->

    <!--By default, automatic injection is not allowed-->
    <bean id="autowireDIA0" class="com.spring.core.AutowireDI.AutowireDIA"/>

    <!--byType Will automatically setter injection autowireDIC If the container has more than one of the same type bean,Then an exception is thrown:-->
    <!--expected single matching bean but found 2: autowireDIC,autowireDIC2-->
    <bean id="autowireDIA1" class="com.spring.core.AutowireDI.AutowireDIA" autowire="byType"/>

    <!--byName Will automatically setter injection autowireDIC-->
    <bean id="autowireDIA2" class="com.spring.core.AutowireDI.AutowireDIA" autowire="byName"/>

    <!--constructor Inject automatic constructor into autowireDIC If the container has more than one of the same type bean,So it's not sure which one to inject-->
    <bean id="autowireDIA3" class="com.spring.core.AutowireDI.AutowireDIA" autowire="constructor"/>
</beans>

Test:

    @Test
    public void selectAutowire() {
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
        System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
        System.out.println("autowireDIB: " + ac.getBean("autowireDIB", AutowireDI.AutowireDIB.class));
        System.out.println("autowireDIB2: " + ac.getBean("autowireDIB2", AutowireDI.AutowireDIB.class));
        System.out.println("autowireDIA0: " + ac.getBean("autowireDIA0", AutowireDI.AutowireDIA.class));
        System.out.println("autowireDIA1;" + ac.getBean("autowireDIA1", AutowireDI.AutowireDIA.class));
        System.out.println("autowireDIA2: " + ac.getBean("autowireDIA2", AutowireDI.AutowireDIA.class));
        System.out.println("autowireDIA3: " + ac.getBean("autowireDIA3", AutowireDI.AutowireDIA.class));
    }

As a result, the injection was successfully selected:

[autowireDIB, autowireDIB2, autowireDIC, autowireDIA0, autowireDIA1, autowireDIA2, autowireDIA3]
autowireDIB: com.spring.core.AutowireDI$AutowireDIB@64485a47
autowireDIB2: com.spring.core.AutowireDI$AutowireDIB@25bbf683
autowireDIA0: AutowireDIA{autowireDIB=null, autowireDIC=null}
autowireDIA1;AutowireDIA{autowireDIB=null, autowireDIC=null}
autowireDIA2: AutowireDIA{autowireDIB=null, autowireDIC=com.spring.core.AutowireDI$AutowireDIC@6ec8211c}
autowireDIA3: AutowireDIA{autowireDIB=com.spring.core.AutowireDI$AutowireDIB@25bbf683, autowireDIC=null}

13 method injection

This knowledge point can be understood.

The dependencies between beans in the IoC container are usually determined by attributes, which may cause some problems due to different bean declaration cycles. For example, a bean a is a singleton and can only be created once at startup. It depends on a bean b, but this bean b is not a singleton. The business requirement is that each access to bean a needs to inject the latest bean b instance.

Obviously, setting the scope attribute of bean b to prototype alone cannot solve the problem, because the container only creates a singleton bean a once, so it only gets the opportunity to set the attribute bean b once. In the future, bean a accesses the reference of bean b saved by itself every time, rather than asking for bean b (getBean) from the IoC container, so bean b is equivalent to a single example.

For the following example, there is a MethodIn class for testing the above problems:

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

    private MethodInInner methodInInner1;
    private MethodInInner methodInInner2;

    public MethodIn(MethodInInner methodInInner1) {
        this.methodInInner1 = methodInInner1;
    }

    public void setMethodInInner2(MethodInInner methodInInner2) {
        this.methodInInner2 = methodInInner2;
    }

    public MethodInInner getMethodInInner1() {
        return methodInInner1;
    }

    public MethodInInner getMethodInInner2() {
        return methodInInner2;
    }

    public static class MethodInInner {
        public MethodInInner() {
            System.out.println("MethodInInner initialization:" + this);
        }
    }
}

Profile:

<!--bean methodIn You need to get the latest information for each call methodInInner object--><bean id="methodIn" class="com.spring.core.MethodIn">    <constructor-arg name="methodInInner1" ref="methodInInner"/>    <property name="methodInInner2" ref="methodInInner"/></bean><!--methodInInner of scope Set to prototype,See if you can--><bean class="com.spring.core.MethodIn.MethodInInner" id="methodInInner" scope="prototype"/>

Test:

@Testpublic void methodNi() {    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));    MethodIn methodIn = ac.getBean("methodIn", MethodIn.class);    //Get methodinner multiple times and find the same object system.out.println ("methodinner1:" + methodin. Getmethodinner1()); System.out.println("methodInInner1: " + methodIn.getMethodInInner1());    System.out.println("methodInInner2: " + methodIn.getMethodInInner2());    System.out.println("methodInInner2: " + methodIn.getMethodInInner2());}

The results are as follows. It is found that setting methodinner to prototype does not solve the problem. It is still initialized only when methodIn is initialized for the first time:

MethodInInner initialization: com.spring.core.MethodIn$MethodInInner@4d41ceeMethodInInner initialization: com.spring.core.MethodIn$MethodInInner@631330c[methodIn, methodInInner]methodIn: com.spring.core.MethodIn@12c8a2c0methodIn: com.spring.core.MethodIn@12c8a2c0methodIn: com.spring.core.MethodIn@12c8a2c0

In this case, we can use method injection to solve it!

13.1 search method injection

Lookup Method Injection refers to the IoC container rewriting the method specified in the configuration file. The return result of the injected method will return another named bean in the container.

The principle is relatively simple. When parsing the < bean > tag, the dynamic proxy technology of cglib will be used to generate the dynamic subclass of the class for the bean with the < lookup method > tag. The proxy class will proxy the method specified by the name attribute of < lookup method >, and finally return the bean object with the name specified by the bean attribute. This return is to ask the container for the object. Then, if the bean object is of prototype type, a new object must be generated and returned every time. If the bean object is of singleton type, the same object must be returned every time!

In the following example, there is a LookupMethodIn class to find the test of method injection:

public class LookupMethodIn {

    public static class LookupMethodInA {
        private LookupMethodInB lookupMethodInB;
        /**
         * In fact, you haven't injected the lookupMethodInC attribute at all
         */
        private LookupMethodInC lookupMethodInC;

        public LookupMethodInB getLookupMethodInB() {
            return lookupMethodInB;
        }

        public void setLookupMethodInB(LookupMethodInB lookupMethodInB) {
            this.lookupMethodInB = lookupMethodInB;
        }

        /**
         * In fact, each one is looking for the container object
         */
        public LookupMethodInC getLookupMethodInC() {
            //Call the createLookupMethodInC method
            return createLookupMethodInC();
        }

        /**
         * The method that will be replaced by the dynamic proxy, find the container object
         */
        public LookupMethodInC createLookupMethodInC() {
            return lookupMethodInC;
        }

        public void setLookupMethodInC(LookupMethodInC lookupMethodInC) {
            this.lookupMethodInC = lookupMethodInC;
        }

        @Override
        public String toString() {
            return "LookupMethodInA{" +
                    "lookupMethodInB=" + lookupMethodInB +
                    ", lookupMethodInC=" + lookupMethodInC +
                    '}';
        }
    }

    public static class LookupMethodInB {
    }

    public static class LookupMethodInC {
    }
}

Profile:

<bean class="com.spring.core.LookupMethodIn.LookupMethodInA" 
id="lookupMethodInA">
    <!--ordinary setter injection-->
    <property name="lookupMethodInB" ref="lookupMethodInB"/>
    <!--Lookup method injection name Represents the method name to be dynamically replaced, bean Represents one of the containers bean My name, No i Well, it will be returned from the container bean Examples of-->
    <lookup-method name="createLookupMethodInC" bean="lookupMethodInC"/>
</bean>
<!--Need to be injected bean-->
<bean class="com.spring.core.LookupMethodIn.LookupMethodInB" id="lookupMethodInB" scope="prototype"/>
<bean class="com.spring.core.LookupMethodIn.LookupMethodInC" id="lookupMethodInC" scope="prototype"/>

Test:

@Test
public void lookupmethodNi() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    LookupMethodIn.LookupMethodInA lookupMethodInA = ac.getBean("lookupMethodInA", LookupMethodIn.LookupMethodInA.class);
    System.out.println("obtain lookupMethodInB,Same object");
    System.out.println("lookupMethodInB: " + lookupMethodInA.getLookupMethodInB());
    System.out.println("lookupMethodInB: " + lookupMethodInA.getLookupMethodInB());
    System.out.println("lookupMethodInB: " + lookupMethodInA.getLookupMethodInB());
    System.out.println("obtain lookupMethodInC,Different objects");
    System.out.println("lookupMethodInC: " + lookupMethodInA.getLookupMethodInC());
    System.out.println("lookupMethodInC: " + lookupMethodInA.getLookupMethodInC());
    System.out.println("lookupMethodInC: " + lookupMethodInA.getLookupMethodInC());

    //In fact, the lookupMethodInC attribute of lookupMethodInA has not been injected at all. Every time getLookupMethodInC is directly looking for the container object
    //Since lookupMethodInC is set to prototype, a new object will be obtained every time
    System.out.println("lookupMethodInA: " + lookupMethodInA);
}

The results are as follows. Lookup method injection can ensure that a new object is returned for the prototype attribute every time.

[lookupMethodInA, lookupMethodInB, lookupMethodInC]
obtain lookupMethodInB,Same object
lookupMethodInB: com.spring.core.LookupMethodIn$LookupMethodInB@ba8d91c
lookupMethodInB: com.spring.core.LookupMethodIn$LookupMethodInB@ba8d91c
lookupMethodInB: com.spring.core.LookupMethodIn$LookupMethodInB@ba8d91c
 obtain lookupMethodInC,Different objects
lookupMethodInC: com.spring.core.LookupMethodIn$LookupMethodInC@7364985f
lookupMethodInC: com.spring.core.LookupMethodIn$LookupMethodInC@5d20e46
lookupMethodInC: com.spring.core.LookupMethodIn$LookupMethodInC@709ba3fb
lookupMethodInA: LookupMethodInA{lookupMethodInB=com.spring.core.LookupMethodIn$LookupMethodInB@ba8d91c, lookupMethodInC=null}

In addition, there must be some limitations due to the cglib dynamic proxy technology used in lookup method injection. If the method is abstract, the dynamically generated subclass will automatically implement the method, otherwise it will be overwritten. It should be noted that because inheritance is adopted, neither bean nor proxy methods can be final modified.

If we use annotation development, the annotation injected by the Lookup method is @ Lookup, which can be marked on a method. The value attribute of the annotation may not be written. It will automatically match a bean of the same type in the container according to the type. However, if there are multiple beans of the same type, an exception will be thrown: expected single matching bean but found 2:xxx. (the premise is that annotation config enables annotation configuration, which will be described later).

/**
 * The method that will be replaced by the dynamic proxy, find the container object.
 * <p>
 * During Parameter annotation configuration, the value attribute of the annotation specifies the name of the bean, which can be left blank. It will automatically match a bean of the same type in the container according to the type
 * However, if there are multiple beans of the same type, an exception is thrown: expected single matching bean but found 2:xxx
 */
@Lookup("lookupMethodInC")
public LookupMethodInC createLookupMethodInC() {
    return lookupMethodInC;
}

13.2 substitution by any method

Arbitrary Method Replacement: it can replace the method body and return results, which is equivalent to replacing one method with another at runtime.

In the following case, first of all, there is an arbitrrymethodre class for the test of arbitrary method replacement. There is a TimeConverter inside. Its convert method originally had only two formatting logic. Now we need to add one, so we can use arbitrary method replacement to modify the code! Any method replacement needs to define a method replacer that implements the MethodReplacer method replacer interface and rewrite the reimplement method. The code and return value in this method are the code and return value of the method to be replaced.

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

    /**
     * Time formatter
     */
    public static class TimeConverter {
        /**
         * Method to be replaced
         */
        public String convert(byte type) {
            DateTimeFormatter dateTimeFormater;
            if (type == 1) {
                dateTimeFormater = DateTimeFormatter.ofPattern("yyyy year MM month dd day HH Time mm branch ss second");
            } else {
                dateTimeFormater = DateTimeFormatter.ofPattern("yyyy year MM month dd day HH Time mm branch ss second SSS millisecond");
            }
            LocalDateTime localDateTime = LocalDateTime.now();
            return dateTimeFormater.format(localDateTime);
        }
    }

    /**
     * Method replacer
     */
    public static class ReplaceTimeConverter implements MethodReplacer {

        /**
         * Re implement the given method and add functions. Or the logic of cglib
         *
         * @param obj    Examples of re implemented methods
         * @param method Methods that need to be re implemented
         * @param args   Method
         * @return Method
         * @throws Throwable abnormal
         */
        @Override
        public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
            byte type = (byte) args[0];
            //Add a formatting logic
            DateTimeFormatter dateTimeFormater;
            switch (type) {
                case 1:
                    dateTimeFormater = DateTimeFormatter.ofPattern("yyyy year MM month dd day HH Time mm branch ss second");
                    break;
                case 2:
                    dateTimeFormater = DateTimeFormatter.ofPattern("yyyy year MM month dd day HH Time mm branch ss second SSS millisecond");
                    break;
                default:
                    dateTimeFormater = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss SSS");
            }
            LocalDateTime localDateTime = LocalDateTime.now();
            return dateTimeFormater.format(localDateTime);
        }
    }
}

Profile:

<!--Any method replacement-->
<!--Method replacer-->
<bean class="com.spring.core.ArbitraryMethodRe.ReplaceTimeConverter" id="replaceTimeConverter"/>
<!--To be replaced bean-->
<bean class="com.spring.core.ArbitraryMethodRe.TimeConverter" id="timeConverter">
    <!--Any method replacement name Represents the method to replace replacer Represents a reference to a method replacer-->
    <replaced-method name="convert" replacer="replaceTimeConverter">
        <!--In the case of method overloading, identify the parameters of the replacement method. This label is required only when the method is overloaded and there are multiple variables in the class.-->
        <!-- <arg-type match="byte"/>-->
    </replaced-method>
</bean>

Multiple < Arg type > tags can be used in the element to indicate the method signature of the overridden method. The signature of the parameter is necessary only if the method is overloaded and there are multiple variants in the class. For convenience, the type string of a parameter can be a substring of a fully qualified type name. For example, all of the following matches java.lang.String:

java.lang.String
String
Str

Since the number of parameters is usually sufficient to distinguish each possible choice, you can save a lot of input by typing the shortest string matching the parameter type. Test:

@Test
public void arbitraryMethodRe() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println(Arrays.toString(ac.getBeanDefinitionNames()));
    ArbitraryMethodRe.TimeConverter timeConverter = ac.getBean("timeConverter", ArbitraryMethodRe.TimeConverter.class);
    System.out.println(timeConverter.convert((byte) 3));
}

The results are as follows: Method replacement is successfully realized:

[replaceTimeConverter, timeConverter]
2020/09/02 00:03:04 396

14 scope scope

14.1 scope classification

The scope attribute of the < bean > tag is used to define the scope (SCOPE) of the bean. This is a very powerful attribute, which we have seen many times in the previous learning content.

The so-called scope should actually be "used to declare that the objects in the container should be based on the limited scene or the specific survival time (range) of the object, that is, the container generates and assembles these objects before the objects enter their corresponding scope, and the container usually destroys these objects after the objects are no longer limited by these Scopes" -- Spring uncover secrets.

The Spring 5.x framework supports six scopes, of which the last four scopes are only available in ApplicationContext developed using web, such as XmlWebApplicationContext. Of course, Spring supports creating custom scopes.

  1. singleton:
    1. Default value, single example. A single bean definition is bound to a single bean instance. Each IoC container only saves a single object instance. All subsequent requests and requests for this bean return cached objects.
    2. When the application loads and creates the container, the object is created. As long as the container is, the object remains alive and will not be created again. When the application unloads and destroys the container, the object is destroyed.
  2. prototype:
    1. Prototype, a single bean definition is bound to multiple bean instances. A new bean instance is created when a bean with prototype scope is injected into another bean to be initialized, or when the bean is obtained by calling the getBean () method of the container. In general, you should use the prototype scope for all stateful beans and the Singleton scope for stateless beans. Most beans are stateless.
    2. Spring is not responsible for the full lifecycle of prototype bean s. The container instantiates, configures, or otherwise assembles the prototype object, and then hands it to the client, after which the prototype instance is no longer recorded. Therefore, the initialize lifecycle callback method will be called on all objects, regardless of the lifecycle, but in the case of the prototype, the configured destroy lifecycle callback will not be called.
  3. Request: a single bean definition is bound to a single HTTP request lifecycle. Indicates that a new bean will be generated for each HTTP request, which is only valid in the current HTTP request.
  4. Session: the definition of a single bean is bound to the life cycle of an Http Session. It means that a new bean will be generated for each independent session. It will only survive longer than the bean of request scope. In other aspects, it is really no difference.
  5. application: the definition of a single bean is bound to the life cycle of ServletContext (there may be multiple IoC containers in a web program).
  6. Websocket: the definition of a single bean is bound to the life cycle of websocket.

According to my experience, other scopes in Spring Boot web projects developed based on annotations are rarely used. Generally, they are the default singleton.

14.2 Singleton relies on Prototype

When a prototype bean is a dependency of a singleton bean, ApplicationContext will immediately create a prototype bean when creating a singleton bean, because it must meet the requirements of the dependency of the singleton bean: the prototype bean needs to be initialized immediately and injected into the singleton bean without null.

If you want the singleton bean to repeatedly obtain a new instance of the prototype bean at run time. The prototype bean cannot be injected into the singleton bean through the traditional way (constructor or setter), because the injection occurs only once when the Spring container instantiates the singleton bean and resolves and injects its dependencies. If you need new instances of multiple prototype beans at run time, you should use the method injection described earlier.

6.15 callback extension of bean

The Spring framework provides many bean related callback interfaces to implement different functions.

15.1 bean lifecycle callback

The Lifecycle Callbacks interface is an interface that interacts with the container to manage the bean lifecycle. There are two interfaces: after initialization and before destruction. For self created beans, that is, the lifecycle of beans that cannot be managed by the container, the interface definition is invalid. For example, since the life cycle of a prototype bean is not managed by the container after it is created, it can only call the initialization callback method. It will be called when a bean with prototype scope is injected into another bean to be initialized, or when the bean is obtained by calling the getBean () method of the container, but the destroy callback method will never be called. For singleton bean s, the initialization callback method is called only once, because it is initialized only once, and the destroy callback method is called when the container is closed. Note: the "destroy" here is to remove the bean instance from the container. It is not certain whether the object will be immediately recycled by GC!

To interact with the lifecycle management of beans in the container, the most primitive way is to implement the InitializingBean and DisposableBean interfaces provided by Spring. The container calls the afterpropertieset () method for the former and the destroy() method for the latter so that the bean performs some operations after initialization and before destruction managed by the container.

However, the @ PostConstruct (initial callback) and @ PreDestroy (destroy callback) annotations in the JSR-250 annotation specification are currently the best practices for defining lifecycle callback methods in Spring applications. Using these annotations means that the bean is not coupled to a Spring specific interface (it does not need to inherit the interface as above). Of course, if you do not want to use the annotation of JSR-250, but still want to delete the coupling, we can consider the configuration metadata method based on XML configuration file: init method and destroy method attributes, which define the callback after initialization and the callback before destruction respectively.

For the following example, first of all, there is a LifecycleCallback class, which is used to test the lifecycle callback:

/**
 * bean Lifecycle Callback 
 *
 * @author lx
 */
public class LifecycleCallback {

    /**
     * Callback based on implementing InitializingBean and DisposableBean interfaces
     */
    public static class LifecycleCallbackImp implements InitializingBean, DisposableBean {
        public LifecycleCallbackImp() {
            System.out.println("LifecycleCallbackImp Constructor call");
        }

        @Override
        public void afterPropertiesSet() {
            System.out.println("LifecycleCallbackImp Initialize callback");
        }

        @Override
        public void destroy() {
            System.out.println("LifecycleCallbackImp Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("LifecycleCallbackImp Destroy");
        }
    }

    /**
     * XML based callback
     */
    public static class LifecycleCallbackXml {
        public LifecycleCallbackXml() {
            System.out.println("LifecycleCallbackXml Constructor call");
        }

        public void afterPropertiesSet() {
            System.out.println("LifecycleCallbackXml Initialize callback");
        }

        public void destroy() {
            System.out.println("LifecycleCallbackXml Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("LifecycleCallbackXml Destroy");
        }
    }

    /**
     * Annotation based callback
     */
    public static class LifecycleCallbackAnn {
        public LifecycleCallbackAnn() {
            System.out.println("LifecycleCallbackAnn Constructor call");
        }

        @PostConstruct
        public void afterPropertiesSet() {
            System.out.println("LifecycleCallbackAnn Initialize callback");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("LifecycleCallbackAnn Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("LifecycleCallbackAnn Destroy");
        }
    }

    /**
     * prototype Callback test
     */
    public static class LifecycleCallbackPro {
        public LifecycleCallbackPro() {
            System.out.println("prototype LifecycleCallbackPro Constructor call");
        }

        @PostConstruct
        public void afterPropertiesSet() {
            System.out.println("prototype LifecycleCallbackPro Initialize callback");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("prototype LifecycleCallbackPro Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("prototype LifecycleCallbackPro Destroy");
        }
    }
}

Profile:

<!--bean Lifecycle Callback -->

<!--Implementation based interface-->
<bean class="com.spring.core.LifecycleCallback.LifecycleCallbackImp" id="lifecycleCallbackImp"/>
<!--be based on xml to configure init-method The value is the initialization callback method name  destroy-method The value is the name of the destroy callback method  -->
<bean class="com.spring.core.LifecycleCallback.LifecycleCallbackXml" id="lifecycleCallbackXml"
      init-method="afterPropertiesSet" destroy-method="destroy"/>
<!--Annotation support needs to be enabled based on annotations, which will be described later-->
<context:annotation-config/>
<bean class="com.spring.core.LifecycleCallback.LifecycleCallbackAnn" id="lifecycleCallbackAnn"/>

<!--prototype bean The initialization callback is called every time an instance is obtained from the container, but the destroy callback is never called-->
<bean class="com.spring.core.LifecycleCallback.LifecycleCallbackPro" id="lifecycleCallbackPro" scope="prototype"/>

Test:

@Test
public void lifecycleCallback() {
    //Open container
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    //Only when the bean is actively obtained can the callback be performed
    ac.getBean("lifecycleCallbackPro", LifecycleCallback.LifecycleCallbackPro.class);
    ac.getBean("lifecycleCallbackPro", LifecycleCallback.LifecycleCallbackPro.class);
    //close closes the container. It must be closed before it is triggered
    ac.close();
    //gc doesn't necessarily happen, and objects don't have to be completely destroyed
    System.gc();
}

The results are as follows:

LifecycleCallbackImp Constructor call
LifecycleCallbackImp Initialize callback
LifecycleCallbackXml Constructor call
LifecycleCallbackXml Initialize callback
LifecycleCallbackAnn Constructor call
LifecycleCallbackAnn Initialize callback
prototype LifecycleCallbackPro Constructor call
prototype LifecycleCallbackPro Initialize callback
prototype LifecycleCallbackPro Constructor call
prototype LifecycleCallbackPro Initialize callback
LifecycleCallbackAnn Destroy callback
LifecycleCallbackXml Destroy callback
LifecycleCallbackImp Destroy callback
prototype LifecycleCallbackPro Destroy
prototype LifecycleCallbackPro Destroy
LifecycleCallbackAnn Destroy
LifecycleCallbackXml Destroy
LifecycleCallbackImp Destroy

15.1.1 unified default callback

If we set callback methods based on XML or annotation, if a project has its own specifications, the method declarations of initialization methods and callback methods in different classes are consistent, that is, they are uniformly named init(), initialize(), dispose(), etc.

If there is a unified specification for method naming, you can configure the Spring container to "find" initialization and destruction methods with specific names. By configuring the default init method = "xxx" of < beans >, it indicates that all beans under the beans will call the method named xxx in the bean at an appropriate time when the IOC container is initialized. By configuring the default destroy method = "yyy" of < beans >, it indicates that all beans under the beans will call the method named yyy in the bean at an appropriate time when the IOC container is closed. If you want to override the default callback function, you can define init method or destroy method in < bean >.

In the following example, there is a DefaultLifecycleCallback class to test the default callback:

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

    public static class DefaultLifecycleCallbackA {
        public DefaultLifecycleCallbackA() {
            System.out.println("DefaultLifecycleCallbackA Constructor call");
        }

        public void init() {
            System.out.println("DefaultLifecycleCallbackA Initialize callback");
        }

        public void destroy() {
            System.out.println("DefaultLifecycleCallbackA Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("DefaultLifecycleCallbackA Destroy");
        }
    }

    public static class DefaultLifecycleCallbackB {
        public DefaultLifecycleCallbackB() {
            System.out.println("DefaultLifecycleCallbackB Constructor call");
        }

        public void init() {
            System.out.println("DefaultLifecycleCallbackB Initialize callback");
        }

        public void destroy() {
            System.out.println("DefaultLifecycleCallbackB Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("DefaultLifecycleCallbackB Destroy");
        }
    }

    public static class DefaultLifecycleCallbackC {
        public DefaultLifecycleCallbackC() {
            System.out.println("DefaultLifecycleCallbackC Constructor call");
        }

        public void initialize() {
            System.out.println("DefaultLifecycleCallbackC Initialize callback");
        }

        public void dispose() {
            System.out.println("DefaultLifecycleCallbackC Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("DefaultLifecycleCallbackC Destroy");
        }
    }

    public static class DefaultLifecycleCallbackD {
        public DefaultLifecycleCallbackD() {
            System.out.println("DefaultLifecycleCallbackD Constructor call");
        }

        public void initialize() {
            System.out.println("DefaultLifecycleCallbackD Initialize callback");
        }

        public void dispose() {
            System.out.println("DefaultLifecycleCallbackD Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("DefaultLifecycleCallbackD Destroy");
        }
    }

    public static class DefaultLifecycleCallbackE {
        public DefaultLifecycleCallbackE() {
            System.out.println("prototype DefaultLifecycleCallbackE Constructor call");
        }

        public void initialize() {
            System.out.println("prototype DefaultLifecycleCallbackE Initialize callback");
        }

        public void dispose() {
            System.out.println("prototype DefaultLifecycleCallbackE Destroy callback");
        }

        @Override
        protected void finalize() {
            System.out.println("prototype DefaultLifecycleCallbackE Destroy");
        }
    }
}

Profile:

<!--Default lifecycle callback-->
<beans default-init-method="init" default-destroy-method="destroy">
    <bean class="com.spring.core.DefaultLifecycleCallback.DefaultLifecycleCallbackA"
          id="defaultLifecycleCallbackA"/>
    <bean class="com.spring.core.DefaultLifecycleCallback.DefaultLifecycleCallbackB"
          id="defaultLifecycleCallbackB"/>
</beans>
<beans default-init-method="initialize" default-destroy-method="dispose">
    <bean class="com.spring.core.DefaultLifecycleCallback.DefaultLifecycleCallbackC"
          id="defaultLifecycleCallbackC"/>
    <bean class="com.spring.core.DefaultLifecycleCallback.DefaultLifecycleCallbackD"
          id="defaultLifecycleCallbackD"/>
    <bean class="com.spring.core.DefaultLifecycleCallback.DefaultLifecycleCallbackE" id="defaultLifecycleCallbackE"
          scope="prototype"/>
</beans>

Test:

@Test
public void defaultlifecycleCallback() {
    //Open container
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    //Only when the bean is actively obtained can the callback be performed
    ac.getBean("defaultLifecycleCallbackE", DefaultLifecycleCallback.DefaultLifecycleCallbackE.class);
    ac.getBean("defaultLifecycleCallbackE", DefaultLifecycleCallback.DefaultLifecycleCallbackE.class);
    //close closes the container. It must be closed before it is triggered
    ac.close();
    //gc doesn't necessarily happen, and objects don't have to be completely destroyed
    System.gc();
}

The results are as follows:

DefaultLifecycleCallbackA Constructor call
DefaultLifecycleCallbackA Initialize callback
DefaultLifecycleCallbackB Constructor call
DefaultLifecycleCallbackB Initialize callback
DefaultLifecycleCallbackC Constructor call
DefaultLifecycleCallbackC Initialize callback
DefaultLifecycleCallbackD Constructor call
DefaultLifecycleCallbackD Initialize callback
prototype DefaultLifecycleCallbackE Constructor call
prototype DefaultLifecycleCallbackE Initialize callback
prototype DefaultLifecycleCallbackE Constructor call
prototype DefaultLifecycleCallbackE Initialize callback
DefaultLifecycleCallbackD Destroy callback
DefaultLifecycleCallbackC Destroy callback
DefaultLifecycleCallbackB Destroy callback
DefaultLifecycleCallbackA Destroy callback
prototype DefaultLifecycleCallbackE Destroy
prototype DefaultLifecycleCallbackE Destroy
DefaultLifecycleCallbackD Destroy
DefaultLifecycleCallbackC Destroy
DefaultLifecycleCallbackB Destroy
DefaultLifecycleCallbackA Destroy

15.1.2 bean callback summary

When calling the new container, the refresh method will be called, which will destroy the existing bean (execute the destroy callback) and reinitialize the bean of the singleton (execute the initialize callback). When calling the close method, the existing bean will also be destroyed (execute the destroy callback).

Since spring 2.5, there are three ways to control the lifecycle callback of bean s:

  1. Implement InitializingBean and DisposableBean callback interfaces;
  2. Customize the init () and destroy () methods, and configure the init method and destroy method using XML; Customize init() and destroy()
  3. Method, using @ PostConstruct and @ PreDestroy annotations;

Three methods can be used together. If multiple methods are used together, and each method is configured with a different method name, their execution order will be as shown in the following order. However, if they are all configured with the same method name, the method will be executed only once.

If multiple different methods are configured for the initialization method, the execution sequence is as follows:

  1. PostConstruct annotation modification method;
  2. The afterpropertieset() method in the InitializingBean interface;
  3. Custom initialization method based on XML.

If multiple different methods are configured for the destruction method, the execution sequence is as follows:

  1. @PreDestroy annotation modification method;
  2. destroy() method in DisposableBean interface;
  3. Custom destruction method based on XML.

15.2 container status callback

The following container callback knowledge points can be understood!

The ApplicationContext container also has its own state. Spring provides some interfaces. We can register to listen to different state changes and call different callback methods.

15.2.1 Lifecycle callback

The Lifecycle interface is generally used to turn on and off the activities of some background components. When ApplicationContext receives a start (start method) or stop (stop, close method) signal, it will pass the signal to all Lifecycle interface implementations, judge and try to call the corresponding methods.

In addition to bean initialization and destruction callbacks, Spring managed beans can also implement the Lifecycle interface so that these beans can participate in the startup and shutdown processes driven by the container's own Lifecycle.

/**
 * Lifecycle Lifecycle Callback 
 * Listen for container start and stop events
 */
public interface Lifecycle {

    /**
     * Start current component
     */
    void start();

    /**
     *Stop the component. When the method is completed, the component will be completely stopped. When asynchronous stop is required
     * When stopping behavior, consider implementing SmartLifecycle and its stop(Runnable) method variant.
     */
    void stop();

    /**
     * Check whether this component is running. This method will be called before calling the start or stop method
     * 1. The start method is executed only when the method returns false.
     * 2. Only when the method returns true will the stop(Runnable callback) or stop() method be executed.
     *
     * @return Is the current component running
     */
    boolean isRunning();
}

For the following example, there is a StartShutCallback class for Lifecycle testing:

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

    public static class StartShutCallbackA implements Lifecycle {

        private boolean running;

        @Override
        public void start() {
            System.out.println("StartShutCallbackA start");
            running = true;
        }

        @Override
        public void stop() {
            System.out.println("StartShutCallbackA stop");
            running = false;
        }

        @Override
        public boolean isRunning() {
            return running;
        }


    }

    public static class StartShutCallbackB implements Lifecycle{

        private boolean running;

        @Override
        public void start() {
            System.out.println("StartShutCallbackB start");
            running = true;
        }

        @Override
        public void stop() {
            System.out.println("StartShutCallbackB stop");
            running = false;
        }

        @Override
        public boolean isRunning() {
            return running;
        }

        public StartShutCallbackB() {
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
        }
    }
}

Profile:

<!--Startup and Shutdown Callbacks-->
<bean class="com.spring.core.StartShutCallback.StartShutCallbackA" id="startShutCallbackA"/>
<bean class="com.spring.core.StartShutCallback.StartShutCallbackB" id="startShutCallbackB"/>

Test:

@Test
public void startShutCallback() {
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    ac.start();
    //ac.stop();
    ac.close();
}

The results are as follows:

StartShutCallbackA start
StartShutCallbackB start
StartShutCallbackA stop
StartShutCallbackB stop

The org.springframe.context.Lifecycle interface only responds to the start and stop behaviors, but does not implement the function of automatically starting when the context is refreshed. To listen to this behavior, you can use the Lifecycle processor interface. In fact, Lifecycle also entrusts Lifecycle processor to practice.

The Lifecycle processor interface inherits the Lifecycle interface and is an extension of Lifecycle. It also adds two other methods to react to containers that are being refreshed and closed. The control of components is more detailed!

/**
 * Lifecycle processor, which inherits the functions of lifecycle
 * Add refresh and close events of listening container
 */
public interface LifecycleProcessor extends Lifecycle {

    /**
     * Callback of container when refresh
     */
    void onRefresh();

    /**
     * Callback when the container is close d
     */
    void onClose();

}

If you want more fine-grained control (including startup level) over the automatic startup of a specific bean, you should implement the org.springframework.context.SmartLifecycle interface.

The order in which components start and close calls can be important. If there is a "dependency" relationship between any two objects, the relying party starts after the dependency and stops before the dependency. Sometimes, however, direct dependencies are unknown. You may only know that objects of one type should start before objects of another type. In this case, the SmartLifecycle interface should be used. The getPhase() method defined on its parent interface Phased.

/**
 * Start up period
 */
public interface Phased {

    /**
     * Returns the int value of the object startup period
     */
    int getPhase();
}

SmartLifecycle provides default implementations of some methods:

/**
 * Intelligent cycle callback
 */
public interface SmartLifecycle extends Lifecycle, Phased {

    /**
     * Default startup period, Integer.MAX_VALUE
     */
    int DEFAULT_PHASE = Integer.MAX_VALUE;


    /**
     * If the container wants to automatically call back when calling the refresh method (that is, calling the start method), it returns true. By default, it returns true
     * Returning false indicates that the component is intended to be started by explicitly calling start(), which is similar to an ordinary Lifecycle implementation.
     */
    default boolean isAutoStartup() {
        return true;
    }

    /**
     * SmartLifecycle The custom stop method accepts the callback function and will be executed after stop is completed
     *
     * @param callback Callback function
     */
    default void stop(Runnable callback) {
        stop();
        callback.run();
    }

    /**
     * Returns the start time of the current bean component. Default is returned_ PHASE
     */
    @Override
    default int getPhase() {
        return DEFAULT_PHASE;
    }
}

The int value returned by the getPhase() method indicates the start order. The object with the lowest phase starts first. When it stops, it is executed in the reverse order. The default implementation returns Integer.MAX_VALUE, indicating that the component will start last and stop first. The default phase for any other Lifecycle object that does not implement SmartLifecycle is 0. In addition, any negative phase value means that an object should start before these standard components start (and stop after they stop).

In addition, the stop method defined by SmartLifecycle itself accepts a callback thread task. Any implemented object must perform this task after the stop scheme is completed. Asynchronous shutdown can be realized when necessary, because the default implementation of the lifecycleprocessor interface DefaultLifecycleProcessor can wait for a timeout value when executing the callback task in the object in each phase. The default timeout for each phase is 30 seconds. You can configure a bean named lifecycle processor in xml to override the default lifecycle processor instance. If you only want to modify the timeout, it is sufficient to modify the default lifecycle processor instance definition:

<bean id="lifecycleProcessor" 
class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

15.2.1.1 cases

For the following example, there is a StartShutCallback class for Lifecycle testing:

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

    /**
     * Smart component, phase=Integer.MAX_VALUE AutoStartup=true
     */
    public static class SmartLifecycleCallbackA implements SmartLifecycle {
        private boolean running;
        private static int count;


        /**
         * refresh Or it will be called when start ing. It is also judged according to the return value of isRunning
         */
        @Override
        public void start() {
            System.out.println("SmartLifecycleCallbackA start");
            if (++count == 2) {
                running = true;
            }
        }

        /**
         * close Or stop, which is also determined according to the return value of isRunning
         */
        @Override
        public void stop() {
            System.out.println("SmartLifecycleCallbackA stop");
            if (++count == 4) {
                running = false;
            }
        }

        @Override
        public boolean isRunning() {
            return running;
        }

        //You can also override the default methods of isAutoStartup, getPhase and stop to implement your own logic

        @Override
        public int getPhase() {
            return 1;
        }
    }

    /**
     * Smart component, phase=-1 AutoStartup=false
     */
    public static class SmartLifecycleCallbackB implements SmartLifecycle {
        private boolean running;
        private static int count;


        /**
         * refresh Or it will be called when start ing. It is also judged according to the return value of isRunning
         */
        @Override
        public void start() {
            System.out.println("SmartLifecycleCallbackB start");
            if (++count == 1) {
                running = true;
            }
        }

        /**
         * close Or stop, which is also determined according to the return value of isRunning
         */
        @Override
        public void stop() {
            System.out.println("SmartLifecycleCallbackB stop");
            if (++count == 3) {
                running = false;
            }
        }

        @Override
        public boolean isRunning() {
            return running;
        }

        //You can also override the default methods of isAutoStartup, getPhase and stop to implement your own logic
        @Override
        public int getPhase() {
            return -1;
        }

        @Override
        public boolean isAutoStartup() {
            return false;
        }
    }

    /**
     * Normal component, used as the default value for comparison: phase=0 AutoStartup=false
     */
    public static class SmartLifecycleCallbackC implements Lifecycle {

        private boolean running;

        @Override
        public void start() {
            System.out.println("SmartLifecycleCallbackC start");
            running = true;
        }

        @Override
        public void stop() {
            System.out.println("SmartLifecycleCallbackC stop");
            running = false;
        }

        @Override
        public boolean isRunning() {
            return running;
        }
    }
}

Profile:

<bean class="com.spring.core.SmartLifecycleCallback.SmartLifecycleCallbackA" id="smartLifecycleCallbackA"/>
<bean class="com.spring.core.SmartLifecycleCallback.SmartLifecycleCallbackB" id="smartLifecycleCallbackB"/>
<bean class="com.spring.core.SmartLifecycleCallback.SmartLifecycleCallbackC" id="smartLifecycleCallbackC"/>

Test:

@Test
public void smartLifecycleCallback() {
    System.out.println("new The code inside the container is actually called once refresh Operation, so it will start automatically");
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    System.out.println("start call");
    ac.start();
    System.out.println("stop call");
    ac.stop();
    System.out.println("close call");
    ac.close();
}

The results are as follows:

new The code inside the container is actually called once refresh Operation, so it will start automatically
SmartLifecycleCallbackA start
start call
SmartLifecycleCallbackB start
SmartLifecycleCallbackC start
SmartLifecycleCallbackA start
close call
SmartLifecycleCallbackA stop
SmartLifecycleCallbackC stop
SmartLifecycleCallbackB stop

Component A implements SmartLifecycle and sets AutoStartup=true, so start will be called once when A new container is created. The subsequent container start method is valid for all three components. Start from small to large according to phase: B - > C - > A. the subsequent close method is also valid for all three components. Stop from large to small according to phase: A - > C - > B.

15.3 elegant closing container in non web applications

At present, our cases are all non web application cases. Basically, the container ends with the end of the JVM, and there is no safe shutdown process. We can register a shutdown hook hook method for the container into the JVM. When the JVM closes, it will safely close the container and release all resources (actually the logic of callback close method), which is also a callback method.

Test:

@Test
public void shutdownHook() {
    System.out.println("new The code inside the container is actually called once refresh Operation, so it will start automatically");
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("DI.xml");
    //Adding a callback method will call the logic of the container close method when the JVM is closed. If it is not added, the container will not close normally
    //ac.registerShutdownHook();
    System.out.println("start call");
    ac.start();
}

The results are as follows: when the callback is not turned on or off:

new The code inside the container is actually called once refresh Operation, so it will start automatically
SmartLifecycleCallbackA start
start call
SmartLifecycleCallbackB start
SmartLifecycleCallbackC start
SmartLifecycleCallbackA start

When opening and closing callback:

new The code inside the container is actually called once refresh Operation, so it will start automatically
SmartLifecycleCallbackA start
start call
SmartLifecycleCallbackB start
SmartLifecycleCallbackC start
SmartLifecycleCallbackA start
SmartLifecycleCallbackA stop
SmartLifecycleCallbackC stop
SmartLifecycleCallbackB stop

As you can see, the container is closed normally!

7 Summary

This article focuses on the introduction of Spring and the XML core configuration of IoC. It does not speak the source code, which is suitable for Spring beginners. First learn to use, and then look at the source code, which is the core idea of this series of articles. Later, there will be a special article on the source code analysis of the core principles!

About IoC or DI, I think beginners should not read too many theoretical articles. They should first understand what it is, and then practice with cases. In continuous practice, they will naturally understand what IoC and DI are and what benefits they bring compared with traditional code.

This time we just introduced the XML based configuration method. In fact, the new Spring projects are basically based on annotations and Java code. XML is generally used in some old projects. We will introduce these things one by one later, and we will know the benefits of annotation based configuration at that time! As a series of articles, Spring 5.x will be constantly updated and can be followed at any time!

Related articles

  1. Spring Framework 5.x learning
  2. Spring Framework 5.x source code
  3. https://spring.io/

If you need to communicate, or the article is wrong, please leave a message directly. In addition, I hope to like, collect and pay attention. I will constantly update all kinds of Java learning blogs!

Posted by nsbrown on Fri, 10 Sep 2021 14:33:13 -0700