@SpringBootApplication注解解析

本文详细解读了SpringBoot中启动类的定义,特别是如何使用@SpringBootApplication注解,以及其包含的自动配置、包扫描和排除策略。重点介绍了@EnableAutoConfiguration和@ComponentScan的作用和使用方法。
摘要由CSDN通过智能技术生成

定义启动类

@EnableFeignClients
@EnableDiscoveryClient
@EnableTransactionManagement
@SpringBootApplication//使用这个注解的类代表当前项目的启动类
public class CDAApplication extends SpringBootServletInitializer {

    public static void main(String[] args){
        System.setProperty("spring.devtools.restart.enabled", "false");
        SpringApplication.run(CDAApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {

        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(CDAApplication.class);
    }

}

@SpringBootApplication注解类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration//这个注解代表是一个配置类
@EnableAutoConfiguration//这个注解代表是启动自动配置
//这个注解代表包扫描,这里要扫描的路径是启动类所在的包及其子包
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	/**
	 * 这个注解属性映射到@EnableAutoConfiguration注解种的exclude属性
	 * 指定不需要加载的自动配置类
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

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

	/**
		这个属性映射到@ComponentScan注解的basePackages属性
	 * 指定包扫描的路径
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

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

	/**
	 * 这个属性映射到@ComponentScan注解nameGenerator属性
	 * 指定beanName的生成策略
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	/**
	 * 这个属性映射到@Configuration注解的proxyBeanMethods属性
	 * 默认值为true
	 * 为@Configuration类中的@Bean产生代理类
	 * 在SpringBoot工程中,最后不要修改此值,因为可能会影响到自动配置类产生代理对象
	 */
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

@SpringBootConfiguration注解类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
	/**
	这里不再做解释,请看@SpringBootApplication注解解析
	*/
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}

@EnableAutoConfiguration注解类

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

	/**
	 * 当启用自动配置时,可以修改一些自动配置属性值
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 *这里不再解释,请看@SpringBootApplication注解解释
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
		public String[] selectImports(AnnotationMetadata annotationMetadata) {
		//这里的annotationMetadata 就是加了@SpringBootApplication注解的启动类
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		//解析@SpringBootApplication注解中的属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//获取所有的自动配置类
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		//去重
		configurations = removeDuplicates(configurations);
		//获取用户指定不需要加载的自动配置类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		//检查exclusions中的自动配置类不在configurations中的存在的自动配置类
		checkExcludedClasses(configurations, exclusions);
		//从configurations移除不需要程序加载的自动配置类
		configurations.removeAll(exclusions);
		//加载AutoConfigurationExcludeFilter过滤器,并且过滤掉不符合条件的自动配置类
		configurations = getConfigurationClassFilter().filter(configurations);
		//获取所有的AutoConfigurationImportListener监听器,并调用onAutoConfigurationImportEvent方法执行
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//从指定的配置文件中获取自动配置类(spring-boot-autoconfigure-2.5.6.jar!\META-INF\spring.factories)
//以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key对应的所有的value值
		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;
	}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.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();
		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 {
			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;
	}

@AutoConfigurationPackage注解类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
/**
	用于确定哪些包应被视为 Spring Boot 的自动配置包。这是基于给定的包和父包进行的。其主要用途是在 Spring Boot 的自动配置过程中确定哪些组件或特性应该被考虑和应用。
默认情况下,basePackages的路径就是启动类所在的包路径
会自动扫描项目中那些是自动配置类,然后进行加载
*/
public @interface AutoConfigurationPackage {

	/**
	 * 同下
	 */
	String[] basePackages() default {};

	/**
	 * 指定SpringBoot自动配置类应该扫描那些包
	 */
	Class<?>[] basePackageClasses() default {};

}
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值