Bean的作用域及生命周期

85 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

p命名空间

配置

<bean id="studentSix" class="com.sentiment.pojo.Student"
      p:sid="111" p:sname="Sentiment" p:teacherMap-ref="studentMap"></bean>

测试

public void testByPnamesapce(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Student studentSix = ioc.getBean("studentSix", Student.class);
    System.out.println(studentSix);
}

spring管理数据源和引入外部属性文件

依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>
<!--数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>

配置文件

jdbc.properties用的是mybatis里的

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--引入jdbc.properties配置文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
</beans>

测试

@Test
public void dataSourceTest() throws SQLException {
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml");
    DruidDataSource bean = ioc.getBean(DruidDataSource.class);
    System.out.println(bean.getConnection());
}

image-20220916104336436.png

bean的作用域及生命周期

作用域

在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,属性如下

常规情况下同一个ioc容器获取的bean值是相等的,在比较时会返回true

public void ScopeTest(){
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml");
    Student bean1 = ioc.getBean(Student.class);
    Student bean2 = ioc.getBean(Student.class);
    System.out.println(bean1 == bean2);
}

当设置了scope标签并且值为prototype时(默认为singleton),两个bean值则会不同,返回false

<bean id="student" class="com.sentiment.pojo.Student" scope="prototype">
    <property name="sid" value="1842"></property>
    <property name="sname" value="Sentiment"></property>
</bean>

如果是在WebApplicationContext环境下会有另外两个作用域

取值含义
request在一个请求范围内有效
session在一个会话范围内有效

作用域注解: @Scope(value="singleton")

注意: 该注解可以用在@Bean标识的方法中,也可以标识在@Component标识的类中;从而表明获取到的对象为单例或多例模式

生命周期

生命周期过程

  1. 实例化
  2. 依赖注入(给对象设置属性)
  3. bean对象初始化之前的操作(由bean的后置处理器负责)
  4. 初始化:通过bean的init-method属性指定初始化方法
  5. bean对象初始化后的操作(由bean的后置处理器负责)
  6. bean对象的使用
  7. 销毁:通过bean的destroy-method属性来指定销毁的方法
  8. IOC容器的关闭

第一步是实例化,是由于ioc容器管理对象时,是通过工厂和反射获取的,所以会默认使用无参构造。

创建一个User类

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    public User() {
        System.out.println("生命周期1:实例化");
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public void setId(Integer id) {
        System.out.println("生命周期2:依赖注入");
        this.id = id;
    }
    void init(){
        System.out.println("生命周期3:初始化");
    }
    void destroy(){
        System.out.println("生命周期4:销毁");
    }
​
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + ''' +
                ", password='" + password + ''' +
                ", age=" + age +
                '}';
    }
}

初始化和销毁需要在配置文件中定义

<!--init-method表示bean初始化方法,destory-meethod表示销毁方法-->
<bean id="student" class="com.sentiment.pojo.Student" scope="prototype">
    <property name="sid" value="1842"></property>
    <property name="sname" value="Sentiment"></property>
</bean>

测试

public void test(){
    ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    User bean = ioc.getBean(User.class);
    System.out.println(bean);
    ioc.close();
}

最后的销毁部分是通过ioc.close()来完成的,而这里的用的是ConfigurableApplicationContext类型,因为ApplicationContext中没有close方法,而ConfigurableApplicationContext是他的子接口其中定义了刷新和关闭的方法。这里用原来的ClassPathXmlApplicationContext也是可以的

image-20220917130000992.png

作用域对生命周期的影响

其实当执行第一步的时候就已经,初始化了,而这里的初始化只指单例模式的

image-20220917145854081.png 如果换成多例模式即:配置中加上scope="prototype"后

此时运行便没有任何结果,而它的初始化则是在获取bean的时候生成

bean的后置处理器

在bean的声明周期过程中,初始化前后还有两个操作但是在上边并没有体现到

bean对象初始化之前的操作(由bean的后置处理器postProcessBeforeInitialization负责)
​
bean对象初始化后的操作(由bean的后置处理器postProcessAfterInitialization负责)

bean的后置处理器会在声明周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

创建bean的后置处理器:

package com.sentiment.process;
​
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
​
public class MyBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前执行——>postProcessBeforeInitialization");
        return bean;
    }
​
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后执行——>postProcessAfterInitialization");
        return bean;    }
}

配置到IOC容器中

<bean id="mybeanprocessor" class="com.sentiment.process.MyBeanProcessor"></bean>

此时在执行则会调用bean的后置处理器

image-20220917151016013.png