购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 运作原理源码解析之@EnableAutoConfiguration

@EnableAutoConfiguration是开启自动配置的注解,在创建的Spring Boot项目中并不能直接看到此注解,它是由组合注解@SpringBootApplication引入的。下面我们先来了解一下入口类和@SpringBootApplication注解的功能,然后再深入了解@EnableAutoConfiguration注解的构成与作用。

2.2.1 入口类和@SpringBootApplication注解

Spring Boot项目创建完成会默认生成一个*Application的入口类。在默认情况下,无论是通过IDEA还是通过官方创建基于Maven的Spring Boot项目,入口类的命名规则都是artifactId+Application。通过该类的main方法即可启动Spring Boot项目,代码如下。


@SpringBootApplication
public class SpringLearnApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这里的main方法并无特别之处,就是一个标准的Java应用的main方法,用于启动Spring Boot项目的入口。在默认情况下,按照上述规则命名并包含main方法的类称为入口类。

在Spring Boot入口类(除单元测试外)中,唯一的一个注解就是@SpringBootApp-lication。它是Spring Boot项目的核心注解,用于开启自动配置,准确说是通过该注解内组合的@EnableAutoConfiguration开启了自动配置。

@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 {
    // 排除指定自动配置类
    @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 {};

    // 指定是否代理@Bean方法以强制执行bean的生命周期行为
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}

通过源代码可以看出,该注解提供了以下成员属性(注解中的成员变量以方法的形式体现)。

·exclude:根据类(Class)排除指定的自动配置,该成员属性覆盖了@SpringBoot-Application中组合的@EnableAutoConfiguration中定义的exclude成员属性。

·excludeName:根据类名排除指定的自动配置,覆盖了@EnableAutoConfiguration中的excludeName的成员属性。

·scanBasePackages:指定扫描的基础package,用于激活@Component等注解类的初始化。

·scanBasePackageClasses:扫描指定的类,用于组件的初始化。

·proxyBeanMethods:指定是否代理@Bean方法以强制执行bean的生命周期行为。此功能需要通过运行时生成CGLIB子类来实现方法拦截。该子类有一定的限制,比如配置类及其方法不允许声明为final等。proxyBeanMethods的默认值为true,允许配置类中进行inter-bean references(bean之间的引用)以及对该配置的@Bean方法的外部调用。如果@Bean方法都是自包含的,并且仅提供了容器使用的普通工程方法的功能,则可设置为false,避免处理CGLIB子类。Spring Boot 2.2版本上市后新增该成员属性,后面章节涉及的自动配置类中基本都会用到proxyBeanMethods,一般情况下都配置为false。

通过以上源代码我们会发现,Spring Boot中大量使用了@AliasFor注解,该注解用于桥接到其他注解,该注解的属性中指定了所桥接的注解类。如果点进去查看,会发现@SpringBootApplication定义的属性在其他注解中已经定义过了。之所以使用@AliasFor注解并重新在@SpringBootApplication中定义,更多是为了减少用户使用多注解带来的麻烦。

@SpringBootApplication注解中组合了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。因此,在实践过程中也可以使用这3个注解来替代@SpringBootApplication。

在Spring Boot早期版本中并没有@SpringBootConfiguration注解,版本升级后新增了@SpringBootConfiguration并在其内组合了@Configuration。@EnableAutoConfiguration注解组合了@AutoConfigurationPackage。

我们忽略掉一些基础注解和元注解,@SpringBootApplication注解的组合结构可以参考图2-2。

在图2-2中,@SpringBootApplication除了组合元注解之外,其核心作用还包括:激活Spring Boot自动配置的@EnableAutoConfiguration、激活@Component扫描的@ComponentScan、激活配置类的@Configuration。

其中@ComponentScan注解和@Configuration注解在日常使用Spring时经常用到,也非常基础,大家应该都有一些了解,这里就不再赘述了。下面详细介绍@EnableAuto-Configuration的功能。

图2-2 @SpringBootApplication注解组合结构图

2.2.2 注解@EnableAutoConfiguration功能解析

在未使用Spring Boot的情况下,Bean的生命周期由Spring来管理,然而Spring无法自动配置@Configuration注解的类。而Spring Boot的核心功能之一就是根据约定自动管理该注解标注的类。用来实现该功能的组件之一便是@EnableAutoConfiguration注解。

@EnableAutoConfiguration位于spring-boot-autoconfigure包内,当使用@SpringBootApplication注解时,@EnableAutoConfiguration注解会自动生效。

@EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中。

举个例子:如果将tomcat-embedded.jar添加到classpath下,那么@EnableAutoConfiguration会认为你准备使用TomcatServletWebServerFactory类,并帮你初始化相关配置。与此同时,如果自定义了基于ServletWebServerFactory的Bean,那么@EnableAutoConfiguration将不会进行TomcatServletWebServerFactory类的初始化。这一系列的操作判断都由Spring Boot来完成。

下面我们来看一下@EnableAutoConfiguration注解的源码。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 用来覆盖配置开启/关闭自动配置的功能
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    // 根据类(Class)排除指定的自动配置
    Class<?>[] exclude() default {};
    // 根据类名排除指定的自动配置
    String[] excludeName() default {};
}

@EnableAutoConfiguration注解提供了一个常量和两个成员参数的定义。

·ENABLED_OVERRIDE_PROPERTY:用来覆盖开启/关闭自动配置的功能。

·exclude:根据类(Class)排除指定的自动配置。

·excludeName:根据类名排除指定的自动配置。

正如上文所说,@EnableAutoConfiguration会猜测你需要使用的Bean,但如果在实战中你并不需要它预置初始化的Bean,可通过该注解的exclude或excludeName参数进行有针对性的排除。比如,当不需要数据库的自动配置时,可通过以下两种方式让其自动配置失效。


// 通过@SpringBootApplication排除DataSourceAutoConfiguration
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 
public class SpringLearnApplication {
}

或:


// 通过@EnableAutoConfiguration排除DataSourceAutoConfiguration
@Configuration
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
public class DemoConfiguration {
}

需要注意的是,被@EnableAutoConfiguration注解的类所在package还具有特定的意义,通常会被作为扫描注解@Entity的根路径。这也是在使用@SpringBootApplication注解时需要将被注解的类放在顶级package下的原因,如果放在较低层级,它所在package的同级或上级中的类就无法被扫描到。

而对于入口类和其main方法来说,并不依赖@SpringBootApplication注解或@EnableAuto-Configuration注解,也就是说该注解可以使用在其他类上,而非入口类上。 SCiOVaUPtBF/rGTx9uZr6YZdOGBhc4T7MQUPGpl4dxadhhcAo9DMOqLZ0n6kxK4U

点击中间区域
呼出菜单
上一章
目录
下一章
×