2、基于XML的依赖注入详细配置

news2024/12/24 11:24:10

一、依赖注入详细配置

1、通过P名称空间为Bean注入值

1、简化setter方式注入的配置,可以不使用<property />子标签来为属性设置值,直接在<bean />标签中设置各依赖项的值。
2、注入值类型说明:
3、实现步骤:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--Teacher类配置-->
    <bean id="teacher1" class="com.itan.beans.Teacher" p:name="张三" p:age="30"/>
</beans>
@Test
public void test01() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Teacher bean = context.getBean("teacher1", Teacher.class);
    System.out.println(bean);
}
/**
 * 运行结果:
 * setter属性注入age
 * setter属性注入name
 * Teacher{姓名='张三', 年龄=25}
 */

2、null值注入

1、Spring将默认值为空的属性的值视为空字符串。如果将value设置为null,Spring会识别成“null”字符串,如果要真正的设置为null,就应该使用<null />标签,才表示真正的注入null值。
<bean id="teacher2" class="com.itan.beans.Teacher">
    <!--空的value属性被当做空字符串-->
    <property name="name" value="" />
    <property name="age" value="18" />
    <!--只有null标签才表示真正的注入null-->
    <property name="sex">
        <null />
    </property>
</bean>
@Test
public void test01() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Teacher bean = context.getBean("teacher2", Teacher.class);
    System.out.println(bean.getName() == null);
    System.out.println(bean.getAge());
    System.out.println(bean.getSex() == null);
    System.out.println(bean);
}
/**
 * 运行结果:
 * false
 * 18
 * true
 * Teacher{姓名='', 年龄=18', 性别=}
 */

3、ref引用

1、在<constructor-arg />、<property />、<entry />标签中有一个ref属性,用于将Bean的指定的属性的值设置为容器管理的另一个Bean的引用,也就是引用类型属性设置依赖的方式。
2、在为引用类型属性设置依赖之前,必须保证被引用的Bean已经被初始化,ref属性的值要与被引用的Bean的id或name属性中的一个值相等。
3、使用ref为引用类型设置依赖,获取的到的Bean与直接通过IoC容器中获取到的Bean,是同一个Bean。
public class Subject {
    private String name;

    private Teacher teacher;

    public Subject(String name, Teacher teacher) {
        this.name = name;
        this.teacher = teacher;
        System.out.println("构造器依赖注入");
    }
    
    public Teacher getTeacher() {
        return teacher;
    }

    @Override
    public String toString() {
        return "Subject{" +
                "科目='" + name + '\'' +
                ", 老师='" + teacher + '\'' +
                '}';
    }
}
<!--Teacher类配置-->
<bean id="teacher3" class="com.itan.beans.Teacher">
    <property name="name" value="张三" />
    <property name="age" value="32" />
    <property name="sex" value="" />
</bean>

<!--Subject类配置-->
<bean id="subject" class="com.itan.beans.Subject">
    <constructor-arg value="Java" />
    <!--使用ref属性引用teacher-->
    <constructor-arg ref="teacher3" />
    <!--或者使用ref标签引用teacher-->
    <!--<constructor-arg>-->
    <!--    <ref bean="teacher3"></ref>-->
    <!--</constructor-arg>-->
</bean>
@Test
public void test01() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取bean对象
        Subject subject = context.getBean("subject", Subject.class);
        System.out.println(subject);
        // 通过subject获取teacher
        Teacher teacher1 = subject.getTeacher();
        // 通过ioc容器直接获取teacher
        Teacher teacher2 = context.getBean("teacher3", Teacher.class);
        // 比较两个是否相等
        System.out.println("teacher1与teacher2是否相等:" + (teacher1 == teacher2));
	}
}
/**
 * 运行结果:
 * Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}
 * teacher1与teacher2是否相等:true
 */

4、内部Bean

1、在<constructor-arg />、<property />、<entry />以及一些集合标签中可以使用<bean />子标签,表示一个内部Bean,如果要为一个引用类型的属性设置依赖,在不使用ref属性或者ref标签的情况下,可以定义自己的内部的Bean。
2、与外部Bean的区别是:
<!--Subject类配置-->
<bean id="subject1" class="com.itan.beans.Subject">
    <constructor-arg value="Java" />
    <constructor-arg>
        <!--注意:内部bean设置id或name属性无效,也无法从IoC中获取到-->
        <bean class="com.itan.beans.Teacher">
            <property name="name" value="张三" />
            <property name="age" value="32" />
            <property name="sex" value="" />
        </bean>
    </constructor-arg>
</bean>
@Test
public void test02() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Subject subject = context.getBean("subject1", Subject.class);
    System.out.println(subject);
}
/**
 * 运行结果:
 * Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}
 */

5、集合属性的注入

