@SpringBootApplication注解

启动一个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 annotation() default Annotation.class;
}

可以看到@AliasFor 本身也使用了自身的注解,该注解表示属性别名,即attributevalue互为别名,访问的值是一致的。同时@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 nameGenerator() default BeanNameGenerator.class;

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

}

比如scanBasePackages方法,就是等同于ComponentScanbasePackages 属性,所以回归到最开始,Spring api里说的@SpringBootApplication 就等同于多个注解的申明,主要是通过@AliasFor 实现的。

参考

SpringBoot 官网

SpringBoot 2.3.1.Release API

你可能感兴趣的:(@SpringBootApplication注解)