关于 Bean 的作用域用的场景并不是很多, 我们只需要掌握其概念, 了解每个作用域的应用场景即可, 当然也会举一个简单的🌰进行分析;
注意: 后四中是基于 Spring MVC 生效.
关于 Bean 的作用域的内容有些头皮发麻, 不用太担心, 只需知道怎么一回事即可.
描述: 该作用域下在 IoC 容器中只存在一个实例, 获取 Bean 及装配 Bean 都是同一个对象; 这里需要说明 Bean 默认情况下是单例状态, 也就是所有人的使用都是同一个对象, 使用单例可以很大程度上提高性能, 因此在 Spring 中 Bean 的作用域默认是单例的;
使用场景: 当 Bean 对象的属性状态不需要更新的情况下使用该作用域, 也就是说无状态的 Bean 使用该作用域.
描述: 每次对该作用域下的 Bean 的请求都会创建新的实例: 获取 Bean 及装配 Bean 都是新的对象实例;
使用场景: 当 Bean 对象的属性状态需要更新的情况下使用该作用域, 也就是说有状态的 Bean 使用该作用域.
描述: 每次 Http 请求都会创建新的 Bean 实例, 与 prototype 类似;
使用场景: 一次 http 的请求和响应的共享 Bean.
描述: 在一个 Http Session 中定义一个 Bean 实例;
使用场景: 用户回话的共享 Bean, 例如记录一个用户的登录状态.
描述: 在一个 Http Servlet Context 中定义了一个 Bean 实例;
使用场景: Web 应用的上下文信息, 例如记录一个应用的共享信息.
描述: 在一个 Http WebSocket 的生命周期中, 定义一个 Bean 实例;
使用场景: WebSocket 的每次会话中, 保存了一个 Map 结构的头信息, 将用来包裹客户端消息头, 第一次初始化后, 直到 WebSocket 结束都是同一个 Bean.
🌰:
我们以 prototype 为例简单说一下:
第一步: 首先定义一个 bean:
@Component
public class UserBean {
@Bean(name = "user1")
public User user1() {
// 查询数据库并返回对象
// 这里我们使用伪代码来构建一个对象
User user = new User();
user.setId(1);
user.setName("Java");
user.setAge(18);
return user;
}
}
第二步: 在 controller 中写两个方法:
方法一: 将 User 对象里面的 name 改成 “C++”
@Component
public class BeanScope1 {
@Autowired
private User user1;
public User getUser() {
User user = user1;
user.setName("C++");
return user1;
}
}
方法二: 不做改变
@Component
public class BeanScope2 {
@Autowired
private User user1;
public User getUser() {
return user1;
}
}
第三步: 在启动类里面获取到 Bean 的实例:
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
User user1 = beanScope1.getUser();
System.out.println("BeanScope1: " + user1);
BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
User user2 = beanScope2.getUser();
System.out.println("beanScope2: " + user2);
}
}
因为我们没有修改其作用域, 上文已经说了如果没有修改作用域的话, Spring 默认 Bean 的作用域是单例模式, 也就是 singleton 模式, 因此运行后的结果如下:
第四步: 如果这里我们将单例作用域改成多例作用域, 也就是加上 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE), 只需要在UserBean 里面加上, 如下:
@Component
public class UserBean {
@Bean(name = "user1")
@Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE)
public User user1() {
// 查询数据库并返回对象
// 这里我们使用伪代码来构建一个对象
User user = new User();
user.setId(1);
user.setName("Java");
user.setAge(18);
return user;
}
}
这时候其他地方不变, 运行结果则如下:
注意
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- @Scope(“prototype”)
推荐使用第一种写法.
以上就是 Bean 的 作用域有关的内容.
关于 Bean 的生命周期比较繁琐, 总结如下:
- 执行各种通知, 各种 Aware;
- 执行初始化的前置方法;
- 执行构造方法, 这里有两种执行方式, 一种是执行 @PostConstruct, 另一种是 init-method;
- 执行初始化的后置方法.
- @PreDestroy;
- 重写 DisposableBean 接口方法;
- destory-method.
看完其生命周期是不是更头皮发麻!!!
还是以一个生活中的🌰来描述一下吧!!!
例如我们要买一套房子:
补充: 也可看如下流程图: