Spring底层分析--7.Scope

一.Scope的类型

Spring中的scope有五种类型,分别是:

singleton,prepertype,request,session,applicati

其中前两个我们已经很熟悉了,singleton就是单例模式,每次从容器中获取的bean都是同一个,prepertype是每次获取bean都是新创建的一个bean,

下面我们来讨论后面三中情况:

二.web项目中的三种Scope

 我们创建一个springboot项目,引入spirng-boot-starter-web依赖,然后我们编写以下三个类:

@Scope("request")
@Component
public class BeanForRequest {

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


@Scope("session")
@Component
public class BeanForSession {

    private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);

    @PreDestroy
    public void destroy(){
        log.info("destroy...");
    }
    
}

@Scope("application")
@Component
public class BeanForApplication {

    private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);

    @PreDestroy
    public void destroy(){
        log.info("destroy....");
    }

}

我们编写Controller类

@RestController
public class TestScopeController {

    @Lazy
    @Autowired
    private BeanForRequest beanForRequest;
    @Lazy
    @Autowired
    private BeanForSession beanForSession;
    @Lazy
    @Autowired
    private BeanForApplication beanForApplication;



    @GetMapping("/test")
    public String testScope(){

        return  "<ul>" +
                  "<li>" +  beanForRequest +    "</li>" +
                  "<li>" +  beanForSession +    "</li>"+
                  "<li>" +  beanForApplication+ "</li>"+
                "</ul>";


    }


}

注意:这里我们在使用注入的时候要加上@Lazy注解,这是因为实际上是调用了他们的代理对象,如果不加会报错

 然后我们启动项目,在浏览器中访问: localhost:8080/test

得到结果:

156342c4e52947faad4d1fc47e5ecd8d.png

 

然后我们看一下控制台,发现:

2023-09-19 17:27:35.228  INFO 15628 --- [nio-8080-exec-4] com.jjh.component.BeanForRequest         : destroy....

这是因为Scope域是一次请求,在请求完成后就会销毁bean,

我们再次访问localhost:8080/test

b8fa4cb8796742d0abded402efa16974.png

我们发现这个BeanForRequest对象和上一次的不一样,但是后面两个是一样的,这就说明了一次请求创建一次bean对象,每次请求都是不是不同的对象

 现在让我们换另外一个浏览器:

 3b0064ebac1e43ef802d6974c1ec8711.png

我们发现这次BeanForSession也不一样了,这是不同的浏览器代表不同的会话,但是BeanForApplication都是一样的,因为我们的项目是同一个 

三.关于在一个单例对象中注入多例失效的问题

1.验证

当在一个单例的类中注入多例对象时,返回的结果还是同一个,下面我们来验证一下:

创建三个类:

@Component
public class E {

    @Autowired
    private F1 f1;

    public F1 getF1() {
        return f1;
    }
}

@Scope("prototype")
@Component
public class F1 {
}

@Scope("prototype")
@Component
public class F2 {
}

然后我们通过E对象获取F1对象,观察一下它的值:

@SpringBootApplication
public class InitAndDestroyApplication {

    private static final Logger log = LoggerFactory.getLogger(InitAndDestroyApplication.class);

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(InitAndDestroyApplication.class, args);


        E e = context.getBean(E.class);

        log.info("{}",e.getF1());
        log.info("{}",e.getF1());
        log.info("{}",e.getF1());

    }
}

运行查看结果:

2023-09-19 17:47:13.995  INFO 18968 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@6232ffdb
2023-09-19 17:47:13.997  INFO 18968 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@6232ffdb
2023-09-19 17:47:13.998  INFO 18968 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@6232ffdb

2.原理分析 

我们发现这三次获取的F1都是同一个对象,注解@Scope("prototype")失效了,这是为什么呢?

我们来看一下图:

2ef3ae97b37e43749cd1d56990bccbf8.png

 因为单例对象的依赖注入只发生了一次,所以只能注入了一次F1,所以是同一个F1

解决方法:

0ef87b6c82f740969cfa023db38fafc6.png

 我们使用代理对象,这样每次调用方法时,由代理对象来创建新的F1对象

3.解决方法:

1.使用@Lazy注解

2.@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)

是在要代理的那个对象上添加

   @Lazy
    @Autowired
    private F1 f1;

在F1上添加@Lazy,然后我们再次获取这个F1对象:

2023-09-19 17:57:38.115  INFO 2600 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@6a12c7a8
2023-09-19 17:57:38.117  INFO 2600 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@5740ff5e
2023-09-19 17:57:38.117  INFO 2600 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F1@67f77f6e

可以看到,这次三个对象都是不一样的了,下面,我们获取F1对象的类型:

2023-09-19 17:58:57.600  INFO 2044 --- [           main] com.jjh.InitAndDestroyApplication        : class com.jjh.component.F1$$EnhancerBySpringCGLIB$$bd3dda3e

我们发现这个F1的类型已经不是F1本身了,它是使用了CGLB动态代理

 继续创建F3,F4类:

@Scope("prototype")
@Component
public class F3 {
}

@Scope("prototype")
@Component
public class F4 {
}

3.使用ObjectFactory<F3> 

我们可以使用工厂来创建对象:

 @Autowired
    private ObjectFactory<F3> objectFactory;

public F3 getF3(){
        return objectFactory.getObject();
    }

然后我们运行一下:

2023-09-19 18:09:31.504  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F3@6aa3bfc
2023-09-19 18:09:31.505  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F3@28f4f300
2023-09-19 18:09:31.505  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F3@6ca8fcf3

成功了!

4.注入ApplicationContext容器通过getBean()获取

 @Autowired
    private ApplicationContext context;

 public F4 getF4(){
        return context.getBean(F4.class);
    }

这是利用容器获取F4对象,这样肯定就是获取到F4本身了,

运行一下:

2023-09-19 18:09:31.505  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F4@69f0b0f4
2023-09-19 18:09:31.505  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F4@66933239
2023-09-19 18:09:31.505  INFO 9672 --- [           main] com.jjh.InitAndDestroyApplication        : com.jjh.component.F4@2f7efd0b

可以看到成功了!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值