1、Spring支持集合注入方式,提供了一系列的标签<list>、<set>、<map>、<props>、<array>分别为Java中的List、Set、Map、Properties、Array类型的集合注入依赖,由于集合的元素既可以是基本类型也可以是对象甚至集合,因此集合注入非常灵活。另外集合注入支持泛型转换,注入的时候会自动将value的字符串值转换为对应泛型类型。
@Data
public class CollectionBean {
    private List<Object> list;

    private Set<Object> set;

    private Map<Object, Object> map;

    private Properties properties;

    private Object[] array;
}

List注入示例:

<!--CollectionBean类配置-->
<bean id="collectionBean" class="com.itan.beans.CollectionBean">
    <property name="list">
        <!--list标签表示list集合,用于定义多个元素-->
        <list>
            <!--value标签用于定义字面量的值作为集合元素-->
            <value>111</value>
            <!--也可以使用Bean标签来定义Bean对象作为集合的元素-->
            <bean class="com.itan.beans.Teacher">
                <property name="name" value="张三" />
                <property name="age" value="32" />
                <property name="sex" value="" />
            </bean>
            <!--也可以使用ref引用一个外部Bean作为集合的元素-->
            <ref bean="teacher3" />
        </list>
    </property>
</bean>
@Test
public void test03() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    CollectionBean collectionBean = context.getBean("collectionBean", CollectionBean.class);
    List<Object> list = collectionBean.getList();
    System.out.println(list);
}
/**
 * 运行结果:
 * [111, Teacher{姓名='张三', 性别='男', 年龄=32}, Teacher{姓名='张三', 性别='男', 年龄=32}]
 */

Set注入示例:

<!--CollectionBean类配置-->
<bean id="collectionBean1" class="com.itan.beans.CollectionBean">
    <property name="set">
        <!--set标签表示set集合,用于定义多个元素-->
        <set>
            <!--value标签用于定义字面量的值作为集合元素-->
            <value>111</value>
            <!--也可以使用Bean标签来定义Bean对象作为集合的元素-->
            <bean class="com.itan.beans.Teacher">
                <property name="name" value="张三" />
                <property name="age" value="32" />
                <property name="sex" value="" />
            </bean>
            <!--也可以使用ref引用一个外部Bean作为集合的元素-->
            <ref bean="teacher3" />
        </set>
    </property>
</bean>
@Test
public void test04() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    CollectionBean collectionBean = context.getBean("collectionBean1", CollectionBean.class);
    Set<Object> set = collectionBean.getSet();
    System.out.println(set);
}
/**
 * 运行结果:
 * [111, Teacher{姓名='张三', 性别='男', 年龄=32}, Teacher{姓名='张三', 性别='男', 年龄=32}]
 */

Map注入示例:

<!--CollectionBean类配置-->
<bean id="collectionBean2" class="com.itan.beans.CollectionBean">
    <property name="map">
        <!--map标签表示set集合,用于定义多个元素-->
        <map>
            <!--map标签中首先需要定义entry标签,表示一个键值对-->
            <entry key="测试" value="哈哈哈哈" />
            <!--key和value都可以引用外部bean-->
            <entry>
                <!--key可以使用内部bean,或者集合等等-->
                <key>
                    <bean class="com.itan.beans.Teacher">
                        <property name="name" value="张三" />
                        <property name="age" value="32" />
                        <property name="sex" value="" />
                    </bean>
                </key>
                <!--也可以引用外部Bean-->
                <ref bean="subject" />
            </entry>
            <!--注意value标签只能是注入字面量值,如果想要对象类型的value,那么直接使用Bean标签就行了-->
            <entry key="Oracle">
                <!--<value></value>-->
                <bean class="com.itan.beans.Teacher">
                    <property name="name" value="李四" />
                    <property name="age" value="35" />
                    <property name="sex" value="" />
                </bean>
            </entry>
        </map>
    </property>
</bean>
@Test
public void test05() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    CollectionBean collectionBean = context.getBean("collectionBean2", CollectionBean.class);
    Map<Object, Object> map = collectionBean.getMap();
    System.out.println(map);
}
/**
 * 运行结果:
 * {测试=哈哈哈哈, Teacher{姓名='张三', 性别='男', 年龄=32}=Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}, Oracle=Teacher{姓名='李四', 性别='男', 年龄=35}}
 */

Properties注入示例:

<!--CollectionBean类配置-->
<bean id="collectionBean3" class="com.itan.beans.CollectionBean">
    <property name="properties">
        <!--props标签表示properties集合,prop表示一个键值对-->
        <props>
            <!--表示一个String类型键值对-->
            <prop key="测试">哈哈哈</prop>
            <prop key="123">456</prop>
        </props>

        <!--实际上也可以放map,但是要求String类型的key和value-->
        <!--<map>-->
        <!--    <entry key="测试" value="哈哈哈"/>-->
        <!--</map>-->
    </property>
</bean>
@Test
public void test06() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    CollectionBean collectionBean = context.getBean("collectionBean3", CollectionBean.class);
    Properties properties = collectionBean.getProperties();
    System.out.println(properties);
}
/**
 * 运行结果:
 * {123=456, 测试=哈哈哈}
 */

