BeanDefinitionRegistry

BeanDefinition registered interface in the Spring, the realization of common DefaultListableBeanFactory and GenericApplicationContext. Take a look at its interface listing:

public interface BeanDefinitionRegistry extends AliasRegistry {
    // Register a BeanDefinition instance in the registry
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;
	// Remove the BeanDefinition with the BeanName from the registry
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// Get BeanDefinition from the registry via beanName
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// Check whether the BeanDefinition of the beanName is currently registered
	boolean containsBeanDefinition(String beanName);
	// Get the names of all currently registered BeanDefinitions
	String[] getBeanDefinitionNames();
	// Get the number of beanDefinitions currently registered
	int getBeanDefinitionCount(a);
	// Whether beanName is occupied
	boolean isBeanNameInUse(String beanName);
}
Copy the code

UML

  • SimpleBeanDefinitionRegistryBeanDefinitionRegistry is a simple implementation that provides registration capabilities, but does not provide factory-level capabilities. It can be used as a simple example for test use.
  • DefaultListableBeanFactory:Spring’s first factory class that can run independently, not only has container registration, but also is a full-featured Bean factory.
  • GenericApplicationContext: The Spring generic context requires more custom implementations.

DefaultListableBeanFactory registration function

Future review

Mentioned above, BeanDefinitionReader after call BeanDefinitionHolder bdHolder = delegate. ParseBeanDefinitionElement (ele); After, can call BeanDefinitionReaderUtils. RegisterBeanDefinition (bdHolder, getReaderContext (.) getRegistry ()); Method to register the BeanDefinition.

DefaultListableBeanFactory will register after BeanDefinition into a ConcurrentHashMap stored in the Map, the Key for beanName, the Value of BeanDefinition.

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
Copy the code

Entry to registration

There is introduced into two parameters, bdHolder as BeanDefinition wrapper, getReaderContext () getRegistry () for DefaultListableBeanFactory. Why is DefaultListableBeanFactory? Because Spring passes the container itself as a parameter when it instantiates XmlBeanDefinitionReader, this can also be called a delegate in design mode.

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
Copy the code

DefaultListableBeanFactory multiple functions

DefaultListableBeanFactory itself is not only used as a factory, but also can be used as a registry, resource loader.

	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		/ / here are the embodiment of entrust DefaultListableBeanFactory as structure parameters was introduced into the XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}
Copy the code

Into the constructor, found DefaultListableBeanFactory here has served for the function of the registry.

	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}
Copy the code

