Abstract

Now that we’ve got the basics of Java under our belt, we’ll start a new series of Spring Debunks, the spring framework that is the most important framework around our Java Web. Spring Boot based on the Spring framework, It makes it easier for me to write Java projects. Spring Boot makes it easy to create standalone, as the spring Boot column on the Spring website says. production-grade Spring based Applications that you can “just run”.

This series is based on the book Spring Secrets, with the Spring 5.x.x source code.

The overall Spring Architecture

The Spring framework has iterated many times, and its essence remains the same, all in order to provide a variety of services to facilitate our Java development. The general framework is shown below.

The ioc container

Spring architecture diagram we can understand as a tree, ioc container as the root, branches and leaves are the extension of Spring, so we start from the IOC container to understand Spring.

IoC, the full name of IoC is Inversion of Control, which is commonly referred to as dependency injection. He offers us whatever functionality we need. To take a simple example, in the era without IOC containers, when I was thirsty and wanted to drink water, FIRST I had to get a cup, then I went to a water dispenser to get water, and finally I could drink water happily. Now when there is an IOC container, I just call the IOC container and he sends me a bottle of drinking water, which I can drink directly. It saves the trouble of finding a water fountain. The premise is that the IOC container registers a water delivery function.Copy the code

I’ll show the above examples in pseudocode so we can better understand the IOC container

// No IOC container era

// class Dispenser{pubilc int carriage = 1; Private int water; private int water; Void setWater(dispenser){this.water = dispenser. Carriage} pubilc void setWater(){if(water == 1){ Print (" Drink some water "); My.setwater (new Dispenser()) My.drink(); I have to rely on the water fountain for waterCopy the code

// Ioc container era

Class Dispenser{pubilc int carriage = 1; } class My{private int water; @autowired // dependency injection pubilc void setWater(dispenser){this.water = dispenser.carriage} pubilc void drink(){ If (water == 1){print(" water "); My.setwater (new Dispenser()) My.drink(); }} Directly eliminates the need to find a water dispenser, and with the special capability of the Ioc container, dependency injection, we can go straight to our final goal.Copy the code

There is a more vivid graphic illustration in the book. With ioc, you have a super butler who does everything for you.

Spring provides two IOC container types: BeanFactory and ApplicationContext.

BeanFactory

The BeanFactory. Base type IoC container that provides complete IoC service support. If no specific initialization policy is specified, lazy-load is used by default. Initialization and dependency injection are performed only when the client object needs to access a managed object in the container. As a result, containers are relatively quick to start up and require limited resources. For scenarios with limited resources and less stringent functional requirements, BeanFactory is a suitable IoC container choice.

ApplicationContext

ApplicationContext. ApplicationContext is a relatively advanced container implementation built on top of the BeanFactory. In addition to having all the support of the BeanFactory, ApplicationContext also provides other advanced features such as event publishing, support for internationalization information, and so on. Objects managed by ApplicationContext are all initialized and bound by default after the container of that type is started. Therefore, the ApplicationContext requires more system resources than the BeanFactory, and the container takes longer to start than the BeanFactory container because all initialization is done at startup. In scenarios where system resources are plentiful and more functionality is required, containers of type ApplicationContext are the appropriate choice.The ApplicationContext inherits the BeanFactory, the ability to extend the BeanFactory, which is essentially the ability to provide beans, commonly referred to as Java objects. So how does BeanFactory accomplish dependency injection? Let’s look at the spring 5.x source code to find out why.

How to load beanFactory in spring 5.x?Load the Spring context applicationContext

Invoke Factory Processors registered as beans in the context. Invoke factory Processors registered as beans in the context invokeBeanFactoryPostProcessors(beanFactory); //bean scanner, To get the package name ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner (enclosing registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); // Register all spring-annotated elements under this package with beanFactory Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); }}}Copy the code
The property of the applicationContext is to register beans first, // Instantiate all Remaining (non-lazy-init) Singletons. finishBeanFactoryInitialization(beanFactory); Left / / Instantiate all remaining (non - lazy - init) singletons. The beanFactory. PreInstantiateSingletons ();Copy the code

Let’s take a look at how Spring’s getBean method does dependency injection for a class.For now, let’s just look at beans in singleton modeAbstractAutowireCapableBeanFactory# populateBean dependency injectionInjection is done through a different processor

/ / AutowiredAnnotationBeanPostProcessor: here is the resolution of the Bean Autowired information, and then to inject into @ Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; } / / CommonAnnotationBeanPostProcessor: logic and above a MAO, it handles the JSR - 250 annotations, Such as @ Resource @ Override public PropertyValues postProcessPropertyValues (PropertyValues PVS, PropertyDescriptor [] PDS, Object bean, String beanName) throws BeansException { InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); } return pvs; } / / RequiredAnnotationBeanPostProcessor: It's going to check, annotated @required, There lies the Bean must be @ Override public PropertyValues postProcessPropertyValues (PropertyValues PVS, PropertyDescriptor [] PDS, Object bean, String beanName) throws BeansException { if (! this.validatedBeanNames.contains(beanName)) { if (! shouldSkip(this.beanFactory, beanName)) { List<String> invalidProperties = new ArrayList<>(); for (PropertyDescriptor pd : pds) { if (isRequiredProperty(pd) && ! pvs.contains(pd.getName())) { invalidProperties.add(pd.getName()); } } if (! invalidProperties.isEmpty()) { throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName)); } } this.validatedBeanNames.add(beanName); } return pvs; }Copy the code
Completed in different processing methods of inject # inject we commonly used commonly to the @autowired AutowiredAnnotationBeanPostProcessor# inject @ Override protected void inject(Object bean, @Nullable String beanName, @nullable PropertyValues PVS) throws Throwable {Field Field = (Field) this.member; Object value; // If (this.cached) {value = resolvedCachedArgument(beanName, this.cachedfieldValue); // If (this.cached) {value = resolvedCachedArgument(beanName, this.cachedfieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet<>(1); Assert.state(beanFactory ! = null, "No BeanFactory available"); / / converter, there is no manual registration, the default is SimpleTypeConverter = = = = = = = = = = = = = = = TypeConverter TypeConverter. = the beanFactory getTypeConverter (); Try {// Parse out the bean on which the current bean depends (from the Spring container, Or somewhere else to get ~ ~ ~) value = the beanFactory. ResolveDependency (desc, beanName autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } synchronized (this) { if (! This.cached) {// If (value! = null || this.required) { this.cachedFieldValue = desc; RegisterDependentBeans (beanName, autowiredBeanNames); // Since it is injected by type, there must be only one dependent Bean for this, Otherwise the above resolution has been an error if (autowiredBeanNames. The size () = = 1) {String autowiredBeanName = autowiredBeanNames. The iterator (). Next (); If (beanFactory.containsBean(autowiredBeanName) &&beanFactory.istypematch (autowiredBeanName, field.getType())) { this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType());  } } } else { this.cachedFieldValue = null; } this.cached = true; }} // dependency injection if (value! = null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); }}}Copy the code

A small summary

Ioc container dependency injection, from the beginning of parsing bean information, and then to bean injection, are handed to different beans to deal with, such a design concept makes the Spring framework has a strong ability to expand. Spring does a lot of preparatory work up front and then the injection process at the end, so this rigorous approach makes the IOC process suitable for more scenarios.