SpringBoot进阶之道-@SpringBootApplication

Posted 草莓君_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot进阶之道-@SpringBootApplication相关的知识,希望对你有一定的参考价值。

相信小伙伴们在写springboot项目的时候,在启动类上加上@SpringBootApplication注解引导,就可以自动装配。例如下面这样:

@SpringBootApplication
public class SpringbootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }
}
  • 那么@SpringBootApplication注解是怎么做到的?
  • 什么叫 @Component派生注解?

1、理解@SpringBootApplication语义

引用官方的话:@SpringBootApplication被用于激活@EnableAutoConfiguration@ComponentScan@Configuration三个特性。其中,@EnableAutoConfiguration负责激活SpringBoot自动装配机制,@ComponentScan激活@Component的扫描,@Configuration声明被标注为配置类。官方文档继续告诉开发人员@SpringBootApplication注解等同于@Configuration@EnableAutoConfiguraion@ComponentScan注解,且它们均使用默认属性。我们这样改造上面的代码:

//@SpringBootApplication
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SpringbootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }
}

我们启动运行,观察日志,没错,一切正如你想象的正常。

额~ 相信有不少人翻过springBoot2.x的@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 {
    ....
}

由此可见,那么有个疑问?@SpringBootApplication是不是不等价上面介绍的@EnableAutoConfiguration@ComponentScan@Configuration注解?

那么我们分析,由上可见,@SpringBootApplication等价于@SpringBootConfiguration@ComponentScan@EnableAutoConfiguration,不过@ComponentScan并非使用了默认值,而是添加了排除的TypeFilter实现:TypeExcludeFilter和AutoConfigurationExcludeFilter。前者由Springboot1.4引入,用于查找BeanFactory中已注册的TypeExcludeFilter Bean,作为代理执行对象:

public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
        ...
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory && this.getClass() == TypeExcludeFilter.class) {
            Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
            Iterator var4 = delegates.iterator();

            while(var4.hasNext()) {
                TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }

        return false;
    }
        ...
}

而后者从SpringBoot1.5开始支持,用于排除其他同时标注@Configuration和@EnableAutoConfiguration的类:

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
        ...
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
    }

    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
    }

    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
    }

    protected List<String> getAutoConfigurations() {
        if (this.autoConfigurations == null) {
            this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
        }

        return this.autoConfigurations;
    }
}

我们来对比一下SpringBoot1.3.8的@SpringBootApplication的声明:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration和
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
    ....
}

由此可见,SpringBoot1.3.8的实现与官方文档描述的是一样的。尽管SpringBoot1.4之后的@SpringBootApplication通常不会表现出与文档相异的行为,但官方没更新文档也没给出具体有什么不一样的解释。

2、@Component ‘派生性’

但我们知道,从SpringBoot1.4开始,@SpringBootApplication注解不再标注@Configuration,而是@SpringBootConfiguration,不过两者在运行上的行为并没有什么不一样,这种类似于对象之间的继承关系,我们称之为“多层次 @Component‘派生性’”,哈哈哈,并且这种能力也允许我们扩展使用的,是不是很嗨。我们看下@Configuration注解,它其实标注了@Component注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    ...
}

所以我们得知,@Configuration实际上是@Component的派生性注解,同理@SpringBootConfiguration标注了@Configuration:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

因此三者的关系为:

  • @component

    • @Configuration

      • @SpringBootConfiguration

我们知道,@CompoentScan仅扫描带有@Component注解的类,并注册成bean,然而由于@SpringBootConfiguration属于多层次的@Component“派生”注解,所以能被@CompoentScan识别。但是我们知道@CompoentScan属于Spring Framework,而@SpringBootConfiguration来自SpringBoot,那么是什么机制让@CompoentScan能够识别@SpringBootConfiguration注解呢?这种机制就是前面提到的“多层次 @Component ‘派生性’”。

3、小结

SpringBoot是通过注解@EnableAutoConfiguration的方式,去查找,过滤,加载所需的Configuration,@ComponentScan扫描我们自定义的bean,@SpringBootConfiguration(派生性@Component)使得被@SpringBootApplication注解的类声明为注解类,因此@SpringBootApplication的作用等价于同时组合使用@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration。

以上是关于SpringBoot进阶之道-@SpringBootApplication的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot进阶之道-@Enable模块驱动

SpringBoot进阶之道-Starter机制

助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoo

手把手教你用VUE开发后台管理系统:搭建SpringBoo 2.xt环境

Spark修炼之道(进阶篇)——Spark入门到精通:第十节 Spark SQL案例实战

医学方-科研论文绘图之道(视频版):R语言SCI绘图进阶教程