一般我们会使用如 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 实例。
欢迎关注公众号