Register BeanDefinition- Register name and aliases

	public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		// Inject the name of the BeanDefinition and BeanDefinition itself into the container
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		// If the BeanDefinition has an alias, then register the alias
		String[] aliases = definitionHolder.getAliases();
		if(aliases ! =null) {
			for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

DefaultListableBeanFactory#registerBeanDefinition

Note the already reached the DefaultLististableBeanFactory registerBeanDefinition, the registry point to an DefaultLististableBeanFactory above. Here’s the main logic

    1. Check whether it is an instance of AbstractBeanDefinition. If so, check whether the BeanDefinition has a declaration method that requires overrides. If so, create a proxy class for the proxy.
    1. frombeanDefinitionMapTo try to get the BeanDefinition of the beanName.
    1. ifbeanDefinitionMapCheck if BeanDefinition is allowed to override. If allowed, update operations are performed with PUT.
    1. If frombeanDefinitionMapIf not, verify that it is a new BeanDefinition. Check to see if the bean in the factory is in the created phase. If so, verify that it is a way to dynamically register the bean. usesynchronizedrightbeanDefinitionMapLock it up and let it goputOperation, and then update in turnbeanDefinitionNamesAnd from themanualSingletonNames(Manually register the list of names of singletons, in order of registration.) Remove the beanName
    1. If the BeanDefinition already exists and is in the singleton cache, proceedresetBeanDefinitionThis is a recursive process that clears the merged BeanDefinition cache, removes the BeanName from the singleton cache, and tells the processor to reset.
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		// Check whether this beanDefinition is an instance of AbstractBeanDefinition
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				// Check whether the look up and replace methods exist and their arguments are valid. If so, proceed with the BeanDefinition
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex); }}// Get the BeanDefinition instance from the registry
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if(existingDefinition ! =null) {
			// Whether to allow BeanDefinition overridden, container configuration
			if(! isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]"); }}else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]"); }}// Place beanName as the key and BeanDefinition as the value in the container map
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			// Whether to start creating an instance of the bean. If the instance already exists, this time it is dynamic
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				// Lock the registry
				synchronized (this.beanDefinitionMap) {
					/ / update the beanDefinition
					this.beanDefinitionMap.put(beanName, beanDefinition);
					// Create a list with length beanDefinitionNames+1
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					// Store beanDefinitionName first
					updatedDefinitions.addAll(this.beanDefinitionNames);
					// Store the incremental beanName sequentially
					updatedDefinitions.add(beanName);
					// Update the entire BeanDefinitionNames
					this.beanDefinitionNames = updatedDefinitions;
					/ / remove the BeanName, from the single example BeanName list if this contains the BeanName manualSingletonNames, perform the remove operationremoveManualSingletonName(beanName); }}else {
				// Still in startup registration phase
				// Start phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			// Clear the beanName list frozen during registration, a volatile String array, and Spring memory optimization operations
			this.frozenBeanDefinitionNames = null;
		}
		// Check whether the BeanDefinition exists and exists in IOC
		if(existingDefinition ! =null || containsSingleton(beanName)) {
			// Reset the registered BeanDefinition cache
			// Includes the parent class of BeanDefinition and the merged BeanDefinition cache, mergeBeanDefinition
			// Spring merges Bean properties with parent properties
			resetBeanDefinition(beanName);
		}
		else if(isConfigurationFrozen()) { clearByTypeCache(); }}Copy the code

DefaultListableBeanFactory#resetBeanDefinition

	protected void resetBeanDefinition(String beanName) {
		// Remove the merged bean definition for the given bean, if already created.
		// If this beanName has been merged,remove the merged beanDefinition
		clearMergedBeanDefinition(beanName);

		// Remove corresponding bean from singleton cache, if any. Shouldn't usually
		// be necessary, rather just meant for overriding a context's default beans
		// (e.g. the default StaticMessageSource in a StaticApplicationContext).
		// Remove the beanName from the singleton cache
		destroySingleton(beanName);

		// Notify all post-processors that the specified bean definition has been reset.
		/ / notify MergedBeanDefinitionPostProcessor, this special beanDefinition has been reset
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			if (processor instanceofMergedBeanDefinitionPostProcessor) { ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); }}// Reset all bean definitions that have the given bean as parent (recursively).
		// Find the bd in the container that is different from the current beanName and check whether paretName is the same as the current beanName.
		// If the parent bean is cleared, the child bean should also be reset
		for (String bdName : this.beanDefinitionNames) {
			if(! beanName.equals(bdName)) { BeanDefinition bd =this.beanDefinitionMap.get(bdName);
				// Ensure bd is non-null due to potential concurrent modification
				// of the beanDefinitionMap.
				if(bd ! =null && beanName.equals(bd.getParentName())) {
					If the bean has child beans, continue to clean upresetBeanDefinition(bdName); }}}}Copy the code

The flow chart

conclusion

  • The registration of BeanDefinition is shown as the followingDefaultListableBeanFactoryIn thebeanDefinitionMapAdd a BeanDefinition to.
  • BeanDefinitionReader delegates the registration function toDefaultListableBeanFactoryRegister. The registry performs construction injection at initialization.
  • During registration, Spring verifies that BeanDefinition overwriting is allowed.
  • Spring supports dynamic registration of BeanDefinitions after factory Bean instances are created.