1. 问题,Spring管理的某个Bean需要使用多例

在使用了Spring的web工程中,除非特殊情况,我们都会选择使用Spring的IOC功能来管理Bean,而不是用到时去new一个。Spring管理的Bean默认是单例的(即Spring创建好Bean,需要时就拿来用,而不是每次用到时都去new,又快性能又好),但有时候单例并不满足要求(比如Bean中不全是方法,有成员,使用单例会有线程安全问题,可以搜索线程安全与线程不安全的相关文章),你上网可以很容易找到解决办法,即使用 @Scope("prototype") 注解,可以通知Spring把被注解的Bean变成多例,如下所示:

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
    private String name;
    @RequestMapping(value = "/{username}",method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        try {
            for(int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getId() + "name:" + name);
                Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        return;

  分别发送请求http://localhost:8043/testScope/aaahttp://localhost:8043/testScope/bbb,控制台输出:

34name:aaa
34name:aaa
35name:bbb
34name:bbb

  (34和35是两个线程的ID,每次运行都可能不同,但是两个请求使用的线程的ID肯定不一样,可以用来区分两个请求。)可以看到第二个请求bbb开始后,将name的内容改为了“bbb”,第一个请求的name也从“aaa”改为了“bbb”。要想避免这种情况,可以使用@Scope("prototype"),注解加在TestScope这个类上。加完注解后重复上面的请求,发现第一个请求一直输出“aaa”,第二个请求一直输出“bbb”,成功。

2. 问题升级,多个Bean的依赖链中,有一个需要多例

  第一节中是一个很简单的情况,真实的Spring Web工程起码有Controller、Service、Dao三层,假如Controller层是单例,Service层需要多例,这时候应该怎么办呢?

2.1 一次失败的尝试

  首先我们想到的是在Service层加注解@Scope("prototype"),如下所示:
controller类代码

import com.example.test.service.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
    @Autowired
    private Order order;
    private String name;
    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        order.setOrderNum(name);
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                        Thread.currentThread().getId()
                                + "name:" + name
                                + "--order:"
                                + order.getOrderNum());
                Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        return;

Service类代码

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class Order {
    private String orderNum;
    public String getOrderNum() {
        return orderNum;
    public void setOrderNum(String orderNum) {
        this.orderNum = orderNum;
    @Override
    public String toString() {
        return "Order{" +
                "orderNum='" + orderNum + '\'' +

  分别发送请求http://localhost:8043/testScope/aaahttp://localhost:8043/testScope/bbb,控制台输出:

32name:aaa--order:aaa
32name:aaa--order:aaa
34name:bbb--order:bbb
32name:bbb--order:bbb

  可以看到Controller的name和Service的orderNum都被第二个请求从“aaa”改成了“bbb”,Service并不是多例,失败。

2.2 一次成功的尝试

  我们再次尝试,在Controller和Service都加上@Scope("prototype"),结果成功,这里不重复贴代码,读者可以自己试试。

2.3 成功的原因(对2.1、2.2的理解)

  Spring定义了多种作用域,可以基于这些作用域创建bean,包括:

  • 单例( Singleton):在整个应用中,只创建bean的一个实例。
  • 原型( Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。

  对于以上说明,我们可以这样理解:虽然Service是多例的,但是Controller是单例的。如果给一个组件加上@Scope("prototype")注解,每次请求它的实例,spring的确会给返回一个新的。问题是这个多例对象Service是被单例对象Controller依赖的。而单例服务Controller初始化的时候,多例对象Service就已经注入了;当你去使用Controller的时候,Service也不会被再次创建了(注入时创建,而注入只有一次)。

2.4 另一种成功的尝试(基于2.3的猜想)

  为了验证2.3的猜想,我们在Controller钟每次去请求获取Service实例,而不是使用@Autowired注入,代码如下:
Controller类

import com.example.test.service.Order;
import com.example.test.utils.SpringBeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/testScope")
public class TestScope {
    private String name;
    @RequestMapping(value = "/{username}", method = RequestMethod.GET)
    public void userProfile(@PathVariable("username") String username) {
        name = username;
        Order order = SpringBeanUtil.getBean(Order.class);
        order.setOrderNum(name);
        try {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                        Thread.currentThread().getId()
                                + "name:" + name
                                + "--order:"
                                + order.getOrderNum());
                Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        return;

用于获取Spring管理的Bean的类

package com.example.test.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringBeanUtil implements ApplicationContextAware {
     * 上下文对象实例
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
     * 获取applicationContext
     * @return
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
     * 通过name获取 Bean.
     * @param name
     * @return
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
     * 通过class获取Bean.
     * @param clazz
     * @param <T>
     * @return
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
     * 通过name,以及Clazz返回指定的Bean
     * @param name
     * @param clazz
     * @param <T>
     * @return
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);

  Order的代码不变。
  分别发送请求http://localhost:8043/testScope/aaahttp://localhost:8043/testScope/bbb,控制台输出:

31name:aaa--order:aaa
33name:bbb--order:bbb
31name:bbb--order:aaa
33name:bbb--order:bbb

  可以看到,第二次请求的不会改变第一次请求的name和orderNum。问题解决。我们在2.3节中给出的的理解是对的。

3. Spring给出的解决问题的办法(解决Bean链中某个Bean需要多例的问题)

  虽然第二节解决了问题,但是有两个问题:

  • 方法一,为了一个多例,让整个一串Bean失去了单例的优势;
  • 方法二,破坏IOC注入的优美展现形式,和new一样不便于管理和修改。

  Spring作为一个优秀的、用途广、发展时间长的框架,一定有成熟的解决办法。经过一番搜索,我们发现,注解@Scope("prototype")(这个注解实际上也可以写成@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,使用常量比手打字符串不容易出错)还有很多用法。
  首先value就分为四类:

  • ConfigurableBeanFactory.SCOPE_PROTOTYPE,即“prototype”
  • ConfigurableBeanFactory.SCOPE_SINGLETON,即“singleton”
  • WebApplicationContext.SCOPE_REQUEST,即“request”
  • WebApplicationContext.SCOPE_SESSION,即“session”

  他们的含义是:

  • singleton和prototype分别代表单例和多例;
  • request表示请求,即在一次http请求中,被注解的Bean都是同一个Bean,不同的请求是不同的Bean;
  • session表示会话,即在同一个会话中,被注解的Bean都是使用的同一个Bean,不同的会话使用不同的Bean。

  使用session和request产生了一个新问题,生成controller的时候需要service作为controller的成员,但是service只在收到请求(可能是request也可能是session)时才会被实例化,controller拿不到service实例。为了解决这个问题,@Scope注解添加了一个proxyMode的属性,有两个值ScopedProxyMode.INTERFACESScopedProxyMode.TARGET_CLASS,前一个表示表示Service是一个接口,后一个表示Service是一个类。
  本文遇到的问题中,将@Scope注解改成@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)就可以了,这里就不重复贴代码了。
  问题解决。

  Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,并将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象。如果每次从Spring容器中获取对象时,都要创建一个新的实例对象,那么该如何处理呢?此时就需要使用【@Scope】注解来设置组件的作用域了。 1、@Scope注解概述 @Scope注解能够设置组件的作用域,我们先来看看@Scope注解类的源码,如下所示。 从@Scope注解类的源码中可以看出,在@Scope注解
@Scope(“prototype”) springbean的scope属性,有如下5种类型: singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例 prototype表示每次获得bean都会生成一个新的对象 request表示在一次http请求内有效(只适用于web应用) session表示在一个用户会话内有效(只适用于web应用) globalSession表示在全局会话内有效(只适用于web应用) 在多数情况,我们只会使用singleton和pr
Spring注解@Scope(“prototype”) Spring在Action上面注解@Scope(“prototype”) 表示每次接收一个请求创建一个对象 如若改成其他,例如单例模式,则很多请求公用同一个bean. 一个注册的例子,如果没加上这个注解,注册完成后,下一个请求再注册一次,Action里会保留上一次注册的信息springbean的scope主要是用来指定如何创建bean
之前的联想G455换掉了,换成了惠普的,配置高很多,价钱低了1000多,高科技产品更新太快了! http://s.click.taobao.com/t_8?e=7HZ6jHSTbIceqCkLODFcBjoE1aJnDy%2BXdptjV7lef1tE&p=mm_33424343_0_0 当我们在一个ACTION类里面写很多个方法的时候(其实是一种按功能划分模块编程的思想),每个方法的返回状
该系列文章主要学习 雷丰阳老师的《Spring注解驱动》课程总结。 原课程地址:课程地址 包括了自己阅读其他书籍《Spring揭秘》《Spring Boot 实战》等课程。 该系列文档会不断的完善,欢迎大家留言及提意见。 文章目录1. singleton探讨 Bean 的生成时机2. prototype模式探讨 bean 的生成时机 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,并将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不.
Spring注解——@Scope前言源码实战 了解过SpringXml形式开发的同学,一定都知道xml中的bean标签里面有个scope标签,他的作用就是来定义此Bean的创建模式。@Scope注解的功能和这个scope标签是一样的。 一共四种创建模式: 单例模式:每次创建都是同一个对象,Spring中只会帮我们创建一次 多例模式:每次创建都是新创建一个对象。 基于Request的创建模式:每次请求会帮我们创建一个新的对象 基于Session的创建模式:每个Session,都会创建一个对象。(后端的
Spring Bean一、Spring的概述1.1 Spring Bean概念1.2 Spring的工厂类二、Spring Bean管理——XML方式2.1 Bean的实例化三种方式2.1.1 使用类构造器实例化(默认无参数)2.1.2 使用静态工厂方法实例化(简单工厂模式)2.1.3 使用实例工厂方法实例化(工厂方法模式)三、Spring Bean管理——注解方式3.1 Spring的工厂类 一、Spring的概述 1.1 Spring Bean概念 先说一下Java Bean的概念: JavaBean其
当类被@Scope(BeanDefination.SCOPE_PROTOTYPE)修饰时,说明每次依赖注入时,都会产生新的对象 但是在多线程编程中,如果一个类被@Scope(BeanDefination.SCOPE_PROTOTYPE)修饰,且在另外的对象中如果需要引用此对象时,最好不要使用@Autowired依赖注入,而是使用 ApplicationContext.getBean()的方式去调用。 举例说明: @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE
每次访问都是同一个Action对象,数据不安全 struts2 是要求 每次次访问 都对应不同的Action scope=“prototype” 可以保证 当有请求的时候 都创建一个Action对象 singleton是单例模式 prototype多例模式 此外还有request、session等作用域
1.注入bean @Scope(“prototype”) 开启多例 这里注入了一个 name为student type为Student 的bean @Service public class StudentService { @Bean @Scope("prototype") public Student student(){ System.out.println("Bean被加载到容器"); return new Student("张三",23)
当我们在使用Spring框架进行开发时,@Scope和@Bean注解可以一起使用来创建一个具有特定作用域的Bean实例。 @Scope注解用于指定Bean的作用域,例如singleton(默认值)、prototype、request、session等等。@Bean注解用于将一个方法返回的对象注册为一个Bean实例。 例如,我们可以在配置类中使用以下代码: @Configuration public class AppConfig { @Bean @Scope("prototype") public MyBean myBean() { // 创建并返回一个MyBean实例 return new MyBean(); 在这个例子中,我们使用@Scope("prototype")来指定创建的MyBean实例具有prototype作用域,即每次请求该Bean时都会创建一个新的实例。同时,我们使用@Bean来将myBean()方法返回的对象注册为一个Bean实例。 当我们在其他组件中使用@Autowired或@Inject注解来注入MyBean时,Spring框架会自动创建并返回一个新的MyBean实例。
解决centos安装mysql报错 错误:依赖检测失败mysql-community-common(x86-64) >= 5.7.9 被 mysql-community-libs需要 m0_57102730: 删了还是一样 Java socket详解,看这一篇就够了 medmi: 写的太棒了 远程调用Feign返回IPage、INode等会直接进入FallBack 这个问题卡了我半天表情包 java 输出流转输入流 宇智波小强: 模拟:在一个类中向OutputStream里面写数据,而另一个类则从InputStream中读取这些数据 java 输出流转输入流 LYLshadow: 请问下class1和class2是什么