国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

除了反射,還有其他方式初始化 Bean 嗎?

開發(fā) 前端
大家知道,Spring 中 Bean 的配置方式有很多種,但是正常來(lái)說(shuō),無(wú)論你是 XML 文件配置,還是用類似 @Service 注解這種配置,本質(zhì)上最終都是通過(guò)反射去完成 Bean 的初始化的;@Bean 注解則稍微特殊一點(diǎn),往往我們?cè)?@Bean 注解中是自己 new 出來(lái)目標(biāo) Bean,但是 @Bean 注解所標(biāo)記的方法也是通過(guò)反射調(diào)用的。

他提到他遇到了一個(gè)面試題,面試官問(wèn)他:

  • Spring 中 Bean 的實(shí)例化有哪些方式?

大家知道,Spring 中 Bean 的配置方式有很多種,但是正常來(lái)說(shuō),無(wú)論你是 XML 文件配置,還是用類似 @Service 注解這種配置,本質(zhì)上最終都是通過(guò)反射去完成 Bean 的初始化的;@Bean 注解則稍微特殊一點(diǎn),往往我們?cè)?@Bean 注解中是自己 new 出來(lái)目標(biāo) Bean,但是 @Bean 注解所標(biāo)記的方法也是通過(guò)反射調(diào)用的。

似乎 Bean 的實(shí)例化離不開反射。

那么除了上面這些方案,還有沒(méi)有其他方案呢?松哥之前其實(shí)寫過(guò)一篇文章,今天再拎出來(lái)和小伙伴們分享一下。

以下內(nèi)容基于 Spring6.0.4。

小伙伴們知道,當(dāng)我們使用 Spring 容器的時(shí)候,如果遇到一些特殊的 Bean,一般來(lái)說(shuō)可以通過(guò)如下三種方式進(jìn)行配置:

  • 靜態(tài)工廠方法
  • 實(shí)例工廠方法
  • FactoryBean

不過(guò)從 Spring5 開始,在 AbstractBeandefinition 類中多了一個(gè)屬性,對(duì)于特殊的 Bean 我們有了更多的選擇:

/**
* Specify a callback for creating an instance of the bean,
* as an alternative to a declaratively specified factory method.
* <p>If such a callback is set, it will override any other constructor
* or factory method metadata. However, bean property population and
* potential annotation-driven injection will still apply as usual.
* @since 5.0
* @see #setConstructorArgumentValues(ConstructorArgumentValues)
* @see #setPropertyValues(MutablePropertyValues)
*/
public void setInstanceSupplier(@Nullable Supplier<?> instanceSupplier) {
this.instanceSupplier = instanceSupplier;
}

/**
* Return a callback for creating an instance of the bean, if any.
* @since 5.0
*/
@Nullable
public Supplier<?> getInstanceSupplier() {
return this.instanceSupplier;
}

接下來(lái)松哥就來(lái)和大家簡(jiǎn)單聊一聊這個(gè)話題。

一、傳統(tǒng)解決方案

1.1 問(wèn)題

不知道各位小伙伴們有沒(méi)有用過(guò) OkHttp,這是一個(gè)專門做網(wǎng)絡(luò)請(qǐng)求的工具,在微服務(wù)的 HTTP 調(diào)用組件中,我們可以配置底層使用 OkHttp 這個(gè)工具。

一般來(lái)說(shuō),如果我們想直接使用 OkHttp,代碼如下:

OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
Request getReq = new Request.Builder().get().url("http://www.javaboy.org").build();
Call call = client.newCall(getReq);
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
System.out.println("e.getMessage() = " + e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
System.out.println("response.body().string() = " + response.body().string());
}
});

先通過(guò)建造者模式創(chuàng)建出來(lái)一個(gè) OkHttpClient 對(duì)象,然后還是建造者模式創(chuàng)建出來(lái) Request 對(duì)象,接下來(lái)去發(fā)送請(qǐng)求就可以了。那么對(duì)于這樣的代碼,我們可以將 OkHttpClient 對(duì)象交由 Spring 容器統(tǒng)一管理,那么該如何將 OkHttpClient 注冊(cè)到 Spring 容器中呢?

1.2 靜態(tài)工廠方法

首先可以采用靜態(tài)工廠方法,也就是工廠方法是一個(gè)靜態(tài)方法,如下:

public class OkHttpStaticFactory {
private static OkHttpClient okHttpClient;
static {
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
}
public static OkHttpClient getOkHttpClient() {
return okHttpClient;
}
}

然后在 Spring 配置文件中進(jìn)行注入:

