『SpringBoot 源码分析』自动配置

『SpringBoot 源码分析』自动装配

  • 基于 2.2.9.RELEASE
  • 问题:Spring Boot 到底是如何进行自动配置的,都把哪些组件进行了自动配置?
  1. 首先创建测试主程序
package com.lagou;

@SpringBootApplication//标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 创建测试 Controller
package com.lagou.controller;

@RestController
public class TestController {

	@RequestMapping("/test")
	public String test(){
		System.out.println("源码环境构建成功...");
		return "源码环境构建成功";
	}
}

@SpringBootConfiguration

  1. 首先在主程序当中,我们通常会在主程序标记一个 @SpringBootApplication 注解
@SpringBootApplication // 1. 标注在类上说明这个类是`SpringBoot`的主配置类
public class SpringBootMytestApplication{

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 当点击进去注解之后,会发现注解是个组合注解,其中 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 是 Spring 定义的
@Target(ElementType.TYPE) // 注解的适用范围,Type 表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期,Runtime 运行时
@Documented // 表示注解可以记录在 javadoc 中
@Inherited // 表示可以被子类继承该注解
@SpringBootConfiguration  // 1. 标明该类为配置类
@EnableAutoConfiguration  // 2. 启动自动配置功能
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 3. 注解扫描
public @interface SpringBootApplication {

	// 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	// 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	// 扫描特定的包,参数类似是Class类型数组。
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

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

}
  1. @SpringBootConfiguration:当我们再点击进去之后,会发现其实是个包装了 @Configuration 的注解,也就是说标注了 @SpringBootApplication 的类,其实是主配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 1. 能力同Configuration
public @interface SpringBootConfiguration {

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

@EnableAutoConfiguration

  1. 上面只是阐述了 @SpringBootConfiguration 是标注主配置类的一个关键配置,但是并没有引入自动装配的组件,还有一个注解 @EnableAutoConfiguration,用于启动自动配置功能
  • 重点:Spring 很多以 Enable 开头的注解,其作用是借助 @Import 来收集并注册特定场景的 bean,并加载到 IOC 容器中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 1. 自动配置包
@Import(AutoConfigurationImportSelector.class) // 2. Spring的底层注解@Import,给容器中导入一个组件;
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};
	String[] excludeName() default {};
}
  1. 其中 @AutoConfigurationPackage 其实也是使用 @Import 导入组件 AutoConfigurationPackages.Registrar.class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 1. Spring 的底层注解 @Import,给容器中导入一个组件
// 导入的组件是 AutoConfigurationPackages.Registrar.class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
  1. 当导入 AutoConfigurationPackages.Registrar.class 时,会调用 registerBeanDefinitions() 将信息注入到注册中心
public abstract class AutoConfigurationPackages {
    ...
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			// 1. 将注解标注的元信息传入,获取到相应的包名
			// 其中 new PackageImport(metadata).getPackageName() 获取到的是报名是 com.lagou
			register(registry, new PackageImport(metadata).getPackageName());
		}
     ...
	}
}
  1. 当调用 register() 方法,首先判断注册中心,是否已经包含名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的 BasePackages.classbeanDefinition,没有的话,则创建,并且把包名 com.lagou 注册进去,供之后使用,例如给 JPA entity 的扫描器用来扫描开发人员通过注解 @Entity 定义的 entity 类
public abstract class AutoConfigurationPackages {
	
	// 1. 类名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages
	private static final String BEAN = AutoConfigurationPackages.class.getName();
	...
	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 1. 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解
		// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包

		if (registry.containsBeanDefinition(BEAN)) {
			// 如果该 bean 已经注册,则将要注册包名称添加进去
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
			// 2. 如果该 bean 尚未注册,则注册该 bean,参数中提供的包名称会被设置到 bean 定义中去
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);// Holder for the base package
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);// packageNames放入BasePackages属性中
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}
	...
	static final class BasePackages {
		...
	}
}

@Import(AutoConfigurationImportSelector.class)

  1. AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 IOC 容器中。从继承的关系就可以看到,AutoConfigurationImportSelector 实现了各种 Aware,可以获取到 IOC 容器的关键配置。同时实现了 DeferredImportSelector 接口,而关键的自动配置相关逻辑,就在 DeferredImportSelectorGrouping.getImports() 方法里
    『SpringBoot 源码分析』自动配置_第1张图片
  2. 我们先直接来看调用,首先 DeferredImportSelectorGrouping.getImports() 会遍历 deferredImports 属性,然后通过 DeferredImportSelector.Group 来处理自动装配
class ConfigurationClassParser {
    ...
	private static class DeferredImportSelectorGrouping {
	    
