1.request, session, application 作用域
@SpringBootApplication
public class A08copy {
public static void main(String[] args) {
SpringApplication.run(A08copy.class, args);
}
}
@Scope("application")
@Component
public class BeanForApplication {
private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);
@PreDestroy
public void destroy() {
log.debug("destroy");
}
}
@Scope("request")
@Component
public class BeanForRequest {
private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);
@PreDestroy
public void destroy() {
log.debug("destroy");
}
}
@Scope("session")
@Component
public class BeanForSession {
private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);
@PreDestroy
public void destroy() {
log.debug("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 sc = request.getServletContext();
String sb = "<ul>" +
"<li>" + "request scope:" + beanForRequest + "</li>" +
"<li>" + "session scope:" + beanForSession + "</li>" +
"<li>" + "application scope:" + beanForApplication + "</li>" +
"</ul>";
return sb;
}
}
注:打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果
如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED即可。
同一浏览器刷新 第一个变了后面两个没变因为每次刷新是不同的请求。而不同的浏览器
Request和session不同但aplicition一样。
总结:在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope
1 singleton,容器启动时创建(未设置延迟),容器关闭时销毁
2 prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁
3 request,每次请求用到此 bean 时创建,请求结束时销毁
4 session,每个会话用到此 bean 时创建,会话结束时销毁
5 application,web 容器用到此 bean 时创建,容器停止时销毁
有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃。
2. scope 的四种解决方法
@ComponentScan("com.itheima.a09")
public class A09 {
private static final Logger log = LoggerFactory.getLogger(A09.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(A09.class);
E e = context.getBean(E.class);
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
context.close();
}
}
@Component
public class E {
@Autowired
private F1 f1;
public F1 getF1() {
return f1;
}
}
@Scope("prototype")
@Component
public class F1 {
}
虽然F1是多例,我们尝试获取e的F1变量但运行输出
可见三个F1相同。
这是因为对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F 。
解决方案 1.仍然使用 @Lazy 生成代理
2.代理对象虽然还是同一个,但当每次**使用代理对象的任意方法**时,由代理创建新的 f 对象(原视频又建了F2进行演示,比较简单就不写了)
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}加上这个注释就就好了proxyMode = ScopedProxyMode.TARGET_CLASS,实际也是生成代理对象。
第三种方法 ObjectFactory
@Component
public class E {
@Autowired
@Lazy
private F1 f1;
@Autowired
private ObjectFactory<F3> f3;
public F1 getF1() {
return f1;
}
public F3 getF3() {
return f3.getObject();
}
}
@Scope("prototype")
@Component
public class F3 {
}
@ComponentScan("com.itheima.a09")
public class A09 {
private static final Logger log = LoggerFactory.getLogger(A09.class);
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(A09.class);
E e = context.getBean(E.class);
log.debug("{}", e.getF1().getClass());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
log.debug("{}", e.getF1());
log.debug("{}", e.getF3());
log.debug("{}", e.getF3());
context.close();
}
}
可见两个F3是不同对象
第四种 ApplicationContext
@Component
public class E {
@Autowired
@Lazy
private F1 f1;
@Autowired
private ObjectFactory<F3> f3;
@Autowired
private ApplicationContext context;
public F1 getF1() {
return f1;
}
public F3 getF3() {
return f3.getObject();
}
public F4 getF4() {
return context.getBean(F4.class);
}
}
A09中运行
log.debug("{}", e.getF4());
log.debug("{}", e.getF4());输出
总结
-
单例注入其它 scope 的四种解决方法
-
@Lazy
-
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
-
ObjectFactory
-
ApplicationContext
-
-
解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取