Spring之FactoryBean的处理底层源码分析

2024-10-09 11:49:15 浏览数 (5)

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对象就是在这里调用创建的。

后续的操作就是将其缓存起来。

到这里前面的疑问就清除了,为什么加&符号和不加&符号获取到的对象不一样了。

0 人点赞