Detailed explanation of Spring's Bean Definition Registration Center BeanDefinitionRegistry

Preface

One of the most important concepts of Spring Framework is Bean, and Spring Bean definition needs to be scanned and registered to achieve unified management.

The startup process, classification, and detailed explanation of Bean definition information of the Spring container have been introduced previously. However, I found that some readers left messages asking some questions about the Bean Definition Registration Center, so this article mainly explains BeanDefinitionRegistry.

BeanDefinitionRegistry

BeanDefinitionRegistry is an interface that implements the AliasRegistry interface and defines some common operations on beans.
In fact, AliasRegistry has already been introduced:
[Xiaojia Spring] Share the source code analysis of a small and elegant class SimpleAliasRegistry in Spring (alias registration, manager)

It probably has the following functions:

  1. Register beans in the form of Map<String, BeanDefinition>
  2. Delete and get beanDefiniation based on beanName
  3. Get the number of beanDefiniations held
  4. Determine whether beanDefiniation is included based on beanName

The BeanDefinitionRegistry interface is as follows

// 它继承自 AliasRegistry 
public interface BeanDefinitionRegistry extends AliasRegistry {

	// 关键 -> 往注册表中注册一个新的 BeanDefinition 实例 
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
	// 移除注册表中已注册的 BeanDefinition 实例
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 从注册中心取得指定的 BeanDefinition 实例
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 判断 BeanDefinition 实例是否在注册表中(是否注册)
	boolean containsBeanDefinition(String beanName);
	
	// 取得注册表中所有 BeanDefinition 实例的 beanName(标识)
	String[] getBeanDefinitionNames();
	// 返回注册表中 BeanDefinition 实例的数量
	int getBeanDefinitionCount();
	// beanName(标识)是否被占用
	boolean isBeanNameInUse(String beanName);
}

Let's take a look at its inheritance system:
Insert image description here
It can be seen that there are three main default implementation classes: SimpleBeanDefinitionRegistry, DefaultListableBeanFactory, and GenericApplicationContext

SimpleBeanDefinitionRegistry

It is the default implementation and a very simple implementation. ConcurrentHashMap is used for storage, which can ensure thread safety.

// @since 2.5.2可以看到提供得还是比较晚的
// AliasRegistry和SimpleAliasRegistry都是@since 2.5.2之后才有的~~~
public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
	
	// 采用的ConcurrentHashMap来存储注册进来的Bean定义信息~~~~
	/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
	... // 各种实现都异常的简单,都是操作map
	// 这里只简单说说这两个方法

	// 需要注意的是:如果没有Bean定义,是抛出的异常,而不是返回null这点需要注意
	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			throw new NoSuchBeanDefinitionException(beanName);
		}
		return bd;
	}
	// beanName是个已存在的别名,或者已经包含此Bean定义了,那就证明在使用了嘛
	// 它比单纯的containsBeanDefinition()范围更大些~~~
	@Override
	public boolean isBeanNameInUse(String beanName) {
		return isAlias(beanName) || containsBeanDefinition(beanName);
	}
}

DefaultListableBeanFactory

