1. 引言
在Spring框架中,Bean的多例作用域(prototype)提供了一种重要的实例化模式,它允许每次从Spring容器中请求Bean时都创建一个新的实例。这种模式在处理需要频繁创建和销毁实例的场景,以及不希望在不同请求或会话之间共享状态的Bean时特别有用。本文将通过深度剖析多例作用域的原理,并结合实战案例,展示其在实际开发中的应用。
2. 多例(Prototype)作用域概述
多例作用域是Spring Bean的一种非默认作用域,表示每次从Spring容器中请求Bean时,都会创建一个新的Bean实例。这种作用域适用于需要频繁创建和销毁实例的Bean,以及不希望在不同请求或会话之间共享状态的Bean。
3. 多例作用域的特性
- 非单例性:与单例(singleton)作用域不同,多例作用域中的Bean是非单例的,即每次从容器中获取Bean时都会创建一个新的实例。
- 状态独立性:由于每次请求都会生成新的Bean实例,因此多例作用域中的Bean具有状态独立性,不会受到其他请求或会话的影响。
- 性能考虑:虽然多例作用域提供了状态独立性和灵活性,但由于需要频繁创建和销毁Bean实例,因此在使用时需要考虑性能问题。对于需要大量实例的场景,可以通过合理的缓存和池化策略来优化性能。
4. 多例作用域的用法
- XML配置:在XML配置文件中,可以通过
<bean>
标签的scope
属性设置为prototype
来指定Bean的作用域为多例。
<bean id="person" class="org.example.Person" scope="prototype" />
- 注解配置:在注解配置中,可以使用
@Scope
注解来指定Bean的作用域为多例。
@Component
@Scope("prototype")
public class Person {
// ...
}
5. 实战案例
案例一:DTO(数据传输对象)的创建
在Web应用中,DTO常用于在层与层之间传递数据。由于DTO通常只用于单次请求,且不希望被其他请求或会话共享,因此非常适合使用多例作用域。
@Component
@Scope("prototype")
public class UserDTO {
private String username;
private String email;
// getters and setters...
}
// 在服务层中,每次需要传递用户数据时,都可以从容器中获取一个新的UserDTO实例
@Service
public class UserService {
@Autowired
private ApplicationContext applicationContext;
public UserDTO getUserData() {
UserDTO userDTO = applicationContext.getBean(UserDTO.class);
// populate userDTO with data...
return userDTO;
}
}
案例二:线程安全的Bean
在多线程环境下,如果Bean的状态被多个线程共享,可能会导致数据不一致的问题。通过使用多例作用域,可以为每个线程创建一个独立的Bean实例,从而实现线程安全。
@Component
@Scope("prototype")
public class ThreadSafeBean {
// state variables...
public void executeTask() {
// task execution logic that requires thread safety...
}
}
// 在线程池或并发处理中,可以为每个任务注入一个新的ThreadSafeBean实例
@Service
public class TaskExecutor {
@Autowired
private ApplicationContext applicationContext;
public void executeTasks() {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int taskId = i;
executorService.submit(() -> {
ThreadSafeBean threadSafeBean = applicationContext.getBean(ThreadSafeBean.class);
threadSafeBean.executeTask();
// ...
});
}
executorService.shutdown();
}
}
6. 源码分析
- Scope接口
Scope
接口定义了作用域的基本行为,包括获取对象、移除对象等操作。
public interface Scope {
Object get(String name, ObjectFactory<?> objectFactory);
Object remove(String name);
// ...其他方法
}
- PrototypeScope类
PrototypeScope
类实现了Scope
接口,用于处理多例作用域的逻辑。其核心方法是get,负责在每次请求时创建一个新的Bean实例。
public class PrototypeScope implements Scope, Serializable {
// ...省略其他方法
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return objectFactory.getObject(); // 直接调用ObjectFactory的getObject方法创建新实例
}
// ...其他方法实现
}
- AbstractBeanFactory中的doGetBean方法
AbstractBeanFactory
是Spring框架中Bean工厂的核心实现类之一,其doGetBean
方法负责Bean的创建和获取。对于多例作用域的Bean,会调用PrototypeScope
的get方法。
protected <T> T doGetBean(
final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
throws BeansException {
// ...省略其他代码
// 判断是否是多例作用域
if (mbd.isPrototype()) {
// 对于多例Bean,不缓存,直接创建新实例
Object prototypeInstance = null;
try {
// 调用Scope的get方法,即PrototypeScope的get方法
prototypeInstance = ((Scope) this.scopes.get(scopeName)).get(beanName, () -> {
return createBean(beanName, mbd, args);
});
}
// ...省略异常处理和返回结果代码
}
// ...省略其他代码
}
- 自定义作用域
- 除了Spring内置的作用域外,我们还可以自定义作用域。自定义作用域需要实现Scope接口,并在Spring配置中注册。
7. 总结
多例作用域在Spring框架中提供了一种重要的实例化模式,允许根据需求为每个请求或任务创建独立的Bean实例。通过实战案例和源码的展示,可以看到多例作用域在处理DTO、线程安全Bean等场景时的实用性。同时,也需要注意到多例作用域可能带来的性能问题,并通过合理的缓存和池化策略来优化性能。作为高级Java工程师,应该深入理解多例作用域的原理和用法,以便在实际开发中能够灵活应用。
标签:...,实例,多例,作用域,Spring,Bean,源码,Scope From: https://blog.csdn.net/m0_51176516/article/details/139287835