spring 获取bean_Spring中的scope有哪些有趣的场景?

作用域有几种

首先我们先说一下Spring中bean的作用域有几种,Spring中共有5中作用域,分别是:

  1. singleton(单例):Spring容器中只有一个该类型的bean,每次从容器中取的时候都返回同一个。
  2. prototype(原型):每次从Spring容器中获取该类型的bean时都会创建一个新的bean并返回。
  3. request(web请求):在一个web请求中从Spring容器获取bean时会返回一个新的,请求结束bean就销毁,每个请求对应一个bean。
  4. session(web会话):在一个web会话中从Spring容器获取bean时会返回一个新的,会话结束bean就销毁,每个会话对应一个bean。
  5. application(应用):在整个应用内从Spring容器中获取bean都会返回同一个。

通常用的最多的是单例bean,其他类型的很少用到,虽然很少用到,我们还是怀着一种好奇心来一探究竟。

看看以下这几个问题吧:

第一个问题:http请求和原型Controller的关系

当HTTP请求到来时被设置成原型模式的Controller会是同一个吗?如下代码,我们把这个Controller通过@Scope注解设置为原型模式:

@RestController()@RequestMapping("prototype")@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class PrototypeController{ @GetMapping("index") @ResponseBody public String index() { return this.toString(); }}

然后启动服务,通过http请求访问一下这个路径会发现每次请求都会返回不同的对象,如下举四个例子:

com.controller.PrototypeController@11265a97com.controller.PrototypeController@74e6e0e8com.controller.PrototypeController@6696915ecom.controller.PrototypeController@72826ab2...

说明当HTTP请求到来时每次都是一个新的Controller来处里。其实,通过Spring源码我们发现,当每一个http请求到达时,SpringMVC都会重新从容器中去获取和这个请求对应的Controller。上述例子中我们把这个Controller设置成了原型模式,所以每个请求获得的Controller都是一个新的Controller,验证的结果也说明了这一点。

单例bean依赖单例bean

那么当单例的Controller依赖单例的service时又是会怎样呢?

单例的Controller:

@RestController()@RequestMapping("singleton")public class SingletonController{ @Autowired private SingletonService singletonService; @GetMapping("index") @ResponseBody public String index() { return singletonService.toString(); }}

我们通过http请求访问这个地址,会发现返回的Controller对象和Service对象每次都一样:

com.SingletonController@e4fff13;com.SingletonServiceImpl@36186e00com.SingletonController@e4fff13;com.SingletonServiceImpl@36186e00...

这也说明了重新从Spring容器中获取的Controller不会变,这个Controller依赖的单例bean(singletonService)也不会变,其实这也是一种最简单也是工作中最常见的一种情况,bean都是按照单例设置的。那么当这个Controller依赖的service是一个原型bean的时候,我们每次发送http请求时,这个service会不会还是同一个呢?我们一起来看一下:

单例bean依赖原型bean

如下代码说明了这种情况:

单例模式下的Controller:

@RestController()@RequestMapping("singletonWithPrototypeService")public class SingletonWithPrototypeServiceController{ @Autowired private PrototypeService prototypeService; @GetMapping("index") @ResponseBody public String index() { return prototypeService.toString(); }}

原型模式下的Service:

@Service()@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class PrototypeServiceImpl implements PrototypeService {...}

我们发送多次http请求得出的结果是这个原型bean还是同一个,并不会创建新的,结果如下:

com.service.PrototypeServiceImpl@69565494com.service.PrototypeServiceImpl@69565494...

现在我们说一下为什么PrototypeService已经被声明成原型模式,每次http请求调用的这个service还是同一个呢?答案就是:因为Controller是单例。Controller是单例说明这个Controller还是同一个啊并没有发生变化,同样的它的属性也没有发生变化,说得更专业一点就是当HTTP请求到来时处理这个HTTP请求的service并不是从Spring容器中重新获取的,还是在Controller被初始化时注入的那个service。所以无论有多少个HTTP请求过来,这个service都是同一个。现在应该明白了吧。

那么既然每次单例bean A调用原型bean B这个原型B还是同一个,那么在单例bean中该如何使用原型bean呢?其实也很简单,只要重新从Spring容器中获取一个就可以了,如下代码我们实现ApplicationContextAware接口来获得一个ApplicationContext对象然后从ApplicationContext对象中重新获取一下这个service就可以了:

@RestController()@RequestMapping("singletonWithPrototypeService")public class SingletonWithPrototypeServiceController implements ApplicationContextAware{ private ApplicationContext applicationContext; @GetMapping("index") @ResponseBody public String index() { return applicationContext.getBean(PrototypeService.class).toString(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}

单例bean依赖请求域作用下的bean

当单例bean依赖请求域作用下的bean又会是什么情况呢?先提前说一下,因为是请求域下的bean,所以在一个请求过来时都会是一个新的bean,这已经很好理解了。但是当容器刚启动时,因为没有接收到HTTP请求所以这个请求域下的bean并没有创建,那么此时注入进去的bean又是什么呢?此时我们就通过spring容器启动后的一个监听器来一探究竟。我们在一个单例的Controller里注入一个请求作用域下的Service,如下:

单例Controller代码如下:

@RestController()@RequestMapping("singletonWithRequestService")public class SingletonWithRequestServiceController{ @Autowired private RequestService requestService; public RequestService getRequestService() { return requestService; }}

请求域service如下:

@Service(BeanNameConstant.RequestService)@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES)public class RequestServiceImpl implements RequestService {...}

监听器代码如下:

@Componentpublic class ApplicationOnRefreshListener implements ApplicationListener { @Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext context = event.getApplicationContext(); SingletonWithRequestServiceController controller = context.getBean(SingletonWithRequestServiceController.class); String name = controller.getRequestService().getClass().getName(); System.out.println(name); }}

当启动容器时打印出来的信息:com.sun.proxy.$Proxy53

是的你没看错,是一个代理类。容器启动时Spring会先注入一个代理类,当HTTP请求到来时真实的service会被创建而且此时代理类会调用这个真实的service的方法进行处理。

Scope注解的参数有哪些值?

第一个参数value表示这个bean的范围,一共有5个值:

1. ConfigurableBeanFactory.SCOPE_SINGLETON:单例2. ConfigurableBeanFactory.SCOPE_PROTOTYPE:原型3. WebApplicationContext.SCOPE_REQUEST:请求4. WebApplicationContext.SCOPE_SESSION:会话5. WebApplicationContext.SCOPE_APPLICATION:应用

第二个参数proxyMode表示创建代理的模式,一共有4个值:

1. ScopedProxyMode.DEFAULT:默认2. ScopedProxyMode.NO:不代理3. ScopedProxyMode.INTERFACES:jdk接口的代理4. ScopedProxyMode.TARGET_CLASS:cglib子类的代理

以上就是今天的内容,喜欢的小伙伴可以关注一下哦。有问题可以在评论区留言哦。

990a859c5c37193d9de9afbcf8518ec5.png
8641f33870acf07573737c0cf9682010.png
83c10ef042eb43d98c232af51ef8365f.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值