Spring5 全家桶 | 09 - Spring Framework 5.3

221 阅读4分钟

一、Bean生命周期

生命周期:即Bean从初始化到销毁的整个过程 在Tesla实体中增加两个方法,初始化方法和销毁方法

public class Tesla {

    private String name;
    private String battery; // 电池
    private String engine; // 电机
    private String chassis; //底盘
    private String bodywork; // 车身
    private String owner; // 车主

    public void init(){
        System.out.println(this.getClass().getName() + "init方法被调用");
    }

    public void destroy(){
        System.out.println(this.getClass().getName() + "destroy方法被调用");
    }
    
    // 此处省略getter/setter/toString方法
}    

在bean_life_cycle.xml中配置Tesla,初始化方法使用init-method属性表示,销毁方法使用destroy-method表示

<?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:util="http://www.springframework.org/schema/util"
       xmlns:p = "http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd">

    <bean id="tesla" class="com.citi.entity.Tesla" init-method="init" destroy-method="destroy">
        <property name="name" value="tesla"></property>
        <property name="owner" value="musk"></property>
    </bean>
</beans>

新增测试类BeanLifeCycleTest,增加测试方法

public class BeanLifeCycleTest {

    @Test
    public void testGetBean(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean_life_cycle.xml");

        System.out.println("IoC容器创建完成");

        Tesla tesla = context.getBean("tesla",Tesla.class);
        System.out.println(tesla);
        context.close();
    }

}

控制台打印,初始化方法和销毁方法都被调用

image.png 修改xml中Bean的配置为多实例

<bean id="tesla" scope="prototype" class="com.citi.entity.Tesla" init-method="init" destroy-method="destroy">
    <property name="name" value="tesla"></property>
    <property name="owner" value="musk"></property>
</bean>

执行测试方法,控制台打印如下,多实例情况下只会调用初始化方法,不会调用销毁方法

image.png

  • 多实例获取Bean的时候才会调用初始化方法,容器关闭不会调用destroy方法
  • 单实例容器创建完成之前会调用初始化方法,Bean会在容器创建前创建,容器关闭时会调用Bean的destroy方法

Bean的后置处理器

BeanPostProcessor后置处理器接口可以定义Bean初始化前后执行的方法,新增config包,增加自定义的BeanPostProcessor

public class CustBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "初始化前调用该方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "初始化后调用该方法");
        return bean;
    }
}

在bean_life_cycle.xml增加CustBeanPostProcessor的xml配置,再增加一个 Person实体类的bean xml配置

<bean id="stark" class="com.citi.entity.Person">
    <property name="lastName" value="stark"></property>
</bean>
<bean class="com.citi.config.CustBeanPostProcessor"></bean>

执行测试方法,每个Bean初始化前后都调用了后置处理器,后置处理器对容器中的每一个Bean都起作用 image.png

管理连接池及引用外部配置文件

以数据库连接池为例,数据库连接池最好是单例模式的,一个连接池中有多个数据库连接.

使用Spring Bean配置文件创建和管理数据库连接池

首先增加数据驱动及连接池的依赖

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.16</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.14</version>
</dependency>

在connection_config.xml中增加数据库连接池的配置

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="initialSize" value="5"/>
    <property name="maxActive" value="20"/>
</bean>

增加测试类

public class JdbcConnectionTest {

    @Test
    public void testGetBean() throws SQLException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:connection_config.xml");

        System.out.println("IoC容器创建完成");

        DruidDataSource dataSource = context.getBean("dataSource",DruidDataSource.class);
        System.out.println(dataSource);
        System.out.println(dataSource.getConnection());
    }
}

数据库连接被成功创建 image.png

引用外部配置文件

在resources目录下创建数据库信息配置文件datasource.properties

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username=root
password=root
initialSize=5
maxActive=20

修改connect_config.xml中的配置,增加引用外部配置文件的配置,并修改连接池的配置

<!--引用外部配置文件-->
<context:property-placeholder location="classpath:database.properties"></context:property-placeholder>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${driverClassName}"/>
    <property name="url" value="${url}" />
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    <property name="initialSize" value="${initialSize}"/>
    <property name="maxActive" value="${maxActive}"/>
</bean>

执行测试方法,成功获取数据库连接

image.png

基于XML的自动装配

在之前章节中为各种属性正确赋值都是通过property标签进行手动赋值,使用bean标签中的autowire属性可以设置自动赋值,autowire配置的含义

  • default:不自动装配,默认配置
  • byName:以属性名作为ID进行赋值
  • byType: 按照类型进行自动赋值,如果有多个相同类型的Bean在容器中会会报错
  • constructor: 使用有参构造器进行赋值,先按照够再起的参数类型进行装配,如果容器中不存在car,直接赋值null,存在多个使用属性名作为区分

byName

在autowire.xml中增加配置

<bean id="car" class="com.citi.entity.Car">
    <property name="carName" value="JAUGAR"></property>
    <property name="color" value="英国绿"></property>
    <property name="price" value="580000"></property>
</bean>

<bean id="stark" class="com.citi.entity.Person" autowire="byName">
    <property name="lastName" value="stark"></property>
</bean>

Person实体类中有一个属性名为car,autowire使用byName配置可以自动将容器id为car的Bean自动赋值为Person

增加测试类AutowireBeanTest,测试方法testGetBean()

public class AutowireBeanTest {

    @Test
    public void testGetBean(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:autowire.xml");

        System.out.println("IoC容器创建完成");

        Person stark = context.getBean("stark",Person.class);
        System.out.println(stark);
    }
}

已成功将容器中的car自动装配的Person中的car,如果容器中不存在car,则无法自动赋值,Person bean中的car属性为null image.png

byType

将xml配置中的byName改为byType,再次执行测试,可以将Person实体类中的Car属性及Map中的Car属性都自动装配上以及一些环境变量

image.png

如果容器中有多个Car,在xml中多配置一个Car Bean标签,执行测试

image.png

constructor

Person实体类中增加只包含Car的有参构造器

public Person(Car car) {
    System.out.println("只包含Car的有参构造器被调用");
    this.car = car;
}

autowire.xml中只保留一个car的xml配置,将person的xml配置中的autowire属性改为constructor,执行测试方法

image.png 如果存在多个car bean,增加一个car配置

<bean id="car01" class="com.citi.entity.Car">
    <property name="carName" value="F-Type"></property>
    <property name="color" value="英国绿"></property>
    <property name="price" value="580000"></property>
</bean>

执行测试,成功赋值name为JAUGAR的car,当存在多个同类型的Bean时,优先按照属性名进行自动赋值 image.png

自动装配即自动赋值

Spring 表达式 (SpEL)

使用#{}表示Spring 表达式

  • 支持使用字面量
  • 支持引用其他Bean及Bean的属性
  • 支持调用静态及非静态方法
  • 支持所有的运算符 xml配置文件中修改Person xml配置
<bean id="stark" class="com.citi.entity.Person" autowire="constructor">
    <property name="lastName" value="stark"></property>
    <property name="age" value="#{1*3*4*5}"></property>
    <property name="car" value="#{car}"></property>

    <!--调用静态方法-->
    <property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>

    <property name="gender" value="#{car.getCarName()}"></property>
</bean>

执行测试方法,各属性使用EL表达式成功赋值 image.png