vieyahn2017 / javaway

java on the way
5 stars 3 forks source link

12.1 spring中的bean的属性scope #65

Closed vieyahn2017 closed 1 year ago

vieyahn2017 commented 5 years ago

spring中的bean的属性scope https://www.cnblogs.com/wgbs25673578/p/5617700.html

spring中bean的scope属性,有如下5种类型:

singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例 prototype表示每次获得bean都会生成一个新的对象 request表示在一次http请求内有效(只适用于web应用) session表示在一个用户会话内有效(只适用于web应用) globalSession表示在全局会话内有效(只适用于web应用)

在多数情况,我们只会使用singleton和prototype两种scope,如果在spring配置文件内未指定scope属性,默认为singleton。

vieyahn2017 commented 5 years ago

spring boot之Singleton、Prototype和spring el资源调用 https://blog.csdn.net/z3133464733/article/details/79189416

public static <T> T getBean(Class<T> clazz)

测试的例子

同时,实体类Value的注解 Spring Expression Language (SpEL)


/**
 * spring el 和资源调用
 * 使用场景:普通文件、网址、配置文件、系统环境变量等、
 * 使用:主要在注解@value的参数中使用表达式
 * @author Hiiso
 *
 */
@Configuration
@ComponentScan("com.zh.ch2.el")
@PropertySource("classpath:/test.properties")//7
public class ElConfig {

  @Value("I Love You!") //1注入普通字符串
  private String normal;

  @Value("#{systemProperties['os.name']}") //2注入操作系统属性
  private String osName;

  @Value("#{ T(java.lang.Math).random() * 100.0 }") //3注入表达式结果
  private double randomNumber;

  @Value("#{demoService.another}") //4注入其他bean属性
  private String fromAnother;

  @Value("classpath:/test.txt") //5注入文件资源
  private Resource testFile;

  @Value("http://www.baidu.com") //6注入网址资源
  private Resource testUrl;

  @Value("${book.name}") //7注入配置文件
  private String bookName;

  @Autowired
  private Environment environment; //7

  @Bean //7
  public static PropertySourcesPlaceholderConfigurer propertyConfigure() {
    return new PropertySourcesPlaceholderConfigurer();
  }