Array注入示例:

<!--CollectionBean类配置-->
<bean id="collectionBean4" class="com.itan.beans.CollectionBean">
    <property name="array">
        <!--array标签表示array数组,用于定义多个元素-->
        <array>
            <!--value标签用于定义字面量的值作为数组元素-->
            <value>111</value>
            <!--也可以使用Bean标签来定义Bean对象作为数组的元素-->
            <bean class="com.itan.beans.Teacher">
                <property name="name" value="张三" />
                <property name="age" value="32" />
                <property name="sex" value="" />
            </bean>
            <!--也可以使用ref引用一个外部Bean作为集合的元素-->
            <ref bean="teacher3" />
        </array>
    </property>
</bean>
@Test
public void test07() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    CollectionBean collectionBean = context.getBean("collectionBean4", CollectionBean.class);
    Object[] array = collectionBean.getArray();
    for (int i = 0; i < array.length; i++) {
        System.out.println(array[i]);
    }
}
/**
 * 运行结果:
 * 111
 * Teacher{姓名='张三', 性别='男', 年龄=32}
 * Teacher{姓名='张三', 性别='男', 年龄=32}
 */

6、value字面量

1、对于基本类型、String、包装类类型的属性,可以直接使用value属性的字符串值来注入值。
2、在最后注入的时候Spring会自动将这些值从String 转换为属性或参数的实际类型。
3、也可以使用<value />标签来设置值
4、上面一些示例中使用到value标签,请参考。

7、级联属性赋值

1、级联属性可以修改属性的属性,但是原来的Bean的属性值可能会被修改
<!--Teacher类配置-->
<bean id="teacher3" class="com.itan.beans.Teacher">
    <property name="name" value="张三" />
    <property name="age" value="32" />
    <property name="sex" value="" />
</bean>

<!--Subject类配置-->
<bean id="subject2" class="com.itan.beans.Subject">
    <constructor-arg value="Java" />
    <!--使用ref属性引用teacher-->
    <constructor-arg ref="teacher3" />
    <!--为级联属性赋值,修改属性的属性-->
    <property name="teacher.name" value="李四" />
</bean>
@Test
public void test08() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Teacher teacher = context.getBean("teacher3", Teacher.class);
    // 获取bean对象
    Subject subject = context.getBean("subject2", Subject.class);
    System.out.println(teacher);
    System.out.println(subject);
}
/**
 * 运行结果:可以看到在修改级联属性后,被引用的原Bean中的属性值被改变
 * Teacher{姓名='李四', 性别='男', 年龄=32}
 * Subject{科目='Java', 老师='Teacher{姓名='李四', 性别='男', 年龄=32}'}
 */

8、parent继承(复用属性值)

1、在<bean />、<ref />标签中有一个parent属性,它用于指定目标Bean将使用父Bean的属性和配置,用于相同属性以及值的复用,如果当前Bean中有与父Bean相同的属性,且当前Bean中为某一个相同的属性重新设置了值,那么当前Bean的其他属性值将复用父Bean的,重新设置值的属性将用自己的值。
2、parent属性的值与目标父Bean的id属性或name属性中的一个值相同。
<!--Teacher类配置-->
<bean id="teacher4" class="com.itan.beans.Teacher">
    <property name="name" value="张三" />
    <property name="age" value="32" />
    <property name="sex" value="" />
</bean>

<!--Teacher类配置,使用parent指定继承自teacher4这个bean-->
<bean id="teacher5" class="com.itan.beans.Teacher" parent="teacher4">
    <!--为name设置自己的值-->
    <property name="name" value="李四" />
</bean>
@Test
public void test09() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Teacher teacher = context.getBean("teacher5", Teacher.class);
    System.out.println(teacher);
}
/**
 * 运行结果:
 * Teacher{姓名='李四', 性别='男', 年龄=32}
 */

9、abstract抽象(只允许被继承)

1、在<bean />标签中有一个abstract属性,当值为true时,表示当前Bean是一个抽象的,不能获取它的实例,只能被别人用来继承,类似于一个属性和值的模版;如果尝试获取则会抛出如下错误:Error creating bean with name 'XXXXXXX': Bean definition is abstract
<!--Teacher类配置-->
<bean id="teacher4" class="com.itan.beans.Teacher" abstract="true">
    <property name="name" value="张三" />
    <property name="age" value="32" />
    <property name="sex" value="" />
</bean>

<!--Teacher类配置-->
<bean id="teacher5" class="com.itan.beans.Teacher" parent="teacher4">
    <property name="name" value="李四" />
</bean>
@Test
public void test09() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 获取bean对象
    Teacher teacher5 = context.getBean("teacher5", Teacher.class);
    System.out.println(teacher5);
    Teacher teacher4 = context.getBean("teacher4", Teacher.class);
    System.out.println(teacher4);
}
/**
 * 运行结果:
 * Teacher{姓名='李四', 性别='男', 年龄=32}
 * org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'teacher4': Bean definition is abstract
 */

