. SpringBoot application development is already very common. It's true that SpringBoot brings us a lot of convenience for development, but in fact, SpringBoot does not add any new features, it just mixes upgrades based on Spring notes.The essence of SpringBoot is automated assembly (@EnableAutoConfiguration). We don't need to introduce as many dependent packages as we used to when building projects with Maven, and we also need to deal with all kinds of package conflicts. It's just too annoying to rely on SpringBoot core packages to complete simple application development, so understanding Spring core principles is essential.Face We started to analyze Spring's underlying principles from the source point of view.
Below we first define an entity class and a configuration class
/** * @Description: Dog Entity Class * @Auther: wenqi * @Date: 2019/4/19 20:22 */ @Data @ToString public class Dog { private String name; private Integer age; public Dog(){ System.out.println("------>constructor call"); } public void init(){ System.out.println("------>init function call"); } } /** * @Description: Configuration Configuration Class * @Auther: wenqi * @Date: 2019/4/19 20:30 */ @ComponentScan("com.study.spring") @Configuration public class DogConfiguration { public Dog dog(){ return new Dog(); } }
Next, decide on a test class
/** * @Description: Test Class * @Auther: wenqi * @Date: 2019/4/19 20:31 */ public class Test { @org.junit.Test public void test(){ AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(DogConfiguration.class); Dog dog = (Dog) ac.getBean("dog"); System.out.println(dog); } }
Starting with the AnnotationConfigApplicationContext class, let's explore how Spring's IOC container loads beans from the following sources:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); //Focus on the method below to create a refresh container refresh(); }
We then continue to trace down this refresh() method, which is under the abstract class AbstractApplicationContext
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. Preprocessing before refreshing prepareRefresh(); // 2. Get BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. Prepare for BeanFactory (BeanFactory has some settings) prepareBeanFactory(beanFactory); try { // 4. Post-processing after BeanFactory preparation is complete postProcessBeanFactory(beanFactory); // 5. Ways to execute BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 6. Register BeanPostProcessor (Bean's PostProcessor), BeanPostProcessor of different interface types; Execution timing before and after Bean creation is different registerBeanPostProcessors(beanFactory); // 7. Initialize the MessageSource component (for internationalization; message binding, message resolution) initMessageSource(); // 8. Initialize event dispatcher; initApplicationEventMulticaster(); // 9. Leave to subcontainers (subclasses), empty methods onRefresh(); // 10. Register the ApplicationListener inside all projects in the container registerListeners(); // 11. Focus on this, initialize all remaining single instance beans (custom single beans) finishBeanFactoryInitialization(beanFactory); // 12. Complete the initial creation of the BeanFactory; the IOC container is created 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. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
Through the above steps, we find that Spring's IOC container mainly accomplishes the following:
1, Spring containers will save the definition information of all registered beans when they start up;
1) Register bean s with xml;
2) Annotate registration beans; @Service, @Component, @Bean, xxx
2, Spring containers will create these beans at the right time
1) When you use this bean; use getBean to create the bean; create it and save it in a container later;
(2) when all remaining bean s are created uniformly; finishBeanFactoryInitialization();
3, Post Processor; BeanPostProcessor
1) Each bean is created and processed with a variety of post-processors to enhance its functionality;
AutowiredAnnotationBeanPostProcessor: Handles automatic injection
AnnotationAwareAspectJAutoProxyCreator: to do AOP functions;
xxx…
Enhanced Functional Notes:
AsyncAnnotationBeanPostProcessor
…
4, Event Driven Model;
ApplicationListener; event monitoring;
ApplicationEventMulticaster; event dispatch:
Next, let's look at the process of loading our own defined single beans, mainly through 11, finishBeanFactoryInitialization(beanFactory); methods.
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // 1. Determine if there is a ConversionService(bean Property Type Conversion Service Interface) and initialize it 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)); } // 2. If the beanFactory does not contain EmbeddedValueResolver, add an EmbeddedValueResolver to it // EmbeddedValueResolver-->Parse placeholders and expressions in bean s if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } // 3. Initialize LoadTimeWeaverAware type bean s // LoadTimeWeaverAware-->Weave in third-party modules such as AspectJ when loading Spring Bean String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // 4. Release Temporary Class Loader beanFactory.setTempClassLoader(null); // 5. BeanDefinition metadata frozen cache beanFactory.freezeConfiguration(); // 6. Focus on instantiating all remaining (non-lazy init) singletons (including custom singleton beans) beanFactory.preInstantiateSingletons(); }
Continue tracking down the code, 6, beanFactory.preInstantiateSingletons() is located under the class DefaultListableBeanFactory with the following source code:
@Override public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } // 1. Get Bean Definition Information List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames); // 2. Trigger initialization of non-lazy load singleton beans for (String beanName : beanNames) { // 2.1. RootBeanDefinition to get beans from beanName RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 2.2. When FactoryBean is judged if (isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { // 2.2.1, Focus on this, Get Create bean getBean(beanName); } } // Non FactoryBean else { getBean(beanName); } } } // 3. After all beans are created using get Bean s, check if all beans are SmartInitializingSingleton interfaces; if so, execute afterSingletonsInstantiated();? for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { smartSingleton.afterSingletonsInstantiated(); return null; } }, getAccessControlContext()); } else { smartSingleton.afterSingletonsInstantiated(); } } } }
Focus code is 2.2.1, getBean(beanName); based on the beanName to get the single bean information, we continue to track the code, located in the class AbstractBeanFactory
@Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 1. Convert the name of the bean, remove the & prefix, and use the alias first if the bean has an alias final String beanName = transformedBeanName(name); Object bean; // 2. Getting bean s from the cache Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } // 3. If the bean cannot be retrieved from the cache, re-create the bean else { // Fail if we're already creating this bean instance: We're assumably within a circular reference. // 3.1 Determines if a specified prototype pattern bean is currently being created (within the current thread) and throws an exception if it is -> (Spring does not resolve the circular dependency of the prototype pattern bean) if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. // 3.2 Detect whether the bean definition exists in the beanFactory BeanFactory parentBeanFactory = getParentBeanFactory(); // If the current BeanFactory does not contain a beanDefinition definition for a given beanName and the parent beanFactory is not empty, go to the parent beanFactory and look again if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // 3.3 Convert name to original beanName // Because the name here has already been canonicalized by the beanName, for example: &myBean-->canonical-->myBean // So when we go to the parent beanFactory again, we need to convert the beanName back to the original beanName, myBean-->&myBean String nameToLookup = originalBeanName(name); // 3.4 The various getBean method overloads are recursively called below to load beans from the parent factoryBean of the current bean if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegate parentBeanFactory to use explicit parameter tuning if parameter is not empty return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // Empty parameter delegates parentBeanFactory to get bean s using the standard getBean method return parentBeanFactory.getBean(nameToLookup, requiredType); } else { // Otherwise, delegate parentBeanFactory to use the default getBean method return (T) parentBeanFactory.getBean(nameToLookup); } } // 3.3 If the current bean is not for type checking, mark the bean as either created or about to be created if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 3.4 Merge beanDefinition, traverses all its parent beans if the specified bean is a child bean final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Check the combined beanDefinition and throw an exception if the validation fails checkMergedBeanDefinition(mbd, beanName, args); // 3.5 Ensure that the beans on which the current beans depend are initialized. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { // Loop through all dependent bean s and instantiate them recursively for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } // Registration Dependency registerDependentBean(dep, beanName); try { // Instantiate dependent bean s getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // 3.6 Create a single bean if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { // Create bean s 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); } // Create prototype pattern 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 { 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; } } // Check if required 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) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
This process is preparation for creating the current single bean.These include detection of BeanDefinition, detection of parent BeanFactory if BeanDefinition is not included in the current BeanFactory, consolidation of BeanDefinition, initialization of dependent beans, and so on.When you get the beans, you fetch them from the cache in the cached Map first, and if they are not in the cache, go to step 3.6 and start creating a single bean.
Due to the length of this article, we will continue with the Bean creation process in the following article.