成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring5 中更優(yōu)雅的第三方 Bean 注入

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

小伙伴們知道,當(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è)話題。

1. 傳統(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)的解決方案。

2. 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 的感覺(jué)了~

在 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)推薦

2022-05-23 13:50:20

開發(fā)封裝

2017-05-16 13:24:02

LinuxCentOS第三方倉(cāng)庫(kù)

2015-11-05 16:44:37

第三方登陸android源碼

2011-10-08 14:37:59

漏洞

2017-12-11 15:53:56

2019-07-30 11:35:54

AndroidRetrofit庫(kù)

2014-07-23 08:55:42

iOSFMDB

2014-07-25 09:33:22

2016-10-21 14:09:10

2009-12-31 14:38:34

Silverlight

2021-03-17 09:26:48

代碼注入密鑰簽名

2019-09-03 18:31:19

第三方支付電商支付行業(yè)

2017-11-01 06:40:33

2025-04-01 05:00:00

OAuth2服務(wù)器身份驗(yàn)證

2019-09-02 14:59:41

蘋果維修設(shè)備

2021-03-03 09:42:26

鴻蒙HarmonyOS圖片裁剪

2022-01-14 09:57:14

鴻蒙HarmonyOS應(yīng)用

2009-01-14 12:45:05

MSNIM蘋果

2013-08-12 16:04:19

第三方移動(dòng)應(yīng)用

2021-09-26 10:43:08

注冊(cè)Istio集成
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 亚洲欧美在线一区 | 国产偷自视频区视频 | 久久久久一区 | 99免费在线观看视频 | 一区在线观看 | 亚洲欧美日韩在线不卡 | 美国av片在线观看 | 日韩欧美国产精品 | 欧美久久国产精品 | 日日夜夜天天久久 | 成人免费视频网站在线看 | 欧美日韩国产三级 | 欧美精品tv| 欧美性极品xxxx做受 | 国产成人免费视频网站高清观看视频 | 亚洲色图插插插 | 国产一区二区三区四区五区加勒比 | 欧美日韩在线观看一区 | 国产美女在线看 | 中文字字幕一区二区三区四区五区 | 国产成人在线视频免费观看 | 成人久久18免费网站麻豆 | 亚洲成人自拍 | 在线久草 | 欧美色成人 | 欧美成人在线影院 | 影视先锋av资源噜噜 | 一区二区三区免费 | 黄网站免费在线观看 | 欧美成人精品一区二区男人看 | 日韩三级 | 亚洲欧美自拍偷拍视频 | 一区二区三区视频播放 | 欧美1区| 亚洲高清在线观看 | 久久精品二区 | 免费一区 | 亚洲一区二区中文字幕 | 国产激情在线 | 国产成人精品午夜 | av片在线播放|