自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring 的 Bean 明明設(shè)置了 Scope 為 Prototype,為什么還是只能獲取到單例對象?

開發(fā) 前端
對于有些場景,我們可能需要對應(yīng)的 Bean? 是原型的,所謂原型就是希望每次在使用的時(shí)候獲取到的是一個(gè)新的對象實(shí)例,而不是單例的,這種情況下很多小伙伴肯定會(huì)說,那還不簡單,只要在對應(yīng)的類上面加上 @scope? 注解,將 value? 設(shè)置成 Prototype 不就行了。

Spring? 作為當(dāng)下最火熱的Java? 框架,相信很多小伙伴都在使用,對于 Spring? 中的 Bean? 我們都知道默認(rèn)是單例的,意思是說在整個(gè) Spring 容器里面只存在一個(gè)實(shí)例,在需要的地方直接通過依賴注入或者從容器中直接獲取,就可以直接使用。

測試原型

對于有些場景,我們可能需要對應(yīng)的 Bean? 是原型的,所謂原型就是希望每次在使用的時(shí)候獲取到的是一個(gè)新的對象實(shí)例,而不是單例的,這種情況下很多小伙伴肯定會(huì)說,那還不簡單,只要在對應(yīng)的類上面加上 @scope? 注解,將 value? 設(shè)置成 Prototype 不就行了。如下所示:

HelloService.java

package com.example.demo.service;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-07-17 21:20<br>
* <b>Desc:</b>無<br>
*/
@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class HelloService {

public String sayHello() {
return "hello: " + this.hashCode();
}
}

HelloController.java 代碼如下:

package com.example.demo.controller;

import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-07-17 15:43<br>
* <b>Desc:</b>無<br>
*/
@RestController
public class HelloController {

@Autowired
private HelloService service;

@GetMapping(value = "/hello")
public String hello() {
return service.sayHello();
}
}

簡單描述一下上面的代碼,其中 HelloService? 類我們使用了注解 Scope?,并將值設(shè)置為 SCOPE_PROTOTYPE?,表示是原型類,在 HelloController? 類中我們調(diào)用 HelloService? 的 sayHello? 方式,其中返回了當(dāng)前實(shí)例的 hashcode。

我們通過訪問 http://127.0.0.1:8080/hello 來獲取返回值,如果說每次獲取到的值都不一樣,那就說明我們上面的代碼是沒有問題的,每次在獲取的時(shí)候都會(huì)使用一個(gè)新的 HelloService 實(shí)例。

圖片

然而在阿粉的電腦上,無論刷新瀏覽器多少次,最后的結(jié)果卻沒有發(fā)生任何變化,換句話說這里引用到的 HelloService 始終就是一個(gè),并沒有原型的效果。

那么問題來了,我們明明給 HelloService 類增加了原型注解,為什么這里沒有效果呢?

原因分析

我們這樣思考一下,首先我們通過瀏覽器訪問接口的時(shí)候,訪問到的是 HelloController? 類中的方法,那么 HelloController? 由于我們沒有增加 Scope? 的原型注解,所以肯定是單例的,那么單例的 HelloController? 中的 HelloService 屬性是什么怎么賦值的呢?

那自然是 Spring? 在 HelloController? 初始化的時(shí)候,通過依賴注入幫我們賦值的。Spring? 注入依賴的賦值邏輯簡單來說就是創(chuàng)建 Bean? 的時(shí)候如果發(fā)現(xiàn)有依賴注入,則會(huì)在容器中獲取或者創(chuàng)建一個(gè)依賴 Bean?,此時(shí)對應(yīng)屬性的 Bean? 是單例的,則容器中只會(huì)創(chuàng)建一個(gè),如果對應(yīng)的 Bean? 是原型,那么每次都會(huì)創(chuàng)建一個(gè)新的 Bean?,然后將創(chuàng)建的 Bean 賦值給對應(yīng)的屬性。

在我們這里 HelloService? 類是原型的,所以在創(chuàng)建 HelloController Bean? 的時(shí)候,會(huì)創(chuàng)建一個(gè) HelloService? 的 Bean? 賦值到 service? 屬性上;到這里都沒有問題,但是因?yàn)槲覀?nbsp;HelloController Bean? 是單例的,初始化的動(dòng)作在整個(gè)生命周期中只會(huì)發(fā)生一次,所以即使 HelloService 類是原因的,也只會(huì)被依賴注入一次,因此我們上面的這種寫入是達(dá)不到我們需要的效果的。

解法

解法一

