# Scope
# Scope 类型有哪些
- Singleton
- Prototype
- request
- session
- applicaion
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| @Scope("application") @Component public class BeanForApplication { public static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
@PreDestroy public void destroy() { log.info("destroy"); } }
@Scope("request") @Component public class BeanForRequest { public static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
@PreDestroy public void destroy() { log.info("destroy"); } }
@Scope("session") @Component public class BeanForSession { public static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
@PreDestroy public void destroy() { log.info("destroy"); } }
@RestController public class MyController { @Lazy @Autowired private BeanForRequest beanForRequest; @Lazy @Autowired private BeanForSession beanForSession; @Lazy @Autowired private BeanForApplication beanForApplication;
@GetMapping(value = "/test", produces = "text/html") public String test(HttpServletRequest request, HttpSession session) { ServletContext context = request.getServletContext(); String sb = "<ul>" + "<li>" + "request scope:" + beanForRequest + "</li>" + "<li>" + "request scope:" + beanForSession + "</li>" + "<li>" + "request scope:" + beanForApplication + "</li>" + "</ul>"; return sb; } }
|
- request 每次请求都会更新
- session 每次会话更新
- application 每次应用容器更新 (重启 ServletContext 更新)
# Scope 失效情况
当一个单例对象 E 中含有一个多例对象 F 时,此时通过 E 对象的 getF 方法只能获取到同一个 F, 因为在单例模式中只执行一次,此时 F 上面的 @Scope (“prototype”) 失效
1 2 3 4 5 6 7 8
| graph LR
e1(e 创建) e2(e set 注入 f)
f1(f 创建)
e1-->f1-->e2
|
我们有如下四种方式可以解决 :
# @Lazy 注解
在需要每次返回多例的时候,在其属性上增加 @Lazy 注解,其原理是通过代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| graph LR
e1(e 创建) e2(e set 注入 f代理)
f1(f 创建) f2(f 创建) f3(f 创建)
e1-->e2 e2--使用f方法-->f1 e2--使用f方法-->f2 e2--使用f方法-->f3
|
# @Scope (proxyMode 属性)
与 @Lazy 达到一样的效果,也是通过代理类不断生成新的对象,不过是在多例对象中配置,不是在含有多例对象的对象上配置
# 对象工厂
通过创建一个对象工厂,即可不断返回新的实例,不通过代理
# ApplicationContext
通过容器不断 getBean 也可以返回不同的实例,不通过代理
以上四种方法的理念都是相同的,都是推迟 bean 的获取,单例对象只会注入一次,所以要想在单例对象中不断地返回不同的多例对象,需要注入代理类,对象工厂或容器,将多例对象的生成推迟到每次调用 getF () 方法,然而,代理会有一定的性能损耗,所以后两种方式会好一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Component public class E { @Lazy @Autowired private F1 f1; @Autowired private F2 f2; @Autowired private ObjectFactory<F3> f3; @Autowired private ApplicationContext context;
public F2 getF2() { return f2; }
public F3 getF3() { return f3.getObject(); }
public F1 getF1() { return f1; }
public F4 getF4() { return context.getBean(F4.class); } }
@Scope("prototype") @Component public class F1 { }
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) @Component public class F2 { }
|