当前位置: 首页 > news >正文

springboot源码解读二-----(自动配置原理解析上)

源码注释环境

  • jdk 1.8 +
  • springboot 2.7.x
  • idea 2022
  • win 10

自动配置核心配置类 @EnableAutoConfiguration

EnableAutoConfiguration 中包含了两个注解:

# 里面包含一个 Import注解,作用是通过 AutoConfigurationPackages.Registrar.class 类把 AutoConfigurationPackage 所在的包信息注册进spring容器中
# 以供有需要的组件使用
@AutoConfigurationPackage
# 把 AutoConfigurationImportSelector.class 导入到项目中,
# AutoConfigurationImportSelector.class可以收集所有需要注册的类的信息,统一存储
@Import(AutoConfigurationImportSelector.class)

一: @AutoConfigurationPackage

@EnableAutoConfiguration 的作用就是注册一个基本包的bean信息。这个基本包信息后续有的程序或者插件可能会用到,基本包的信息通过: AutoConfigurationPackages.Registrar.class 注册。

// 源注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 把 AutoConfigurationPackages.Registrar.class 导入项目中,使用 AutoConfigurationPackages.Registrar.class可以
// 注册基本包的信息,以供其他插件使用
// 基本包信息: basepackage 默认情况下,程序会取 使用 @AutoConfigurationPackage 注解的包的地址,因为 @AutoConfigurationPackage 是
// 复合在 @SpringBootApplication 使用的,所以基本包的信息就是 使用@SpringBootApplication 注解的包的信息。
// 【总结】 @AutoConfigurationPackage 其实就是把AutoConfigurationPackages.Registrar.class注入程序,并通过该类把程序包的bean注册进map。
// key为 "org.springframework.boot.autoconfigure.AutoConfigurationPackages" value 为 BasePackagesBeanDefinition对象(里面存储了 basePackage
// 的信息)
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	/**
	 * Base packages that should be registered with {@link AutoConfigurationPackages}.
	 * <p>
	 * Use {@link #basePackageClasses} for a type-safe alternative to String-based package
	 * names.
	 * @return the back package names
	 * @since 2.3.0
	 */
	String[] basePackages() default {};

	/**
	 * Type-safe alternative to {@link #basePackages} for specifying the packages to be
	 * registered with {@link AutoConfigurationPackages}.
	 * <p>
	 * Consider creating a special no-op marker class or interface in each package that
	 * serves no purpose other than being referenced by this attribute.
	 * @return the base package classes
	 * @since 2.3.0
	 */
	Class<?>[] basePackageClasses() default {};

}
  • AutoConfigurationPackages.Registrar.class

    	/**
    	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
    	 * configuration.
    	 */
    	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    		// metadata 为 注解的元信息,registry为 bean 默认bean注册表(bean注册中心)
    		// 
    		@Override
    		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    			// new PackageImports(metadata).getPackageNames().toArray(new String[0]) 获取注解所在的包。例如: com.ruoyi
    			// 把该包信息注册为bean
    			// 调用 
    			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    		}
    
    		@Override
    		public Set<Object> determineImports(AnnotationMetadata metadata) {
    			return Collections.singleton(new PackageImports(metadata));
    		}
    
    	}
    
  • register 方法

    	// registry bean注册表(注册中心)	packageNames 包名,例如: "com.ruoyi"
    	// BEAN 即为 basePackage注册的key 值。BEAN: "org.springframework.boot.autoconfigure.AutoConfigurationPackages"
    	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    		if (registry.containsBeanDefinition(BEAN)) {
    			BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
    			beanDefinition.addBasePackages(packageNames);
    		}
    		else {
    			// 第一次走这个逻辑,把basePackage信息注册到一个map,key: BEAN 的值,value为 basePackage的 beanDefinition
    			// BeanDefinition描述了一个bean实例,它具有属性值、构造函数参数值以及具体实现提供的进一步信息。
    			registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
    		}
    	}
    
  • DefaultListableBeanFactory的 方法: registerBeanDefinition

    
    	//---------------------------------------------------------------------
    	// Implementation of BeanDefinitionRegistry interface
    	//---------------------------------------------------------------------
    	// 把 beanName通过beanDefinition携带的bean信息注册。
    	@Override
    	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");
    
    		if (beanDefinition instanceof AbstractBeanDefinition) {
    			try {
    				((AbstractBeanDefinition) beanDefinition).validate();
    			}
    			catch (BeanDefinitionValidationException ex) {
    				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    						"Validation of bean definition failed", ex);
    			}
    		}
    
    		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    		if (existingDefinition != null) {
    			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 + "]");
    				}
    			}
    			this.beanDefinitionMap.put(beanName, beanDefinition);
    		}
    		else {
    			// 判断有没有已经创建的bean, 有的话,需要加入进来,并把 beanName 注册 进来
    			if (hasBeanCreationStarted()) {
    				// Cannot modify startup-time collection elements anymore (for stable iteration)
    				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;
    					removeManualSingletonName(beanName);
    				}
    			}
    			else {
    				// 直接把 beanName 注册进来,放到一个map中,key beanName ,value: beanDefinition
    				// Still in startup registration phase
    				this.beanDefinitionMap.put(beanName, beanDefinition);
    				// 把 beanName 的key值存放金 一个 list中
    				this.beanDefinitionNames.add(beanName);
    				removeManualSingletonName(beanName);
    			}
    			this.frozenBeanDefinitionNames = null;
    		}
    
    		if (existingDefinition != null || containsSingleton(beanName)) {
    			resetBeanDefinition(beanName);
    		}
    		else if (isConfigurationFrozen()) {
    			clearByTypeCache();
    		}
    	}
    
    

