前言html
前两篇文章介绍了如何使用
@Component
,@Import
注解来向spring容器中注册组件(javaBean),本文将介绍经过FactoryBean
接口继续向spring容器中注册组件。可能第一印象是spring中BeanFactory
接口,可是这里确实说的是FactoryBean
。java
推荐阅读spring
根据接口名称,咱们也能够简单看出二者的区别缓存
接下来将进入本文正题,如何经过FactoryBean
接口向spring容器中注册组件app
正如前面说的,FactoryBean也是spring中的一个Bean,可是它又是一个特殊的Bean,它的存在是为了生产其余的JavaBean。首先咱们看看FactoryBean
自身的接口定义ide
public interface FactoryBean<T> { /** * 从Spring容器中获取Bean时会调用此方法,返回一个T对象 */ @Nullable T getObject() throws Exception; /** * 此工厂Bean返回对象的类型 */ @Nullable Class<?> getObjectType(); /** * 工厂Bean建立的对象是否为单例, * 若是返回false,说明getObject方法的实例对象不是单例的, * Spring每次从容器中获取T对象时,都调用getObject方法建立一个对象 */ default boolean isSingleton() { //spring 5 接口默认返回true(单例) return true; } }
FactoryBean
接口定义简单明了,就是用来获取一个Bean的基本信息,下面咱们本身实现该接口,来生产一个javaBeanpost
/** * 产生 Bike 对象的工厂Bean */ @Component public class BikeFactoryBean implements FactoryBean<Bike> { public Bike getObject() throws Exception { System.out.println("......开始建立Bike对象......"); return new Bike(); } public Class<?> getObjectType() { return Bike.class; } public boolean isSingleton() { return true; } }
自定义的一个JavaBean类学习
/** * 待注册的自定义组件 */ @Data public class Bike { private String id = "by FactoryBean"; }
添加spring容器启动的引导类测试
/** * spring 容器启动引导类,测试 FactoryBean 功能 */ @ComponentScan("com.spring.study.ioc.factorybean") public class TestFactoryBeanBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestFactoryBeanBootstrap.class); //获取工厂Bean自己的Id String[] beanNames = applicationContext.getBeanNamesForType(BikeFactoryBean.class); System.out.println("BikeFactoryBean names:" + Arrays.asList(beanNames)); //获取工厂Bean产生的Bean的Id beanNames = applicationContext.getBeanNamesForType(Bike.class); System.out.println("Bike bean names:" + Arrays.asList(beanNames)); Object bean = applicationContext.getBean("bikeFactoryBean"); System.out.println(bean); bean = applicationContext.getBean(Bike.class); System.out.println(bean); // 获取工厂Bean 自己的实例对象 bean = applicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX + "bikeFactoryBean"); System.out.println(bean); applicationContext.close(); } }
启动spring容器,控制台打印结果:ui
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......开始建立Bike对象......
Bike(id=by FactoryBean)
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由结果能够看出
BikeFactoryBean
类上加了@Component
注解,可是从spring容器仍然能够获取到Bike
类的信息&
符,也就是说,工厂Bean定义的Id实际为getObject()方法返回Bean的Id,而工厂Bean自己的Id被添加了一个前缀&
符&
符,而此前缀在BeanFactory
接口中已经定义了FACTORY_BEAN_PREFIX
若是将BikeFactoryBean
的isSingleton()
方法返回了false
public boolean isSingleton() { return false; }
从新启动spring容器,能够看以下结果:
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......开始建立Bike对象......
Bike(id=by FactoryBean)
......开始建立Bike对象......
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由结果能够看出,惟一的变化在于从spring容器中屡次获取实际Bean时,工厂Bean的getObject()方法被屡次进行了调用。这与spring容器中被标识为原型的普通Bean相同,每次从spring中获取Bean时都会被实例化。
要想了解FactoryBean的执行过程,就须要结合Spring容器启动的过程来进行分析。而spring容器的启动过程通过了纷繁复杂的步骤。为了尽量少的入坑和挖坑,下面仅结合FactoryBean相关的源码进行说明。
在开始入坑之旅以前,结合前面的例子作几点说明,方便后续讲解
前面定义的 BikeFactoryBean 类上面直接添加了@Component注解,这样spring会默认以类名首字母小写(bikeFactoryBean)做为beanName;若是使用@Bean进行注册时,spring默认会以方法名做为beanName,下面继续以“BikeFactoryBean”为例。
BeanFactoryPostProcessor
,BeanPostProcessor
以及注册Listener
后会执行org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization()
方法,此方法会对剩下全部的非Abstract
、非LazyInit
的单实例Bean进行实例化,如下为部分代码片断。@Override public void preInstantiateSingletons() throws BeansException { ...省略代码... // 1. 拷贝一份副本:spring容器中的全部的Bean名称 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // 2. 遍历每个beanName,尝试经过getBean()方法进行实例化 // 在getBean()方法内部会先尝试从容器singletonObjects中获取Bean,若是没有才会进行实例化操做 for (String beanName : beanNames) { // 3. 经过beanName获取Bean定义信息 BeanDefinition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 4. 根据BeanDefinition判断该Bean是否不是抽象的,单例的,非懒加载的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 5. 知足上面的条件后,在根据beanName判断此Bean是不是一个工厂Bean(实现了FactoryBean接口) if (isFactoryBean(beanName)) { // 6. 若是是一个工厂Bean,则在此处进行工厂Bean自己的实例化 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); ...省略代码... } else { // 若是不是工厂bean,也是调用getBean()方法进行实例化 getBean(beanName); } } } ...省略代码... }
preInstantiateSingletons()是在finishBeanFactoryInitialization()方法内部调用的
根据上面的代码流程,在第6步时,会对
BikeFactoryBean
类自己进行实例化,而且能够看出传递的beanName为初始注册的name前添加了&
符前缀即&bikeFactoryBean
,用于在genBean()方法内部标识它是一个工厂Bean。可是在跟踪源码后发现,在getBean()方法内部,会先将传入的beanName(&bikeFactoryBean
)开头的&
符去除,而且最终实例化Bean后,在容器中保存的beanName仍是不带&
符前缀的名称即bikeFactoryBean
Bike
类的beanName
和Bean实例时,又是怎么获取到的呢?带着疑问,咱们继续看后面的代码。首先,上面的例子中调用了applicationContext.getBeanNamesForType(Bike.class)
方法来获取Bike
类的beanName
。因此继续跟踪此方法看看到底发生了什么。// getBeanNamesForType()方法内部最终调用了此方法,可断点跟踪至此 private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { // 用来保存符合条件的结果(beanName集合) List<String> result = new ArrayList<>(); // 与上面的代码类似,遍历spring容器中注册的全部的beanNames for (String beanName : this.beanDefinitionNames) { if (!isAlias(beanName)) { try { // 根据beanName获取Bean的定义信息 BeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 根据BeanDefinition 进行检查 if (!mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { // 根据beanName和Bean的定义信息判断是不是工厂Bean boolean isFactoryBean = isFactoryBean(beanName, mbd); BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); boolean matchFound = (allowEagerInit || !isFactoryBean || (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) && (includeNonSingletons || (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) && // 根据Bean的定义信息判断完后,在此方法中判断此beanName对应的Bean实例是否与传入的类型相匹配 isTypeMatch(beanName, type); // 若是根据beanName得到的是一个工厂Bean,而且与传入的类型不匹配,则知足条件,将beanName添加 & 符前缀 if (!matchFound && isFactoryBean) { // 对于工厂Bean,接下来尝试匹配工厂Bean实例自己 beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type); } // 若是获取的Bean实例与传入的类型匹配,将beanName添加到结果集合中 if (matchFound) { result.add(beanName); } } } catch (CannotLoadBeanClassException ex) { // ...省略代码... } catch (BeanDefinitionStoreException ex) { // ...省略代码... } } } // ...省略代码... return StringUtils.toStringArray(result); }
isTypeMatch()方法中的部分代码
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { // 对beanName进行处理,将开头的 & 符过滤 String beanName = transformedBeanName(name); // 从spring容器中获取单实例Bean,因为spring容器启动时已经将单实例Bean进行了实例化, // 因此此时能够直接在容器中获得Bean实例 Object beanInstance = getSingleton(beanName, false); if (beanInstance != null && beanInstance.getClass() != NullBean.class) { // 获取到Bean实例后,判断是否为工厂Bean if (beanInstance instanceof FactoryBean) { // 若是是工厂Bean,而且获取的beanName不是以&符开头 if (!BeanFactoryUtils.isFactoryDereference(name)) { // 将实例强转为 FactoryBean 并调用 FactoryBean接口的getObjectType()方法, // 获取工厂Bean所生产的实例类型 Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance); // 判断工厂Bean生产的实例类型与传入的类型是否匹配 return (type != null && typeToMatch.isAssignableFrom(type)); } else { return typeToMatch.isInstance(beanInstance); } } // ...省略代码... } // ...省略代码... }
getTypeForFactoryBean()方法中的代码
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) { try { if (System.getSecurityManager() != null) { return AccessController.doPrivileged((PrivilegedAction<Class<?>>) factoryBean::getObjectType, getAccessControlContext()); } else { // 直接调用 FactoryBean 接口的 getObjectType()方法,获取生产的类型 return factoryBean.getObjectType(); } } catch (Throwable ex) { // Thrown from the FactoryBean's getObjectType implementation. logger.info("FactoryBean threw exception from getObjectType, despite the contract saying " + "that it should return null if the type of its object cannot be determined yet", ex); return null; } }
以上即是getBeanNamesForType()
方法通过的部分重要代码
由此能够看出,当咱们想要获取
BikeFactoryBean
自己的beanName时,doGetBeanNamesForType方法内部将bikeFactoryBean
前添加了&
符前缀,因而便获取到了&bikeFactoryBean
;
当咱们想要获取Bike类型的beanName时,spring会经过容器遍历已经注册的全部的beanNames,而后根据beanName及对应的Bean定义信息BeanDefinition进行判断过滤,而且对于全部的工厂Bean,会获取spring容器中已经实例化的Bean对象,调用 FactoryBean 接口的 getObjectType()方法,获得工厂Bean所生产的实例类型,而后与Bike.class相比较,若是匹配,则将此beanName保存到结果集中,最后返回。因此,当咱们想要获取Bike类型的beanName时,从spring容器中即可以找到bikeFactoryBean
。
beanName
后,咱们继续获取Bike实例
beanName
后再最终调用doGetBean
方法获取实例对象。下面看看部分源码protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 对传入的beanName进行过滤,去除&符前缀 final String beanName = transformedBeanName(name); Object bean; // 从spring容器中获取实例,因为spring容器启动时已经将单实例Bean进行实例化,因此此时能够直接得到 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } // 获取指定的Bean实例,若是是工厂bean,则为Bean实例自己或其建立的对象。 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } // ...省略代码... return (T) bean; }
从上面的代码能够看出,获取Bike实例的具体代码还在getObjectForBeanInstanc()
方法内部,咱们继续查看
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 判断beanName是不是以&符开头的 if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } } // 根据beanName从spring容器中获取的Bean实例若是不是工厂Bean,或者beanName是以&符开头,就直接返回这个Bean实例 // 当咱们获取Bike类型的实例时,beanName为“bikeFactoryBean”, // beanInstance为“BikeFactoryBean”类型,是一个工厂Bean,因此条件不知足,继续向下走 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; if (mbd == null) { // 根据beanName从缓存中获取Bean实例,第一次来获取Bike实例时为空, // factoryBeanObjectCache.get(beanName); // 后续再获取时,即可以在此得到到,而后返回 object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // 将获取的工厂Bean强转为 FactoryBean 类型,以便下面调用其getObject()方法获取对象 FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // 获取bean定义信息 if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); //在此方法内部调用 FactoryBean 接口的 getObject()方法获取对象 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
距离真相还差两步了,坚持就是胜利,咱们继续看getObjectFromFactoryBean()
的源码
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { // 此处调用 FactoryBean 的isSingleton()方法,判断是不是一个单列 // 若是是单例的,走if内部,获取到对象后,会保存到factoryBeanObjectCache缓存中,以便后续使用 if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // 检查缓存中是否已经存在 Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // 调用最后一个方法,执行FactoryBean 的 getObject()方法获取对象 object = doGetObjectFromFactoryBean(factory, beanName); // 再次检查缓存 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { // ... 省略代码 ... if (containsSingleton(beanName)) { // 将获取的对象放入factoryBeanObjectCache缓存中,以便后续使用 this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } // 若是不是单例的,每次获取的对象直接返回,不会放入缓存中,因此每次都会调用getObject()方法 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; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 直接调用 FactoryBean 接口的 getObject()方法获取实例对象 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } // ... 省略代码 ... return object; }
通过如此多的代码,spring终于帮咱们获取到Bike对象实例
经过BikeFactoryBean来获取Bike类的实例时,spring先获取Bike类型对应的beanName(bikeFactoryBean),而后根据beanName获取到工厂Bean实例自己(BikeFactoryBean),最终spring会调用BikeFactoryBean 的 getObject()方法来获取Bike对象实例。而且根据 BikeFactoryBean 实例的 isSingleton() 方法来判断Bike类型的实例是否时单例的,依此来决定要不要将获取的Bike对象放入到缓存中,以便后续使用。
本文主要讲解了如何经过 FactoryBean
接口向spring容器中注入组件,经过简单的案例进行模拟,并根据案例对源码的执行过程进行跟踪,分析了FactoryBean
接口的执行过程。
另外,在每一次跟踪spring源码时,都会有新的收获。在spring庞大的体系下,只有定位好本身的目标,明确本身的需求,才不会被spring无限的代码所淹没。
学习永远都不是一件简单的事情,能够有迷茫,能够懒惰,可是前进的脚步永远都不能中止。
不积跬步,无以致千里;不积小流,无以成江海;