Spring core DI
Official website interpretation
Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean.
Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.
DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.
There are two main methods: constructor injection and Setter injection. As it lets you implement application components as immutable objects and guarantees that required dependencies are not null Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. Immutable object injection and is not null.
When the container is created, the Spring container verifies the configuration of each Bean. However, the Bean property itself is not set until the Bean is actually created. When you create a container, beans with singleton scope and set as pre instantiated (default) are created. Defined in Bean Scopes. Otherwise, the Bean is created only when requested. When creating and allocating Bean dependencies and their dependencies (and so on), creating a Bean may result in the creation of a Bean. Note that mismatches between these dependencies can delay resolution, that is, when the affected Bean is first created.
Circular dependencies how?
Core solutions: * * a circular dependency between bean a and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken and egg scenario)** A typical chicken or an egg 😂, Object a is injected into object B before it is fully initialized.
Constructor injection into spring is not handled.
Let's start analyzing the source code.
Source code
Single ordinary bean injection
spring initializes the complete singleton by default.
Test code
/** * Worker * * @Author Hireek * @Date: 2021/9/5 21:14 */ @Component public class Worker { public void hi(String name) { System.out.println(name); } } /** * Test class * * @author Hireek * @date 2022/1/19 19:53 */ public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.hireek.webdemo.entity"); Object student = context.getBean("worker"); System.out.println(student); } }
AnnotationConfigApplicationContext initialization method
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); } // AbstractApplicationContext. refresh() -> finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) -> beanFactory. preInstantiateSingletons() -> getBean(beanName). Complete the initialization of (non lazy init) singletons. @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
AbstractBeanFactory.doGetBean
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // beanName normalization String beanName = transformedBeanName(name); Object beanInstance; // Eagerly check singleton cache for manually registered singletons. 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 + "'"); } } beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { 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); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. 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 { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. if (mbd.isSingleton()) { // Note: lambda dynamic loading is a lazy loading 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; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } 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); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { 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); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(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(); } } return adaptBeanInstance(name, beanInstance, requiredType); }
DefaultSingletonBeanRegistry. Getsingleton (string beanname, Boolean allowrearlyreference) to find data in the cache
/** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). // allowEarlyReference Resolve circular references * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */ @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); // First level cache, final finished product object, product (online) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); // L2 cache, semi-finished product, solving circular dependency if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // Third level cache to solve aop circular dependency if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }
Follow the doGetBean() method, line 87.
Line 87 executes defaultsingletonbeanregistry The getSingleton method does not execute the lambda expression until line 234. Let's look at getSingleton first.
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } // The single instance is marked as creation status. If the marking fails, throw an exception, singletonscurrentlyincreation add(beanName) beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { // lambda will be executed here. Call the createBean method to create a Bean and return. singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // The flag instance is no longer in the creation state. The flag fails and an exception is thrown afterSingletonCreation(beanName); } if (newSingleton) { // Add the L1 cache, clear the L2 and L3 caches, and record the beanName (number of singletons) of the singleton bean. See the following code addSingleton(beanName, singletonObject); } } return singletonObject; } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
AbstractAutowireCapableBeanFactory.createBean->doCreateBean.
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // Create reflection objects and wrap them instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // The post processor calls postProcessMergedBeanDefinition applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // Wrap it into a singletonFactory object, add it to the L3 cache (for circular reference), clear the L2 cache, and register beanName to registeredsinglets. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { // Fill properties populateBean(beanName, mbd, instanceWrapper); // Initialize the given bean instance, applying factory callbacks as well as init methods and bean post processors. exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { // This is mainly used to verify the circular dependency. The ordinary bean returns null. If it is not null, it indicates that there is a circular dependency. The bean obtained here is the L2 cache bean. Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // If = =, the initializeBean does not change the exposedObject, because the L3 cache is before initializeBean. if (exposedObject == bean) { exposedObject = earlySingletonReference; } // Traversal depends on this bean. If it exists, an error will be reported. Because the previous beans have been injected, they are not the same. They are not singleton definitions. else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; } protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
createBeanInstance
/** * Create a new instance for the specified bean, using an appropriate instantiation strategy: * factory method, constructor autowiring, or simple instantiation. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a BeanWrapper for the new instance * @see #obtainFromSupplier * @see #instantiateUsingFactoryMethod * @see #autowireConstructor * @see #instantiateBean */ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Resolve to class Class<?> beanClass = resolveBeanClass(mbd, beanName); // check if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } // Does instanceSupplier have a value Supplier<?> instanceSupplier= mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } // Factory method instantiation if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... // Whether to use cache, non singleton mode Record the last time you created an object boolean resolved = false; // Is a parametric constructor used boolean autowireNecessary = false; // No external parameters if (args == null) { synchronized (mbd.constructorArgumentLock) { // The resolved constructor or factory method is not empty if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; // autowireNecessary = mbd.constructorArgumentsResolved; } } } // Use cache if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Candidate constructors for autowiring? // Specify constructor Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // Preferred constructors for default construction? // Default null ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
instantiateBean, which instantiates the given bean using its default constructor
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged( (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext()); } else { // The default cglibsubclassing instantiationstrategy initialization policy. Finally, the default constructor of the object will be obtained directly, and the object will be created through reflection. beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
Cache analysis
Getsingleton (string beanname, Boolean allowrearlyreference) to view the cache.
Before singleton creation (beanname), mark the status of being created.
Addsingletonfactory (string beanName, objectfactory <? > singletonfactory), add L3 cache, register beanName, and remove L2 cache
addSingleton(String beanName, Object singletonObject), add L1 cache, remove L2 and L3 cache, and register beanName.
To sum up, there is no need to cache without circular dependency.
Ordinary bean circular dependency
Test code
@Component public class Student { @Autowired private Worker worker; } @Component public class Worker { @Autowired private Student student; } /** * describe * * @author Hireek * @date 2022/1/19 19:53 */ public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.hireek.webdemo.entity"); Object student = context.getBean("worker"); System.out.println(student); } }
Focus on the populateBean method.
if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // Take out the studeng object in the factory (incomplete instance) this.earlySingletonObjects.put(beanName, singletonObject); // Store in L2 cache this.singletonFactories.remove(beanName); // remove L3 cache } }
getStudeng object - > inject worker attribute - > getworker object (no cache) - > inject studeng attribute - > getStudeng object - > take out and delete the objects in the third level cache and put them into the second level cache - > get the incomplete instance student, but the worker instance is complete - > finally fill the worker into the student object, so the studeng object is initialized.
Single proxy bean injection.
Proxy bean circular dependency. Let's look at this after learning aop.
Come on, mark Tubu!