目录
单例作用域(Singleton)VS 全局作用域(application)
假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,当A把这个公共Bean修改之后,会导致 B ⽤户获取到这个Bean的时候是修改之后的Bean。
案例如下:
公共Bean:
- @Component
- public class Users {
- @Bean
- public User user1() {
- User user = new User();
- user.setId(1);
- user.setName("Java"); // 【重点:名称是 Java】
- return user;
- }
- }
A用户使用时,进行了修改操作:
- @Controller
- public class BeanScopesController {
- @Autowired
- private User user1;
- public User getUser1() {
- User user = user1;
- System.out.println("Bean 原 Name:" + user.getName());
- user.setName("悟空"); // 【重点:进⾏了修改操作】
- return user;
- }
- }
B用户使用公共Bean:
- @Controller
- public class BeanScopesController2 {
- @Autowired
- private User user1;
- public User getUser1() {
- User user = user1;
- return user;
- }
- }
打印A用户和B用户所持有的公共Bean:
-
- public class BeanScopesTest {
- public static void main(String[] args) {
- ApplicationContext context =
- new ClassPathXmlApplicationContext("spring-config.xml");
- BeanScopesController beanScopesController =
- context.getBean(BeanScopesController.class);
- System.out.println("A 对象修改之后 Name:" +
- beanScopesController.getUser1().toString());
- BeanScopesController2 beanScopesController2 =
- context.getBean(BeanScopesController2.class);
- System.out.println("B 对象读取到的 Name:" +
- beanScopesController2.getUser1().toString());
- }
- }
运行结果:
原因分析:
操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中Bean 的作⽤域默认也是 singleton 单例模式。
限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值
Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:
Singleton
prototype
request
session
application
答: 可以通过 @Scope 注解来设置 Bean 的作用域,它的设置方式有以下两种
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Component;
- @Component
- public class Users {
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- @Bean(name = "u1")
- public User user1() {
- User user = new User();
- user.setId(1);
- user.setName("Java"); // 【重点:名称是 Java】
- return user;
- }
- }
所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。
Bean 的⽣命周期分为以下 5 ⼤部分:
需要注意的是:第二步和第三步不能进行调换位置,因为可能在初始化时候用到Bean的属性。
如下所示,可能Bean会在初始化时候调用自己的方法,如果没有设置属性,那么就会导致程序出错。
- @Service
- public class UserService {
- public UserService(){
- System.out.println("调⽤ User Service 构造⽅法");
- }
- public void sayHi(){
- System.out.println("User Service SayHi.");
- }
- }
- @Controller
- public class UserController {
- @Resource
- private UserService userService;
- @PostConstruct
- public void postConstruct() {
- userService.sayHi();
- System.out.println("执⾏ User Controller 构造⽅法");
- }
- }
下面举个买房子的例子,方便理解Bean的生命周期:
以下为代码演示Bean的生命周期过程:
spring-config.xml如下:(bean为BeanComponent)
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:content="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
- <content:component-scan base-package="com.java.demo">content:component-scan>
- <bean id="beanCompont"
- class="com.java.demo.component.BeanCompont" init-method="myInit" scope="prototype">bean>
- beans>
Bean(BeanComponent)如下:
- package com.java.demo.component;
-
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.BeanNameAware;
- import org.springframework.beans.factory.config.BeanPostProcessor;
-
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
-
- public class BeanCompont implements BeanNameAware, BeanPostProcessor {
- @Override
- public void setBeanName(String s) {
- System.out.println("执行了通知 BeanName -> " + s);
- }
-
- /**
- * xml 方式的初始化方法
- */
- public void myInit() {
- System.out.println("XML 方式初始化");
- }
-
- @PostConstruct
- public void doPostConstruct() {
- System.out.println("注解初始化方法");
- }
-
- public void sayHi() {
- System.out.println("执行 sayHi()");
- }
-
- @PreDestroy
- public void doPreDestroy() {
- System.out.println("do PreDestroy");
- }
-
- // 初始化前置方法
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("do postProcessBeforeInitialization");
- return bean;
- }
- // 初始化后置方法
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
- System.out.println("do postProcessAfterInitialization");
- return bean;
- }
- }
调用类:
- package com.java.demo;
-
- import com.java.demo.component.BeanCompont;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class App {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("spring-config.xml");
- BeanCompont compont = context.getBean("beanCompont", BeanCompont.class);
- compont.sayHi();
- context.close();
- }
- }
执行结果如下:
说明:这里同时用注解和xml方式进行Bean的初始化,注解方式的优先级是比较高的。