寫到這里有的小伙伴就會(huì)想到,那如果我把 HelloController? 類也設(shè)置成原型呢?這樣不就可以了么。給 HelloController? 增加上注解 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)? 重啟過后我們重新訪問 http://127.0.0.1:8080/hello ,發(fā)現(xiàn)確實(shí)是可以的。也很好理解,因?yàn)榇藭r(shí) HelloController? 是原型的,所以每次訪問都會(huì)創(chuàng)建一個(gè)新的實(shí)例,初始化的過程中會(huì)被依賴注入新的 HelloService 實(shí)例。

但是不得不說,這種解法很不優(yōu)雅,把 Controller 類設(shè)置成原型,并不友好,所以這里我們不推薦這種解法。

解法二

除了將 HelloController? 設(shè)置成原型,我們還有其他的解法,上面我們提到 HelloController? 在初始化的時(shí)候會(huì)依賴注入 HelloService?,那我們是不是可以換一個(gè)方式,讓 HelloController? 創(chuàng)建的時(shí)候不依賴注入 HelloService,而是在真正需要的時(shí)候再從容器中獲取。如下所示:

package com.example.demo.controller;

import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-07-17 15:43<br>
* <b>Desc:</b>無<br>
*/
@RestController
public class HelloController {

@Autowired
private ApplicationContext applicationContext;

@GetMapping(value = "/hello")
public String hello() {
HelloService service = getService();
return service.sayHello();
}

public HelloService getService() {
return applicationContext.getBean(HelloService.class);
}
}

通過測試這種方式也是可以的,每次從容器中重新獲取的時(shí)候都是重新創(chuàng)建一個(gè)新的實(shí)例。

解法三

上面解法二還是比較常規(guī)的,除了解法二之外還有一個(gè)解法,那就是使用 Lookup? 注解,根據(jù) Spring 的官方文檔,我們可以看到下面的內(nèi)容。

圖片

簡單來說就是通過使用 Lookup? 注解的方法,可以被容器覆蓋,然后通過  BeanFactory 返回指定類型的一個(gè)類實(shí)例,可以在單例類中使用獲取到一個(gè)原型類,示例如下:

package com.example.demo.controller;

import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-07-17 15:43<br>
* <b>Desc:</b>無<br>
*/
@RestController
public class HelloController {

@GetMapping(value = "/hello")
public String hello() {
HelloService service = getService();
return service.sayHello();
}

@Lookup
public HelloService getService() {
return null;
}
}

寫法跟我們解法二比較相似,只不過不是我們顯示的通過容器中獲取一個(gè)原型 Bean? 實(shí)例,而是通過 Lookup? 的注解,讓容器來幫我們覆蓋對應(yīng)的方法,返回一個(gè)原型實(shí)例對象。這里我們的 getService? 方法里面可以直接返回一個(gè) null,因?yàn)檫@里面的代碼是不會(huì)被執(zhí)行到的。

我們打個(gè)斷點(diǎn)調(diào)試,會(huì)發(fā)現(xiàn)通過 Lookup? 注解的方法最終后走到org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept 這里。

圖片

圖片

這里我們可以看到,動(dòng)態(tài)從容器中獲取實(shí)例。不過需要注意一點(diǎn),那就是我們通過 Lookup? 注解的方法是有要求的,因?yàn)槭切枰恢貙?,所以針對這個(gè)方法我們只能使用下面的這種定時(shí)定義,必須是 public? 或者 protected,可以是抽象方法,而且方法不能有參數(shù)。

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

總結(jié)

今天阿粉通過幾個(gè)例子,給大家介紹了一下如何在單例類中獲取原型類的實(shí)例,提供了三種解法,其中解法一不推薦,解法二和解法三異曲同工,感興趣的小伙伴可以自己嘗試一下。

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2022-05-27 08:25:55

容器Spring

2020-10-29 09:19:11

索引查詢存儲(chǔ)

2022-06-23 10:47:57

Spring容器工具

2024-12-31 11:40:05

2022-08-04 08:22:49

MySQL索引

2023-10-08 10:14:12

2017-04-17 11:50:13

51CTO 學(xué)院

2024-01-05 08:38:20

SpringBeanScope

2021-04-29 07:18:21

Spring IOC容器單例

2021-05-08 08:55:54

CPUIBMIntel

2022-05-26 09:24:09

volatile懶漢模式

2011-03-18 09:27:00

Spring

2021-03-08 08:40:25

Spring Bean 創(chuàng)建單例對象

2021-07-05 08:43:46

Spring Beanscope作用域

2009-06-17 17:20:14

BeanFactorySpring

2023-01-13 07:41:20

BeanSpring容器

2021-09-13 10:03:54

藍(lán)牙連接藍(lán)牙藍(lán)牙設(shè)備

2024-05-28 07:55:31

SpringBean用域

2021-04-15 09:18:22

單例餓漢式枚舉

2021-07-01 10:45:18

Bean對象作用域
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號