  public void outputResource() {
    try {
      System.out.println("//1注入普通字符串:"+normal);
      System.out.println("//2注入操作系统属性:"+osName);
      System.out.println("//3注入表达式结果:"+randomNumber);
      System.out.println("//4注入其他bean属性:"+fromAnother);

      System.out.println("//5注入文件资源:"+IOUtils.toString(testFile.getInputStream()));
      System.out.println("//6注入网址资源:"+IOUtils.toString(testUrl.getInputStream()));
      System.out.println("//7注入配置文件:"+bookName);
      System.out.println("//7通过environment获取中获取配置文件属性:"+environment.getProperty("book.author"));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}
vieyahn2017 commented 5 years ago

spring的controller、service、dao都是默认singleton的,在singleton的模式下spring只会生成一个对象来处理并发的请求

vieyahn2017 commented 5 years ago

xiaoli:

常用注解: 1、控制层类上一般用:@Controller
2、业务层的实现类的上面一般用:@Service
3、持久层类的实现类上面一般用:@Repository 但是mybatis框架与spring整合后,持久层的接口上面(Mapper接口)上面只能用@Mapper
4、如果不确定是什么层的时候,又需要spring管理对象的时候,可以使用:@Component

vieyahn2017 commented 5 years ago

spring 创建bean有单例模式(singleton)和原始模型模式(prototype)这两种模式。

在默认的情况下,Spring中创建的bean都是单例模式的(注意Spring的单例模式与GoF提到的单例模式略微有些不同,详情参考Spring的官方文档)。

一般情况下,有状态的bean需要使用prototype模式,而对于无状态的bean一般采用singleton模式(一般的dao都是无状态的)。

所谓的状态场景是:

每次调用bean的方法,prototype都会提供一个新的对象(重新new),并不保存原有的实例,而singleton不同,多次调用bean实际上使用的是同一个singleton对象,而且保存了对象的状态信息。


作者:上l善l若l水 来源:CSDN 原文:https://blog.csdn.net/Kevin12306/article/details/56012208 版权声明:本文为博主原创文章,转载请附上博文链接!

vieyahn2017 commented 5 years ago

Spring中原型prototype的准确使用
https://blog.csdn.net/unifirst/article/details/50482031

使用原型

在Spring中,@Service默认都是单例的。用了私有全局变量,若不想影响下次请求,就需要用到原型模式,即@Scope(“prototype”)

所谓单例,就是Spring的IOC机制只创建该类的一个实例,每次请求,都会用这同一个实例进行处理,因此若存在全局变量,本次请求的值肯定会影响下一次请求时该变量的值。 原型模式,指的是每次调用时,会重新创建该类的一个实例,比较类似于我们自己自己new的对象实例。

prototype陷阱

在进行以上改动后,运行发现并没有生效,依然是一个实例。这说明只加一个@Scope注解还不够。

在调用改service的controller层,是这样注入的:

    @Autowired
    private MyReportExporter myReportExporter;

而controller同样是默认单例的,因此只实例化了一个controller对象,在其中依赖注入的MyReportExporter对象也就只会实例化一次。

在不想改变controller单例模式的情况下,可以如下修改: 放弃使用@Autowired方式,改用getBean方式:

private static ApplicationContext applicationContext;
MyReportExporter myReportExporter = applicationContext.getBean(MyReportExporter.class);

可以自己写个Spring工厂类,如下:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.quhuhu.cesar.common.utils.LogUtils;

public class SpringBeanFactory implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 获取某个Bean的对象
     */
    public static <T> T getBean(Class<T> clazz) {
        try {
            return applicationContext.getBean(clazz);
        } catch (Exception e) {
            LogUtils.errorMail("Spring getBean:" + clazz, e);
        }
        return null;
    }
}

然后,通过如下方式调用:

SpringBeanFactory.getBean(MyReportExporter.class).doSth()

小结

Spring中依赖注入的默认对象为单例形式,@Scope(“prototype”)注解可以将其改变为原型模式。

改变底层(如service层)的对象为原型时,同时改变上层调用层(如controller层)的调用方式,原型模式才会生效。

注:多谢评论中指出,应该是有更好的方式:lookup-method ,专门就是为了解决这个问题的,就是看起来bean的配置会多不少,大家可以移步。

vieyahn2017 commented 5 years ago

Spring - lookup-method方式实现依赖注入

https://www.cnblogs.com/ViviChan/p/4981619.html

spring作用域protoType踩过的坑

https://blog.csdn.net/zhuweili/article/details/84874564
【不让spring管理我们的httpClient注入了(不用@Autowired) ,@Lookup 此注解来自己管理。】

prototype  容器中存在多个Bean对象,每次访问容器获取对象,都会生成一个新对象,即每次调用getBean都会获取一个新的对象

prototype   容易被误认为 只要设定scope为模式,每次请求都是不同的对象,其实不是,还应该有一个前提,每次请求都有从容器中去取对象了

笔者有一个需求,访问一个接口(直接访问http方式),测试环境是http 生产环境为https,

笔者想到使用profile根据目前配置的环境动态选择创建对象。

然后想到我们这个连接每次完成请求后都要关闭,下次再过来连接请求,需要重新创建个httpClient对象,于是加上了@Scope,定义作用域为protoType,想实现每次请求过来都创建新httpClient的需求。

测试发现, httpClient的引用者HttpClientService是单例的,在容器启动的时候创建对象并注入其所有的引用,然后每次请求都使用此对象,对象httpClient只有这一次注入机会, 所以两次不同的请求,使用的httpClient是同一个对象,

怎么解决呢,不让spring管理我们的httpClient注入了 ,@Lookup 此注解来自己管理

vieyahn2017 commented 5 years ago

Spring中的Profile功能其实早在Spring 3.1的版本就已经出来,它可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。举个更具体的例子,我们以前所定义的Bean,当Spring容器一启动的时候,就会一股脑的全部加载这些信息完成对Bean的创建;而使用了Profile之后,它会将Bean的定义进行更细粒度的划分,将这些定义的Bean划分为几个不同的组,当Spring容器加载配置信息的时候,首先查找激活的Profile,然后只会去加载被激活的组中所定义的Bean信息,而不被激活的Profile中所定义的Bean定义信息是不会加载用于创建Bean的。

【译】Spring 4 @Profile注解示例
https://www.cnblogs.com/chenpi/p/6213849.html 译文链接:http://websystique.com/spring/spring-profile-example/