Spring Framework Source Read: FactoryBean

Spring Framework Source Reading (3): FactoryBean ...
Spring Framework Source Reading (3): FactoryBean
FactoryBean
Create module
Source Code Analysis

Spring Framework Source Reading (3): FactoryBean

FactoryBean and BeanFactory, although named similar, have different roles. BeanFactory is responsible for creating and managing beans in Spring containers. It establishes a unified set of processes for beans from initialization to destruction, allowing users to focus on complex processes without having to focus on the logic of the beans themselves. FactoryBeans provide the ability to customize beans to initialize processes, so they can be used to create a class of beans.

FactoryBean

public interface FactoryBean<T> { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; @Nullable T getObject() throws Exception; @Nullable Class<?> getObjectType(); default boolean isSingleton() { return true; } }
  • If a bean implements the FactoryBean interface, it will act as a factory for a class of beans, not as a bean directly.
  • Beans that implement the FactoryBean interface cannot be used as ordinary beans. FactoryBeans are defined as beans, but objects referenced by beans are always objects created by getObject s.
  • FactoryBean (not just the FactoryBean interface) can support singletons and prototypes, create objects as lazy loads as needed, or create objects immediately at startup. The SmartFactoryBean interface allows finer-grained behavior metadata.
  • FactoryBean interfaces are heavily used in the Spring framework, such as ProxyFactoryBean (AOP) or JndiObjectFactoryBean. It can also be used to customize component s.

SmartFactoryBean Interface Source:

public interface SmartFactoryBean<T> extends FactoryBean<T> { default boolean isPrototype() { return false; } default boolean isEagerInit() { return false; } }

Create module

First add an application module to the Spring Framework source, which was described in a previous blog post, and will not be repeated here:


IMessageService interface:

package com.kaven.service; /** * @Author: ITKaven * @Date: 2021/09/25 14:04 * @Blog: https://kaven.blog.csdn.net * @Leetcode: https://leetcode-cn.com/u/kavenit * @Notes: */ public interface IMessageService { default String getMessage() { return "default message"; } }

MessageServiceImpl implementation class:

package com.kaven.service.impl; import com.kaven.service.IMessageService; /** * @Author: ITKaven * @Date: 2021/09/25 14:05 * @Blog: https://kaven.blog.csdn.net * @Leetcode: https://leetcode-cn.com/u/kavenit * @Notes: */ public class MessageServiceImpl implements IMessageService { public String message; public MessageServiceImpl(String message) { this.message = message; } @Override public String getMessage() { return message; } }

MessageServiceFactoryBean class (implements FactoryBean interface):

package com.kaven.factory; import com.kaven.service.IMessageService; import com.kaven.service.impl.MessageServiceImpl; import org.springframework.beans.factory.FactoryBean; import org.springframework.stereotype.Component; /** * @Author: ITKaven * @Date: 2021/10/28 20:01 * @Blog: https://kaven.blog.csdn.net * @Leetcode: https://leetcode-cn.com/u/kavenit * @Notes: */ @Component("myFactoryBean") public class MessageServiceFactoryBean implements FactoryBean<IMessageService> { @Override public IMessageService getObject() throws Exception { return new MessageServiceImpl("Hello Spring"); } @Override public Class<?> getObjectType() { return IMessageService.class; } @Override public boolean isSingleton() { return false; } }

Application Startup Class:

package com.kaven; import com.kaven.factory.MessageServiceFactoryBean; import com.kaven.service.IMessageService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import java.util.Arrays; /** * @Author: ITKaven * @Date: 2021/09/25 13:54 * @Blog: https://kaven.blog.csdn.net * @Leetcode: https://leetcode-cn.com/u/kavenit * @Notes: */ @ComponentScan({"com.kaven"}) public class Application { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class); IMessageService messageServiceBean = (IMessageService) applicationContext.getBean("myFactoryBean"); System.out.println(messageServiceBean.getMessage()); IMessageService messageServiceBean2 = (IMessageService) applicationContext.getBean("myFactoryBean"); System.out.println("messageServiceBean == messageServiceBean2: " + (messageServiceBean == messageServiceBean2)); MessageServiceFactoryBean messageServiceFactoryBean = (MessageServiceFactoryBean) applicationContext.getBean("&myFactoryBean"); System.out.println("isSingleton: " + messageServiceFactoryBean.isSingleton()); System.out.println(applicationContext.getBeanDefinitionCount()); Arrays.stream(applicationContext.getBeanDefinitionNames()).forEach(System.out::println); } }

Run Output:

> Task :application:Application.main() Hello Spring messageServiceBean == messageServiceBean2: false isSingleton: false 6 org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory application myFactoryBean

Obviously, as expected, getting a bean by the bean name of a FactoryBean will only get the instance returned by the getObject() method in that FactoryBean, which is the MessageServiceImpl instance here. If you want to get the bean of the FactoryBean itself, you need to stitch a &character in front of the bean name of the FactoryBean, as demonstrated in the code.

To find out why these four bean s were created, read this blog:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory

Source Code Analysis

Now you can begin to analyze why the FactoryBean interface is implemented so that you can customize the process of creating a class of beans and get the beans of the FactoryBean itself, and why you want to stitch a &character in front of the bean name of the FactoryBean.

Custom bean s, mostly created in the preInstantiateSingletons method of the DefaultListableBeanFactory class (with some code removed):

@Override public void preInstantiateSingletons() throws BeansException { List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); } } } }

Debug is shown in the following figure:

Clearly, the beanName is the bean name for FactoryBean, and FACTORY_BEAN_PREFIX + beanName is &myFactoryBean, so here is the bean that gets the FactoryBean itself. The source code for the SmartFactoryBean interface is shown above. Simply, it inherits the FactoryBean interface and adds two more methods, such as the isEagerInit() method, which appears above. These sources are all related, so you need to go to Debug s yourself.

Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
String FACTORY_BEAN_PREFIX = "&";

Therefore, in the ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class); At the end of this line of code, FactoryBean's own beans are already stored in the Spring container in the beanFactory property (DefaultListableBeanFactory instance) of the AnnotationConfigApplicationContext instance so that a class of beans corresponding to that FactoryBean can be created later.

The bean is then obtained by the bean name of the FactoryBean, which is the type of bean that the FactoryBean corresponds to (the instance returned by the getObject method).

IMessageService messageServiceBean = (IMessageService) applicationContext.getBean("myFactoryBean");

Bebug is shown in the following figure:

The getObjectForBeanInstance method of the AbstractBeanFactory abstract class is called (with some code removed), and the factory parameter is the bean of the FactoryBean itself that was previously created.

protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; if (mbd != null) { mbd.isFactoryBean = true; } else { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }

Focus on this line:

object = getObjectFromFactoryBean(factory, beanName, !synthetic);

The getObjectFromFactoryBean method of the FactoryBeanRegistrySupport abstract class was called (some code was removed):

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); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { 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; } }

Here factory.isSingleton() returns false, so do the following:

Object object = doGetObjectFromFactoryBean(factory, beanName);

The doGetObjectFromFactoryBean method of the FactoryBeanRegistrySupport abstract class was called:

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(); } } return object; }

Eventually object = factory.getObject();, The factory parameter is the bean of the FactoryBean itself that was previously created, so the getObject method in the FactoryBean is called to create the bean that corresponds to the FactoryBean.

Debug is shown in the following figure:

Read the source code with patience and step by step Debug. Everyone has a different understanding. If the blogger is wrong or everyone has different opinions, you are welcome to comment and add.

28 October 2021, 15:55 | Views: 9034

Add new comment

For adding a comment, please log in
or create account

0 comments