Front knowledge

  • Only beans in singleton mode are exposed ahead of time through level 3 caching to solve the problem of loop dependency. Instead, beans that are not singletons are recreated each time they are fetched and are not put into the tertiary cache, so the multi-instance bean loop dependency problem cannot be solved.

  • The first step is to understand where the beans at each stage are placed. In DefaultSingletonBeanRegistry class

/** Level cache, Private Final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** private final Map<String, ObjectFactory<? >> singletonFactories = new HashMap<String, ObjectFactory<? > > (16); /** Secondary cache, Private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);Copy the code

The test code

Public class ServiceAA {public ServiceAA() {system.out.println ("AA created "); } private ServiceBB serviceBB; public ServiceBB getServiceBB() { return serviceBB; } public void setServiceBB(ServiceBB serviceBB) { this.serviceBB = serviceBB; }}Copy the code
Public class ServiceBB {public ServiceBB() {system.out.println ("BB already created "); } private ServiceAA serviceAA; public ServiceAA getServiceAA() { return serviceAA; } public void setServiceAA(ServiceAA serviceAA) { this.serviceAA = serviceAA; }}Copy the code
    <bean id="serviceAA" class="org.song.circularDepend.ServiceAA" >
        <property name="serviceBB" ref="serviceBB"/>
    </bean>
 
    <bean id="serviceBB" class="org.song.circularDepend.ServiceBB">
        <property name="serviceAA" ref="serviceAA"/>
    </bean>

Copy the code

process

  • In the code above, ServiceAA depends on ServiceBB, and ServiceBB depends on ServiceAA, and the beans depend on each other to form a closed loop. How does Spring solve this problem?

  • Spring creates a bean in two steps. The first step is to create the original bean, and the second step is to assign and initialize properties.

  • Each time a bean is created, it is checked in the cache to see if there is a current bean. Since it is a singleton, there can only be one

  • When the bean serviceAA is created, it is added to the level 3 cache, and it is time to populate the bean’s properties

  • You find that you need to rely on Bean serviceBB, and then you create serviceBB. Repeat the process 1-3

  • In this case, the discovery depends on Bean serviceAA. Bean serviceAA exists in the level 3 cache. Therefore, you do not need to create the bean serviceAA again. Inject bean serviceAA into Bean serviceBB

  • At this point bean serviceBB is created, and the recursion continues to assign attributes to bean serviceAA. A closed-loop complete

The source code

public void refresh() throws BeansException, An IllegalStateException {synchronized (enclosing startupShutdownMonitor) {try {/ / initializes the rest of the single instance bean finishBeanFactoryInitialization(beanFactory); } 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(); }}}Copy the code
Protected void finishBeanFactoryInitialization (ConfigurableListableBeanFactory the beanFactory) {/ / instantiate the single instance of the bean beanFactory.preInstantiateSingletons(); }Copy the code
@Override
	public void preInstantiateSingletons() throws BeansException {
		
		getBean(beanName);
			
	}

Copy the code
public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

Copy the code
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; Null Object sharedInstance = getSingleton(beanName); final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, New ObjectFactory<Object>() {@override public Object getObject() throws BeansException {try {// Create bean return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; }}}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }}Copy the code
Protected Object getSingleton(String beanName, Boolean allowEarlyReference) {// Whether the current bean exists in the level cache (serviceAA), At this point to null / / isSingletonCurrentlyInCreation (beanName), whether the current bean is marked as / / is created, returns false, so does not perform follow-up, Direct return null 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 ! = NULL_OBJECT ? singletonObject : null); }Copy the code
public Object getSingleton(String beanName, ObjectFactory<? > singletonFactory) { beforeSingletonCreation(beanName); singletonObject = singletonFactory.getObject(); addSingleton(beanName, singletonObject); return (singletonObject ! = NULL_OBJECT ? singletonObject : null); }Copy the code
Protected void beforeSingletonCreation(String beanName) {// Mark the bean serviceAA being created as being created, Added to the collection singletonsCurrentlyInCreation if (! this.inCreationCheckExclusions.contains(beanName) && ! this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }}Copy the code
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {service AA Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; }Copy the code
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; InstanceWrapper = createBeanInstance(beanName, MBD, args); / / the current to true Boolean earlySingletonExposure = (MBD) isSingleton () && enclosing allowCircularReferences &&isSingletonCurrentlyInCreation(beanName)); If (earlySingletonExposure) {// If there is no current bean in the level-1 cache, AddSingletonFactory (beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); }}); } // Initialize the bean Object exposedObject = bean; // Assign populateBean(beanName, MBD, instanceWrapper) to the current bean serviceAA; if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); } return exposedObject; }Copy the code
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); }}}Copy the code
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {// Get all properties of the current bean sercieAA PropertyValues PVS = mbd.getPropertyValues(); / /... ApplyPropertyValues (beanName, MBD, bw, PVS); }Copy the code
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues PVS) {// Encapsulate a set of properties, skip List<PropertyValue> original; for (PropertyValue pv : original) { String propertyName = pv.getName(); Object originalValue = pv.getValue(); / / parsing attribute Object reference type resolvedValue = valueResolver. ResolveValueIfNecessary (pv, originalValue); } // Apply attribute process skipped}Copy the code
public Object resolveValueIfNecessary(Object argName, If (value instanceof RuntimeBeanReference) {RuntimeBeanReference ref = (RuntimeBeanReference) value; Service BB return resolveReference(argName, ref); }Copy the code
private Object resolveReference(Object argName, RuntimeBeanReference ref) { String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); // Start recursively calling the getBean method. The current bean is serviceBB Object bean = this.beanFactory.getBean(refName); }Copy the code
  • ServiceBB recursively calls the getBean() method to repeat the process, focusing on step 5. At this point, the bean serviceBB is instantiated and starts assigning attributes, one of which is serviceAA. Try getting serviceAA.

  • Go to the this.beanFactory.getBean() method. Enter the doGetBean() method. GetSingleton (String beanName, Boolean allowEarlyReference)

Protected Object getSingleton(String beanName, Boolean allowEarlyReference) {// Whether the current bean exists in the level cache (serviceAA), At this point to null / / isSingletonCurrentlyInCreation (beanName), whether the current bean is marked as / / is created, the current is true, To determine the Object singletonObject = this. SingletonObjects. Get (beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { / / determine whether level 2 cache has bean serviceAA, current returns null singletonObject = this. EarlySingletonObjects. Get (beanName); If (singletonObject == null && allowEarlyReference) {// Get ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory ! = null) {/ / at this time point singletonObject serviceAA object instantiated completed before singletonObject = singletonFactory. GetObject (); / / add serviceAA to the second level cache, and deleted from the three levels of cache enclosing earlySingletonObjects. Put (beanName singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject ! = NULL_OBJECT ? singletonObject : null); }Copy the code
  • The attribute assignment for serviceBB is complete, and the populateBean method of serviceBB is complete. The attribute Service AA points to bean serviceAA, which has not yet executed the populateBean() method, waiting for a subsequent recursive return.
protected void addSingleton(String beanName, Object singletonObject) {synchronized (enclosing singletonObjects) {/ / put serviceBB in l1 enclosing singletonObjects. Put (beanName, (singletonObject ! = null ? singletonObject : NULL_OBJECT)); / / from l3 cache, level 2 cache to delete this. SingletonFactories. Remove (beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}Copy the code
  • ServiceBB is created. The recursive return continues the bean serviceAA that has not completed attribute assignment. Call the addSingleton() method to place the Bean serviceAA into the level 1 cache as well

conclusion

Welcome to pay attention to the public number: the future has light, receive a line of large factory Java interview questions summary + the knowledge points learning thinking guide + a 300 page PDF document Java core knowledge points summary!