# Scope

# Scope 类型有哪些

  1. Singleton
  2. Prototype
  3. request
  4. session
  5. 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 {
}