1.简单Demo:
FactoryBean是Spring中一个非常重要的扩展点,很多第三方组件就是通过FactoryBean来整合进Spring的,比如:OpenFeign,下面给出简单的demo:
这里有一个简单的user类:
代码语言:javascript复制csharp 代码解读复制代码public class User {
public void getUser(){
System.out.println(" get user");
}
}
下面是一个LxFactoryBean类实现了FactoryBean:并且在它的getObject()方法中返回了一个User对象。
代码语言:javascript复制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:
代码语言:javascript复制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前面加一个&符号:
代码语言:javascript复制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处理的部分源码:
代码语言:javascript复制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)这个方法:
代码语言:javascript复制ini 代码解读复制代码String beanName = transformedBeanName(name);
而这个方法的核心逻辑就是处理调用getBean()方法传入的这个name,这里我就不进去看它源码了,其实很简单,我直接告诉大家逻辑:
它的处理逻辑就是:将传入的这个name进行处理,不管你的这个name前面有没有带上&符号,那么最终都会被处理为不带&符号的name:
如果传入的name为:pcsService,那么最终得到的这个beanName为:pcsService。
如果传入的name为:&pcsService,那么最终得到的这个beanName也为:pcsService。
这就是这个方法的目的。
处理完name后,接着就是去单例池中根据name获取到对应的bean对象:
代码语言:javascript复制ini 代码解读复制代码Object sharedInstance = getSingleton(beanName);
上面的demo是在main方法中调用getBean()方法获取对象,我们知道Spring在启动的时候会去扫描,生成对应的bean对象然后存储在单例池中,所以在这里肯定能够从单例池中获取到对应的bean对象。
获取到对应的bean对象后,就会进入下面的这段逻辑,这段逻辑就是处理FactoryBean的:
代码语言:javascript复制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前面带了&符号:
代码语言:javascript复制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)这个方法实现的:
代码语言:javascript复制less 代码解读复制代码 public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
如果带有了&符号并且满足是一个FactoryBean的话,那么就直接返回这个对象,这就是为什么在上面的demo中,调用getBean()方法的时候name前面待一个&符号获取到的是LxFactory对象了。
第二段:传入的name前面没有带&符号:
代码语言:javascript复制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,然后就是先去缓存中获取,如果缓存中不存在就走下面的逻辑:
代码语言:javascript复制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()方法:
代码语言:javascript复制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()方法:
代码语言:javascript复制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对象就是在这里调用创建的。
后续的操作就是将其缓存起来。
到这里前面的疑问就清除了,为什么加&符号和不加&符号获取到的对象不一样了。