以前一直只知道spring bean有做用域,没有怎么关注具体内容,今天特地看了,记录过程以做备忘。java
做用域总计5种:singleton, prototype, request, session, global session
其中singleton, prototype为常规bean中均可以使用,默认为singleton;request, session, global session为web中特有,主要配置在如controller,action中具体配置方式有多种,此例中以springboot为示例web
spring 容器中只存在一个具体实例,如
定义UserServiceImplspring
@Service public class UserServiceImpl implements IUserService {}
@RestController @RequestMapping("/user") public class UserController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private IUserService userService; @RequestMapping("/{id}") @ResponseBody public User get(@PathVariable(required = false) Long id) { logger.info("userService:{}", userService.toString()); return userService.get(1l); } } @RestController @RequestMapping("/test") public class TestController { private static final Logger logger = LoggerFactory.getLogger(TestController.class); @Autowired private IUserService userService; @GetMapping("/user") public User testUser() { logger.info("testController:{}", this.toString()); logger.info("userService:{}", userService.toString()); return userService.get(1l); } }
结果:在调用两个地址后,userService.toString()打印出来的结果是一致的
代码以下:
改变服务提供方 UserServiceImpl安全
@Service @Scope(value = "prototype") public class UserServiceImpl implements IUserService {}
再次调用springboot
结果:为两个调用者生成不一样的实例,但同一个调用者只生成一个
可在scope中加入 proxyMode= ScopedProxyMode.TARGET_CLASS网络
改变服务提供方 UserServiceImplsession
@Service @Scope(value = "prototype",proxyMode= ScopedProxyMode.TARGET_CLASS) public class UserServiceImpl implements IUserService {}
再次调用app
结果:每一次请求(调用),生成了不一样的实例
2018-3-26更新开始
若是调用者为原型模式,即ide
@Scope(value = "prototype") public class UserController {...}
则UserServiceImpl
设置为@Scope(value = "prototype")
便可,无需配置proxyMode= ScopedProxyMode.TARGET_CLASS
ui
2018-3-26更新结束
request, session, global session为web中特有,做用域大体对应HTTP中的Request, Session和Application,本例只以request为例
保持上面代码修改UserController,代码以下:
@RestController @RequestMapping("/user") @Scope(value = "request") public class UserController { ... }
再次调用
结果:每次调用会生成新的UserController 实例
@Scope(value = "request")
只能用于 controller/restcontroller 相似web组件中,如用于service中会出现以下异常:
Error creating bean with name 'userServiceImpl': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
网络中有说singleton的bean不能引用prototype的bean,经实验,是能够引用,但能力有限,未能明确引用是否会有线程安全或其余问题
此处仅作引用示例,代码以下
修改UserController,代码以下:
@RestController @RequestMapping("/user") public class UserController { ... }
修改
@Service @Scope(value = "prototype",proxyMode= ScopedProxyMode.TARGET_CLASS) public class UserServiceImpl implements IUserService {}
再次调用
结果:在singleton的bean能够引用prototype的bean