		private final DeferredImportSelector.Group group;
		private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
		...
		public Iterable<Group.Entry> getImports() {
			// 1. 遍历 this.deferredImports 属性 
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				// 2. 其中 deferredImport.getConfigurationClass() 为 ConfigurationClass: beanName 'springBootMytestApplication', com.lagou.SpringBootMytestApplication
				// deferredImport.getImportSelector() 为 AutoConfigurationImportSelector
				// this.group 为 AutoConfigurationImportSelector.AutoConfigurationGroup
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
	}
}
  1. 当调用 process() 方法时,会先将 deferredImportSelector 转换为 AutoConfigurationImportSelector,然后继续调用 getAutoConfigurationEntry() 方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
		
		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			...
			// 1. 调用 getAutoConfigurationEntry() 方法得到自动配置类放入 autoConfigurationEntry 对象中
			// annotationMetadata 为 com.lagou.SpringBootMytestApplication 的元数据
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
			...
		}
	}
}
  1. 接下来会调用 getCandidateConfigurations() 方法,去扫描所有的配置类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		// 1. 得到 spring.factories 文件配置的所有自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		...
	}
}
  1. 接下来继续会去调用 SpringFactoriesLoader.loadFactoryNames() 方法,主要是去获取所有 spring.factories 中,key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有装配类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		...
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		// 1. 这个方法需要传入两个参数 getSpringFactoriesLoaderFactoryClass() 和 getBeanClassLoader()
		// getSpringFactoriesLoaderFactoryClass() 这个方法返回的是 EnableAutoConfiguration.class
		// getBeanClassLoader() 这个方法返回的是 beanClassLoader(类加载器)
		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;
	}
}
  1. 当调用 loadFactoryNames() 时,会先扫描所有的 META-INF/spring.factories 下的内容,然后拿到键为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有值,并返回
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) {
		String factoryTypeName = factoryType.getName();
		// 2. 返回 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// 1. 扫描所有 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 = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
}
  • META-INF/spring.factories 所在的位置
    『SpringBoot 源码分析』自动配置_第2张图片
  • 示例内容如下
    『SpringBoot 源码分析』自动配置_第3张图片
  1. 当拿到自动装配类以后,回到 getAutoConfigurationEntry() 方法,继续执行一些过滤步骤,包括移除重新的配置类,排除 exclude 指定的类等等
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		// 获取是否有配置spring.boot.enableautoconfiguration属性,默认返回true
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);

		// 1. 得到 spring.factories 文件配置的所有自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

		// 2. 利用 LinkedHashSet 移除重复的配置类
		configurations = removeDuplicates(configurations);

		// 3. 得到要排除的自动配置类,比如注解属性 exclude 的配置类
		// 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
		// 将会获取到 exclude = FreeMarkerAutoConfiguration.class 的注解数据
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);

		// 4. 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
		checkExcludedClasses(configurations, exclusions);

		// 5. 将要排除的配置类移除
		configurations.removeAll(exclusions);

		// 6. 过滤 @ConditionOnxxx 生效的配置
		// 因为从 spring.factories 文件获取的自动配置类太多,如果有些不必要的自动配置类都加载进内存,会造成内存浪费,因此这里需要进行过滤
		configurations = filter(configurations, autoConfigurationMetadata);

		// 7. 获取了符合条件的自动配置类后,此时触发 AutoConfigurationImportEvent 事件,
		// 目的是告诉 ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类
		fireAutoConfigurationImportEvents(configurations, exclusions);

		// 8. 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}
  1. 我们简单看看 filter() 流程,核心点在于过滤所有 @ConditionOnxxx 的条件,例如 CacheAutoConfiguration,它得存在相应的 bean 时,才会进行装配
    『SpringBoot 源码分析』自动配置_第4张图片
  2. 完成过滤以后,返回到 process() 中,还会把返回的 AutoConfigurationEntry 装进 autoConfigurationEntries 属性集合中以及把符合条件的自动装配的类作为 key,存入 entries 属性集合中
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
		
		@Override
		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			...
			// 调用 getAutoConfigurationEntry() 方法得到自动配置类放入 autoConfigurationEntry 对象中
			// annotationMetadata 为 com.lagou.SpringBootMytestApplication 的元数据
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);

			// 1. 又将封装了自动配置类的 autoConfigurationEntry 对象装进autoConfigurationEntries集合
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			// 2. 遍历刚获取的自动配置类
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				// 这里符合条件的自动配置类作为 key,annotationMetadata 作为值放进 entries 集合
				// 举例 key 为:org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
	}
}
  1. 当处理完类的读取以后,DeferredImportSelectorGrouping 还需要执行一下 selectImports() 方法
