『SpringBoot 源码分析』自动配置

『SpringBoot 源码分析』自动装配

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

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

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

@RestController
public class TestController {

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

@SpringBootConfiguration

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

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}
}
  1. 当点击进去注解之后,会发现注解是个组合注解,其中 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 是 Spring 定义的
java 复制代码
@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 的类,其实是主配置类
java 复制代码
@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 容器中
java 复制代码
@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
java 复制代码
@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() 将信息注入到注册中心
java 复制代码
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 类
java 复制代码
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() 方法里
  2. 我们先直接来看调用,首先 DeferredImportSelectorGrouping.getImports() 会遍历 deferredImports 属性,然后通过 DeferredImportSelector.Group 来处理自动装配
java 复制代码
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() 方法
java 复制代码
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() 方法,去扫描所有的配置类
java 复制代码
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 的所有装配类
java 复制代码
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 的所有值,并返回
java 复制代码
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 所在的位置
  • 示例内容如下
  1. 当拿到自动装配类以后,回到 getAutoConfigurationEntry() 方法,继续执行一些过滤步骤,包括移除重新的配置类,排除 exclude 指定的类等等
java 复制代码
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 时,才会进行装配
  2. 完成过滤以后,返回到 process() 中,还会把返回的 AutoConfigurationEntry 装进 autoConfigurationEntries 属性集合中以及把符合条件的自动装配的类作为 key,存入 entries 属性集合中
java 复制代码
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() 方法
java 复制代码
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 进行排序
java 复制代码
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. 总结
  • 源码流程
  • 自动配置类总结

HttpEncodingAutoConfiguration

  1. HttpEncodingAutoConfiguration 装配为例来简单再回归一下装配过程,首先肯定是去先扫描 META-INF/spring.factories 下所有的文件 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,经过一定条件过滤以后,再装入到容器当中。其中就包含了 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
  2. HttpEncodingAutoConfiguration 为什么没被过滤掉呢?我们简单看看它上面条件
java 复制代码
// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@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,它就会自动装到对应属性中
java 复制代码
@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
java 复制代码
...
@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 标记,所以对象就可以扫到
相关推荐
救救孩子把11 分钟前
深入理解 Java 对象的内存布局
java
落落落sss13 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节19 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭26 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由32 分钟前
速成java记录(上)
java·速成
一直学习永不止步38 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明38 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端