bean的作用域和生命周期和后置处理器以及作用域对生命周期的影响~

简介: bean的作用域和生命周期和后置处理器以及作用域对生命周期的影响~

scope属性可以指定bean的作用范围:

在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参照表如下:

编写spring_test.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student_one" class="poij.student">
    <property name="sid" value="101"></property>
    <property name="sname" value="张三"></property>
    <property name="age" value="19"></property>
    <property name="gender" value="男"></property>
</bean>
</beans>

编写其中的测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import poij.student;
public class scope_test {
    @Test
    public void scopeText(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-scope.xml");
        student student1=ioc.getBean(student.class);
        student student2=ioc.getBean(student.class);
        //通过比较student1和student2是否为同一个进而判断bean对象是否为单例
        System.out.println(student1.equals(student2));
    }
}

输出:

true

在本例测试中,我们并没有设置scope属性的值,它与设置scope=“singleton”,所输出结果是一样的,都是表示获取该bean所对应的对象都是同一个

设置scope属性的值是prototype,表示获取该bean所对应的对象都不是同一个

<bean id="student_one" class="poij.student" scope="prototype">

再次进行测试,输出结果如下所示;

false

bean的生命周期

Spring容器负责管理bean的生命周期,可以在配置文件中通过配置来控制bean的创建初始化和销毁。同时,也可以通过实现BeanPostProcessor接口来自定义bean的初始化和销毁过程中的操作

第一阶段:实例化:Spring容器会根据配置信息创建bean的实例。可以通过构造函数实例化或者通过工厂方法实例化。
第二阶段:属性赋值:Spring容器会将配置文件中定义的属性值或者引用注入到bean实例中。可以通过setter方法注入属性值,或者通过@Autowired注解、@Resource注解等自动注入属性。
第三阶段:初始化:Spring容器会调用bean的初始化方法,可以自定义初始化方法。可以通过实现InitializingBean接口的afterPropertiesSet()方法,或者在配置文件中使用init-method属性指定初始化方法。
第四阶段:使用:bean对象可以被其他对象使用,执行业务逻辑。
第五阶段:销毁:Spring容器会调用bean的销毁方法,可以自定义销毁方法。可以通过实现DisposableBean接口的destroy()方法,或者在配置文件中使用destroy-method属性指定销毁方法。
第六阶段:IOC容器关闭

创建实体类user:

package projo;
public class User {
    private String username;
    private  Integer id;
    private  String password;
    private  Integer age;
    public String getUsername() {
        return username;
    }
    public Integer getId() {
        return id;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void setId(Integer id) {
        System.out.println("生命周期2:依赖注入");
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public User() {
        System.out.println("生命周期1:实例化");
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", id=" + id +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
    public User(int id,String username,String password,int age) {
        this.age=age;
        this.password=password;
        this.id=id;
        this.username = username;
    }
   //创建初始化的过程
    public void initMethod(){
        System.out.println("生命周期3:初始化");
    }
    //创建销毁的过程
    public void destroyMethod(){
        System.out.println("生命周期4:销毁");
    }
}

spring_lifecycle.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="user" class="projo.User">
            <property name="id" value="1"></property>
            <property name="username" value="张三"></property>
            <property name="password" value="124"></property>
            <property name="age" value="16"></property>
        </bean>
</beans>

创建测试类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;
public class lifecycle {
    @Test
    public void test(){
        ApplicationContext ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");
        User user=ioc.getBean(User.class);
        System.out.println(user);
    }
}

输出:

生命周期1:实例化
生命周期2:依赖注入
User{username='张三', id=1, password='124', age=16}

我们在User类中,不仅创建了生命周期,而且还创建了销毁和初始化的方法,但是在输出结果中,销毁和初始化方法中的内容并没有输出,原因是:如果我们想要调用初始化和销毁的方法,需要在spring.xml文件中通过bean标签中的init-method和destory-method属性指定的,如下所示:


<bean id="user" class="projo.User" init-method="initMethod" destroy-method="destroyMethod">

再次测试,输出结果如下所示:

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}

此时的初始化方法被调用了,但销毁方法为什么依然没调用呢?


那么销毁的方法什么时候才会执行呢?我们知道bean对象都是通过IOC容器创建和管理的,一旦IOC容器关闭就意味着对象被销毁了


那么容器怎么关闭呢?之前在java中,我们都是通过调用close方法区关闭资源,那么这里也可以吗?


一试便知,如下所示,当我们尝试调用close方法想要关闭IOC容器时,却发现没有这个方法。

原因是:ApplicationContext类中并没有提供刷新或者关闭容器的功能,而其功能是在子接口[ConfigurableApplicationContext]中提供的

解决方案:

将ApplicationContext类修改为ConfigurableApplicationContext

import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;
public class lifecycle {
    @Test
    public void test(){
      //获取IOC容器
        ConfigurableApplicationContext  ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");
        //获取bean
        User user=ioc.getBean(User.class);
        //输出bean
        System.out.println(user);
        //关闭IOC
        ioc.close();//关闭容器
    }
}

输出如下所示:

当容器关闭时,对象被成功销毁

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}
生命周期4:销毁

bean的作用域对生命周期的影响:

对上述的lifecycle类中,我们只保留获取IOC容器的代码[如下所示],那么会输出什么?

ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("applications.xml");

输出如下所示:

生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化

通过输出结果我们会发现,生命周期的前三个方法是在获取IOC容器时,就执行的,并不是我们在获取bean时,才执行的,原因是,我们当前在IOC容器中配置的bean对象,默认是单例,那么就说明,我们根据这个IOC容器获取到的永远都是一个唯一的bean对象,那么我们根本没必要在获取的时候再去创建它,直接在创建IOC容器的时候就将其创建好,以后使用的都是同一个


既然是与单例还是多例有关,那么如果我们在bean标签中将其设置为多例,bean对象的创建还能否在获取IOC容器时就执行呢?


如下所示:

将当前的bean对象通过scope属性设置为多例:

<bean id="user" class="projo.User" init-method="initMethod" destroy-method="destroyMe

我们再次运行测试类,会发现什么都没输出

那么也就是说,我们通过将bean的作用域设置为多例,每次获取到的对象都是新的,那么也就没有必要在获取IOC容器的时候将其创建好

将获取bean对象的代码添加,我发现和上述不同的时,对象创建有关的三个方法都被调用,那么足以说明,当bean对象的作用域为多例的情况下,对象的创建是在获取bean对象时完成的


由此还有一点不知道大家是否注意到,此时我们即使调用了close方法,但是销毁的方法依然没有被调用,原因是:当我们将bean对象的作用域设置为多例的时候,其销毁的方法就不由IOC容器控制了,我们必须手动调用销毁方法。

bean的后置处理器:

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


创建新的类,使其实现BeanPostProcessor接口:

package process;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
}