This class is the basic implementation class of the BeanDefinitionRegistry interface, but it also implements other interface functions. Here we only explore its related methods for registering BeanDefinition instances.

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
	...
	/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
	// 保存所有的Bean名称
	/** List of bean definition names, in registration order */
	private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

	// 注册Bean定义信息~~
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		...
		BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		// 如果不为null,说明这个beanName对应的Bean定义信息已经存在了~~~~~
		if (oldBeanDefinition != null) {
			// 是否允许覆盖(默认是true 表示允许的)
			if (!isAllowBeanDefinitionOverriding()) {
				// 抛异常
			}
			// 若允许覆盖  那还得比较下role  如果新进来的这个Bean的role更大			
			// 比如老的是ROLE_APPLICATION(0)  新的是ROLE_INFRASTRUCTURE(2) 
			// 最终会执行到put,但是此处输出一个warn日志,告知你的bean被覆盖啦~~~~~~~(我们自己覆盖Spring框架内的bean显然就不需要warn提示了)
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// 仅仅输出一个logger.warn
			}
			// 最终会执行put,但是内容还不相同  那就提醒一个info信息吧
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				// 输出一个info信息
			}
			else {
				// 输出一个debug信息
			}
			// 最终添加进去 (哪怕已经存在了~) 
			// 从这里能看出Spring对日志输出的一个优秀处理,方便我们定位问题~~~
			this.beanDefinitionMap.put(beanName, beanDefinition);
			// 请注意:这里beanName并没有再add了,因为已经存在了  没必要了嘛
		}
		else {
			// hasBeanCreationStarted:表示已经存在bean开始创建了(开始getBean()了吧~~~)
			if (hasBeanCreationStarted()) {
				// 注册过程需要synchronized,保证数据的一致性	synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition); // 放进去
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
			
					// 
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				// 表示仍然在启动  注册的状态~~~就很好处理了 put仅需,名字add进去
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				
				// 手动注册的BeanNames里面移除~~~ 因为有Bean定义信息了,所以现在不是手动直接注册的Bean单例~~~~
				this.manualSingletonNames.remove(beanName);
			}

			// 这里的意思是:但凡你新增了一个新的Bean定义信息,之前已经冻结的就清空呗~~~
			this.frozenBeanDefinitionNames = null;
		}
		
		// 最后异步很有意思:老的bean定义信息不为null(beanName已经存在了),或者这个beanName直接是一个单例Bean了~
		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			// 做清理工作:
			// clearMergedBeanDefinition(beanName)
			// destroySingleton(beanName);  销毁这个单例Bean  因为有了该bean定义信息  最终还是会创建的
			// Reset all bean definitions that have the given bean as parent (recursively).  处理该Bean定义的getParentName  有相同的也得做清楚  所以这里是个递归
			resetBeanDefinition(beanName);
		}
	}

	@Override
	public void removeBeanDefinition(String beanName) throws 		NoSuchBeanDefinitionException {
		// 移除整体上比较简单:beanDefinitionMap.remove
		// beanDefinitionNames.remove
		// resetBeanDefinition(beanName);

		BeanDefinition bd = this.beanDefinitionMap.remove(beanName);
		// 这里发现移除,若这个Bean定义本来就不存在,事抛异常,而不是返回null 需要注意~~~~
		if (bd == null) {
			throw new NoSuchBeanDefinitionException(beanName);
		}

		if (hasBeanCreationStarted()) {
			synchronized (this.beanDefinitionMap) {
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames);
				updatedDefinitions.remove(beanName);
				this.beanDefinitionNames = updatedDefinitions;
			}
		} else {
			this.beanDefinitionNames.remove(beanName);
		}
		this.frozenBeanDefinitionNames = null;
		resetBeanDefinition(beanName);
	}

	// 这个实现非常的简单,直接从map里拿
	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			throw new NoSuchBeanDefinitionException(beanName);
		}
		return bd;
	}

@Override
	public boolean containsBeanDefinition(String beanName) {
		return this.beanDefinitionMap.containsKey(beanName);
	}
	@Override
	public int getBeanDefinitionCount() {
		return this.beanDefinitionMap.size();
	}
	@Override
	public String[] getBeanDefinitionNames() {
		String[] frozenNames = this.frozenBeanDefinitionNames;
		if (frozenNames != null) {
			return frozenNames.clone();
		}
		else {
			return StringUtils.toStringArray(this.beanDefinitionNames);
		}
	}
	
	// ==========这个方法非常有意思:它是间接的实现的===============
	// 因为BeanDefinitionRegistry有这个方法,而它的父类AbstractBeanFactory也有这个方法,所以一步小心,就间接的实现了这个接口方法
	public boolean isBeanNameInUse(String beanName) {
		// 增加了一个hasDependentBean(beanName);  或者这个BeanName是依赖的Bean 也会让位被使用了
		return isAlias(beanName) || containsLocalBean(beanName) || hasDependentBean(beanName);
	}
}

This is the Bean factory implementation we most want: DefaultListableBeanFactory, which implements the implementation logic of the Bean definition registration center interface.

XmlBeanFactory

