扫描下方二维码或者微信搜索公众号 菜鸟飞呀飞
,即可关注微信公众号,阅读更多Spring源码分析文章
首先需要说明的是,FactoryBean和BeanFactory虽然名字很像,但是这两者是完全不同的两个概念,用途上也是天差地别。BeanFactory是一个Bean工厂,在一定程度上我们可以简单理解为它就是我们平常所说的Spring容器(注意这里说的是简单理解为容器),它完成了Bean的创建、自动装配等过程,存储了创建完成的单例Bean。而FactoryBean通过名字看,我们可以猜出它是Bean,但它是一个特殊的Bean,究竟有什么特殊之处呢?它的特殊之处在我们平时开发过程中又有什么用处呢?
FactoryBean的特殊之处在于它可以向容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。先通过如下示例代码来感受下FactoryBean的用处吧。
package com.tiantang.study.component; import com.tiantang.study.service.UserService; import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Component; @Component public class CustomerFactoryBean implements FactoryBean<UserService> { @Override public UserService getObject() throws Exception { return new UserService(); } @Override public Class<?> getObjectType() { return UserService.class; } } 复制代码
package com.tiantang.study.service; public class UserService { public UserService(){ System.out.println("userService construct"); } } 复制代码
com.tiantang.study.component
package com.tiantang.study.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.tiantang.study.component") public class AppConfig { } 复制代码
public class MainApplication { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); System.out.println("容器启动完成"); UserService userService = applicationContext.getBean(UserService.class); System.out.println(userService); Object customerFactoryBean = applicationContext.getBean("customerFactoryBean"); System.out.println(customerFactoryBean); } } 复制代码
com.tiantang.study.component
这个包下的类,按照我们的常规理解,这个时候应该只会有CustomerFactoryBean这个类被放进Spring容器中了,UserService并没有被扫描。而我们在测试时却可以通过 applicationContext.getBean(UserService.class)
从容器中获取到Bean,为什么? CustomerFactoryBean
类的单例对象在容器中的beanName是customerFactoryBean。所以这个时候我们调用方法getBean(beanName)通过beanName去获取Bean,这个时候理论上应该返回的是CustomerFactoryBean类的单例对象。然而,我们将结果打印出来,却发现,这个对象的hashCode竟然和userService对象的hashCode一模一样,这说明这两个对象是同一个对象,为什么会出现这种情况呢?为什么不是CustomerFactoryBean类的实例对象呢?
以上3个问题的答案可以用一个答案解决,那就是FactoryBean是一个特殊的Bean。我们自定义的CustomerFactoryBean实现了FactoryBean接口,所以当CustomerFactoryBean被扫描进Spring容器时,实际上它向容器中注册了两个bean,一个是CustomerFactoryBean类的单例对象;另外一个就是getObject()方法返回的对象,在demo中,我们重写的getObject()方法中,我们通过new UserService()返回了一个UserService的实例对象,所以我们从容器中能获取到UserService的实例对象。如果我们想通过beanName去获取CustomerFactoryBean的单例对象,需要在beanName前面添加一个 &
符号,如下代码,这样就能根据beanName获取到原生对象了。
public class MainApplication { public static void main(String[] args) { CustomerFactoryBean rawBean = (CustomerFactoryBean) applicationContext.getBean("&customerFactoryBean"); System.out.println(rawBean); } } 复制代码
通过上面的示例代码,我们知道了FactoryBean的作用,也知道该如何使用FactoryBean,那么接下来我们就通过源码来看看FactoryBean的工作原理。
public void preInstantiateSingletons() throws BeansException { // 从容器中获取到所有的beanName List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 在此处会根据beanName判断bean是不是一个FactoryBean,实现了FactoryBean接口的bean,会返回true // 此时当beanName为customerFactoryBean时,会返回true,会进入到if语句中 if (isFactoryBean(beanName)) { // 然后通过getBean()方法去获取或者创建单例对象 // 注意:在此处为beanName拼接了一个前缀:FACTORY_BEAN_PREFIX // FACTORY_BEAN_PREFIX是一个常量字符串,即:& // 所以在此时容器启动阶段,对于customerFactoryBean,应该是:getBean("&customerFactoryBean") Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); // 下面这一段逻辑,是判断是否需要在容器启动阶段,就去实例化getObject()返回的对象,即是否调用FactoryBean的getObject()方法 if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } } } } 复制代码
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { if (mbd.isSingleton()) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } } return (T) bean; } 复制代码
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } } // 如果bean不是factoryBean,那么会直接返回Bean // 或者bean是factoryBean但name是以&特殊符号开头的,此时表示要获取FactoryBean的原生对象。 // 例如:如果name = &customerFactoryBean,那么此时会返回CustomerFactoryBean类型的bean if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } // 如果是FactoryBean,那么先从cache中获取,如果缓存不存在,则会去调用FactoryBean的getObject()方法。 Object object = null; if (mbd == null) { // 从缓存中获取。什么时候放入缓存的呢?在第一次调用getObject()方法时,会将返回值放入到缓存。 object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); // 在getObjectFromFactoryBean()方法中最终会调用到getObject()方法 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; } 复制代码
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { // 如果BeanFactory的isSingleton()方法返回值是true,表示getObject()返回值对象是单例的 if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // 再一次判断缓存中是否存在。(双重检测机制,和平时写线程安全的代码类似) Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // 在doGetObjectFromFactoryBean()中才是真正调用getObject()方法 object = doGetObjectFromFactoryBean(factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { // 下面是进行后置处理,和普通的bean的后置处理没有任何区别 if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { return object; } beforeSingletonCreation(beanName); try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { afterSingletonCreation(beanName); } } // 放入到缓存中 if (containsSingleton(beanName)) { this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } // 非单例 else { Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; } } 复制代码
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException { Object object; if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 调用getObject()方法 object = factory.getObject(); } return object; } 复制代码
现在知道了FactoryBean的原理,那么在平时工作中,你见过哪些FactoryBean的使用场景。如果没有留意过的话,笔者在这里就拿Spring整合Mybatis的原理来举例吧。
mybatis-spring
,然后是配置数据源。最后还需要一个配置,如果你是通过XML配置的话,还需要如下配置: <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
复制代码
@Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } 复制代码
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { // ...省略其他代码 public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; } } 复制代码
public void afterPropertiesSet() throws Exception { // buildSqlSessionFactory()方法会根据mybatis的配置进行初始化。 this.sqlSessionFactory = buildSqlSessionFactory(); } 复制代码
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) public @interface MapperScan { } 复制代码
getSqlSession().getMapper(this.mapperInterface)
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } @Override public Class<T> getObjectType() { return this.mapperInterface; } @Override public boolean isSingleton() { // 返回true是为了让Mapper接口是一个单例的 return true; } } 复制代码
public class MapperProxyFactory<T> { protected T newInstance(MapperProxy<T> mapperProxy) { // JDK动态代理 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } } 复制代码
在SpringBoot中整合MyBatis的原理是一样的,虽然使用的是mybatis-spring-boot-starter这个依赖,但最终的整合原理和Spring是一模一样的。SpringBoot的最主要的功能是自动配置,和其他框架的整合原理与Spring相比几乎没变。
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnBean({DataSource.class}) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration { @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); // 省略部分代码 return factory.getObject(); } } 复制代码
扫描下方二维码即可关注微信公众号 菜鸟飞呀飞
,一起阅读更多Spring源码。