Spring中Bean的作用域与生命周期

image-20210907110707170

image-20210907112258695

作用域

singleton

Spring中的默认的作用域为singleton,即单例模式。

在xml配置文件中的bean标签属性可以省略scope="singleton"的配置。

对于同一个实现类无论调用多少次getBean()方法,只会生产一个bean对象。

例如下面代码:

// 创建一个接口
public interface UserDao {
    public void say();
}
// 创建接口的实现类
public class UserDaoImpl implements UserDao {

    @Override
    public void say() {
        System.out.println("userDaoImpl say ...");
    }
}

编写xml配置文件,默认可以省略scope属性,因为默认为singleton实例。

也可以使用注解的方式规定作用域。@scope("singleton")

<?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">


    <!-- 默认是scope="singleton",可以省略不写 -->
    <bean class="top.lukeewin.scope.UserDaoImpl" id="user" scope="singleton"></bean>
</beans>
// 创建测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SingletonTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean01.xml");
        UserDao user = (UserDao) ctx.getBean("user");
        UserDao user2 = (UserDao) ctx.getBean("user");
        System.out.println(user == user2);
        user.say();
    }
}

运行结果:

image-20210907115517590

从运行结果中可以得出结论:作用域定义为singleton时,只会产生一个实例,并且该实例由Spring容器产生,也由Spring管理。

prototype

原型模式。该模式下,只会由Spring创建,而管理交给客户代码。

xml配置文件中bean标签scope属性值为proptotype。在该模式下,每获取一个Bean对象都会重新创建。

代码如下:

我这里只写配置文件,其它的和上面的代码一样。

在原来的基础上增加一下代码

也可以使用注解的方式规定作用域:@scope("prototype")

<bean class="top.lukeewin.scope.UserDaoImpl" id="userDao" scope="prototype"></bean>

然后编写测试类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SingletonTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean01.xml");
        // singleton
        UserDao user = (UserDao) ctx.getBean("user");
        UserDao user2 = (UserDao) ctx.getBean("user");

        // prototype
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        UserDao userDao2 = (UserDao) ctx.getBean("userDao");

        System.out.println(user == user2);
        System.out.println(userDao == userDao2);
        user.say();
    }
}

运行结果:

image-20210907120710124

从运行结果中可以得出结论:与singleton不同的是,作用域定义为prototype时,每次获取到的Bean实例都是不一样的。并且是由Spring产生,但是产生后就不属于Spring管理了,而是交给客户代码管理。

request

当一个bean的作用域为request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。

session

当一个bean的作用域为session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

globalSession

当一个bean的作用域为globalSession,表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

生命周期

Spring中Bean的实例化过程:

image-20210907121743026

Bean的生命周期:

image-20210907121838830

Bean实例化生命周期的执行过程如下:

  • Spring对bean进行实例化,默认bean是单例;
  • Spring对bean进行依赖注入;
  • 如果bean实现了BeanNameAware接口,Spring将bean的名称传递给setBeanName()方法;
  • 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
  • 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization()方法将被调用;
  • 如果bean中有方法添加了@PostConstruct注解,那么该方法将被调用;
  • 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet()接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
  • 如果在xml文件中通过标签的init-method元素指定了初始化方法,那么该方法将被调用;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization()接口方法将被调用;
  • 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
  • 如果bean中有方法添加了@PreDestroy注解,那么该方法将被调用;
  • 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;

scope="singleton"时,默认情况下,会在Spring容器启动时实例化并完全初始化。如果想要延迟初始化,可以使用lazy-init="true"来延迟初始化。

scope="prototype"时,默认情况下,只会实例化,并不会初始化,而是在请求bean时才初始化。在第一次请求每一个prototype的bean时,Spring容器都会调用其构造器创建这个对象,然后调用init-method属性值中所指定的方法。对象销毁的时候,Spring容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。

Q.E.D.


热爱生活,热爱程序