1

之前一直只知道spring bean有作用域,没有怎么关注具体内容,今天特意看了,记录过程以作备忘。

作用域(5类)

作用域总计5种:singleton, prototype, request, session, global session
其中singleton, prototype为常规bean中都可以使用,默认为singleton;request, session, global session为web中特有,主要配置在如controller,action中具体配置方式有多种,此例中以springboot为示例

单例模式(singleton)

spring 容器中只存在一个具体实例,如
定义UserServiceImpl

@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()打印出来的结果是一致的

图片描述

原型模式(prototype)

每次注入时提供一个新的实例

代码如下:
改变服务提供方 UserServiceImpl

@Service
@Scope(value = "prototype")
public class UserServiceImpl implements IUserService {}

再次调用

结果:为两个调用者生成不同的实例,但同一个调用者只生成一个

clipboard.png

每次调用时生成不同的实例

可在scope中加入 proxyMode= ScopedProxyMode.TARGET_CLASS

改变服务提供方 UserServiceImpl

@Service
@Scope(value = "prototype",proxyMode= ScopedProxyMode.TARGET_CLASS)
public class UserServiceImpl implements IUserService {}

再次调用

结果:每一次请求(调用),生成了不同的实例

clipboard.png

2018-3-26更新开始

如果调用者为原型模式,即

@Scope(value = "prototype")
public class UserController {...}

UserServiceImpl 设置为@Scope(value = "prototype")即可,无需配置proxyMode= ScopedProxyMode.TARGET_CLASS


2018-3-26更新结束

request

request, session, global session为web中特有,作用域大致对应HTTP中的Request, Session和Application,本例只以request为例

保持上面代码修改UserController,代码如下:

@RestController
@RequestMapping("/user")
@Scope(value = "request")
public class UserController {
...
}

再次调用

结果:每次调用会生成新的UserController 实例

clipboard.png

@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的说明

网络中有说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

clipboard.png


soft_xiang
38 声望5 粉丝