仅仅实现该接口,并没有实现其中的方法,我们会发现,也是没有任何的报错的,Ctrl+b跟进源码, 如下所示,我们发现该接口中的方法原来是使用default关键字修饰的,

package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

那么这两个方法到底有什么作用呢?

看方法名postProcessBeforeInitialization[初始化前的后置处理],postProcessAfterInitialization[初始化后的后置处理],下面我们对其进行重写,输出一些信息,如下所示:

package process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor--- >后置处理器postProcessBeforeInitialization");
        //此方法在bean的生命周期初始化之前执行
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("MyBeanPostProcessor--- >后置处理器postProcessAfterInitialization");
        //此方法在bean的生命周期初始化之后执行
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

你以为这就可以对bean对象进行处理了吗?当然不是,我们还需要将其配置到IOC容器中,如下所示:

<bean id="myBeanPostProcessor" class="process.MyBeanPostProcessor">

此时进行测试,输出如下:

我们发现上述在MyBeanPostProcessor类中所添加的两条语句都被输出了,那么后置处理器到底是对那个bean起作用呢?答案是:对当前IOC容器中的每个bean起作用,它会对每个bean在初始化前后都添加额外的操作,而不是针对某一个。

相关文章
|
7月前
|
XML Java 数据格式
SpringBean的生命周期
SpringBean的生命周期
72 0
|
7月前
|
前端开发 Java 开发者
Bean的生命周期和作用域
Bean的生命周期和作用域
|
7月前
|
XML Java 数据格式
Spring框架学习 -- Bean的生命周期和作用域
Spring框架学习 -- Bean的生命周期和作用域
57 2
|
7月前
|
存储 设计模式 Java
Bean 作用域和生命周期
Bean 作用域和生命周期
|
7月前
|
存储 安全 IDE
C/C++ 作用域,生命周期,执行线程的概念
C/C++ 作用域,生命周期,执行线程的概念
79 2
|
7月前
将生命周期方法添加到类中
将生命周期方法添加到类中
|
存储 安全 Java
Bean 的作用域和生命周期
Bean 的作用域和生命周期
75 1
|
XML 安全 Java
Bean作用域和生命周期
Bean作用域和生命周期
|
XML Java 数据格式
延迟初始化Bean会影响依赖注入吗
延迟初始化Bean会影响依赖注入吗
57 0
|
XML 存储 Java
Bean的作用域和生命周期(下)
Bean的作用域和生命周期(下)