Spring boot自动装配源码解析

       Spring boot自动装配源码解析


       Spring boot和spring一样就是为了整合技术,但是它基于约定优于配置的思想,实现开箱即用,无代码生成,无需xml配置。配置全在application.properties或者application.yml中,配置文件与配置类进行绑定。其核心功能是起步依赖和自动配置。那么它是怎么实现绑定和自动装配的,我们一起来探究一下。注:Spring boot版本2.1.8.RELEASE,此处没有使用Spring boot的源码,而是直接通过idea反编译出来的结果。


Spring boot注解之@SpringBootApplication

      讲Spring boot的东西毫无疑问要从@SpringBootApplication注解入手,有@SpringBootApplication标注的类即为项目的主程序类,也就是主入口类、主配置类。

@SpringBootApplication //这是主程序类,
public class HelloWorld {
    public static void main(String[] args) {
    //调用这个类的run方法开始运行整个程序
        SpringApplication.run(HelloWorld.class,args);
    }
}

      点击进入@SpringBootApplication注解,我们看到这是一个组合注解。

@Target(ElementType.TYPE) //这注解是标记只在类上使用
@Retention(RetentionPolicy.RUNTIME)//运行时可见
@Documented   //文档
@Inherited
@SpringBootConfiguration//底层就是@Configuration配置类注
@EnableAutoConfiguration//重点注解,自动装配
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })//包扫描,过滤哪些

      @SpringBootConfiguration:Spring boot的注解,底层是@Configuration注解。标注在哪个类上,表示他就是一个Spring boot配置类

      @Configuration:配置类上标注这个注解,配置类又对应配置文件,配置类也是容器中的一个组件。@Component

      我们探究的是自动装配,也就是@EnableAutoConfiguration注解

      点击进入@EnableAutoConfiguration注解,底层依旧是组合注解,它将告诉Spring boot开启自动配置,以前需要配置的东西现在都不需要自动配置了。而@AutoConfigurationPackage是重点注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

      进入@AutoConfigurationPackage注解,得到底层是@Import,@Import是Spring的注解,给容器导入一个组件。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

      @Import(AutoConfigurationPackages.Registrar.class)

      AutoConfigurationPackages.Registrar.class这个类是AutoConfigurationPackages类下边的一个静态类。目的就是拿到主配置类所在包及子包下的所有注解所有bean信息,然后放入容器。

//注册bean的定义信息,通过这个方法拿到注解类的信息和注解的信息
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
	@Override  //AnnotationMetadata metadata这个参数是获取注解的原信息,有注解的名字
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImport(metadata).getPackageName()/** 该注解所在包名 */);
	}
		//此处打断点调试
	@Override
	public Set<Object> determineImports(AnnotationMetadata metadata) {
		return Collections.singleton(new PackageImport(metadata));
	}
}

      通过idea的工具计算new PackageImport(metadata).getPackageName()。在这里插入图片描述

      debug结果可以看到包信息注解信息都在。

在这里插入图片描述

在这里插入图片描述

      然后再回来看@EnableAutoConfiguration注解怎么实现自动装配
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)//自动导入组件的选择器,
public @interface EnableAutoConfiguration {

      重点是AutoConfigurationImportSelector.class类。点进去

//点进来
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

      AutoConfigurationImportSelector.class实现了DeferredImportSelector接口,DeferredImportSelector接口继承ImportSelector接口、public interface DeferredImportSelector extends ImportSelector,还有BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered等接口,通过他们几乎可以获取整个spring底层所有东西。
      找到selectImports方法,方法处打断点逐步运行。

@Override
//方法的到String数组,就是全类名,此处打点debug,后续逐步运行,
public String[] selectImports(AnnotationMetadata annotationMetadata /** 这个参数是获取注解的原信息 */) {
	if (!isEnabled(annotationMetadata)) {//没有自动装备的注解
		return NO_IMPORTS;
	}
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry/**获取配置类,点进去*/(autoConfigurationMetadata,
			annotationMetadata);
    //将所有需要导入容器的组件全部以全类名的方式导入容器
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations/**再点进去*/(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
        AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames/**主要是这个方法,再进去*/(getSpringFactoriesLoaderFactoryClass(),/**参数一,进去得到EnableAutoConfiguration.class*/
				getBeanClassLoader()/**参数二,进去得到this.beanClassLoader*/);//使用类加载器机制
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader)/**再进去*/.getOrDefault(factoryClassName, Collections.emptyList());
	}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
//通过类加载器得到一个资源,然后urls
		try {
       //配置文件位置public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION/**这里点进去得到配置文件位置*/):
                                     
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
                //再通过资源得到一个properties配置文件,可见每一个配置类斗鱼配置文件相关联。
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

      配置文件位置public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;就是从指定位置META-INF/spring.factories获取EnableAutoConfiguration类指定的值。导出的类是通过SpringFactoriesLoader.loadFactoryNames()读取了ClassPath下面的META-INF/spring.factories文件。

在这里插入图片描述
在这里插入图片描述

      @EnableAutoConfiguration最后会给容器导入很多自动配置类,给容器中导入场景需要的所有自动配置类xxAutoConfiguration。这就是自动装配的源码的全过程。

      简而言之就是@EnableAutoConfiguration注解可以帮springboot把所有符合条件的配置都加载到当前springboot创建并使用的IOC容器,其中springboot的原有的一个工具类springFactoriesLoader就发挥了很大的作用。有了自动配置类,就不用手动书写配置类和注入功能组件,底层被spring boot的配置类代替了。

发布了4 篇原创文章 · 获赞 11 · 访问量 298

猜你喜欢

转载自blog.csdn.net/VVVVVxVVVVV/article/details/104466406