The truth about FactoryBean in Spring

One, FactoryBean and BeanFactory

  • FactoryBean: First of all, it is a Bean, but not just a Bean. It is a factory bean that can produce or modify object generation, similar to the factory pattern and decorator pattern in design patterns. It can produce an object when needed, and it is not limited to itself, it can return any Bean instance.
  • BeanFactory: It is the top-level interface of the Bean factory in Spring, and is also the SpringIOC container we often say. It sets some specifications and common methods of the IOC container and manages all Beans in Spring.

Two, FactoryBean Secret

Define a FactoryBean for the production of A objects:

public class MyFactoryBean implements FactoryBean<A> {
    
    
    @Override
    public A getObject() throws Exception {
    
    
        return new A();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return A.class;
    }
}

Inject the defined MyFactoryBean into the xml:

<bean id="myFactoryBean" class="com.bobo.MyFactoryBean"></bean>

1, parsed as beanDefinition

Through debug debugging, when the source code is finished obtainFreshBeanFactory(), it MyFactoryBeanhas been parsed into BeanDefinitionand put into BeanDefinitionMapit:

Insert picture description here

2. Instantiate the object

Continuing to debug, when we finish the finishBeanFactoryInitialization(beanFactory);method, we can see that it MyFactoryBeanhas been instantiated and cached singletonObjectsin:

Insert picture description here

Conclusion: When the IOC container is started, the FactoryBean will be instantiated and cached in singletonObjectsit. Note: What is instantiated here is not an A object

3. Debug the context.getBean("myFactoryBean") method:

The above container has been initialized, let's get the bean from the spring container through the test code

public class Test {
    
    

    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println(context.getBean("myFactoryBean"));
    }
}

The above getBean will go to the source code doGetBean:

protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
    
    
    	/**
		 * 提取对应的beanName,很多人会认为此处直接使用即可,为什么还要进行转换呢?
		 * 原因在于当bean对象实现FactoryBean接口之后就会变成&beanName,同时如果存在别名,也需要把别名进行转换
		 */
		String beanName = transformedBeanName(name);
		Object bean;

		// 提前检查单例缓存中是否有手动注册的单例对象,跟循环依赖有关联
    	// 此处会获取到singletonObjects中缓存的MyBeanFactory实例对象
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
    
    
            
			// 返回对象的实例,很多人理解不了这句话存在的意义,当你实现了FactoryBean接口的对象,需要获取具体的对象的时候就需要此方法来进行获取了
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
}

As you can see from the above source code, Object sharedInstance = getSingleton(beanName);this code is an object singletonObjectsobtained beanNamefrom myBeanFactorythe first-level cache . Obviously, it is possible to obtain the cached container when it is initialized.

Although the MyBeanFactoryobject has been obtained here , this is not what we want, what we want is the A object. Don't worry, it's not over here, let's continue to look down the bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);source code:

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    
    

		// 通过beanName判断是否有factoryBean的前缀
		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;
		}

		// 当我们有了bean的实例之后,这个实例可能是正常的bean,也可能是FactoryBean,如果是FactoryBean那么就直接创建实例,
		// 但是如果用户想要直接获取工厂实例而不是工厂的getObject方法对应的实例,那么传入的参数应该加&前缀
		if (!(beanInstance instanceof FactoryBean)) {
    
    
			return beanInstance;
		}

		Object object = null;
		if (mbd != null) {
    
    
			mbd.isFactoryBean = true;
		}
		else {
    
    
			// 尝试从缓存中加载bean
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
    
    
			// 将beanInstance转换为FactoryBean类型
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			if (mbd == null && containsBeanDefinition(beanName)) {
    
    
				// 将存储xml配置文件的GernericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子Bean的话,同时会合并父类的相关属性
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			//判断当前bean是否是用户定义的,而不是应用程序本身定义的
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}
String FACTORY_BEAN_PREFIX = "&";
public static boolean isFactoryDereference(@Nullable String name) {
    
    
		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
	}

The above code: If it nameis &at the beginning, it means the obtained FactoryBeaninstance, and it will return directly, such as:

context.getBean("&myFactoryBean")

This is an FactoryBeanexample.

But at this time what we get is this form:

context.getBean("myFactoryBean")

Then continue down:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    
    
		if (factory.isSingleton() && containsSingleton(beanName)) {
    
    
			synchronized (getSingletonMutex()) {
    
    
                // 从缓存中获取,如果能取到直接返回,否则调用doGetObjectFromFactoryBean方法获取实例
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
    
    
					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;
			}
		}
		else {
    
    
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			// 省略了后置处理器的调用
			return object;
		}
	}

factoryBeanObjectCacheGet it from the cache first , if not in the cache then call the doGetObjectFromFactoryBean()method:

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    
    
		Object object;
		// 直接调用getObject方法,返回具体的对象
		object = factory.getObject();
		
		return object;
	}

By factory.getObject()directly calling back into the MyFactoryBean的getObject()method

public class MyFactoryBean implements FactoryBean<A> {
    
    
    @Override
    public A getObject() throws Exception {
    
    
        return new A();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return A.class;
    }
}

Obtain the A object directly, and then cache it factoryBeanObjectCache, and finally return an instance of the A object.

If it is a singleton case, it will be retrieved directly from the factoryBeanObjectCachecache next time it is retrieved .

Three, summary

  • getBean(beanName):Is obtained FactoryBeanin the getObject()method returns an instance of the object in its cache factoryBeanObjectCache, the first time acquired will be created (referring to the default singleton object).
  • getBean(&beanName):What is obtained is the FactoryBeaninstance object, which is cached in singletonObjects, and the spring container is initialized and created (referring to the default singleton object).

Guess you like

Origin blog.csdn.net/u013277209/article/details/109742074