10、depends-on强制顺序

1、如果某个Bean是另一个Bean的依赖项,则通常意味着这个Bean被设置为另一个Bean的属性。但是,有时Bean之间的依赖关系不太直接,他们之间没有直接的引用关系。
2、<bean />标签中的depends-on属性可以强制在初始化使用此元素的Bean之前初始化一个或多个指定的Bean,depends-on的值是先初始化的Bean的id或者name,需要指定多个时,可以使用“,”、“;”、“ ”符号分隔,Spring会按照depend-on中定义的顺序来初始化Bean。
<bean id="people" class="com.itan.beans.People" depends-on="person,teacher6"/>
<bean id="person" class="com.itan.beans.Person"/>
<bean id="teacher6" class="com.itan.beans.Teacher"/>
@Test
public void test10() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
}
/**
 * 运行结果:
 * Person初始化
 * Teacher初始化
 * People初始化
 */

11、scope作用域

1、<bean />标签中的scope属性用于定义Bean的作用域,Spring Framework支持六个作用域,其中后面四个作用域仅在使用web感知的ApplicationContext时可用。还可以创建自定义范围。
2、Spring 5.x支持的作用域属性如下:
<!--单实例Bean-->
<bean id="person" class="com.itan.beans.Person" scope="singleton" />

<!--多实例Bean-->
<bean id="teacher" class="com.itan.beans.Teacher" scope="prototype"/>
@Test
public void test01() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
}
/**
 * 运行结果:在容器初始化时候,并没有创建Teacher实例
 * Person初始化
 */
@Test
public void test02() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Person person1 = context.getBean("person", Person.class);
    Person person2 = context.getBean("person", Person.class);
    System.out.println("person1与person2是否相等:" + (person1 == person2));
    System.out.println("================================");
    Teacher teacher1 = context.getBean("teacher", Teacher.class);
    Teacher teacher2 = context.getBean("teacher", Teacher.class);
    System.out.println("teacher1与teacher2是否相等:" + (teacher1 == teacher2));
}

/**
 * 运行结果:单例的Person只会初始化一次,并且每次获取实例都是相同的,多例的Teacher在每次获取时,
 * 才会初始化,并且每个实例不相同。
 * Person初始化
 * person1与person2是否相等:true
 * ================================
 * Teacher初始化
 * Teacher初始化
 * teacher1与teacher2是否相等:false
 */

12、静态工厂方式创建Bean

1、本质上是工厂模式的应用,对象由我们创建,但是由Spring调用一次静态的创建方法,将创建的对象存入容器。静态工厂类本身并不会实例化
2、使用静态工厂方法实例化Bean时,<bean />标签中的class属性不再是要获取的Bean的全路径类名,而是静态工厂的全路径类名,并使用factory-method的属性指定获取bean对象的工厂方法的名称(注意该方法必须是静态方法)
/**
 * @Date: 2022/11/13
 * 静态工厂
 */
public class StaticFactory {
    /**
     * 静态方法,调用方式:StaticFactory.getPerson()
     * @return
     */
    public static Person getPerson() {
        System.out.println("静态工厂方法创建对象开始");
        Person person = new Person("张三", 20);
        person.setSex("男");
        person.setEmail("111111@qq.com");
        return person;
    }
}
<!--静态工厂(不需要实例化工厂本身)-->
<bean id="person1" class="com.itan.factory.StaticFactory" factory-method="getPerson" />
@Test
public void test03() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Person person1 = context.getBean("person1", Person.class);
    System.out.println(person1);
}
/**
 * 运行结果:
 * 静态工厂方法创建对象开始
 * Person{姓名='张三', 性别='男', 年龄=20', 邮箱=111111@qq.com}
 */

13、实例工厂方式创建Bean

1、实例工厂本身需要实例化工厂类的,然后调用工厂实例中的非静态方法创建Bean
2、使用实例工厂方法实例化Bean时,<bean />标签中的class属性置空,使用factory-bean的属性指定实例工厂的名字,使用factory-method属性指定非静态工厂方法的名称
3、factory bean虽然代表一个工厂,其实例仍然交给Spring管理。
/**
 * @Date: 2022/11/13
 * 实例工厂
 */
public class InstanceFactory {
    /**
     * 实例方法,调用方式:new InstanceFactory().getPerson()
     * @return
     */
    public Person getPerson() {
        System.out.println("实例工厂方法创建对象开始");
        Person person = new Person("李四", 20);
        person.setSex("女");
        person.setEmail("2222222@qq.com");
        return person;
    }
}
<!--实例工厂(需要实例化工厂本身)-->
<bean id="instanceFactory" class="com.itan.factory.InstanceFactory" />
<!--factory-bean:指定当前bean使用那个工厂创建-->
<bean id="person2" factory-bean="instanceFactory" factory-method="getPerson" />
@Test
public void test04() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Person person1 = context.getBean("person2", Person.class);
    System.out.println(person1);
}
/**
 * 运行结果:
 * 实例工厂方法创建对象开始
 * Person{姓名='李四', 性别='女', 年龄=20', 邮箱=2222222@qq.com}
 */

