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

简介: 本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。

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

相关文章
|
6月前
|
Java 关系型数据库 MySQL
Spring5深入浅出篇:Spring中的FactoryBean对象
Spring5深入浅出篇:Spring中的FactoryBean对象
|
3天前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
19 1
|
19天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
28 1
|
20天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
25 1
|
17天前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
21 0
|
1月前
|
XML 缓存 Java
Spring FactoryBean 的常见使用场景总结
FactoryBean 是 Spring 框架中的一个重要接口,用于自定义 Bean 的创建逻辑。常见使用场景包括: 1. **复杂 Bean 的创建**:如数据源配置。 2. **延迟实例化**:按需创建资源密集型对象。 3. **动态代理**:为 Bean 创建 AOP 代理。 4. **自定义配置**:根据特定配置创建 Bean。 5. **第三方库集成**:利用 FactoryBean 封装外部库的创建过程。
|
5月前
|
存储 安全 Java
Spring Security 6.x OAuth2登录认证源码分析
上一篇介绍了Spring Security框架中身份认证的架构设计,本篇就OAuth2客户端登录认证的实现源码做一些分析。
229 2
Spring Security 6.x OAuth2登录认证源码分析
|
6月前
|
Java 数据库连接 API
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
98 0
|
5月前
|
存储 Java Spring
Spring IOC 源码分析之深入理解 IOC
Spring IOC 源码分析之深入理解 IOC
185 2
|
5月前
|
Java Spring
聊聊Spring中两种创建Bean的方式:BeanDefinition.setInstanceSupplier() 和 FactoryBean
聊聊Spring中两种创建Bean的方式:BeanDefinition.setInstanceSupplier() 和 FactoryBean