preface

In real-world development, if you lean toward system infrastructure development, you should be familiar with the use of FactoryBeans. FactoryBean and BeanFactory are often compared because their names are confusing, yet they work in completely different ways. This article will parse the source code around FactoryBeans.

FactoryBean interface

public interface FactoryBean<T> {
	// Returns the created bean object
	T getObject(a) throws Exception;
    // The Class type of the bean objectClass<? > getObjectType();// Whether the bean object is a singleton.
    default boolean isSingleton(a) {
		return true; }}Copy the code

Realize the FactoryBean

@Component("myDemoService")
public class MyDemoFactoryBean implements FactoryBean<MyDemoService> {
    @Override
    public MyDemoService getObject(a) throws Exception {
        return new MyDemoService();
    }

    @Override
    publicClass<? > getObjectType() {returnMyDemoService.class; }}Copy the code

MyDemoFactoryBean MyDemoService MyDemoFactoryBean MyDemoService MyDemoService

@Configuration
@ComponentScan("com.leon.factorybean")
public class Config {}Copy the code
public class MyApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = newAnnotationConfigApplicationContext(Config.class); String[] myDemoTactoryBeanNames = applicationContext.getBeanNamesForType(MyDemoFactoryBean.class); String[] myDemoServicBeanNames = applicationContext.getBeanNamesForType(MyDemoService.class); System.out.println(JsonUtil.convert(myDemoTactoryBeanNames)); System.out.println(JsonUtil.convert(myDemoServicBeanNames)); }}Copy the code

The result is as follows:

["&myDemoService"]
["myDemoService"]
Copy the code

As you can see, you do get MyDemoFactoryBean and MyDemoService, respectively, from the IOC container. This shows that:

  • FacotryBean is also a bean, managed by the IOC container;
  • FacotryBean can produce the specified bean, which is the object returned by FacotryBean#getObject();
  • BeanName: By default, the BeanName of a FactoryBean is [&] + [class name < lowercase >]; The beanName of the bean it produces is the class name < lowercase >.

Next, let’s look at how factoryBeans are parsed in Spring.

FactoryBean source tracking

1. The entrance

In the Spring when, for example, tracking source ways. The most direct way is through applicatonContext getBean method.

MyDemoFactoryBean myDemoFactoryBean = applicationContext.getBean(MyDemoFactoryBean.class);
Copy the code

2. Entrusted to AbstractApplicationContext# getBean

The actual call is AbstractApplicationContext# getBean (Java. Lang. Class), the method is also shell method, practical to be entrusted to the BeanFactory getBean method.

@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}
Copy the code

3. Then delegated DefaultListableBeanFactory# getBean (Java. Lang. Class)

GetBeanFactory () returns the actually DefaultListableBeanFactory instance. To learn that DefaultListableBeanFactory is the BeanFactory default implementation of Spring. So by default, what we call an IOC container is this. This article won’t do too much about containers, but let’s take a look at what factoryBeans look like.

4. To entrust DefaultListableBeanFactory# getBean (Java. Lang. Class, Java. Lang. Object…).

The above method actually does nothing and is still an overloaded method delegated to getBean. The source code is as follows:

public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
  Assert.notNull(requiredType, "Required type must not be null");
  Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
  if (resolved == null) {
  throw new NoSuchBeanDefinitionException(requiredType);
  }
  return (T) resolved;
  }
Copy the code

5. Then delegated DefaultListableBeanFactory# resolveBean

Finally, we get to the point where we delegate to the internal resolveBean() method, which is the method that ultimately resolves the bean. The source code is as follows:

private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
	// Parse the bean. If the result is not empty, the parsed bean has been obtained. This method is our focus on parsing the method, no one!
  NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
  if(namedBean ! =null) {
  	return namedBean.getBeanInstance();
  }
  // If nothing is returned, the bean is created by beanFactory
  BeanFactory parent = getParentBeanFactory();
  if (parent instanceof DefaultListableBeanFactory) {
  	return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
  }
  else if(parent ! =null) {
  	ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
  	if(args ! =null) {
  		return parentProvider.getObject(args);
  	}
  	else {
  		return(nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable()); }}return null;
}
Copy the code

5. DefaultListableBeanFactory# resolveNamedBean source code parsing

private <T> NamedBeanHolder<T> resolveNamedBean(
			ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {

		Assert.notNull(requiredType, "Required type must not be null");
  		// Get beanName according to type. This method hides the true identity of the FactoryBean.
		String[] candidateNames = getBeanNamesForType(requiredType);
		// If more than one beanName is parsed, it is automatically parsed to its associated bean.
		if (candidateNames.length > 1) {
			List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
			for (String beanName : candidateNames) {
				if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
					autowireCandidates.add(beanName);
				}
			}
			if (!autowireCandidates.isEmpty()) {
				candidateNames = StringUtils.toStringArray(autowireCandidates);
			}
		}
		// If only one beanName exists, the getBean method is called directly. Apparently, there's only one beanName.
		if (candidateNames.length == 1) {
			String beanName = candidateNames[0];
			return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
		}
		else if (candidateNames.length > 1) {
			Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
			for (String beanName : candidateNames) {
				if (containsSingleton(beanName) && args == null) {
					Object beanInstance = getBean(beanName);
					candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
				}
				else {
					candidates.put(beanName, getType(beanName));
				}
			}
			String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
			if (candidateName == null) {
				candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
			}
			if(candidateName ! =null) {
				Object beanInstance = candidates.get(candidateName);
				if (beanInstance == null || beanInstance instanceof Class) {
					beanInstance = getBean(candidateName, requiredType.toClass(), args);
				}
				return new NamedBeanHolder<>(candidateName, (T) beanInstance);
			}
			if(! nonUniqueAsNull) {throw newNoUniqueBeanDefinitionException(requiredType, candidates.keySet()); }}return null;
	}
