FactoryBean 和BeanFactory 名字太像了,以至于容易混淆,但是从功能上讲完全不一样, 理解了它们各自的功能就可以完美区分这俩了
1. BeanFactory
Spring IOC 容器,用于管理bean ,具体细节已经在Spring bean 实例化中介绍过,就不再赘述,可以点击去阅读。
总的来说BeanFactory 时Spring 容器,
在Spring的启动过程,FactoryBean 是一个会被BeanFactory 管理的bean , 但是它相比其他普通业务bean,又有一些特殊的功能,比如生产出复杂配置的bean.
接下来详细介绍FactoryBean
2. FactoryBean 是一个接口
FactoryBean
接口提供了一种灵活的方式来创建复杂的对象。
- 实现
FactoryBean
接口的类,可以通过其 getObject
方法来创建并返回实际的对象实例。FactoryBean
可以用于创建单例、原型、代理对象或其他复杂的初始化逻辑。
FactoryBean
接口定义如下:
1 2 3 4 5
| public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); boolean isSingleton(); }
|
T getObject() throws Exception
:这个方法返回由 FactoryBean
创建的对象。这个对象可以是一个普通的 Bean,也可以是一个代理对象或其他复杂对象。
Class<?> getObjectType()
:这个方法返回由 FactoryBean
创建的对象的类型。Spring 使用这个信息来确定 Bean 的类型。
boolean isSingleton()
:这个方法指示由 FactoryBean
创建的对象是否是单例(singleton)。如果返回 true
,Spring 容器会缓存该实例。
3. Spring 容器启动过程中的FactoryBean
接下来我们将通过 分析Spring 启动过程中 FactoryBean
的实例化以及通过FactoryBean.getObject
的过程来加深对FactoryBean
的理解。
这里我们用 Spring AOP XML配置方式原理详解 中的示例代码进行讲解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public class UserService {
public void createUser(String username) {
System.out.println("Creating user: " + username); }
public void deleteUser(String username) {
System.out.println("Deleting user: " + username); } }
public class LoggingBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Before method: " + method.getName()); } }
public class LoggingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("After method: " + method.getName()); } }
public class LoggingMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println(invocation.getMethod().getName()+ " 方法开始执行");
Object result = invocation.proceed();
long endTime = System.currentTimeMillis(); System.out.println(invocation.getMethod().getName()+ "方法执行时间: " + (endTime - startTime) + "ms");
return result; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.example.codingInAction.service.UserService"/>
<bean id="loggingBeforeAdvice" class="com.example.codingInAction.aop.LoggingBeforeAdvice"/>
<bean id="loggingAfterAdvice" class="com.example.codingInAction.aop.LoggingAfterAdvice"/>
<bean id="loggingMethodInterceptor" class="com.example.codingInAction.aop.LoggingMethodInterceptor"/>
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="userService"/> <property name="interceptorNames"> <list> <value>loggingMethodInterceptor</value> <value>loggingBeforeAdvice</value> <value>loggingAfterAdvice</value>
</list> </property> <property name="proxyTargetClass" value="true"/> </bean> </beans>
|
启动代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class CodingInActionApplication{
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userServiceProxy");
userService.createUser("john"); userService.deleteUser("john");
} }
|
&
前缀的作用
- 使用
getBean("&beanName")
可以获取 FactoryBean
实例本身,而不是 FactoryBean
创建的对象。
- 使用
getBean("beanName")
则获取由 FactoryBean
创建的对象。
3.1 FactoryBean 也是一个bean
FactoryBean 也是一个bean, 和普通bean 使用相同的逻辑进行实例化,所以也是被beanFactory 管理的。
如代码所示
1
| ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
|
在以上Spring 容器的启动过程中,userServiceProxy
作为一个FactoryBean 会通过getBean触发实例化流程。
可以看到getBean传参的时候,beanName 前增加了 & 标识,说明这个getBean 流程需要获取的是userServiceProxy
这个个FactoryBean ,而不是由它创建的对象
在实例化 FactoryBean 的过程中,并不需要&
标识 , 在getBean 是否,需要判断 是返回FactoryBean 实例 还是Factorybean创建的对象时才需要&
标识。
进入getBean, 可以看到namet经过了transformedBeanName方法处理, 又把& 去掉了。
1
| String beanName = transformedBeanName(name);
|
这行代码的作用是将传入的 name
转换为实际的 Bean 名称,在 Spring 框架中,Bean 名称可能包含一些特殊的前缀或后缀,用于处理特殊情况或指示某种行为。transformedBeanName
方法用于解析和处理这些前缀或后缀,以获得实际的 Bean 名称。
在这个例子中就是去掉工厂 Bean 前缀 &
。
&
前缀的作用 告诉程序 ,我是来获取 FactoryBean
本身,而不是获取FactoryBean
创建的对象的。
所以 &
本身不属于bean名称的一部分,需要经过transformedBeanName
方法会去掉这个前缀,以获得实际的 Bean 名称。
对于非FactoryBean 来讲, name 和beanName 都是一样的
3.1.2 getObjectForBeanInstance
FactoryBean只有在实例化后才能通过getObject() 创建其他配置复杂的bean
在看IOC源码的时候,发现即使我们已经创建出来了对象的实例,还是要走一个方法再去处理下,这里就是对FactoryBean的处理,因为它可以产生对象,所以你getBean的时候取到的不是它本身,而是通过它生成的产品。【如果要取它本身,getBean(&+beanName)】 我们先来回忆下IOC源码中那个处理FactoryBean的简略代码:
我们可以看到,无论是直接取单例的bean,还是创建单例、多例、自定义生命周期的bean,都会经过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);这个方法,我们现在就来看看这里到底是做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean factory = (FactoryBean) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT)); } return (object != NULL_OBJECT ? object : null); } } else { return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); } }
private Object doGetObjectFromFactoryBean( final FactoryBean factory, final String beanName, final boolean shouldPostProcess) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return factory.getObject(); } }, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); } if (object == null && isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } if (object != null && shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex); } } return object; }
|
经过getObjectForBeanInstance
处理,最终返回了userServiceProxy
这个FactoryBean 本身
3.2 获取FactoryBean 创建的对象
FactoryBean 本身已经实例化完成了,现在可以获取由它创建的对象了
1 2
| UserService userService = (UserService) context.getBean("userServiceProxy");
|
由于userServiceProxy 是FactoryBean, 现在执行getBean("beanName")
说明我们要获取由它创建的对象了。下面跟着代码进行简单分析。
进入源码,再次来到getBean 流程,可以看到,经过transformedBeanName
处理后,name 和beanName 保持一致。
3.2.2 getObjectForBeanInstance
此时getSingleton,一级缓存中已经有了IOC 容器启动过程的中创建的实例, 直接获取即可。再次进入getObjectForBeanInstance 方法
3.2.3 FactoryBean.getObject
在getObjectForBeanInstance 方法中,逐渐深入最终会进入到ProxyFactoryBean 重写的getObject 方法(getObject 是接口FactoryBean 中定义的方法还记得吗)
在getObject 方法中,可以获取userServiceProxy 创建的对象,即一个代理对象
4. FactoryBean
的应用场景
FactoryBean
可以用于各种高级应用场景,以下是一些典型的使用场景:
4.1 创建代理对象
FactoryBean
常用于创建 AOP 代理对象。例如,Spring AOP 内部大量使用 FactoryBean
来创建代理对象。
在上面的动态代理Demo中,我们就展示了一个通过FactoryBean创建代理对象的复杂例子。
4.2 创建复杂的 Bean 实例
在某些情况下,创建一个 Bean 可能涉及复杂的初始化逻辑。通过 FactoryBean
,我们可以将这些逻辑封装在 getObject
方法中,从而简化配置。
很多开源项目在集成Spring 时都使用到FactoryBean,比如 MyBatis3 提供 mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean:
1 2 3 4 5 6
| <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"></property> </bean>
|
1 2 3 4 5 6 7 8 9 10 11 12
| public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class); ... public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { this.afterPropertiesSet(); } return this.sqlSessionFactory; } ... }
|