preface
This is all preparation, but the next step is to initialize the BeanFactory and instantiate the singleton Bean.
Specific how to operate, then read the source code together!
But before reading the source code, there are some things you need to know.
- What is a FactoryBean?
- How is a FactoryBean used?
- How is the Bean initialized?
- What is the solution to cyclic dependency?
What is a FactoryBean?
In the article “What’s a FactoryBean? There are relevant answers, interested partners can take a look.
Interfaces implemented by internally used objects, beanFactories themselves being factories for individual objects. If the bean implements this interface, it will be used as a factory to expose the object, rather than directly as the bean instance to expose itself.
Note: The bean that implements this interface cannot be used as a normal bean. A FactoryBean is defined in bean-style, but the object exposed for the bean reference (getObject()) is always the object it created.
FactoryBeans can support singletons and prototypes, and objects can be created either on demand or in a hurry at startup.
When a FactoryBean is born, there are two types of Bean: the FactoryBean itself and the Bean of the type it needs to create.
Here is an example:
use
1. PaidComponent
public class PaidComponent {
public PaidComponent(a) {
System.out.println("PaidComponent no-argument construct called"); }}Copy the code
2. PaidComponentFactoryBean
@Component
public class PaidComponentFactoryBean implements FactoryBean<PaidComponent> {
@Override
public PaidComponent getObject(a) throws Exception {
System.out.println("GetObject method called for PaidComponentFactoryBean");
return new PaidComponent();
}
@Override
publicClass<? > getObjectType() {returnPaidComponent.class; }}Copy the code
3 Test
public class AnnotationConfigApplicationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(JavaConfig.class);
context.refresh();
System.out.println(context.getBean("paidComponentFactoryBean"));
System.out.println(context.getBean("&paidComponentFactoryBean")); System.out.println(context.getBean(PaidComponent.class)); }}Copy the code
PaidComponentFactoryBean paidComponentFactoryBean paidComponentFactoryBean
Getting paidComponentFactoryBean directly gets the type returned by FactoryBean’s getObject() method.
FinishBeanFactoryInitialization source
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
// Initialize the type converter
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
// Mainly used for annotation attribute value parsing
if(! beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); }// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
// Initialize the LoadTimeWeaverAware Bean as early as possible to register its converter as early as possible.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false.false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
// Stop using temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// Set the beanDefinition metadata to be unmodifiable
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// Instantiate the singleton bean
beanFactory.preInstantiateSingletons();
}
Copy the code
Focus on the last line here
beanFactory.preInstantiateSingletons();
preInstantiateSingletons
This is entering class DefaultListableBeanFactory class source code.
public void preInstantiateSingletons(a) throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// Place beanDefinitionNames in the collection
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
/ / traverse
for (String beanName : beanNames) {
// Get bd information, as parentBeanDefinition may be defined
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// Non-abstract, singleton, and not lazy loading
if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {// Determine if it is a FactoryBean
if (isFactoryBean(beanName)) {
// FactoryBean needs to be prefixed with &. GetBean (&beanName) gets the FactoryBean itself
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceofFactoryBean) { FactoryBean<? > factory = (FactoryBean<? >) bean;// Determine whether initialization is required
boolean isEagerInit;
if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }// Initialization is required
if(isEagerInit) { getBean(beanName); }}}else{ getBean(beanName); }}}// Trigger post-initialization callback for all applicable beans...
/ / if the Bean implementation SmartInitializingSingleton,
/ / here will be unified call afterSingletonsInstantiated method
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if(System.getSecurityManager() ! =null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else{ smartSingleton.afterSingletonsInstantiated(); } smartInitialize.end(); }}}Copy the code
In the above method, the Bean is initialized by looping beanNames.
You need to distinguish BeanFactory from regular beans. That’s why I first introduced what is a BeanFactory?
Now you need to focus on the getBean(beanName) method.
getBean
public Object getBean(String name) throws BeansException {
return doGetBean(name, null.null.false);
}
Copy the code
The doGetBean method is called in the getBean method.
doGetBean
The doGetBean method returns an instance, which can be shared or independent of the specified bean.
The method accepts four parameters:
Name – the name of the bean to retrieve requiredType – the requiredType of the bean to retrieve, This can be empty args – the parameter to use when creating bean instances with explicit arguments (only applied when creating new instances rather than retrieving existing ones) typeCheckOnly – whether to get instances for type checking rather than actual use
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// Remove the prefix referenced by the factory and transform the alias
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// Check whether the singleton already exists from the cache
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 + "'"); }}If obtained from the cache, the normal Bean returns directly, and the FactoryBean returns the Bean created by the FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// Returns whether the specified prototype bean (of type prototype) is currently being created (in the current thread).
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// Check whether the BeanFactory has a BeanDefinition for this Bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// Check for definitions in the parent container
String nameToLookup = originalBeanName(name);
// Returns the result of the query from the parent container
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if(requiredType ! =null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return(T) parentBeanFactory.getBean(nameToLookup); }}if(! typeCheckOnly) {// Put the current beanName into an alreadyCreated Set.
// This method is not called to get the type of the bean, but to create an instance of the beanName into the alreadyCreated collection, indicating that the bean has been created. Catch clears the beanName if an exception occurs
markBeanAsCreated(beanName);
}
StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if(requiredType ! =null) {
beanCreation.tag("beanType", requiredType::toString);
}
// Get the BeanDefinition of the Bean
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// Make sure the dependent Bean is already initialized, such as @dependson annotation
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
// Create the dependent Bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create bean instance.
if (mbd.isSingleton()) {
/ / the singleton beans
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }// Create the prototype Bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
// Delegate to the implementation class
String scopeName = mbd.getScope();
if(! StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally{ afterPrototypeCreation(beanName); }}); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }catch (IllegalStateException ex) {
throw newScopeNotActiveException(beanName, scopeName, ex); }}}catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally{ beanCreation.end(); }}// Check if required type matches the type of the actual bean instance.
// Check that the desired type matches the type of the actual bean instance.
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
The above code is quite long, and corresponding annotations have been added for the basic steps, which can be basically divided into three steps:
- Get the Bean from the cache and create the corresponding Bean;
- If no Bean is retrieved from the cache, create the corresponding Bean.
- Check that the desired type matches the type of the actual bean instance.
Here are the three steps:
- Get the Bean from the cache and create the corresponding Bean
Object sharedInstance = getSingleton(beanName);
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
/** * returns the (original) singleton registered with the given name. * * Checks for instantiated singletons and allows early references to currently created singletons (parse circular references) */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// The singleton Bean is cached
Object singletonObject = this.singletonObjects.get(beanName);
// If not, and the current Bean is being created
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// The early singleton is first fetched from earlySingletonObjects
singletonObject = this.earlySingletonObjects.get(beanName);
// Not retrieved from earlySingletonObjects cache
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// Get and check again
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// private final Map
> singletonFactories = new HashMap<>(16);
,>
// from the singletonFactories cacheObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
if(singletonFactory ! =null) {
singletonObject = singletonFactory.getObject();
// Add to earlySingletonObjects cache
this.earlySingletonObjects.put(beanName, singletonObject);
// Delete from the singletonFactories cache
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
Copy the code
Here we can see that getting a Bean:
- From the first
singletonObjects
Get Bean from; - Failed to obtain from
earlySingletonObjects
Get Bean from; - Failed to obtain from
singletonFactories
Gets the Bean from.
Of course, this section deals with circular references, and space is limited, so we’ll cover circular references later.
- If no Bean is retrieved from the cache, create the corresponding Bean
// Create bean instance.
if (mbd.isSingleton()) {
/ / the singleton beans
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) {
// Create the prototype Bean
} else {
// Delegate to the implementation class
String scopeName = mbd.getScope();
}
Copy the code
- Check that the desired type matches the type of the actual bean instance
conclusion
Here we mainly introduce the Bean creation process, mainly to have a general understanding and familiarity with the whole process, the process is drawn as follows:
The instantiation of beans focuses on the instantiation of singleton beans, which will be discussed in more detail later.
Related to recommend
- The Spring source study 14: initApplicationEventMulticaster
- Spring source learning 13: initMessageSource
- The Spring source learning 12: registerBeanPostProcessors