14、FactoryBean创建Bean

1、FactoryBean是一个工厂类接口,可以通过实现该接口定制实例化Bean 的逻辑。
2、提供了三种方法:
3、注意:
/**
 * @Date: 2022/11/15
 * 自定义FactoryBean,实现FactoryBean接口,Spring会自动调用工厂方法创建实例
 */
public class CustomFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        System.out.println("自定义FactoryBean工厂");
        Person person = new Person("王武", 40);
        person.setSex("男");
        person.setEmail("333333@qq.com");
        return person;
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
<bean id="customFactoryBean" class="com.itan.factory.CustomFactoryBean" />
@Test
public void test05() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 在获取的时候才会创建customFactoryBean类中返回的对象
    Object customFactoryBean = context.getBean("customFactoryBean");
    System.out.println(customFactoryBean.toString());
}
/**
 * 运行结果:
 * 自定义FactoryBean工厂
 * Person{姓名='王武', 性别='男', 年龄=40', 邮箱=333333@qq.com}
 */

二、生命周期回调

1、概述

1、Spring在容器初始化Bean之后(完成依赖注入后)和销毁前都提供了回调的方法,称之为生命周期的回调方法,Spring提供了三种方式来完成生命周期的回调。

2、回调接口方式

1、初始化回调:实现InitializingBean接口中的afterPropertiesSet方法。在容器为Bean设置了所有必要的属性后,该接口让Bean执行初始化工作。
2、销毁回调:实现DisposableBean接口中的destroy方法。在包含目标Bean的容器被销毁时进行回调。
3、注意:
/**
 * @Date: 2022/11/15
 * 生命周期回调
 */
public class Lifecycle implements InitializingBean, DisposableBean {
    private String name;

    private String sex;

    public Lifecycle() {
        System.out.println("Lifecycle构造方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("Lifecycle设置name属性值");
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        System.out.println("Lifecycle设置sex属性值");
        this.sex = sex;
    }
  
		@Override
    public String toString() {
        return "Lifecycle{" +
                "姓名='" + name + '\'' +
                ", 性别='" + sex +
                '}';
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Lifecycle执行初始化回调方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Lifecycle执行销毁回调方法");
    }
}
<bean id="lifecycle" class="com.itan.beans.Lifecycle">
    <property name="name" value="张三"/>
    <property name="sex" value=""/>
</bean>
@Test
public void test06() {
    // 通过配置文件创建容器对象
    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Lifecycle lifecycle = context.getBean("lifecycle", Lifecycle.class);
    System.out.println(lifecycle);
    // 销毁容器
    context.close();
}
/**
 * 运行结果:
 * Lifecycle构造方法
 * Lifecycle设置name属性值
 * Lifecycle设置sex属性值
 * Lifecycle执行初始化回调方法
 * Lifecycle{姓名='张三', 性别='男}
 * Lifecycle执行销毁回调方法
 */
通过运行结果可以看出:Spring容器启动,初始化Bean时优先执行构造方法,并且在为属性注入值之后,才会执行初始化回调方法,容器销毁时,执行销毁回调方法

3、自定义方法方式

1、<bean />标签中的init-method、destroy-method两个属性用来指定Bean初始化回调和销毁回调方法的。
/**
 * @Date: 2022/11/15
 * 生命周期回调
 */
public class Lifecycle1 {
    private String name;

    private String sex;