<bean class="org.javaboy.bean.OkHttpStaticFactory" factory-method="getOkHttpClient" id="httpClient"/>

靜態(tài)工廠的特點(diǎn)是靜態(tài)方法可以直接調(diào)用,并不必要獲取到工廠類的實(shí)例,所以上面配置的時(shí)候只需要指定 factory-method 就可以了。

這就可以了,將來(lái)我們?nèi)?Spring 容器中查找一個(gè)名為 httpClient 的對(duì)象,拿到手的就是 OkHttpClient 了。

1.3 實(shí)例工廠方法

實(shí)例工廠方法意思就是說(shuō)工廠方法是一個(gè)實(shí)例方法。如下:

public class OkHttpInstanceFactory {
private volatile static OkHttpClient okHttpClient;

public OkHttpClient getInstance() {
if (okHttpClient == null) {
synchronized (OkHttpInstanceFactory.class) {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
}
}
}
return okHttpClient;
}
}

這是一個(gè)簡(jiǎn)單的單例模式。但是這里的工廠方法是一個(gè)實(shí)例方法,實(shí)例方法的調(diào)用必須得先獲取到對(duì)象然后才能調(diào)用實(shí)例方法,因此配置方式如下:

<bean class="org.javaboy.bean.OkHttpInstanceFactory" id="httpInstanceFactory"/>
<bean factory-bean="httpInstanceFactory" factory-method="getInstance" id="httpClient"/>

好啦,接下來(lái)我們就可以去 Spring 容器中獲取一個(gè)名為 httpClient 的對(duì)象了,拿到手的就是 OkHttpClient 實(shí)例。

1.4 FactoryBean

當(dāng)然,也可以通過(guò) FactoryBean 來(lái)解決上述問(wèn)題,F(xiàn)actoryBean 松哥在之前的文章中剛剛和大家介紹過(guò),我們來(lái)看下:

public class OkHttpClientFactoryBean implements FactoryBean<OkHttpClient> {
@Override
public OkHttpClient getObject() throws Exception {
return new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
}

@Override
public Class<?> getObjectType() {
return OkHttpClient.class;
}

@Override
public boolean isSingleton() {
return true;
}
}

最后在 Spring 中配置即可:

<bean class="org.javaboy.bean.OkHttpClientFactoryBean" id="httpClient"/>

這個(gè)就不做過(guò)多解釋了,不熟悉的小伙伴可以翻看前面的文章。

上面這三種方案都是傳統(tǒng)方案。

特別是前兩種,其實(shí)我們用的比較少,前兩種有一個(gè)缺陷,就是我們配置的的 factory-method 都是通過(guò)反射來(lái)調(diào)用的,通過(guò)反射調(diào)用的話,多多少少性能受點(diǎn)影響。

這種 factory-method 在 Spring 中處理的源碼執(zhí)行時(shí)序圖如下:

圖片圖片

所以最終反射是在 SimpleInstantiationStrategy#instantiate 方法中執(zhí)行的,就是大家非常熟悉的反射代碼了:

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
ReflectionUtils.makeAccessible(factoryMethod);
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
try {
currentlyInvokedFactoryMethod.set(factoryMethod);
Object result = factoryMethod.invoke(factoryBean, args);
if (result == null) {
result = new NullBean();
}
return result;
}
finally {
if (priorInvokedFactoryMethod != null) {
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
}
else {
currentlyInvokedFactoryMethod.remove();
}
}
}

好了,這是傳統(tǒng)的解決方案。

二、Spring5 解決方案

Spring5 中開始提供了 Supplier,可以通過(guò)接口回調(diào)獲取到一個(gè) Bean 的實(shí)例,這種方式顯然性能更好一些。

如下:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(Book.class);
definition.setInstanceSupplier((Supplier<Book>) () -> {
Book book = new Book();
book.setName("深入淺出 Spring Security");
book.setAuthor("江南一點(diǎn)雨");
return book;
});
ctx.registerBeanDefinition("b1", definition);
ctx.refresh();
Book b = ctx.getBean("b1", Book.class);
System.out.println("b = " + b);

關(guān)鍵就是通過(guò)調(diào)用 BeanDefinition 的 setInstanceSupplier 方法去設(shè)置回調(diào)。當(dāng)然,上面這段代碼還可以通過(guò) Lambda 進(jìn)一步簡(jiǎn)化:

public class BookSupplier {
public Book getBook() {
Book book = new Book();
book.setName("深入淺出 Spring Security");
book.setAuthor("江南一點(diǎn)雨");
return book;
}
}

