preface

Many articles are analyzing XML configurations, but Spring Boot development is now based on annotations. This article analyzes the Spring IOC container source code from an annotation perspective.

Version:

Spring Boot: 2.1.6.RELEASE Spring FrameWork: 5.1.8.RELEASE Java8

BeanDefinition

The BeanDefinition interface defines a bean instance that contains properties, constructor parameters, and other specific information.

Public Interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {// ConfigurableBeanFactory The singleton and prototype. / / request, session and so on is an extension of the web-based String SCOPE_SINGLETON = ConfigurableBeanFactory. SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; Int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; Void setParentName(@nullable String parentName); @Nullable String getParentName(); Void setBeanClassName(@nullable String beanClassName); // Return the current Bean class name String getBeanClassName(); void setScope(@Nullable String scope); @Nullable String getScope(); // Whether to delay initialization void setLazyInit(Boolean lazyInit); boolean isLazyInit(); Void setDependsOn(@autowire) {// Set a void setson (@nullable String... dependsOn); @Nullable String[] getDependsOn(); // Set whether this Bean can be injected into other beans, only for injection by type, // If injection by name, even if false is set, Void setAutowireCandidate(Boolean autowireCandidate); boolean isAutowireCandidate(); Void setPrimary(Boolean primary); void setPrimary(Boolean primary); boolean isPrimary(); // Specify the factory name if the Bean is generated using the factory method; Otherwise use reflection to generate void setFactoryBeanName(@Nullable String factoryBeanName); @Nullable String getFactoryBeanName(); // specify the factoryMethodName in the factory class void setFactoryMethodName(@nullable String factoryMethodName); @Nullable String getFactoryMethodName(); / / return the constructor parameters of bean ConstructorArgumentValues getConstructorArgumentValues (); default boolean hasConstructorArgumentValues() { return ! getConstructorArgumentValues().isEmpty(); } // The value of the property in the Bean, the returned instance is changed during Bean Factory post-processing MutablePropertyValues getPropertyValues(); default boolean hasPropertyValues() { return ! getPropertyValues().isEmpty(); } void setInitMethodName(@Nullable String initMethodName); @Nullable String getInitMethodName(); void setDestroyMethodName(@Nullable String destroyMethodName); @Nullable String getDestroyMethodName(); void setRole(int role); int getRole(); void setDescription(@Nullable String description); @Nullable String getDescription(); // Read-only attributes boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); @Nullable String getResourceDescription(); @Nullable BeanDefinition getOriginatingBeanDefinition(); }Copy the code

AnnotationConfigUtils#processCommonDefinitionAnnotations(…)

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy ! = null) { abd.setLazyInit(lazy.getBoolean("value")); } else if (abd.getMetadata() ! = metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy ! = null) { abd.setLazyInit(lazy.getBoolean("value")); } } if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn ! = null) { abd.setDependsOn(dependsOn.getStringArray("value")); } AnnotationAttributes role = attributesFor(metadata, Role.class); if (role ! = null) { abd.setRole(role.getNumber("value").intValue()); } AnnotationAttributes description = attributesFor(metadata, Description.class); if (description ! = null) { abd.setDescription(description.getString("value")); }}Copy the code

As you can see, processCommonDefinitionAnnotations method according to the annotation to fill AnnotatedBeanDefinition, these annotations are as follows:

1.Lazy

2.Primary

3.DependsOn

4.Role

5.Description

Looking up at the call, Found in ConfigurationClassBeanDefinitionReader# registerBeanDefinitionForImportedConfigurationClass will be registered as a bean definition.

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { AnnotationMetadata metadata = configClass.getMetadata(); AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); configBeanDef.setScope(scopeMetadata.getScopeName()); String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); / / 1. Through annotations filling configBeanDef AnnotationConfigUtils. ProcessCommonDefinitionAnnotations (configBeanDef, metadata); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); / / 2. The bean definition registered to this in the registry. The registry. RegisterBeanDefinition (definitionHolder. GetBeanName (), definitionHolder.getBeanDefinition()); configClass.setBeanName(configBeanName); if (logger.isTraceEnabled()) { logger.trace("Registered bean definition for imported class '" + configBeanName + "'"); }}Copy the code

Will eventually be AbstractApplicationContext# refresh invokeBeanFactoryPostProcessors (the beanFactory) method call.

@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }... }}Copy the code

The BeanFactory profile

The BeanFactory is the factory that produces beans and is responsible for producing and managing individual bean instances. As you can see in the figure below, ApplicationContext is also a BeanFactory. If the BeanFactory is the heart of Spring, the ApplicationContext is the entire body.

ApplicationContext is a generic interface that provides configuration information at application runtime. The ApplicationContext is immutable while the program is running, but the implementation class can re-enter the configuration information.

There are many implementation classes for ApplicationContext, For example, AnnotationConfigApplicationContext AnnotationConfigWebApplicationContext ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, XmlWebApplicationContext, etc. We’re AnnotationConfigApplicationContext analysis above, the way of using annotations provide configuration information, so that we don’t have to write the XML configuration file, very simple.

The Web container startup process

This article uses Spring Boot development, which is started by the following code:

@SpringBootApplication @EnableScheduling @EnableAspectJAutoProxy public class AppApplication { public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); }}Copy the code

The central point is this:

SpringApplication.run(AppApplication.class, args);
Copy the code

The SpringApplication code will not be analyzed. The purpose of this review is to analyze the source code of the container. The Spring Boot Boot process and other information will be ignored because the Spring code is too large and complex. Analyzing the run method above will eventually trace back to SpringApplication#run(…) Methods.

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}
Copy the code

With context, there are three lines of code:

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Copy the code

The refreshContext method refreshes the given context:

