1.简单Demo:
FactoryBean是Spring中一个非常重要的扩展点,很多第三方组件就是通过FactoryBean来整合进Spring的,比如:OpenFeign,下面给出简单的demo:
这里有一个简单的user类:
csharp
代码解读
复制代码
public class User { public void getUser(){ System.out.println(" get user"); } }
下面是一个LxFactoryBean类实现了FactoryBean:并且在它的getObject()方法中返回了一个User对象。
typescript
代码解读
复制代码
@Component public class LxFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { return new User(); } @Override public Class<?> getObjectType() { return User.class; } }
下面我们通过调用Spring的getBean()方法来获取这个bean:
typescript
代码解读
复制代码
public class TestMain { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); Object lxFactoryBean = applicationContext.getBean("lxFactoryBean"); System.out.println(lxFactoryBean); }
输出结果为:
编辑
编辑
发现它输出的是一个User对象,而不是LxFactoryBean对象。
但是如果我们换一种方式来调用getBean()方法:即给方法传入的name前面加一个&符号:
typescript
代码解读
复制代码
public class TestMain { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); Object lxFactoryBean = applicationContext.getBean("&lxFactoryBean"); System.out.println(lxFactoryBean); }
输出结果为:
编辑
编辑
发现它输出的结果为LxFactoryBean了,不再是上面的User对象了。
2.源码分析:
通过上面的案例可知,如果我调用getBean()方法传入的name不加&符号的话那么获取到的对象为User对象,如果我传入的name加上了&符号的话,那么获取到的对象就为LxFactoryBean对象。
为了一探究竟,我们直接进入getBean()方法:下面我只摘取关于FactoryBean处理的部分源码:
less
代码解读
复制代码
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object beanInstance; // Eagerly check singleton cache for manually registered singletons. 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 "'"); } } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
进入getBean()方法,最终会来到doGetBean()这个方法,而在这个方法中首先进入的是transformedBeanName(name)这个方法:
ini
代码解读
复制代码
String beanName = transformedBeanName(name);
而这个方法的核心逻辑就是处理调用getBean()方法传入的这个name,这里我就不进去看它源码了,其实很简单,我直接告诉大家逻辑:
它的处理逻辑就是:将传入的这个name进行处理,不管你的这个name前面有没有带上&符号,那么最终都会被处理为不带&符号的name:
如果传入的name为:pcsService,那么最终得到的这个beanName为:pcsService。
如果传入的name为:&pcsService,那么最终得到的这个beanName也为:pcsService。
这就是这个方法的目的。
处理完name后,接着就是去单例池中根据name获取到对应的bean对象:
ini
代码解读
复制代码
Object sharedInstance = getSingleton(beanName);
上面的demo是在main方法中调用getBean()方法获取对象,我们知道Spring在启动的时候会去扫描,生成对应的bean对象然后存储在单例池中,所以在这里肯定能够从单例池中获取到对应的bean对象。
获取到对应的bean对象后,就会进入下面的这段逻辑,这段逻辑就是处理FactoryBean的:
csharp
代码解读
复制代码
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 "'"); } } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
而对应的核心方法为:getObjectForBeanInstance():这个方法的入参有name和beanName,name就是你传入的name,可能是带有&符号的,而beanName就是处理好的没有&符号的,下面看看该方法里面的具体实现逻辑:
第一段:传入的name前面带了&符号:
java
代码解读
复制代码
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; }
这一段就是看你传入的这个name有没有带&符号,就是通过isFactoryDereference(name)这个方法实现的:
less
代码解读
复制代码
public static boolean isFactoryDereference(@Nullable String name) { return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); }
如果带有了&符号并且满足是一个FactoryBean的话,那么就直接返回这个对象,这就是为什么在上面的demo中,调用getBean()方法的时候name前面待一个&符号获取到的是LxFactory对象了。
第二段:传入的name前面没有带&符号:
ini
代码解读
复制代码
if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; if (mbd != null) { mbd.isFactoryBean = true; } else { object = getCachedObjectForFactoryBean(beanName); } 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;
首先判断如果不是FactoryBean的话那么直接就返回了。
反之则为FactoryBean,然后就是先去缓存中获取,如果缓存中不存在就走下面的逻辑:
ini
代码解读
复制代码
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); }
而最核心的方法就是:getObjectFromFactoryBean()方法:
typescript
代码解读
复制代码
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); // Only post-process and store if not put there already during getObject() call above // (e.g. because of circular reference processing triggered by custom getBean calls) Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { // Temporarily return non-post-processed object, not storing it yet.. 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; } }
而在这个方法中最核心的就是:doGetObjectFromFactoryBean()方法:
typescript
代码解读
复制代码
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, 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 { 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); } // Do not accept a null value for a FactoryBean that's not fully // initialized yet: Many FactoryBeans just return null then. if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } object = new NullBean(); } return object; }
在这个方法中就是去调用了FactoryBean的getObject()方法获取到对应的对象,在前面的demo中我们直接在getObject()方法中return 了一个User对象,所以User对象就是在这里调用创建的。
后续的操作就是将其缓存起来。
到这里前面的疑问就清除了,为什么加&符号和不加&符号获取到的对象不一样了。
作者:我还没听说过 链接:https://juejin.cn/post/7371716384847626291 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。