白话Spring源码(七):FactoryBean

看清楚是FactoryBean,不是BeanFactory,虽然他们长得很像,但作用确实天差地别,BeanFactory是bean工厂,前面的博客已经介绍过了,他们工厂类的基础接口,而FactoryBean是一种bean,那为什么要有这种bean呢,和一般的bean有什么区别呢?下面跟大家分享一下FactoryBean。

一、为什么需要FactoryBean

我们知道spring里一切皆是bean,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,但是有些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。这时FactoryBean就出现了,FactoryBean就是通过编码的方式灵活的创建bean的实例,这也是工厂模式设计模式的运用。

二、什么是FactoryBean

FactoryBean是一个接口,getObject就是创建bean实例的关键方法。

public interface FactoryBean {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory. As with a BeanFactory, this allows
	 * support for both the Singleton and Prototype design pattern.
	 * @return an instance of the bean (should never be null)
	 * @throws Exception in case of creation errors
	 */
	Object getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates, or null
	 * if not known in advance. This allows to check for specific types of
	 * beans without instantiating objects, e.g. on autowiring.
	 * <p>For a singleton, this can simply return getObject().getClass(),
	 * or even null, as autowiring will always check the actual objects
	 * for singletons. For prototypes, returning a meaningful type here
	 * is highly advisable, as autowiring will simply ignore them else.
	 * @return the type of object that this FactoryBean creates, or null
	 * @see ListableBeanFactory#getBeansOfType
	 */
	Class getObjectType();

	/**
	 * Is the bean managed by this factory a singleton or a prototype?
	 * That is, will getObject() always return the same object?
	 * <p>The singleton status of the FactoryBean itself will
	 * generally be provided by the owning BeanFactory.
	 * @return if this bean is a singleton
	 */
	boolean isSingleton();

}

getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。

boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。

Class<T> getObjectType():返回FactoryBean创建的bean类型。

bean工厂创建bean对象时要判断改对象是否是FactoryBean,如果是FactoryBean则调用他的getObject()方法获取实例化bean。AbstractBeanFactory源码如下:

	protected Object getObjectForSharedInstance(String name, Object beanInstance) {
		String beanName = transformedBeanName(name);

		// Don't let calling code try to dereference the
		// bean factory if the bean isn't a factory
		if (isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(beanName, beanInstance);
		}

		// Now we have the bean instance, which may be a normal bean
		// or a FactoryBean. If it's a FactoryBean, we use it to
		// create a bean instance, unless the caller actually wants
		// a reference to the factory.
		if (beanInstance instanceof FactoryBean) {
			if (!isFactoryDereference(name)) {
				// return bean instance from factory
				FactoryBean factory = (FactoryBean) beanInstance;
				logger.debug("Bean with name '" + beanName + "' is a factory bean");
				try {
					beanInstance = factory.getObject();
				}
				catch (BeansException ex) {
					throw ex;
				}
				catch (Exception ex) {
					throw new BeanCreationException("FactoryBean threw exception on object creation", ex);
				}
				if (beanInstance == null) {
					throw new FactoryBeanCircularReferenceException(
					    "Factory bean '" + beanName + "' returned null object - " +
					    "possible cause: not fully initialized due to circular bean reference");
				}
			}
			else {
				// the user wants the factory itself
				logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
			}
		}

		return beanInstance;
	}

三、demo

(转自:https://www.jianshu.com/p/6f0a59623090

当前微服务日趋流行,项目开发中不可避免的需要去调用一些其他系统接口,这个时候选择一个合适的HTTP client library 就很关键了,本人项目开发中使用 Square 开源的OkHttp 。
OkHttp本身的API 已经非常好用了,结合Spring 以及在其他项目中复用的目的,对OkHttp 创建过程做了一些封装,例如超时时间、连接池大小、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;
    }

}

项目中引用 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>

写个测试类测试一下:

    @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());
    }

猜你喜欢

转载自blog.csdn.net/haoxin963/article/details/85076644
今日推荐