Copy the code

We focus on the getBeanNamesForType method and the getBean method.

6. DefaultListableBeanFactory# getBeanNamesForType parsing

public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
  	Class resolved = type.resolve();
  	// Resolved is class
  	// There are no other attributes defined in MyDemoFactoryBean, so this goes to the if branch.
  	if(resolved ! =null && !type.hasGenerics()) {
  		// This is an overloaded method of getBeanNamesForType, which still ends up calling the doGetBeanNamesForType method, as shown below
  		return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
  	}
  	else {
  		returndoGetBeanNamesForType(type, includeNonSingletons, allowEagerInit); }}Copy the code
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		/ / check all beanDefinitionNames. BeanDefinition in carrying out the new AnnotationConfigApplicationContext (Config. Class) and parsing when finished. So you can just iterate through the loop.
		for (String beanName : this.beanDefinitionNames) {
			// Check if there is an alias. In general, we rarely use aliases. In this case, there is no alias, so go to the if branch
			if(! isAlias(beanName)) {try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
					if(! mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || ! mbd.isLazyInit() || isAllowEagerClassLoading()) && ! requiresEagerInitForType(mbd.getFactoryBeanName()))) {/ /!!!!!! Important! This determines whether the current Type is a FactoryBean type based on beanName and beanDefinition. This parameter is at the heart of the difference between regular beans and factoryBeans.
                        // See isFactoryBean() method parsing in section 7 below
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                        / /!!!!!! Important! The default value is false.
						boolean matchFound = false;
                        // Whether a FactoryBean is allowed to be initialized. By default, it is
						boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
						booleanisNonLazyDecorated = dbd ! =null && !mbd.isLazyInit();
						// Next, the matchFound parameter is set.
                        // If it is not a factoryBean type, the isTypeMatch method is called directly to set the matchFound argument. Normal beans follow this logic.
						if(! isFactoryBean) {if(includeNonSingletons || isSingleton(beanName, mbd, dbd)) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); }}// If it's a factoryBean, go to the else branch below
						else  {
						// First try to set matchFound based on the normal beanName. If true, it is the beanName generated in the FactoryBean.
							if (includeNonSingletons || isNonLazyDecorated ||
									(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
                            / /!!!!!! Important! If matchFound is still false, it is likely to be looking for the FactoryBean itself, so prefix beanName with: & to try to find the FactoryBean itself.
							if(! matchFound) {// In case of FactoryBean, try to match FactoryBean instance itself next.beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); }}// If matchFound is true, add beanName to result. Note that beanName is prefixed with & if we are looking for a FactoryBean type.
						if(matchFound) { result.add(beanName); }}}catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
					if (allowEagerInit) {
						throw ex;
					}
					// Probably a placeholder: let's ignore it for type matching purposes.
					LogMessage message = (ex instanceof CannotLoadBeanClassException) ?
							LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
							LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName); logger.trace(message, ex); onSuppressedException(ex); }}}/ /... Omit other code...
        // Returns the beanName result found
		return StringUtils.toStringArray(result);
	}
Copy the code

7. AbstractBeanFactory# isFactoryBean parsing

The isFactoryBean() method is used to determine whether a FactoryBean type is a FactoryBean.

protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
		Boolean result = mbd.isFactoryBean;
		// If the isFactoryBean property in beanDefinition is null, then the beanType in BD is directly obtained and the FactoryBean is determined
        // The result is then assigned to bd's isFactoryBean property and returned directly.
		if (result == null) { Class<? > beanType = predictBeanType(beanName, mbd, FactoryBean.class); result = (beanType ! =null && FactoryBean.class.isAssignableFrom(beanType));
			mbd.isFactoryBean = result;
		}
		return result;
}
Copy the code

8.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class, java.lang.Object…)

There are many overloads of the getBea method in the ApplicationContext, but it ultimately resolves beanName to find the specific bean based on the beanName. The reason is that in ApplicationContext, beanDefinition, beanType, IOC containers, and so on are mapped based on beanName. Therefore, when we read the source code, we can find that many times we parse beanName first and then do further operations. Because beanName can be defined in a variety of ways, the parsing process is also complicated, so we must be quiet when parsing the source code. Since the getBean method itself involves complex scenarios such as bean life cycle, cycle dependency, etc., the getBean method is isolated for parsing. All we need to know for this article is that the getBean method gets the actual bean.

conclusion

From the above analysis, we have a rough idea of how FactoryBean works. Several conclusions can also be drawn from the source code:

  • A FactoryBean is itself a special bean;
  • A FactoryBean can produce a plain bean through its interface definition;
  • The beanName of a FactoryBean is the beanName of the ordinary bean it produces with an am& prefix. This indirectly means that ordinary beans cannot prefix beanName with an am&, otherwise the container will report an error at startup.

Factorybeans can work wonders when used well in our daily development. Spring integrates Mybatis with SqlSessionFactoryBean. SqlSessionFactoryBean is a FactoryBean. It shields the complex build of the underlying Mybatis. If you have a similar situation at work, you can use this idea.