    public Lifecycle1() {
        System.out.println("Lifecycle1构造方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("Lifecycle1设置name属性值");
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        System.out.println("Lifecycle1设置sex属性值");
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Lifecycle1{" +
                "姓名='" + name + '\'' +
                ", 性别='" + sex +
                '}';
    }

    public void init() throws Exception {
        System.out.println("Lifecycle1执行初始化回调方法");
    }

    public void destroy() throws Exception {
        System.out.println("Lifecycle1执行销毁回调方法");
    }
}
<bean id="lifecycle" class="com.itan.beans.Lifecycle1" init-method="init" destroy-method="destroy">
    <property name="name" value="张三"/>
    <property name="sex" value=""/>
</bean>
@Test
public void test06() {
    // 通过配置文件创建容器对象
    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Lifecycle1 lifecycle = context.getBean("lifecycle", Lifecycle1.class);
    System.out.println(lifecycle);
    // 销毁容器
    context.close();
}
/**
 * 运行结果:
 * Lifecycle1构造方法
 * Lifecycle1设置name属性值
 * Lifecycle1设置sex属性值
 * Lifecycle1执行初始化回调方法
 * Lifecycle1{姓名='张三', 性别='男}
 * Lifecycle1执行销毁回调方法
 */

4、注解方式

1、Spring2.5中引入了注释的支持,可以使用@PostConstruct和@PreDestroy注解实现初始化回调和销毁回调。
/**
 * @Date: 2022/11/15
 * 生命周期回调
 */
@Component
public class Lifecycle2 {
    private String name;

    private String sex;

    public Lifecycle2() {
        System.out.println("Lifecycle2构造方法");
    }

    @Override
    public String toString() {
        return "Lifecycle2{" +
                "姓名='" + name + '\'' +
                ", 性别='" + sex +
                '}';
    }

    @PostConstruct
    public void init() throws Exception {
        this.name = "张三";
        this.sex = "男";
        System.out.println("Lifecycle2执行初始化回调方法");
    }

    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("Lifecycle2执行销毁回调方法");
    }
}
@Test
public void test06() {
  	// 通过注解创建容器对象,配置扫描的类
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.itan.beans");
    Lifecycle2 lifecycle = context.getBean("lifecycle2", Lifecycle2.class);
    System.out.println(lifecycle);
    // 销毁容器
    context.close();
}
/**
 * 运行结果:
 * Lifecycle2构造方法
 * Lifecycle2执行初始化回调方法
 * Lifecycle2{姓名='张三', 性别='男}
 * Lifecycle2执行销毁回调方法
 */

5、生命周期回调方法总结

1、可以将上面3种方式结合使用,如果多种方式一起使用,每种方式都配置了一个不同的方法名,那么他们的执行顺序将会如下面的顺序所示。但是如果他们都配置了同一个方法名,那么该方法只会执行一次。
2、同一个Bean配置多个生命周期机制,初始化方法不同,调用顺序如下:
3、Destroy方法的调用顺序相同:

6、BeanPostProcessor容器扩展点

1、BeanPostProcessor接口也叫后置处理器,其作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加自定义逻辑
2、方法说明:
3、注意:
  • 两个方法不能返回null,如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象,因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中
/**
 * @Date: 2022/11/17
 * 自定义BeanPostProcessor
 */
public class CustomBeanPostProcessor implements BeanPostProcessor {
    /**
     * bean在调用初始化方法之前调用
     * @param bean			bean实例
     * @param beanName		bean的名称
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ":将要调用初始化方法了");
        return bean;
    }

    /**
     * bean在调用初始化方法之后调用
     * @param bean			bean实例
     * @param beanName		bean的名称
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ":初始化方法执行完了");
        return bean;
    }
}
<bean id="lifecycle" class="com.itan.beans.Lifecycle1" init-method="init" destroy-method="destroy">
    <property name="name" value="张三"/>
    <property name="sex" value=""/>
</bean>

<bean id="customBeanPostProcessor" class="com.itan.beans.CustomBeanPostProcessor" />
@Test
public void test07() {
    // 通过配置文件创建容器对象
    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    // 销毁容器
    context.close();
}
/**
 * 运行结果:
 * Lifecycle1构造方法
 * Lifecycle1设置name属性值
 * Lifecycle1设置sex属性值
 * lifecycle:将要调用初始化方法了
 * Lifecycle1执行初始化回调方法
 * lifecycle:初始化方法执行完了
 * Lifecycle1执行销毁回调方法
 */

三、基于XML的自动装配

1、概述

1、Spring支持某些情况下对于依赖自动注入的情况,通过检查ApplicationContext的内容,可以让Spring自动解析、配置Bean的依赖,不需要手写配置文件。
2、自动装配的优点:
3、注意:仅支持自定义类型的Bean进行自动装配,不支持基本数据类型的自动装配

2、自动装配的几种模式

1、使用基于XML的配置元数据时,可以通过设置< bean />标签的autowire属性来设置自动注入模式,共有四种:

3、通过名称装配

public class Room {
    private int length;

    private int width;

    // 依赖其他的属性
    private Bed bed;

    public void setLength(int length) {
        this.length = length;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setBed(Bed bed) {
        this.bed = bed;
    }

    @Override
    public String toString() {
        return "Room{" +
                "长='" + length + '\'' +
                ", 宽='" + width + '\'' +
                ", 床=" + bed +
                '}';
    }
}
public class Bed {
    private String brand;

    private float price;

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Bed{" +
                "品牌='" + brand + '\'' +
                ", 价格='" + price +
                '}';
    }
}
<bean id="bed" class="com.itan.beans.Bed">
    <property name="brand" value="全友床垫" />
    <property name="price" value="1399" />
</bean>
<!--通过autowire属性自动注入依赖的bean-->
<bean id="room" class="com.itan.beans.Room" autowire="byName">
    <property name="length" value="8" />
    <property name="width" value="5" />
</bean>
@Test
public void test08() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Room room = context.getBean("room", Room.class);
    System.out.println(room);
}
/**
 * 运行结果:
 * Room{长='8', 宽='5', 床=Bed{品牌='全友床垫', 价格='1399.0}}
 */
注意:如果存在多个相同类型的Bean,并且自动装配类型设置为byType,那么会抛出如下错误:

在这里插入图片描述

4、通过构造器装配

1、构造器自动装配流程:
2、相比较byName与byType方式的自动装配来说,构造器方式装配不会报错。

在这里插入图片描述

public class Room {
    private int length;

