启动一个SpringBoot的默认启动类如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootAnnotationApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAnnotationApplication.class, args);
}
}
下面我们来看下@SpringBootApplication
注解是什么内容,从 SpringBoot 2.3.1.Release API 中看到该注解等同于@Configuration
、@EnableAutoConfiguration
、@ComponentScan
,下面先单独了解下这几个注解。
@Configuration 注解
先看注解的定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
javadoc 描述为申明该类用于定义多个@Bean
实例,比如:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
通常的执行方式如下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
等同于入下的XML申明
@ComponentScan注解
上面 @Configuration
提供申明Spring容器类的初始化方式,@ComponnentScan
则为找到这样的类提供了扫描机制。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
//something...
}
@ComponentScans
扫描机制包含了指定类名扫描,通过包路径扫描。
两个注解搭配使用:
@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
// various @Bean definitions ...
}
另外,Java8之后增加了@Repeatable
注解,表示可以重复设置。
@EnableAutoConfiguration注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 排除特定的自动配置类,使其不会被应用。
* @return the classes to exclude
*/
Class>[] exclude() default {};
/**
* 排除特定的自动配置类名,使其永远不会被应用。
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
javadoc 描述该注解是启动Spring的上下文自动配置,尝试猜测和配置可能需要的Bean。
这里需要关注一个特别的注解 @Import({AutoConfigurationImportSelector.class})
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List 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
去扫描META-INF/spring.factories
文件中的配置。加载其中所有key 为EnableAutoConfiguration
的类,比如:spring-boot-autoconfigure
包下就有很多
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
...
@AliasFor注解
以上两个注解@Configuration
@ComponentScan
申明时都使用到了AliasFor
注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
Class extends Annotation> annotation() default Annotation.class;
}
可以看到@AliasFor
本身也使用了自身的注解,该注解表示属性别名,即attribute
和value
互为别名,访问的值是一致的。同时@AliasFor
也可以跨注解申明,如下@SpringBootApplication
就是用了很多AliasFor注解。
@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 {
@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;
}
比如scanBasePackages
方法,就是等同于ComponentScan
的basePackages
属性,所以回归到最开始,Spring api里说的@SpringBootApplication
就等同于多个注解的申明,主要是通过@AliasFor
实现的。
参考
SpringBoot 官网
SpringBoot 2.3.1.Release API