class ConfigurationClassParser {
    ...
	private static class DeferredImportSelectorGrouping {
	    
		private final DeferredImportSelector.Group group;
		private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
		...
		public Iterable<Group.Entry> getImports() {
			// 遍历 this.deferredImports 属性 
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				// 其中 deferredImport.getConfigurationClass() 为 ConfigurationClass: beanName 'springBootMytestApplication', com.lagou.SpringBootMytestApplication
				// deferredImport.getImportSelector() 为 AutoConfigurationImportSelector
				// this.group 为 AutoConfigurationImportSelector.AutoConfigurationGroup
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			// 1. 再次进行排除某些类,并且按照 @Order 注解排序
			return this.group.selectImports();
		}
	}
}
  1. 主要是对自动装配类再进行一次过滤,然后根据 @Order 进行排序
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
	private static class AutoConfigurationGroup
			implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
			
		private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
		private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

		...
		@Override
		public Iterable<Entry> selectImports() {// 进行exclude排除和order排除
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			// 1. 这里得到所有要排除的自动配置类的 set 集合
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			// 这里得到经过滤后所有符合条件的自动配置类的set集合
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			// 移除掉要排除的自动配置类
			processedConfigurations.removeAll(allExclusions);

			// 2. 对标注有 @Order 注解的自动配置类进行排序
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}
	}
}
  1. 总结
  • 源码流程
    『SpringBoot 源码分析』自动配置_第5张图片
  • 自动配置类总结
    『SpringBoot 源码分析』自动配置_第6张图片

HttpEncodingAutoConfiguration

  1. HttpEncodingAutoConfiguration 装配为例来简单再回归一下装配过程,首先肯定是去先扫描 META-INF/spring.factories 下所有的文件 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,经过一定条件过滤以后,再装入到容器当中。其中就包含了 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
    『SpringBoot 源码分析』自动配置_第7张图片
  2. HttpEncodingAutoConfiguration 为什么没被过滤掉呢?我们简单看看它上面条件
// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)
// 启动指定类的 ConfigurationProperties 功能;将配置文件中对应的值和 HttpEncodingProperties 绑定起来;
@EnableConfigurationProperties(HttpProperties.class)
// Spring 底层 @Conditional 注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效。
// 判断当前应用是否是 web 应用,如果是,当前配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 判断当前项目有没有这个 CharacterEncodingFilter: SpringMVC中进行乱码解决的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
// 判断配置文件中是否存在某个配置 spring.http.encoding.enabled 如果不存在,判断也是成立的
// matchIfMissing = true 表示即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
	
	// 它已经和 SpringBoot 配置文件中的值进行映射了
	private final HttpProperties.Encoding properties;

	// 只有一个有参构造器的情况下,参数的值就会从容器中拿
	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}
	...
}
  • @EnableConfigurationProperties:该注解起到的作用是说把配置文件(如 application.yml)中的 spring.http 开头的属性,都装到 HttpProperties 类中。例如我们想设置 spring.http.encoding.charset=utf-8,它就会自动装到对应属性中
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
	private final Encoding encoding = new Encoding();
	...
	public static class Encoding {

		public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
		// 1. 覆盖这个属性
		private Charset charset = DEFAULT_CHARSET;
		...
	}
}
  • @ConditionalOnWebApplication:这个是条件之一,要求的是启动的时候,容器类型是 ConditionalOnWebApplication.Type.SERVLET,只要在 pom.xml 配置了 spring-boot-starter-web,这一定会设置成这个类型
  • @ConditionalOnClass 会判断 classpath 是否存在 CharacterEncodingFilter.class 这个类,只要在 pom.xml 配置了 spring-boot-starter-web,肯定会有这个类
  • @ConditionalOnProperty 要求必须在 application.yml 中,配置了 spring.http.encoding.enabled 属性,但是属性上 matchIfMissing 允许缺失,所以这个条件也是通过的
  1. 满足了上面的所有条件后,自动装配类就没被过滤掉,加到容器中来

@ComponentScan

  1. 对于注解 @SpringBootApplication 里面还有一个关键注解为 @ComponentScan
...
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 注解扫描
public @interface SpringBootApplication {
	...
}
  1. 对于 @ComponentScan 里面并没有配置像 value 属性用于指定要扫描的路径。而且现在只配置了 Filter 的过滤,但是为什么 springboot 能够扫描到 @RestController 标注的类并注入容器当中呢?原来是因为 @ComponentScan 如果没配置 value 属性的话,默认会扫当前类所在目录以及子目录下的 Component 标记,所以对象就可以扫到

你可能感兴趣的:(『后端』,spring,boot,java,spring)