在当前版本的 Spring 和 Spring Boot 程序中,支持五种 Scope

  • singleton,容器启动时创建(未设置延迟),容器关闭时销毁

  • prototype,每次使用时创建,不会自动销毁,需要调用 DefaultListableBeanFactory.destroyBean(bean) 销毁

  • request,每次请求用到此 bean 时创建,请求结束时销毁

  • session,每个会话用到此 bean 时创建,会话结束时销毁

  • application,web 容器用到此 bean 时创建,容器停止时销毁

有些文章提到有 globalSession 这一 Scope,也是陈旧的说法,目前 Spring 中已废弃

但要注意,如果在 singleton 注入其它 scope 都会有问题,解决方法有

  • @Lazy

  • @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

  • ObjectFactory

  • ApplicationContext.getBean

案例演示:request, session, application 作用域


/*singleton, prototype, request, session, applicationjdk >= 9 如果反射调用 jdk 中方法,会报非法访问异常jdk <= 8 不会有问题演示 request, session, application 作用域打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED*/
@SpringBootApplication
public class A08 {public static void main(String[] args) {SpringApplication.run(A08.class, args);}
}

使用不同作用域的Bean如下:

 BeanForApplication

@Scope("application")
@Component
public class BeanForApplication {private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}

BeanForRequest

@Scope("request")
@Component
public class BeanForRequest {private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);@PreDestroypublic void destroy() {log.debug("destroy");}}

BeanForSession

@Scope("session")
@Component
public class BeanForSession {private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}

使用的Bean使用代码如下:

@RestController
public class MyController {@Lazy@Autowiredprivate BeanForRequest beanForRequest;@Lazy@Autowiredprivate BeanForSession beanForSession;@Lazy@Autowiredprivate 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;}}

单例使用其他的例,都得使用@azy注解,否则会失效

分析 - singleton 注入其它 scope 失效

  以单例注入多例为例

有一个单例对象 E

@Component
public class E {private static final Logger log = LoggerFactory.getLogger(E.class);private F f;public E() {log.info("E()");}@Autowiredpublic void setF(F f) {this.f = f;log.info("setF(F f) {}", f.getClass());}public F getF() {return f;}
}

要注入的对象 F 期望是多例

@Component
@Scope("prototype")
public class F {private static final Logger log = LoggerFactory.getLogger(F.class);public F() {log.info("F()");}
}

测试

E e = context.getBean(E.class);
F f1 = e.getF();
F f2 = e.getF();
System.out.println(f1);
System.out.println(f2);

输出

com.tangyuan.demo.cycle.F@6622fc65
com.tangyuan.demo.cycle.F@6622fc65

发现它们是同一个对象,而不是期望的多例对象

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F

解决

  • 仍然使用 @Lazy 生成代理

  • 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 f 对象

@Component
public class E {@Autowired@Lazypublic void setF(F f) {this.f = f;log.info("setF(F f) {}", f.getClass());}// ...
}

注意

  • @Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了

  • @Autowired 加在 set 方法的目的类似

输出

E: setF(F f) class com.itheima.demo.cycle.F$$EnhancerBySpringCGLIB$$8b54f2bc
F: F()
com.tangyuan.demo.cycle.F@3a6f2de3
F: F()
com.tangyuan.demo.cycle.F@56303b57

从输出日志可以看到调用 setF 方法时,f 对象的类型是代理类型

4种解决方法

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}
  @Autowiredprivate ObjectFactory<F3> f3;public F3 getF3() {return f3.getObject();}
     @Autowiredprivate ApplicationContext context;public F4 getF4() {return context.getBean(F4.class);}

收获

高级Spring之Scope 详解相关推荐

  1. Spring bean - scope详解

    ​ Scope是定义Spring如何创建bean的实例的. 在创建bean的时候可以带上scope属性,scope有下面几种类型. Singleton 这也是Spring默认的scope,表示Spri ...

  2. Spring Boot 配置文件详解

    2019独角兽企业重金招聘Python工程师标准>>> 第二篇 : Spring Boot配置文件详解 文章首发于微信公众号<程序员果果> 地址:https://mp.w ...

  3. Spring的lazy-init详解

    Spring中lazy-init详解 ApplicationContext实现的默认行为就是在启动服务器时将所有singleton bean提前进行实例化 (也就是依赖注入).提前实例化意味着作为初始 ...

  4. 一文搞定 Spring Data Redis 详解及实战

    转载自  一文搞定 Spring Data Redis 详解及实战 SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问 ...

  5. Spring Boot 单元测试详解+实战教程

    转载自   Spring Boot 单元测试详解+实战教程 Spring Boot 的测试类库 Spring Boot 提供了许多实用工具和注解来帮助测试应用程序,主要包括以下两个模块. spring ...

  6. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  7. 全面的Spring Boot配置文件详解

    全面的Spring Boot配置文件详解 Spring Boot在工作中是用到的越来越广泛了,简单方便,有了它,效率提高不知道多少倍.Spring Boot配置文件对Spring Boot来说就是入门 ...

  8. Spring启动过程详解

    Spring启动过程详解 前言 spring容器启动过程 AnnotationConfigApplicationContext 有参数构造方法 无参数构造 AnnotatedBeanDefinitio ...

  9. Spring Security应用详解(集成SpringBoot)

    Spring Security应用详解 集成SpringBoot 工作原理 认证流程 授权流程 集成SpringBoot 1.Spring Boot介绍 Spring Boot是一套Spring的快速 ...

最新文章

  1. 【opencv4】opencv视频教程 C++(opencv教程)2、加载imread()(以灰度加载),修改,保存图像
  2. python支持list类型吗_Python Numpy不支持的操作数类型“list”“list”
  3. 初一模拟赛总结(2019.5.25)
  4. 经典面试题(22):以下代码将输出的结果是什么?
  5. 携程Apollo分布式配置中心搭建指南
  6. php页面设定语言,php实现获取及设置用户访问页面语言类,php页面_PHP教程
  7. qq html消息,类似于QQ新消息提醒-前端
  8. “衰亡”的雅虎:30 亿用户账号全部泄漏还能怎么活?
  9. 为小程序添加底部导航
  10. 一些软件工程的基础知识
  11. java 打包加密_java打包、加密、发布(源代码保护)
  12. PR 2019 快速入门(8)
  13. fpga如何约束走线_FPGA入门之我见- 布局布线(place route,PAR)
  14. 保利威视后台编辑FLASH播放器
  15. 光阴似锦,关于身体保养的那些事
  16. 4:3 背景图片素材
  17. 中国人民大学计算机专硕好考吗,干货:中国人民大学考研复试成功经验分享,值得收藏!...
  18. 3D 建模费时费力,Python 让照片秒变模型
  19. MySQL(查询语句,关键字)
  20. 小程序获取本地存储数据,然后传参的时候是上次请求的id

热门文章

  1. ClickHouse MergeTree副本表和分布式表(切片)
  2. #企业要求程序员统一电脑桌面,网友:桌面壁纸都不配拥有?
  3. 个人对GDT、GDTR、LDT、LDTR的理解
  4. insmod lsmod rmmod
  5. JAVA使用itextpdf插件生成pdf
  6. linux 清屏命令(clear,reset)
  7. 5G NR 时频结构
  8. Navigation Controllers + UITabBarController
  9. 萨克斯《全球视角的宏观经济学》课后答案
  10. 如何修改图片的dpi?教你一招快速修改照片dpi的方法