一般我們會使用如 XML
、@Bean
、@Componet
等方式去註冊,但是如果在 Bean 的實例化過程非常複雜,如有很多邏輯處理、層層依賴、複雜依賴等,這在第三方整合的時候尤爲重要。FactoryBean
是 Spring 提供的基於接口編碼的方式幫助我們向 Spring 容器中註冊組件,相比註解和配置,更加靈活。
最常見的比如 MyBatis 與 Spring 整合,這是從我之前學習 MyBatis 的筆記中截取的相關配置:
FactoryBean
接口有三個方法:
public interface FactoryBean<T> {
//返回由 FactoryBean 創建的 Bean 實例,會被 Spring 管理,如果是多例,則會多次調用,否則會被(單實例)緩存
@Nullable
T getObject() throws Exception;
//返回 FactoryBean 創建的 Bean 的類型
@Nullable
Class<?> getObjectType();
//是否單例,默認是 true
default boolean isSingleton() {
return true;
}
}
先看一個例子,定義一個 Bean:
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
定義一個 FactoryBean
:
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
測試一下:
@SpringBootApplication
public class SimpleSpringBootApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SimpleSpringBootApplication.class);
application.setWebApplicationType(WebApplicationType.NONE);
ConfigurableApplicationContext context = application.run(args);
UserFactoryBean bean = context.getBean(UserFactoryBean.class);
//Object user = context.getBean("user");
System.out.println("UserFactoryBean.class:" + bean);
Object userFactoryBean = context.getBean("userFactoryBean");
System.out.println("userFactoryBean.name:" + userFactoryBean);
User userBean = context.getBean(User.class);
System.out.println("User.class:" + userBean);
Object usserFactoryBean2 = context.getBean("&userFactoryBean");
System.out.println("&userFactoryBean.name:"+usserFactoryBean2);
}
@Bean
public UserFactoryBean userFactoryBean() {
return new UserFactoryBean();
}
}
輸出:
UserFactoryBean.class:com.example.simplespringboot.bean.UserFactoryBean@2e1792e7
userFactoryBean.name:User{name='null', age=null}
User.class:User{name='null', age=null}
&userFactoryBean.name:com.example.simplespringboot.bean.UserFactoryBean@2e1792e7
可以看到將一個 FactoryBean
交給 Spring 管理,getObject()
返回的對象 User
也被 Spring 管理了,而這個 User
對象的名稱就是FactoryBean
的首字母小寫(默認),想獲得這個 FactoryBean
需要使用 & 加上FactoryBean
的首字母小寫(默認)。即在 BeanFactory
中,FactoryBean
註冊的 Bean 和 FactoryBean
本身存儲的方式是一致的,但是 beanName
會有所區別。
以 org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
方法爲切入點,可以看到本質是調用 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法,會調用 org.springframework.beans.factory.support.AbstractBeanFactory#transformedBeanName
方法:
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
這裏的入參 name
就是 getBean
方法中傳入的 name
,也就是說 Spring 並不會直接使用這個 name
,必須要轉換一下,主要是爲了處理 & 和 Bean 的別名。
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
//如果不是 & 開頭,就是普通 Bean,直接返回
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
//& 開頭 說明是 FactoryBean,返回 FactoryBean beanName
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
再看 org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
方法:
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//name 還是我們傳入的 name,beanName 是轉化後的 name
// Don't let calling code try to dereference the factory if the bean isn't a factory.
//如果是直接獲取 FactoryBean
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return 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)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
//基於 FactoryBean 獲取 Bean
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
方法又會調用 org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
方法:
可以看到本質就是調用 org.springframework.beans.factory.FactoryBean#getObject
獲取 Bean 實例。
歡迎關注公衆號