您现在的位置是:首页 >技术交流 >1.Springboot自动装配原理拆解浅析网站首页技术交流

1.Springboot自动装配原理拆解浅析

哈喽,树先生 2023-06-12 00:00:03
简介1.Springboot自动装配原理拆解浅析

1.springboot自动装配原理在于启动类上的注解@SpringBootApplication

@SpringBootApplication
public class JinyiUserProviderApplication {
    public static void main(String[] args) throws UnknownHostException {
         SpringApplication.run(JinyiUserProviderApplication.class, args);
    }
}

2.@SpringBootApplication拆解分析

@SpringBootApplication是一个组合注解,主要由下面的注解复合而成。
@SpringBootConfiguration 标注在某个类上,表示这是一个Spring Boot的配置类;
@EnableAutoConfiguration 开启自动配置功能
@ComponentScan 扫描包范围,spring底层会自动扫描当前配置类所有在的包及子包

@Target(ElementType.TYPE) //设置当前注解可以标记在哪
@Retention(RetentionPolicy.RUNTIME) //当注解标注的类编译以什么方式保留
@Documented // java doc 会生成注解信息 
@Inherited //是否会被继承 
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

3.@EnableAutoConfiguration拆解分析

@EnableAutoConfiguration 是springboot自动装配的核心注解,内部使用了@Import注解导入了AutoConfigurationImportSelector类

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

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	Class<?>[] exclude() default {};

	String[] excludeName() default {};

}

4.AutoConfigurationImportSelector类拆解

AutoConfigurationImportSelector类内部实现了DeferredImportSelector接口。DeferredImportSelector 接口继承了ImportSelector 接口。由AutoConfigurationImportSelector类实现ImportSelector 接口中的selectImports方法。
selectImports方法大致流程:
1.获取标记了注解的属性数据
2.获取待加载的候选bean配置信息
3.利用LinkedHashSet set集合不能重复的特性 去除重复的待加载的候选bean配置信息
4.获取exclude配置的排除配置信息
5.待加载的候选bean配置信息中移除配置了排除的后选bean配置信息
6.获取过滤链过滤满足Condition条件的候选bean配置信息
7.根据层层条件之后,发布满足自动配置的候选bean的Import事件
8.已经找到了JavaConfig自动配置类的全限定名对应的class, 后续就交给BeanDefinition BeanFactory。然后将所有自动配置类加载到Spring容器中

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
		
	//ImportSelector接口定义的方法
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		//获取满足自动配置条件的Entry
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
	
      protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
			if (!isEnabled(annotationMetadata)) {
				return EMPTY_ENTRY;
			}
			//获取标记了注解的属性数据
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			//获取待加载的候选bean配置信息
			List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
			//LinkedHashSet 利用set集合不能重复的特性 去除重复的待加载的候选bean配置信息
			configurations = removeDuplicates(configurations);
			//获取exclude配置的排除配置信息
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			checkExcludedClasses(configurations, exclusions);
			//待加载的候选bean配置信息中移除配置的排除的后选bean配置信息
			configurations.removeAll(exclusions);
			//获取过滤链过滤满足Condition条件的候选bean配置信息
			configurations = getConfigurationClassFilter().filter(configurations);
			//根据层层条件之后,发布满足自动配置的候选bean的Import事件 
			//已经找到了JavaConfig自动配置类的全限定名对应的class, 后续就交给BeanDefinition  BeanFactory。然后将所有自动配置类加载到Spring容器中
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return new AutoConfigurationEntry(configurations, exclusions);
		}
		//获取标记了注解的属性数据
		protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
			String name = getAnnotationClass().getName();
			AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
			Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
					+ " annotated with " + ClassUtils.getShortName(name) + "?");
			return attributes;
		}
	     //获取待加载的候选bean信息
		protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		    //核心方法
			List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
					getBeanClassLoader());
			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;
		}
}

核心SpringFactoriesLoader 类

public final class SpringFactoriesLoader {

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		//核心的2次方法
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

    //最核心的方法来了
	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
		    //重点:加载"META-INF/spring.factories"里的配置信息
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}
}

其他继承类说明:
DeferredImportSelector 接口继承了ImportSelector 接口

public interface DeferredImportSelector extends ImportSelector {

	@Nullable
	default Class<? extends Group> getImportGroup() {
		return null;
	}

	interface Group {

		void process(AnnotationMetadata metadata, DeferredImportSelector selector);

		Iterable<Entry> selectImports();

		class Entry {

			private final AnnotationMetadata metadata;

			private final String importClassName;

			public Entry(AnnotationMetadata metadata, String importClassName) {
				this.metadata = metadata;
				this.importClassName = importClassName;
			}

			public AnnotationMetadata getMetadata() {
				return this.metadata;
			}

			public String getImportClassName() {
				return this.importClassName;
			}

			@Override
			public boolean equals(@Nullable Object other) {
				if (this == other) {
					return true;
				}
				if (other == null || getClass() != other.getClass()) {
					return false;
				}
				Entry entry = (Entry) other;
				return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
			}

			@Override
			public int hashCode() {
				return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
			}

			@Override
			public String toString() {
				return this.importClassName;
			}
		}
	}

}

ImportSelector 接口内部提供了核心的selectImports方法,由AutoConfigurationImportSelector 类实现。

public interface ImportSelector {

	String[] selectImports(AnnotationMetadata importingClassMetadata);

	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}

}

额外说明:

Bean注入Spring容器的几种方式。

1。@Configuration + @Bean
@Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中。
如下,启动类中顺道加载RestTemplate

@SpringBootApplication
public class JinyiUserProviderApplication {

    public static void main(String[] args) throws UnknownHostException {
        SpringApplication.run(JinyiUserProviderApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

2。@Componet + @ComponentScan
@Componet放在类名上面,然后@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中。最常规的方法

3。@Import注解导入 第三方jar的引入方式

@Import直接导入类
@Import + ImportSelector 优点:一次能引入多个,可以不在本项目的Spring扫描范围内。 springboot加载使用的就是这种
@Import + DeferredImportSelector 延迟导入,内部有一个Group属性,可以保证局部有序加载。
@Import + ImportBeanDefinitionRegistrar 直接引入BeanDefinitionRegistrar bean定义注册器

还有其他的使用FactoryBean接口等。。

总结

@EnableAutoConfiguration自动装配的原理总结一下核心过程
•通过@lmport(AutoConfigurationimportSelector实现配置类的导入,但是这里并不是传统意
义上的单个配置类装配。
•AutoConfigurationimportselector 类实现了 Importselector 接口,重写了方法 selectlmports,
它用于实现选择性批量配置类的装配。
•通过 Spring 提供的 SpringFactoriesLoader 机制,扫描classpath 路径下的 META-INF/spring.factories,
读取需要实现自动装配的配置类。
•通过@Conditional等条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。