由Spring @Autowired 和 getBean() 的区别引出的SpringBean的作用域问题

2,644 阅读3分钟

最近在项目中有一个service中使用了类成员变量,导致了业务并发。(多个人同时使用一个功能) 于是就在此service上添加注解(@Scope("prototype"))使用多例,但是问题出现了,添加了此注解还是存在此问题,于是看了spring Autoware 的加载机制,网上有很多资料,看后也做了测试,得出一下结论。

使用 @Autoware 注入service (多例) 其实每个方法使用的都是同一个service对象,并未出现多例模式(修正:service中@Scope("prototype")的多例模式体现在不同Controller中,单个Controller中使用@Autoware注入的service一直都是同一个,不会重复创建。),测试代码

    private ScopeTestService scopeTestService;
    @GetMapping("/test1")
    @ResponseBody
    public ServerResponse test1(){
        scopeTestService.test1();
        log.info(scopeTestService.toString());
        return ServerResponse.createBySuccess("test1");
    }
    @GetMapping("/test2")
    @ResponseBody
    public ServerResponse test2(){
        scopeTestService.test2();
        log.info(scopeTestService.toString());
        return ServerResponse.createBySuccess("test2");
    }

结果:此方式访问两个接口打印的scopeTestService 是一样的  com.barton.service.impl.ScopeTestServiceImpl@49c4d534

使用getBean 获取 scopeTestService 成功


以上是@Autoware 和 getBean()在多例模式上的小小区别,至于其他方面的区别没再去细细研究

其实多例模式下直接将第一中方式的Controller 变为多例模式(prototype)也可以解决并发问题,简单粗暴。

那么接下来就又引出来一个疑问,Spring的不止prototype作用域是多例的,request也是多实例的,这两种方式又存在怎样的区别呢?

spring bean作用域有以下5个:

  • singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;
  • prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

(下面是在web项目下才用到的)

  • request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听;

  • session:每次会话,同上;

  • global session:全局的web域,类似于servlet中的application。

我们知道,spring有singleton和prototype作用域,而且从spring2.0开始,又增加了三种作用域,request、session、global session。session、global session的作用域是HTTP session和全局session。对于request和prototype,每一次请求都会产生一个新的bean实例,那么它们的具体有什么区别呢?而且,Spring不能对一个prototype bean的整个生命周期负责,这一点我不太能理解,这样它占用的资源就很高昂了,那么prototype的好处是什么?要结束它的生命周围,要怎么做?而且,感觉request和prototype,除了prototype的生命周期不会被回调以外,它们的作用是不是一样?有什么具体的区别呢?

下边是在一个论坛中看到的答案 感觉还比较不错

request依赖于web应用,request就类似与你的servlet,多个用户访问一个servlet,当然是访问servlet的多个实例,prototype实际上是new出来的,你想想,你的bean里new了一个对象,为什么要让spring给你销毁。prototype没有什么好不好的,只不过它能做singleton所不能做得(创建多个实例),在web项目之外也可以做request所不能做得事。prototype的销毁你不必关心,调用它的bean销毁了,它也就销毁了。request是web里所特有的。

综合上边这个老哥给出的答案,我总结了以下几点

  • spring的controller默认是单例,原因有二:

    (1)为了性能:单例不用每次都创建

    (2)不需要多例:只要controller中不定义属性,那么单例完全是安全可用的,如果定义了,那单例肯定会出现竞争访问;非要定义,则通过注解@Scope("prototype"),将其设置为多例模式。

    对于web项目,可以Controller类上加注解@Scope("prototype")@Scope("request"),对非web项目,在Component类上添加注解@Scope("prototype")