然后調(diào)用這個(gè)方法即可:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(Book.class);
BookSupplier bookSupplier = new BookSupplier();
definition.setInstanceSupplier(bookSupplier::getBook);
ctx.registerBeanDefinition("b1", definition);
ctx.refresh();
Book b = ctx.getBean("b1", Book.class);
System.out.println("b = " + b);

這是不是更有一點(diǎn) Lambda 的感覺了~

在 Spring 源碼中,處理獲取 Bean 實(shí)例的時(shí)候,有如下一個(gè)分支,就是處理 Supplier 這種情況的:

AbstractAutowireCapableBeanFactory#createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
//...
return instantiateBean(beanName, mbd);
}

@Nullable
private Object obtainInstanceFromSupplier(Supplier<?> supplier, String beanName) {
String outerBean = this.currentlyCreatedBean.get();
this.currentlyCreatedBean.set(beanName);
try {
if (supplier instanceof InstanceSupplier<?> instanceSupplier) {
return instanceSupplier.get(RegisteredBean.of((ConfigurableListableBeanFactory) this, beanName));
}
if (supplier instanceof ThrowingSupplier<?> throwableSupplier) {
return throwableSupplier.getWithException();
}
return supplier.get();
}
}

上面 obtainFromSupplier 這個(gè)方法,最終會(huì)調(diào)用到第二個(gè)方法。第二個(gè)方法中的 supplier.get(); 其實(shí)最終就調(diào)用到我們自己寫的 getBook 方法了。

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2023-11-12 23:08:17

C++初始化

2025-04-25 11:25:00

SpringBean初始化

2024-10-29 11:27:27

2009-12-25 10:44:37

VDSL上網(wǎng)

2018-05-29 14:57:59

HashMap容量初始化

2015-08-10 14:54:57

公有云云安全數(shù)據(jù)加密

2022-12-02 08:48:16

CSS置灰網(wǎng)站

2023-04-08 14:22:16

Spring初始化對(duì)象

2020-10-25 17:11:29

JDK代理監(jiān)控

2011-06-17 15:29:44

C#對(duì)象初始化器集合初始化器

2021-03-12 10:30:11

SpringMVC流程初始化

2020-12-03 09:50:52

容器IoC流程

2010-07-28 10:22:33

FlexApplica

2022-07-06 10:37:45

SpringServlet初始化

2019-11-04 13:50:36

Java數(shù)組編程語(yǔ)言

2009-11-11 15:29:15

ADO初始化

2009-09-08 09:48:34

LINQ初始化數(shù)組

2024-01-09 09:46:13

數(shù)據(jù)庫(kù)MySQL

2024-01-15 06:34:09

Gin鏡像容器

2022-03-16 11:11:37

SpringBean項(xiàng)目
點(diǎn)贊
收藏

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

