Spring 中有兩種類型的Bean吐绵,一種是普通Bean,另一種是工廠Bean 即 FactoryBean。FactoryBean跟普通Bean不同帜平,其返回的對(duì)象不是指定類的一個(gè)實(shí)例吟温,而是該FactoryBean的getObject方法所返回的對(duì)象序仙。
本文簡單分析工廠FactoryBean的用法。
FactoryBean接口定義
package org.springframework.beans.factory;
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
boolean isSingleton();
}
應(yīng)用場景
FactoryBean 通常是用來創(chuàng)建比較復(fù)雜的bean鲁豪,一般的bean 直接用xml配置即可潘悼,但如果一個(gè)bean的創(chuàng)建過程中涉及到很多其他的bean 和復(fù)雜的邏輯,用xml配置比較困難爬橡,這時(shí)可以考慮用FactoryBean治唤。
應(yīng)用案例
很多開源項(xiàng)目在集成Spring 時(shí)都使用到FactoryBean,比如 MyBatis3 提供 mybatis-spring項(xiàng)目中的 org.mybatis.spring.SqlSessionFactoryBean
:
<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="trade" />
<property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
</bean>
org.mybatis.spring.SqlSessionFactoryBean
如下:
package org.mybatis.spring;
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
......
}
另外糙申,阿里開源的分布式服務(wù)框架 Dubbo 中的Consumer 也使用到了FactoryBean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<!-- 當(dāng)前應(yīng)用信息配置 -->
<dubbo:application name="demo-consumer" />
<!-- 暴露服務(wù)協(xié)議配置 -->
<dubbo:protocol name="dubbo" port="20813" />
<!-- 暴露服務(wù)配置 -->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService" />
</beans>
<dubbo:reference
對(duì)應(yīng)的Bean是com.alibaba.dubbo.config.spring.ReferenceBean
類宾添,如下:
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
}
實(shí)踐
當(dāng)前微服務(wù)日趨流行,項(xiàng)目開發(fā)中不可避免的需要去調(diào)用一些其他系統(tǒng)接口郭宝,這個(gè)時(shí)候選擇一個(gè)合適的HTTP client library 就很關(guān)鍵了辞槐,本人項(xiàng)目開發(fā)中使用 Square 開源的OkHttp ,關(guān)于OkHttp 可以參考 OkHttp Recipes
OkHttp本身的API 已經(jīng)非常好用了粘室,結(jié)合Spring 以及在其他項(xiàng)目中復(fù)用的目的榄檬,對(duì)OkHttp 創(chuàng)建過程做了一些封裝,例如超時(shí)時(shí)間衔统、連接池大小鹿榜、http代理等海雪。
maven依賴:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.6.0</version>
</dependency>
首先,是OkHttpClientFactoryBean 舱殿,代碼如下:
package com.bytebeats.codelab.http.okhttp;
import com.bytebeats.codelab.http.util.StringUtils;
import okhttp3.*;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
/**
* ${DESCRIPTION}
*
* @author Ricky Fung
* @create 2017-03-04 22:18
*/
public class OkHttpClientFactoryBean implements FactoryBean, DisposableBean {
private int connectTimeout;
private int readTimeout;
private int writeTimeout;
/**http proxy config**/
private String host;
private int port;
private String username;
private String password;
/**OkHttpClient instance**/
private OkHttpClient client;
@Override
public Object getObject() throws Exception {
ConnectionPool pool = new ConnectionPool(5, 10, TimeUnit.SECONDS);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
.connectionPool(pool);
if(StringUtils.isNotBlank(host) && port>0){
Proxy proxy = new Proxy(Proxy.Type.HTTP,new InetSocketAddress(host, port));
builder.proxy(proxy);
}
if(StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)){
Authenticator proxyAuthenticator = new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(username, password);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
builder.proxyAuthenticator(proxyAuthenticator);
}
client = builder.build();
return client;
}
@Override
public Class<?> getObjectType() {
return OkHttpClient.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() throws Exception {
if(client!=null){
client.connectionPool().evictAll();
client.dispatcher().executorService().shutdown();
client.cache().close();
client = null;
}
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
public void setWriteTimeout(int writeTimeout) {
this.writeTimeout = writeTimeout;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
項(xiàng)目中引用 applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="okHttpClient" class="com.bytebeats.codelab.http.okhttp.OkHttpClientFactoryBean">
<property name="connectTimeout" value="2000" />
<property name="readTimeout" value="2000" />
<property name="writeTimeout" value="2000" />
<property name="host" value="" />
<property name="port" value="0" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
</beans>
寫個(gè)測試類測試一下:
@Resource
private OkHttpClient client;
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}