It is a subclass of DefaultListableBeanFactory. After Spring 3.1, it is recommended to use DefaultListableBeanFactory instead

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

	// 此处resource资源事传一个xml的文件资源~~~~
	// parentBeanFactory:父工厂~~~
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
}

GenericApplicationContext
This is a general Spring context. As mentioned before, you basically have to handle everything manually:

    public static void main(String[] args) {
        GenericApplicationContext ctx = new GenericApplicationContext();
        //手动使用XmlBeanDefinitionReader
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
        //加载ClassPathResource
        xmlReader.loadBeanDefinitions(new ClassPathResource("spring.xml"));

        //=========我们可议执行多次loadBeanDefinitions,从不同的地方把Bean定义信息给拿过来,最后再启动容器即可==============
         备注:此处的bean都采用properties的方式去配置(基本没人会这么使用了)
        //PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
        //propReader.loadBeanDefinitions(new ClassPathResource("spring.properties"));

        //调用Refresh方法 启动容器(这个都需要手动  哈哈)
        ctx.refresh();

        //和其他ApplicationContext方法一样的使用方式
        System.out.println(ctx.getBean("person")); //com.fsx.bean.Person@1068e947
    }

spring.xml is as follows:

<bean id="person" class="com.fsx.bean.Person"/>

Because it is a manual file, there is a certain threshold for using the API, so we generally do not use it directly. But it has two subcategories that we are familiar with

GenericXmlApplicationContext
uses XML to configure Bean definition information and reads it with XmlBeanDefinitionReader

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

	// 这个很重要:它持有一个DefaultListableBeanFactory的引用
	private final DefaultListableBeanFactory beanFactory;
	
	// 所以下面所有的 BeanDefinitionRegistry 相关的方法都是委托给beanFactory去做了的,因此省略~~~
}

AbstractApplicationContext ultimately delegates to getBeanFactory() to do these things. The final implementation is it: DefaultListableBeanFactory
Note: AnnotationConfigWebApplicationContext and XmlWebApplicationContext both inherit from AbstractRefreshableConfigApplicationContext, and thus ultimately inherit AbstractApplicationContext

AnnotationConfigApplicationContext

The annotation driver scans Bean definition information. First use ClassPathBeanDefinitionScanner to scan in all the files, and then use AnnotatedBeanDefinitionReader to load the Bean definition information that does not exist inside.

Note: It can be seen from the above inheritance relationship that the above top-level interface ApplicationContext does not provide some operations such as direct registration of Bean definition information and deletion. Because ApplicationContext does not implement this interface BeanDefinitionRegistry, but it inherits the interface ListableBeanFactory, so it has the following three capabilities:

	boolean containsBeanDefinition(String beanName);
	int getBeanDefinitionCount();
	String[] getBeanDefinitionNames();

In the ApplicationContext system, only the subsystem GenericApplicationContext can directly operate the registration center. For example, commonly used ones: GenericXmlApplicationContext and AnnotationConfigApplicationContext

Manually register BeanDefinition (programmatically register Bean definition) There
are two ways to manually register beans:

  1. Implement ImportBeanDefinitionRegistrar
  2. The first step to implement BeanDefinitionRegistryPostProcessor
    is not mentioned. It was explained before when I talked about import.
    BeanDefinitionRegistryPostProcessor inherits from BeanFactoryPostProcessor and provides methods:
// @since 3.0.1 它出现得还是比较晚的
// 父类BeanFactoryPostProcessor  第一版就有了
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	// 这里可以通过registry,我们手动的向工厂里注册Bean定义信息
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

There is an important implementation class; ConfigurationClassPostProcessor, which is used to process @Configuration configuration files. It ultimately parses @Import, @Bean, etc. in the configuration file, and then registers all the definition information~~~

Summarize

If you want to register to the spring container and let spring complete the instantiation, the common method is as follows:

Configure through bean nodes in xml;
use @Service, @Controller, @Conponent and other annotations;

For details, see Spring annotation-driven development—7 ways to register beans in the Spring Ioc container

Guess you like

Origin blog.csdn.net/tian830937/article/details/132922567