www.超碰com| 亚洲色图综合| 亚洲制服丝袜在线| 成人性做爰aaa片免费看不忠| 久久99国产精品久久99果冻传媒| 日韩国产大片| 日韩一区二区三区在线| www亚洲人| 欧美猛男gaygay网站| 成人性生交大片免费看午夜| 色综合天天狠狠| 国产免费av高清在线| 欧美日韩另类国产亚洲欧美一级| 色多多视频在线观看| 91精品国产色综合久久不卡电影| www免费在线观看| 日韩精品一区二区三区蜜臀| v片在线观看| 日韩一区二区三| 男人久久天堂| 日韩在线视频一区| 日韩精品视频中文字幕| 97热精品视频官网| 国产精品久久久久无码av| 国产精品99久久久久久久| 久久狠狠一本精品综合网| 在线视频不卡一区二区| 99久久精品国产毛片| 成人亚洲精品777777大片| 亚洲男同性恋视频| 免费黄色在线视频网站| 6080亚洲精品一区二区| 午夜激情在线| 日韩在线免费视频观看| 99香蕉久久| 成人羞羞国产免费| 日本最新不卡在线| 日本免费黄视频| 亚洲影院久久精品| 黄色网页在线观看| 色妞色视频一区二区三区四区| 豆花视频一区二区| http;//www.99re视频| 毛片av一区二区| 男人日女人下面视频| 日本一区二区三区电影免费观看| 中文字幕一区二区三区在线不卡| 91欧洲在线视精品在亚洲| 4438亚洲最大| 97久久精品一区二区三区的观看方式| 欧美主播福利视频| 亚洲永久免费精品| 国产深夜男女无套内射| 欧美日韩日本国产| 成人在线爆射| 国产精品久久久久久av下载红粉 | 青青草综合在线| 中文字幕五月欧美| 欧美jizz18hd性欧美| 理论片在线不卡免费观看| 日韩精品免费观看视频| 久久久久久久午夜| 久久av免费看| 欧美日韩一区二区视频在线观看| 成人福利视频在线| 一区 二区 三区| 正在播放国产一区| 亚洲成av人片乱码色午夜| 日韩视频一二三| 色综合网站在线| 国产免费av国片精品草莓男男| 国产综合福利在线| 成人va在线观看| av网站在线免费播放| 91精品国产高清自在线| 九九在线精品视频| 亚洲色图欧美制服丝袜另类第一页| 色999日韩| 国产精品视频黄色| 久久亚洲国产精品成人av秋霞| av电影在线观看一区| 日韩中文字幕91| 久久婷婷久久一区二区三区| 99re热这里只有精品免费视频| 欧美一级中文字幕| 精品久久久久久| 亚洲视频一区二区在线| 婷婷亚洲最大| 欧美一级欧美一级| 精品国产伦一区二区三区免费| 国产精品1luya在线播放| 蜜桃视频成人在线观看| 欧美一区二区免费观在线| 99久久婷婷这里只有精品| 国产日韩亚洲欧美在线| 555夜色666亚洲国产免| 1024精品久久久久久久久| 成年人视频在线网站| 91精品国产九九九久久久亚洲| 一区二区三区成人| 天堂√8在线中文| 欧美日韩国产成人高清视频| 国产精品亚洲综合一区在线观看| 亚洲影院一区| 日韩欧美亚洲区| 91久久精品午夜一区二区| 国产精品视屏| 久久久精品在线视频| 亚洲免费影视第一页| 老**午夜毛片一区二区三区| 蜜桃成人在线视频| 国产精品一区二区3区| 亚洲欧美精品午睡沙发| 粉嫩精品导航导航| 国产视频一区二区视频| 久久久精品国产网站| 成人亚洲精品久久久久软件| 欧美久久天堂| 91制片厂免费观看| 日韩av在线免播放器| 青椒成人免费视频| 欧美激情网站| 大荫蒂性生交片| 最近2019年好看中文字幕视频| 国产成人欧美日韩在线电影| 丝袜美腿诱惑一区二区三区| 毛片av在线播放| 久久精品99久久久久久久久| 99久久精品国产一区二区三区| 国产传媒视频在线观看| 久久久久免费| 国产资源在线观看入口av| 香蕉视频在线免费| 原千岁中文字幕| 日韩精品视频在线观看视频 | 欧美成人官网二区| 综合色天天鬼久久鬼色| 国产一区二区91| 日韩av二区在线播放| 国产欧美一区| 免费在线播放第一区高清av| 国产不卡网站| 国产精品国产亚洲精品看不卡| 日韩中文理论片| 日韩精品在线视频观看| 欧美草草影院在线视频| 精品国免费一区二区三区| 色哟哟一区二区三区| 欧美日韩亚洲综合一区 | 亚洲欧美日韩国产成人| 97香蕉超级碰碰久久免费的优势| 日韩中文在线字幕| 成人av免费电影网站| 久久精品国产秦先生| 国产婷婷色综合av蜜臀av| 2020久久国产精品| 91丨九色丨蝌蚪富婆spa| 免费在线观看羞羞视频| 91成人免费在线观看| 99re6在线视频| 国产日韩一区二区三免费高清| 美女视频黄 久久| 欧美丰满一区二区免费视频| 粉嫩高清一区二区三区精品视频 | 欧美日韩在线观看视频| 国产精品露脸自拍| 五月天丁香婷| 欧美成熟视频| 精品欧美乱码久久久久久| 日本福利一区二区三区| 日本性爱视频在线观看| 蜜桃精品在线观看| 最近2019年日本中文免费字幕 | 台湾佬综合网| 亚洲小说区图片区| aaaaa毛片| 欧美激情在线一区| 91视频网页| 国产人成在线观看| av电影在线地址| 成人高h视频在线| 亚洲国内高清视频| 国产精品久久久久久久裸模| 精品成人免费| 中文字幕日韩亚洲| 午夜在线视频| 九九热在线免费| 欧美日韩精品免费在线观看视频| 久久久国产视频| 在线播放中文字幕一区| 久久久久久免费毛片精品| 国产精品久久久免费| 北条麻妃一区二区三区在线| 香蕉久久aⅴ一区二区三区| 色播五月综合网| 亚洲精品国产精品国自产观看| 清纯唯美亚洲激情| 亚洲另类激情图| 欧美私模裸体表演在线观看| 中文av一区二区|