    private int width;

    // 依赖其他的属性
    private Bed bed;
    
    // 空参构造
    public Room() {}

    // 构造器
    public Room(Bed bed) {
        System.out.println("构造注入bed");
        this.bed = bed;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setBed(Bed bed) {
        this.bed = bed;
    }

    @Override
    public String toString() {
        return "Room{" +
                "长='" + length + '\'' +
                ", 宽='" + width + '\'' +
                ", 床=" + bed +
                '}';
    }
}
<bean id="bed" class="com.itan.beans.Bed">
    <property name="brand" value="全友床垫" />
    <property name="price" value="1399" />
</bean>
<bean id="bed1" class="com.itan.beans.Bed">
    <property name="brand" value="联乐床垫" />
    <property name="price" value="999" />
</bean>
<!--通过autowire属性自动注入依赖的bean-->
<bean id="room" class="com.itan.beans.Room" autowire="constructor">
    <property name="length" value="8" />
    <property name="width" value="5" />
</bean>
@Test
public void test08() {
    // 通过配置文件创建容器对象
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
    Room room = context.getBean("room", Room.class);
    System.out.println(room);
}
/**
 * 运行结果:
 * 构造注入bed
 * Room{长='8', 宽='5', 床=Bed{品牌='全友床垫', 价格='1399.0}}
 */

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

visio绘图小技巧

1.如何在图框的任意位置添加点&#xff1f; 先选中x点指令&#xff0c;再按住ctrl键&#xff0c;即可在任意位置画点 2.如何画出锯齿形线段&#xff1f; visio里面好像没有现成的锯齿形线段&#xff0c;所以可以利用直线反复折画&#xff0c;但是这里有个小技巧&#xff0c;就…

[附源码]JAVA毕业设计小王防疫副食品配送商城(系统+LW)

[附源码]JAVA毕业设计小王防疫副食品配送商城&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项…

获取网易云音乐开放接口api的推荐歌单

网易云音乐开放api接口 网址&#xff1a;https://binaryify.github.io/NeteaseCloudMusicApi/#/?idneteasecloudmusicapi 项目地址&#xff1a;https://github.com/Binaryify/NeteaseCloudMusicApi 下载下来之后&#xff0c;安装依赖&#xff1a;npm install 启动服务&#xf…

决策树(decision tree)

决策树构建&#xff1a;特征选择、决策树的生成和决策树的修剪。 特征选择 特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率&#xff0c;如果利用一个特征进行分类的结果与随机分类的结果没有很大差别&#xff0c;则称这个特征是没有分类能力的…

Lifeform——站在3D虚拟数字身份与元宇宙结合的风口之上

Web3和元宇宙是公认的下一个风口&#xff0c;吸引了大量用户的关注和参与。从《我的世界》中的场景编辑器&#xff0c;到DecentreLand&#xff0c;以及Sandbox中玩家对自己虚拟地块的装饰。人们在虚拟世界中举办各种活动&#xff0c;比如演唱会、节日庆祝或者比赛、游戏甚至婚礼…

PrizmDoc Viewer添加了新的Excel渲染选项

PrizmDoc Viewer添加了新的Excel渲染选项 添加了新的Microsoft Excel渲染选项。PrizmDoc Viewer现在提供了在MSO渲染模式下查看Excel文档时自动调整单元格宽度和/或高度的选项。 PrizmDoc Viewer Microsoft Office转换附加选项现在与运行在Microsoft Windows Server 2019/2022上…

谷粒商城8:分布式锁使用和springcache的整合

1.分布式锁redisson使用 ①分布式锁简介 ②分布式锁学习过程 ③最终分析 加入锁为原子操作&#xff1a;设置过期时间设置锁 删除锁为原子操作&#xff1a;业务流程删除锁 将锁的过期时间调长 ④Redisson的使用 引入依赖程序化配置使用 创建redisson客户端 Redisson.create(…

6.论文学习Modality-aware Mutual Learning for Multi-modal Medical Image Segmentation

目录一.摘要1.背景解决方法1.如何有效整合来自多模态医学图像的信息2.如何处理常见模式缺失的情况2.解决1.ML2.MA3.结论二.方法2.1模态特定模型Modality-specific Model2.2 Modality-Aware Module2.3互学策略Mutual Learning Strategy三.实验与结果3.1数据集和评估指标3.2运行细…

杨校老师课堂之Spring Boot框架面试题【开发工程师面试前必看】

1. 什么是 Spring Boot&#xff1f; Spring Boot 是 Spring 开源组织下的子项目&#xff0c;是 Spring 组件一站式解决方案&#xff0c;主要是简化了使用Spring 的难度&#xff0c;简省了繁重的配置&#xff0c;提供了各种启动器&#xff0c;使开发者能快速上手。 2. 为什么要用…

c#入门-别名引用

别名引用 如果你引用的命名空间中出现了同名的类&#xff0c;那么会无法判断你使用的类型。 当然&#xff0c;你可以使用完全限定名。 或者&#xff0c;你可以为其中一个类型指代别名。 使用引用命名空间语句&#xff0c;为一个标识符赋值一个类型。 可以使用这个标识符代替…

基于java+springmvc+mybatis+jsp+mysql的通信簿管理系统

项目介绍 前端页面&#xff1a; 功能&#xff1a;首页、日志信息、心情日志、相册信息、个人中心、后台管理 管理员后台页面&#xff1a; 功能&#xff1a;主页、个人中心、用户管理、日志信息管理、日志类别管理、心情日志管理、相册信息管理、系统管理 开发环境 开发语言&…

C#【必备技能篇】Marshal是什么?怎么用?

文章目录学习来源&#xff1a;MSDN官方文档一、Marshal是什么&#xff1f;1、英文释义&#xff1a;2、在MSDN中的定义&#xff1a;3、通俗理解&#xff1a;二、Marshal怎么用&#xff1f;【主要以一些实例来了解】1、Marshal下的方法_部分截图【C#源码】&#xff1a;2、一些示例…

自定义RBAC(3)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; RBAC类型的权限&#xff0c;本质上是一种对资源访问路径的控制&#xff0c;且具有典型的树型层次结构。而树型结构&#xff0c;天然地就有父结点和子结点的关系以…

【HarmonyOS】ArkTS Native开发——使用 system函数创建文件

ArkTS是HamronyOS优选的主力语言&#xff0c;但官方文档指南中对于Native应用开发并没有详细的描述&#xff0c;只有一篇Codelab可以学习&#xff08;简易Native C 示例&#xff08;ArkTS&#xff09; (huawei.com)&#xff09;&#xff0c;本文将在Native应用中使用C/C的syste…

计算机毕业设计ssm+vue基本微信小程序的早茶预定系统 uniapp 小程序

项目介绍 本文介绍了使用微信小程序技术开发早茶预定系统的设计与实现过程,首先对实现该系统的技术进行分析,说明选择Java后台技术和MySQL数据库的必要性,然后对基于微信小程序的早茶预定系统的需求进行分析。并接着对系统进行设计,包括架构设计、功能设计、数据库设计。最后进…

关于商业智能 BI 认知上的误区,你有几个

关于商业智能BI的介绍&#xff0c;网络上有太多的杂音&#xff0c;总而言之会把商业智能BI讲解的貌似很简单&#xff0c;感觉上买了一个工具就可以解决所有的问题&#xff0c;这其实是一个非常大的误区。 商业智能BI - 派可数据商业智能BI可视化分析平台 我这里总结了一下&…

宠物店微信小程序开发步骤_宠物店管理系统用什么做

大多数人对于动物医疗专业知识比较匮乏&#xff0c;再加上宠物医疗费用&#xff0c;日常用品都略高&#xff0c;宠物店/宠物医院的前景&#xff0c;再未来依旧可观。 相比于实体店&#xff0c;线上平台无疑有着更广阔的拓客渠道和销售前景&#xff0c;做宠物店/宠物医院小程序…

如何高效制作数据可视化大屏,大屏高效制作攻略分享,总有一款适合你

做数据可视化大屏可不是一件简单的事情&#xff0c;里面有很多技巧&#xff0c;像布局&#xff0c;配色&#xff0c;图表和素材的选用和搭配等等&#xff0c;里面有很多学问。一时半会也做不出一个大屏&#xff0c;即使你会做也需要一定的时间和精力&#xff0c;在大量的需求下…

ABAP学习笔记之——第十一章:Function ALV

一、Function ALV Function ALV 从名字中可以推出&#xff0c;是由 Function(函数)形式提供&#xff0c;因此开发者只需适当使用以有的功能即可。没有必要创建屏幕也没有必要一一写出代码&#xff0c;因此可认为是生产性比较高的方法。 例&#xff1a; *function ALV GRID和…

【测绘程序设计】——大地坐标与空间直角坐标转换

本文分享了 测绘程序设计——大地坐标(B,L,H)与空间直角坐标(X,Y,Z)转换(C#版与Python版),相关源代码(绝对完整,直接运行)与使用示例如下。 目录 Part.Ⅰ 使用示例Chap.Ⅰ C#版Chap.Ⅱ Python版Part.Ⅱ 代码分析Chap.Ⅰ C#版Chap.Ⅱ Python版Part.Ⅲ 源码下载Part.Ⅰ…