深圳幻海软件技术有限公司 欢迎您!

Spring框架之Bean Scope

2023-02-28

Spring框架支持六个作用域,其中四个只有在使用web感知的ApplicationContext时才可用。Spring支持以下6中beanscopes:singleton:单例模式(默认值),在Spring容器中只会创建一个实例。prototype:原型模式,每次通过Spring容器获取bean时

Spring框架支持六个作用域,其中四个只有在使用web感知的ApplicationContext时才可用。

Spring支持以下6中bean scopes:

  • singleton:单例模式(默认值),在Spring容器中只会创建一个实例。
  • prototype:原型模式,每次通过Spring容器获取bean时,容器都会新建一个实例。
  • request:每次HTTP请求都会创建一个实例,但只在http request范围有效。
  • session:​在http session生命周期内,共享一个实例,不同session有不同的实例。
  • application:在ServletContext生命周期内,只有一个实例。
  • webSocket:在webSocket范围内只有一个实例。

Singleton scope

Spring容器默认的作用域,只有一个共享的单例bean实例被管理,id与bean定义的id匹配的bean请求,spring容器都会返回一个特定的bean实例。换句话说,当您定义一个bean为单例时,Spring IoC容器只会创建该bean的一个实例。这个实例存储在缓存中,所有后续对这个bean的请求和引用都返回缓存对象。

基于java configuration方式定义一个singleton的bean

@Configuration
public class AppConfiguration {
 @Bean
 @Scope("singleton") // default scope 
 public UserService userService(){
  return new UserService();
 }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

prototype scope

原型模式会导致每次请求都会创新一个新的bean实例,就是说当一个bean被注入到另一个bean中,或者通过getBean()方法调用请求它时会新创建一个bean实例。通常,有状态的bean使用prototype scope,无状态的bean使用singleton scope。与其它scope不同,spring容器不管理原型scope的整个生命周期,容器实例化、配置和以其他方式组装原型对象,都将其交给client,而无需进一步记录该原型实例。

基于java configuration方式定义一个prototype的bean

@Configuration
public class AppConfiguration {
 @Bean
 @Scope("prototype")
 public UserService userService(){
     return new UserService();
 }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

具有原型bean依赖项的单例bean

当您将单例bean与原型bean的依赖项一起使用时,请注意,依赖项是在实例化时解析的。因此,如果依赖项将原型注入到单例bean中,则会实例化一个新的原型bean,然后将依赖项注入到单例bean中。原型实例是唯一一个提供给单例bean的实例。

Request、Session、Application和WebSocket作用域

Request、Session、Application、WebSocket作用域仅在使用web感知的Spring ApplicationContext实现(如XmlWebApplicationContext)时可用。如果将这些作用域与常规Spring IoC容器(如ClassPathXmlApplicationContext)一起使用,则会抛出一个IllegalStateException,未知bean作用域。

鉴于目前技术发展采用前后端分离模式开发,已很少单独使用Spring web mvc模式,此处不在讲述这四个作用域,大家可以参考官方文档了解。

InitializingBean和DisposableBean

在Spring中,为了与容器bean的生命周期管理进行交互,可以实现InitializingBean和DisposableBean接口。容器初始化时执行AfterPropertieSet(),销毁是调用destroy(),以便bean在初始化和销毁bean时执行某些操作。

@PostConstruct和@PreDestroy注释通常被认为是新版Spring应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的bean没有耦合到特定于Spring的接口。

  1. 实现InitializingBean接口,在所有bean设置完properties后将运行afterPropertiesSet()。
  2. 实现DisposableBean接口,在Spring容器释放bean后运行destroy()。

InitializingBean和DisposableBean示例

在本例中,我们将使用afterPropertiesSet()方法在应用程序启动期间使用用户对象填充内存中的列表数据结构。我们还将在应用程序关闭期间使用destroy()方法从列表中删除用户对象。

package com.demo.spring;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class DatabaseInitiaizer implements InitializingBean, DisposableBean {
    private List < User > listOfUsers = new ArrayList < > ();
    @Override
    public void afterPropertiesSet() throws Exception {
        User user = new User(1, "User");
        User user1 = new User(2, "Admin");
        User user2 = new User(3, "SuperAdmin");
        listOfUsers.add(user);
        listOfUsers.add(user1);
        listOfUsers.add(user2);
        System.out.println("-----------List of users added in init() method ------------");
        for (Iterator < User > iterator = listOfUsers.iterator(); iterator.hasNext();) {
            User user3 = (User) iterator.next();
            System.out.println(user3.toString());
        }
        // save to database
    }
    @Override
    public void destroy() {
        // Delete from database
        listOfUsers.clear();
        System.out.println("-----------After of users removed from List in destroy() method ------------");
        for (Iterator < User > iterator = listOfUsers.iterator(); iterator.hasNext();) {
            User user3 = (User) iterator.next();
            System.out.println(user3.toString());
        }
        System.out.println("List is clean up ..");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
package com.demo.spring;
public class User {
    private Integer id;
    private String name;
    public User() {}
    public User(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
package com.demo.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.demo.spring")
public class AppConfig {
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

注意:@ComponentScan注解扫描指定包中所有包中包含@Component注解的类,basePackages指定包路径。

package com.demo.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        context.close();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

Output:

-----------List of users added in init() method ------------
User [id=1, name=User]
User [id=2, name=Admin]
User [id=3, name=SuperAdmin]
-----------After of users removed from List in destroy() method -------
List is clean up ..
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.