Spring Bean 作用域
作用域
| 来源 | 说明 |
|---|---|
| singleton | 默认 Spring Bean 作用域,一个 BeanFactory 有且仅有一个实例 |
| prototype | 原型作用域,每次依赖查找和依赖注入生成新 Bean 对象 |
| request | 将 Spring Bean 存储在 ServletRequest 上下文中 |
| session | 将 Spring Bean 存储在 HttpSession 中 |
| application | 将 Spring Bean 存储在 ServletContext 中 |
“singleton” Bean 作用域
配置

“prototype” Bean 作用域
配置

注意事项
• Spring 容器没有办法管理 prototype Bean 的完整生命周期,也没有办法记录示例的存在。销毁回调方法将不会执行,可以利用 BeanPostProcessor 进行清扫工作。
/**
* Bean 的作用域示例
*/
public class BeanScopeDemo implements DisposableBean {
@Bean
// 默认 scope 就是 "singleton"
public static User singletonUser() {
return createUser();
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static User prototypeUser() {
return createUser();
}
private static User createUser() {
User user = new User();
user.setId(System.nanoTime());
return user;
}
@Autowired
@Qualifier("singletonUser")
private User singletonUser;
@Autowired
@Qualifier("singletonUser")
private User singletonUser1;
@Autowired
@Qualifier("prototypeUser")
private User prototypeUser;
@Autowired
@Qualifier("prototypeUser")
private User prototypeUser1;
@Autowired
@Qualifier("prototypeUser")
private User prototypeUser2;
@Autowired
private Map<String, User> users;
@Autowired
private ConfigurableListableBeanFactory beanFactory; // Resolvable Dependency
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(BeanScopeDemo.class);
applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.printf("%s Bean 名称:%s 在初始化后回调...%n", bean.getClass().getName(), beanName);
return bean;
}
});
});
// 启动 Spring 应用上下文
applicationContext.refresh();
// 结论一:
// Singleton Bean 无论依赖查找还是依赖注入,均为同一个对象
// Prototype Bean 无论依赖查找还是依赖注入,均为新生成的对象
// 结论二:
// 如果依赖注入集合类型的对象,Singleton Bean 和 Prototype Bean 均会存在一个
// Prototype Bean 有别于其他地方的依赖注入 Prototype Bean
// 结论三:
// 无论是 Singleton 还是 Prototype Bean 均会执行初始化方法回调
// 不过仅 Singleton Bean 会执行销毁方法回调
scopedBeansByLookup(applicationContext);
scopedBeansByInjection(applicationContext);
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {
for (int i = 0; i < 3; i++) {
// singletonUser 是共享 Bean 对象
User singletonUser = applicationContext.getBean("singletonUser", User.class);
System.out.println("singletonUser = " + singletonUser);
// prototypeUser 是每次依赖查找均生成了新的 Bean 对象
User prototypeUser = applicationContext.getBean("prototypeUser", User.class);
System.out.println("prototypeUser = " + prototypeUser);
}
}
private static void scopedBeansByInjection(AnnotationConfigApplicationContext applicationContext) {
BeanScopeDemo beanScopeDemo = applicationContext.getBean(BeanScopeDemo.class);
System.out.println("beanScopeDemo.singletonUser = " + beanScopeDemo.singletonUser);
System.out.println("beanScopeDemo.singletonUser1 = " + beanScopeDemo.singletonUser1);
System.out.println("beanScopeDemo.prototypeUser = " + beanScopeDemo.prototypeUser);
System.out.println("beanScopeDemo.prototypeUser1 = " + beanScopeDemo.prototypeUser1);
System.out.println("beanScopeDemo.prototypeUser2 = " + beanScopeDemo.prototypeUser2);
System.out.println("beanScopeDemo.users = " + beanScopeDemo.users);
}
@Override
public void destroy() throws Exception {
System.out.println("当前 BeanScopeDemo Bean 正在销毁中...");
this.prototypeUser.destroy();
this.prototypeUser1.destroy();
this.prototypeUser1.destroy();
this.prototypeUser2.destroy();
// 获取 BeanDefinition
for (Map.Entry<String, User> entry : this.users.entrySet()) {
String beanName = entry.getKey();
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition.isPrototype()) { // 如果当前 Bean 是 prototype scope
User user = entry.getValue();
user.destroy();
}
}
System.out.println("当前 BeanScopeDemo Bean 销毁完成");
}
}
org.springframework.context.event.EventListenerMethodProcessor Bean 名称:org.springframework.context.event.internalEventListenerProcessor 在初始化后回调...
org.springframework.context.event.DefaultEventListenerFactory Bean 名称:org.springframework.context.event.internalEventListenerFactory 在初始化后回调...
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor Bean 名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor 在初始化后回调...
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor Bean 名称:org.springframework.context.annotation.internalCommonAnnotationProcessor 在初始化后回调...
User Bean [singletonUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:singletonUser 在初始化后回调...
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
org.geekbang.thinking.in.spring.ioc.bean.scope.BeanScopeDemo Bean 名称:beanScopeDemo 在初始化后回调...
singletonUser = User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
prototypeUser = User{id=1310441063206900, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
singletonUser = User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
prototypeUser = User{id=1310441064716800, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
singletonUser = User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}
User Bean [prototypeUser] 初始化...
org.geekbang.thinking.in.spring.ioc.overview.domain.User Bean 名称:prototypeUser 在初始化后回调...
prototypeUser = User{id=1310441065636900, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
beanScopeDemo.singletonUser = User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}
beanScopeDemo.singletonUser1 = User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}
beanScopeDemo.prototypeUser = User{id=1310441012636300, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
beanScopeDemo.prototypeUser1 = User{id=1310441014759500, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
beanScopeDemo.prototypeUser2 = User{id=1310441016456700, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}
beanScopeDemo.users = {singletonUser=User{id=1310440987738200, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='singletonUser'}, prototypeUser=User{id=1310441024264100, name='null', city=null, workCities=null, lifeCities=null, configFileLocation=null, company=null, context=null, contextAsText='null', beanName='prototypeUser'}}
当前 BeanScopeDemo Bean 正在销毁中...
User Bean [prototypeUser] 销毁中...
User Bean [prototypeUser] 销毁中...
User Bean [prototypeUser] 销毁中...
User Bean [prototypeUser] 销毁中...
User Bean [prototypeUser] 销毁中...
当前 BeanScopeDemo Bean 销毁完成
User Bean [singletonUser] 销毁中...
“request” Bean 作用域
• 配置
- XML -
- Java 注解 - @RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)
• 实现
- API - RequestScope
“session” Bean 作用域
• 配置
- XML -
- Java 注解 - @RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)
• 实现
- API - SessionScope
“application” Bean 作用域
• 配置
- XML -
- Java 注解 - @ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)
• 实现
- API - ApplicationScope
自定义 Bean 作用域
• 实现 Scope
- org.springframework.beans.factory.config.Scope
• 注册 Scope
- API - org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
- 配置
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="...">
</entry>
</map>
</property>
</bean>
/**
* ThreadLocal 级别 Scope
*/
public class ThreadLocalScope implements Scope {
public static final String SCOPE_NAME = "thread-local";
private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal("thread-local-scope") {
public Map<String, Object> initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// 非空
Map<String, Object> context = getContext();
Object object = context.get(name);
if (object == null) {
object = objectFactory.getObject();
context.put(name, object);
}
return object;
}
@NonNull
private Map<String, Object> getContext() {
return threadLocal.get();
}
@Override
public Object remove(String name) {
Map<String, Object> context = getContext();
return context.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
// TODO
}
@Override
public Object resolveContextualObject(String key) {
Map<String, Object> context = getContext();
return context.get(key);
}
@Override
public String getConversationId() {
Thread thread = Thread.currentThread();
return String.valueOf(thread.getId());
}
}
/**
* 自定义 Scope {@link ThreadLocalScope} 示例
*/
public class ThreadLocalScopeDemo {
@Bean
@Scope(ThreadLocalScope.SCOPE_NAME)
public User user() {
return createUser();
}
private static User createUser() {
User user = new User();
user.setId(System.nanoTime());
return user;
}
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(ThreadLocalScopeDemo.class);
applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
// 注册自定义 scope
beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
});
// 启动 Spring 应用上下文
applicationContext.refresh();
scopedBeansByLookup(applicationContext);
// 关闭 Spring 应用上下文
applicationContext.close();
}
private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
// user 是共享 Bean 对象
User user = applicationContext.getBean("user", User.class);
System.out.printf("[Thread id :%d] user = %s%n", Thread.currentThread().getId(), user);
});
// 启动线程
thread.start();
// 强制线程执行完成
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void scopedBeansByInjection(AnnotationConfigApplicationContext applicationContext) {
}
}
课外资料
• Spring Cloud RefreshScope 是如何控制 Bean 的动态刷新?
面试题
Spring 內建的 Bean 作用域有几种?
答:singleton、prototype、request、session、application 以及websocket
singleton Bean 是否在一个应用是唯一的?
答:否,singleton bean 仅在当前 Spring IoC 容器(BeanFactory)中是单例对象。
“application”Bean 是否被其他方案替代?
答:可以的,实际上,“application” Bean 与“singleton” Bean 没有本质区别
5594

被折叠的 条评论
为什么被折叠?



