Spring之Scope注解使用详解

 更新时间:2023年02月07日 14:07:18   作者:tanglin_030907031026  
spring的bean管理中,每个bean都有对应的scope。在BeanDefinition中就已经指定scope,默认的RootBeanDefinition的scope是prototype类型,使用@ComponentScan扫描出的BeanDefinition会指定是singleton,最常使用的也是singleton

在当前版本的 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, application
    jdk >= 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);
    @PreDestroy
    public void destroy() {
        log.debug("destroy");
    }
}

BeanForRequest

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

BeanForSession

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

使用的Bean使用代码如下:

@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;
    }
}

单例使用其他的例,都得使用@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()");
    }
    @Autowired
    public 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
    @Lazy
    public 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 {
}
  @Autowired
    private ObjectFactory<F3> f3;
 public F3 getF3() {
        return f3.getObject();
    }
     @Autowired
    private ApplicationContext context;
 public F4 getF4() {
        return context.getBean(F4.class);
    }

收获

单例注入其它 scope 的四种解决方法

  • @Lazy
  • @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
  • ObjectFactory
  • ApplicationContext

解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取

到此这篇关于Spring之Scope注解使用详解的文章就介绍到这了,更多相关Spring Scope注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Nacos源码之注册中心的实现详解

    Nacos源码之注册中心的实现详解

    这篇文章主要为大家介绍了Nacos源码之注册中心的实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Springboot MBean使用示例解析

    Springboot MBean使用示例解析

    这篇文章主要为大家介绍了Springboot MBean使用示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • JAVA随机打乱数组顺序的方法

    JAVA随机打乱数组顺序的方法

    这篇文章主要介绍了JAVA随机打乱数组顺序的方法,包含了随机数的应用及数组的排序等操作,是Java操作数组的典型应用,需要的朋友可以参考下
    2014-11-11
  • Java实现简单的日历界面

    Java实现简单的日历界面

    这篇文章主要为大家详细介绍了Java实现简单的日历界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Spring MVC学习教程之RequestMappingHandlerAdapter详解

    Spring MVC学习教程之RequestMappingHandlerAdapter详解

    这篇文章主要给大家介绍了关于Spring MVC学习教程之RequestMappingHandlerAdapter的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-11-11
  • Springboot中的Validation参数校验详解

    Springboot中的Validation参数校验详解

    这篇文章主要介绍了Springboot中的Validation参数校验详解,Springboot参数校验是一种常用的验证机制,在传递参数时进行校验,以确保参数的有效性和正确性,该机制可以帮助开发者在代码实现前就避免一些常见的错误,需要的朋友可以参考下
    2023-10-10
  • Java并发编程中使用Executors类创建和管理线程的用法

    Java并发编程中使用Executors类创建和管理线程的用法

    这篇文章主要介绍了Java并发编程中使用Executors类创建和管理线程的用法,文中举了用其启动线程和设置线程优先级的例子,需要的朋友可以参考下
    2016-03-03
  • Java程序图形用户界面设计之标签组件

    Java程序图形用户界面设计之标签组件

    图形界面(简称GUI)是指采用图形方式显示的计算机操作用户界面。与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受,本篇精讲Java语言中关于图形用户界面的标签组件部分
    2022-02-02
  • java联调生成测试数据工具类方式

    java联调生成测试数据工具类方式

    这篇文章主要介绍了java联调生成测试数据工具类方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 如何利用IDEA搭建SpringBoot项目整合mybatis实现简单的登录功能

    如何利用IDEA搭建SpringBoot项目整合mybatis实现简单的登录功能

    这篇文章主要介绍了如何利用IDEA搭建SpringBoot项目整合mybatis实现简单的登录功能,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08

最新评论