【Spring】Bean的作用域(单例、多例、请求、会话、Application)
文章目录
- 概念
- Bean 的作用域
- 代码测试
- 观察 Bean 的作用域
- 1. **单例作用域**
- 2. **多例作用域**
- 3. **请求作用域**
- 4. **会话作用域**
- 5. **`Application` 作用域**
概念
在 Spring Ioc&DI
戒断,我们学习了 Spring
是如何帮助我们管理对象的
- 通过
@Controller
,@Service
,@Repository
,@Component
,@Configuration
,@Bean
来声明Bean
对象 - 通过
ApplicationContext
或者BeanFactory
来获取对象 - 通过
@Autowired
,Setter
方法或者构造方法等来为应用程序注入所依赖的Bean
对象
我们来简单回顾一下
- 通过
@Bean
声明bean
,把bean
存在Spring
容器中
public class Dog { private String name; public void setName(String name) { this.name = name; }
}
@Component
public class DogBeanConfig { @Bean public Dog dog() { Dog dog = new Dog(); dog.setName("旺旺"); return dog; }
}
- 从
Spring
容器中获取Bean
- 通过在代码中直接注入
ApplicationContext
的方式
- 通过在代码中直接注入
@SpringBootTest
class ApplicationTests { @Autowired private ApplicationContext applicationContext; @Test void contextLoads() { DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);; System.out.println(dog1); } }
司改代码,从 Spring
容器中多次获取 Bean
@SpringBootTest
class ApplicationTests { @Autowired private ApplicationContext applicationContext; @Test void contextLoads() { DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);; System.out.println(dog1); DogBeanConfig dog2 = applicationContext.getBean(DogBeanConfig.class);; System.out.println(dog2); }
}
观察运行结果
发现输出的 bean
对象地址是一样的,说明每次从 Spring
容器中取出来的对象都是同一个
- 这也是“单例模式”
- 单例模式:确保一个类只有一个实例,多次创建也不会创建出多个实例
Bean
的作用域是值 Bean
在 Spring
框架中的某种行为模式
比如单例作用域:表示 Bean
在整个 Spring
中只有一份,它是全局共享的。那么当其他人修改了这个值之后,那么另一个人读到的就是被修改后的值
修改上述代码,给 UserController
添加属性 name
@SpringBootTest
class ApplicationTests { @Autowired private ApplicationContext applicationContext; @Test void contextLoads() { Dog dog1 = applicationContext.getBean(Dog.class); dog1.setName("狗狗1"); System.out.println(dog1); System.out.println(dog1.getName()); Dog dog2 = applicationContext.getBean(Dog.class);; System.out.println(dog2); System.out.println(dog2.getName()); }
}
观察运行结果:
dog1
和dog2
为同一个对象,dog2
拿到了dog1
设置的值
那能不能将 bean
对象设置为非单例的(每次获取的 bean
都是一个新对象呢)
- 这就是
Bean
的不同作用域了
Bean 的作用域
在 Spring
中支持 6 中作用域,后 4 种在 Spring MVC
环境才生效
singleton
:单例作用域- 每个
Spring Ioc
容器内同名称的Bean
只有一个实例(单例)(默认)
- 每个
prototype
:原型作用域(多例作用域)- 每次使用该
bean
时会创建新的实例(非单例)
- 每次使用该
request
:请求作用域- 每个
HTTP
请求生命周期内,创建新的实例(Web
环境中,了解)
- 每个
session
:会话作用域- 每个
HTTP Session
生命周期内,创建新的实例(Web
环境中,了解)
- 每个
Application
:全局作用域- 每个
ServletContext
生命周期里内,创建新的实例(web
环境中,了解)
- 每个
websocket
:HTTP WebSocket
作用域- 每个
WebSocket
生命周期内,创建新的实例(web
环境中,了解)
- 每个
我们来看简单的代码实现
定义几个不同作用域的 bean
@Component
public class DogBeanConfig { @Bean public Dog dog() { Dog dog = new Dog(); dog.setName("旺旺"); return dog; } @Bean @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public Dog singleDog() { Dog dog = new Dog(); return dog; } @Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Dog prototype() { Dog dog = new Dog(); return dog; } @Bean @RequestScope public Dog requestDog() { Dog dog = new Dog(); return dog; } @Bean @SessionScope public Dog sessionDog() { Dog dog = new Dog(); return dog; } @Bean @ApplicationScope public Dog applicationDog() { Dog dog = new Dog(); return dog; }
}
@RequestScope
等同于@Scope (value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@SessionScope
等同于@Scope (value =WebApplicationContext.SCOPE_SESSION, proxyMode =ScopedProxyMode.TARGET_CLASS)
@ApplicationScope
等同于@Scope (value =WebApplicationContext. SCOPE_APPLICATION, proxyMode =ScopedProxyMode. TARGET_CLASS)
proxyMode
⽤来为 springbean
设置代理 .proxyMode = ScopedProxyMode.TARGET_CLASS
表⽰这个 Bean
基于 CGLIB
实现动态代理,Request
,session
和 application
作⽤域的 Bean
需要设置 proxyMode
代码测试
测试不同作用域的 bean
取到的对象是否一样
@RestController
public class DogController { @Autowired private Dog singleDog; @Autowired private Dog prototypeDog; @Autowired private Dog requestDog; @Autowired private Dog sessionDog; @Autowired private Dog applicationDog; @Autowired private ApplicationContext applicationContext; @RequestMapping("/single") public String single(){ Dog contexDog = (Dog) applicationContext.getBean("singleDog"); return "dog: " + singleDog.toString() + ", contextDog: " + contexDog; } @RequestMapping("/prototype") public String prototype() { Dog contextDog = (Dog) applicationContext.getBean("prototypeDog"); return "dog: " + prototypeDog.toString() + ", contextDog: " + contextDog; } @RequestMapping("/request") public String request(){ Dog contexDog = (Dog) applicationContext.getBean("requestDog"); return "dog: " + requestDog.toString() + ", contextDog: " + contexDog; } @RequestMapping("/session") public String session() { Dog contextDog = (Dog) applicationContext.getBean("sessionDog"); return "dog: " + sessionDog.toString() + ", contextDog: " + contextDog; } @RequestMapping("/application") public String application() { Dog contextDog = (Dog) applicationContext.getBean("applicationDog"); return "dog: " + applicationDog.toString() + ", contextDog: " + contextDog; }
}
- 每个请求都获取两次
Bean
@Autowired
和applicationContext.getBean("singleDog")
都是从Spring
容器中获取对象
观察 Bean 的作用域
1. 单例作用域
地址: http://127.0.0.1:8080/single
多次访问,得到的都是同一个对象,并且 @Autowired
和 applicationContext.getBean()
也是同一个对象
2. 多例作用域
地址: http://127.0.0.1:8080/prototype
观察 ContextDog
,每次获取的对象都不一样(注入的对象在 Spring
容器启动时,就已经注入了,所以多次请求访问也不会发生变化)
3. 请求作用域
地址: http://127.0.0.1:8080/request
在一次请求中,@Autowired
和 applicationContext.getBean()
也是同一个对象,但是每次请求,都会重新创建对象
4. 会话作用域
地址: http://127.0.0.1:8080/session
在一个 session
中,多次请求,获取到的对象都是同一个
换一个浏览器访问,发现会重新创建对象(另一个 session
)
5. Application
作用域
地址: http://127.0.0.1:8080/application
在一个应用中,多次访问都是同一个对象
Application scope
就是对于整个web
容器来说,bean
的作用域是ServletContext
级别的- 这个和
singleton
有点类似,区别在于:Application scope
是ServletContext
的单例,singleton
是一个ApplicationContext
的单例 - 在一个
web
容器中ApplicationContext
可以有多个(了解即可)