private void refreshContext(ConfigurableApplicationContext context) {
	refresh(context);
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}
Copy the code
protected void refresh(ApplicationContext applicationContext) {
	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
	((AbstractApplicationContext) applicationContext).refresh();
}
Copy the code

Will find the final call to AbstractApplicationContext# refresh method. Annotation reference from: www.javadoop.com/post/spring…

@override public void refresh() throws BeansException, IllegalStateException {// Start or destroy the container before refresh() is complete. The mixed with synchronized (enclosing startupShutdownMonitor) {/ / preparation, record the container tag's start time, in the configuration file is "started" state, the processing placeholder prepareRefresh (); // The configuration file is parsed into Bean definitions and registered with the BeanFactory. // The configuration file is not initialized yet. / / register is the information is stored in the registry (at the end of the day is a core beanName - > beanDefinition map) ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory(); // Set the BeanFactory class loader, add a few BeanPostProcessors, and manually register a few special beans prepareBeanFactory(BeanFactory); Spring calls the postProcessBeanFactory method after the container is initialized. Spring calls the postProcessBeanFactory method after the container is initialized. // This is the extension point provided to subclasses. At this point, all the beans have been loaded and registered. PostProcessBeanFactory (beanFactory); postProcessBeanFactory(beanFactory); / / call spring BeanFactoryPostProcessor each implementation class postProcessBeanFactory invokeBeanFactoryPostProcessors (factory) method (the beanFactory); BeanPostProcessor (factoryPostProcessor) ¶ PostProcessBeforeInitialization and postProcessAfterInitialization / / two methods in the Bean initialization respectively before and after the initialization are implemented. Note that Bean here haven't initialize registerBeanPostProcessors (the beanFactory); InitMessageSource (); // Initialize the MessageSource of the current ApplicationContext. / / initialize the current ApplicationContext event broadcast, here also is not opened initApplicationEventMulticaster (); // As can be seen from the method name, the typical template method (hook method), // Concrete subclasses can initialize some special beans here (before initializing Singleton beans) onRefresh(); // Register event listeners that implement the ApplicationListener interface. These are not important listeners either. / / focus, focus, focus on / / initialize all singleton beans / / (except for the lazy - init) finishBeanFactoryInitialization (the beanFactory); // Finally, the event is broadcast, and the ApplicationContext initialization completes finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. In case some beans will always use the resource destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); }}}Copy the code

The core flow is what’s inside the try block, and we should understand the whole idea; this article is not a line-by-line analysis. If you do that, you become a dictionary entirely…

The bean’s loading

Bean load function call: org. Springframework. Beans. Factory. Support. AbstractBeanFactory# doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, Boolean typeCheckOnly) throws BeansException {// Extract the name of the corresponding bean final String beanName = transformedBeanName(name); Object bean; Important, important, important! 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 + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else {/ / there are circular dependencies if (isPrototypeCurrentlyInCreation (beanName)) {/ / prototype model directly throw an exception (circular dependencies can only under the singleton pattern to solve) throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory ! = null && ! containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); 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); }} // Create a bean if (! typeCheckOnly) { markBeanAsCreated(beanName); } try {// Get RootBeanDefinition, if beanName is a child bean, Need to merge the parent class attribute final RootBeanDefinition MBD = getMergedLocalBeanDefinition (beanName); checkMergedBeanDefinition(mbd, beanName, args); [] dependsOn = mbd.getSon (); 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 { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); If (mbd.issingleton ()) {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); throw ex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); Else if (mbd.isPrototype()) {Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); final 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 new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; If (requiredType!) {if (requiredType!) {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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }Copy the code

As you can see, loading beans is quite complex. The loading steps are as follows:

1. The conversion corresponds to a beanName

2. Try to load a singleton from the cache

3. Instantiation of beans

4. Dependency checking of the prototype pattern

5. Testing parentBeanFactory

6. Convert the configuration file to RootBeanDefinition

7. Look for dependencies

8. Create beans for different scopes

9. Type conversion

Factory Bean

Now that we’ve mentioned BeanFactory, here’s a FactoryBean… Spring is said to provide over 70 implementations of FactoryBeans, which shows its place in the Spring framework. They hide the details of instantiating complex beans, bringing convenience to upper-layer applications.

Public interface FactoryBean<T> {// Return the bean instance created by the FactoryBean. If isSingleton() returns true, @Nullable T getObject() throws Exception; @nullable T getObject() throws Exception. // Return the bean type created by FactoryBean @nullable Class<? > getObjectType(); default boolean isSingleton() { return true; }}Copy the code

Circular dependencies

Circular dependencies are circular references where two or more beans hold each other. So how does Spring address loop dependencies?

There are three cases of cyclic dependencies in Spring:

1. Constructor loop dependencies

2. Setter loop dependencies

3. Prototype scope dependency handling

The constructor loop dependency is irresolvable because a bean is first created through the constructor, but the constructors are interdependent, the Java equivalent of a multithreaded deadlock.

The dependencies created by setter injection are accomplished by the Spring container’s pre-exposure of beans that have just completed constructor injection but have not completed other steps, such as setter injection, and can only resolve singleton scoped bean loop dependencies. By exposing a singleton factory method in advance so that other beans can reference it, the code looks like this:

@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory ! = null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }Copy the code

EarlySingletonObjects are defined as:

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
Copy the code

For prototype-scoped beans, the Spring container cannot do dependency injection because it does not cache prototype-scoped beans.

Bean life cycle

That’s the heart of Spring for interviews, but just remember the general process.

The last

I have compiled a: Spring series family barrel (including -Spring IOC), Java systematic information (including Java core knowledge, interview topics and 20 years of the latest Internet real questions, e-books, etc.) friends who need to pay attention to the public number [procedures yuan Small wan] can obtain.