玩转Spring各种作用域Bean Scope及源码分析( 二 )

如果上面这样配置,启动服务将会报错:
Caused by: JAVA.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-6.0.7.jar:6.0.7]该错误的原因就是你在一个单例bean中注入一个request作用域的bean,而request作用域bean的生命周期是在一个web请求开始创建的,所以这里你当然是没法注入的 。
解决办法:

  • @Scope设置代理模式
@Component@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"request", proxyMode = ScopedProxyMode.TARGET_CLASS)public class Person {}测试结果
ScopeController: com.pack.scopes.Person@106a9684 - 275420804PersonService: com.pack.scopes.Person@106a9684 - 275420804ScopeController: com.pack.scopes.Person@64396678 - 1681483384PersonService: com.pack.scopes.Person@64396678 - 1681483384每次请求接口都获取的不是同一个实例 。并且在一个完整的请求中获取的Person都是同一个 。
  • 使用@RequestScope
该注解原理与上面其实一致的
@Scope(WebApplicationContext.SCOPE_REQUEST)public @interface RequestScope {@AliasFor(annotation = Scope.class)// 设置好了使用代理ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}2.4 会话(session)@Component@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"session", proxyMode = ScopedProxyMode.TARGET_CLASS)// 与request一样,必须设置代理模式或者使用下面这个注解// @SessionScopepublic class Person {}测试
ScopeController: com.pack.scopes.Person@2b56038d - 727057293PersonService: com.pack.scopes.Person@2b56038d - 727057293ScopeController: com.pack.scopes.Person@2b56038d - 727057293PersonService: com.pack.scopes.Person@2b56038d - 727057293多次访问都是同一个session;你再换个浏览器访问
ScopeController: com.pack.scopes.Person@1aa201fd - 446824957PersonService: com.pack.scopes.Person@1aa201fd - 446824957ScopeController: com.pack.scopes.Person@1aa201fd - 446824957PersonService: com.pack.scopes.Person@1aa201fd - 446824957此时对象就是一个新的了,不同的浏览器访问当然不是同一个session了 。
2.5 应用(application)@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"application", proxyMode = ScopedProxyMode.TARGET_CLASS)// @ApplicationScope// 都是web环境,所以情况都一样public class Person {}测试
360浏览器
ScopeController: com.pack.scopes.Person@6371b4b6 - 1668396214PersonService: com.pack.scopes.Person@6371b4b6 - 1668396214Chrome浏览器
ScopeController: com.pack.scopes.Person@6371b4b6 - 1668396214PersonService: com.pack.scopes.Person@6371b4b6 - 1668396214他们是同一个对象 , application作用域生命周期与整个应用一样 , 只有你关闭了服务器,在启动后才会是再重新创建的bean对象 。
3. web作用域原理3.1 注册作用域public abstract class AbstractApplicationContext {public void refresh() {postProcessBeanFactory(beanFactory);}}public class AnnotationConfigServletWebServerApplicationContext {protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);}}public class ServletWebServerApplicationContext {protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// ...registerWebApplicationScopes();}private void registerWebApplicationScopes() {WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());}}public abstract class WebApplicationContextUtils {public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {registerWebApplicationScopes(beanFactory, null);}public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,@Nullable ServletContext sc) {// 注册作用域beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());if (sc != null) {ServletContextScope appScope = new ServletContextScope(sc);beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);}}}这里每一种web作用域都有一个对应的Scope实现RequestScope,SessionScope,ServletContextScope 。


推荐阅读