【【功能总结】】: @EnableAutoConfiguration 的作用就是注册一个基本包的bean信息。这个基本包信息后续有的程序或者插件可能会用到,基本包的信息通过: AutoConfigurationPackages.Registrar.class 注册。

【【流程总结】】

  1. 通过 AutoConfigurationPackages.Registrar.class 调用 registerBeanDefinitions 方法
  2. registerBeanDefinitions 方法中调用 AutoConfigurationPackages 的 register 方法
  3. register 走 第一次走 else 语句 调用 DefaultListableBeanFactory 的 registerBeanDefinition 方法 完成basePackage bean信息的注册
  4. registerBeanDefinition 方法会把bean信息存进 registerBeanDefinition 的 this.beanDefinitionMap 和 this.beanDefinitionNames中。

二: @Import(AutoConfigurationImportSelector.class)

@Import(AutoConfigurationImportSelector.class) 的作用就是把springboot以及第三方工具中所有需要自动配置的类的完成路径名导入程序中,以供后面使用。

  • AutoConfigurationGroup.class (为AutoConfigurationImportSelector中的静态子类) 中有个核心方法 process

    springboot最终就是调用 process 来收集 所有的自动配置的类,并过滤掉那些没有用到的配置。


	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {

		// 所有 自动配置的类, key:完整类名  value: 该注解的元信息
		private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();

		// 所有自动配置类的列表,AutoConfigurationEntry 中存储所有自动配置类的完整类名。
		private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

		private ClassLoader beanClassLoader;

		private BeanFactory beanFactory;

		private ResourceLoader resourceLoader;

		private AutoConfigurationMetadata autoConfigurationMetadata;

		@Override
		public void setBeanClassLoader(ClassLoader classLoader) {
			this.beanClassLoader = classLoader;
		}

		@Override
		public void setBeanFactory(BeanFactory beanFactory) {
			this.beanFactory = beanFactory;
		}

		@Override
		public void setResourceLoader(ResourceLoader resourceLoader) {
			this.resourceLoader = resourceLoader;
		}
		
		// 核心 方法
		// 收集 所有需要自动配置的 类
		// annotationMetadata 注解元信息 deferredImportSelector 使用指定的DeferredImportSelector处理导入类的AnnotationMetadata。
		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			// 读取 所有组件的 spring.factories 中 所有的自动配置类
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			// 把自动配置实体加入 自动配置表
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			// 取出所有的自动配置类名,并加入 entries map中
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

		// 过滤 不需要的 配置类,并把自动配置的类进行排序,按照 @Order注解排序
		@Override
		public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			processedConfigurations.removeAll(allExclusions);
			// 按照 @Order 进行排序
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

		private AutoConfigurationMetadata getAutoConfigurationMetadata() {
			if (this.autoConfigurationMetadata == null) {
				this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
			}
			return this.autoConfigurationMetadata;
		}

		private List<String> sortAutoConfigurations(Set<String> configurations,
				AutoConfigurationMetadata autoConfigurationMetadata) {
			return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
					.getInPriorityOrder(configurations);
		}

		private MetadataReaderFactory getMetadataReaderFactory() {
			try {
				return this.beanFactory.getBean(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
						MetadataReaderFactory.class);
			}
			catch (NoSuchBeanDefinitionException ex) {
				return new CachingMetadataReaderFactory(this.resourceLoader);
			}
		}

	}

相关文章:

  • 网站怎么做聚合/100个常用的关键词
  • 做网站视频是什么专业/公司网络营销策划书
  • 笑话网站 wordpress/网站seo优化总结
  • wordpress中怎么在页面中添加文章/国内seo工具
  • 张家港高端网站制作/精准客户截流软件
  • 怎么做网站推广云浮/淘宝自动推广软件
  • 使用人工智能机器人提高农业效率| 数据标注
  • 金融风控07
  • Java加解密(八)数字证书
  • LeetCode 329. 矩阵中的最长递增路径(C++)*
  • Spark基础【博学谷学习记录】
  • COCO_03 制作COCO格式数据集 dataset 与 dataloader
  • vue3开源项目
  • 4、变量与常量
  • aws beanstalk 理解和使用eb工作线程环境
  • # 【笔记】大话设计模式21-23
  • Android 10.0 修改系统默认的产品类型为设备类型
  • WhatsApp居然有3个版本?深度详解区别!外贸圈获客神器用起来!