一、什么是Springboot的自动配置
Spring Boot自动配置(Auto-configuration)是Spring Boot框架的核心特性之一,它使得开发者可以更容易地创建基于Spring的应用程序,而无需进行大量的手动配置。自动配置基于开发者添加的jar依赖项来自动配置Spring应用程序。
二、核心原理
- 依赖管理:Spring Boot通过Maven或Gradle等构建工具管理项目的依赖关系。当开发者在项目中添加了某个Spring Boot Starter依赖时,Spring Boot会尝试自动配置与该依赖相关的bean。
- 条件化配置:Spring Boot使用条件注解(如
@ConditionalOnClass
、@ConditionalOnProperty
等)来决定是否应该创建和配置某个bean。这些条件注解可以根据类路径上的类是否存在、某个配置属性是否存在或具有特定值等条件来触发自动配置。 - 默认配置:对于许多常见的配置项,Spring Boot提供了默认值。如果开发者没有提供自定义的配置,那么这些默认值将被使用。
三、实现方式
- @SpringBootApplication:这是Spring Boot应用程序的入口点。该注解是
@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
的组合。其中,@EnableAutoConfiguration
注解启用了自动配置功能。 - META-INF/spring.factories:Spring Boot在启动时会加载
META-INF/spring.factories
文件,该文件包含了自动配置类的列表。Spring Boot会遍历这些自动配置类,并根据条件注解来决定是否创建和配置相应的bean。 - Starters:Spring Boot提供了许多Starters,它们是一组方便的依赖描述符,用于简化Maven或Gradle构建文件的依赖管理。每个Starter都包含了一组用于支持特定功能的依赖项和自动配置。
四、自定义配置
- application.properties或application.yml:开发者可以在
src/main/resources
目录下提供application.properties
或application.yml
文件来覆盖自动配置的默认值。这些文件通常用于定义应用程序的特定配置。 - @Configuration和@Bean:如果自动配置不满足需求,开发者可以编写自己的
@Configuration
类,并使用@Bean
注解来定义和配置自己的bean。这些自定义配置将覆盖自动配置的相同bean。
五、调试和禁用自动配置
- debug属性:在
application.properties
或application.yml
文件中设置debug=true
可以启用自动配置的调试日志,以帮助开发者了解哪些自动配置被应用以及为什么。 - exclude属性:使用
@SpringBootApplication
或@EnableAutoConfiguration
注解的exclude
属性可以排除特定的自动配置类。这允许开发者禁用不需要的自动配置。
六、从源码分析springboot自动配置原理
下图为springboot项目的启动类;启动类上面使用了@SpringBootApplication注解
进入该注解查看
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
*/
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
可以看出@SpringBootApplication
注解由以下注解实现
下面对以上注解进行介绍:
- @Target(ElementType.TYPE):
- 这是一个元注解,用于指定它所修饰的注解(在本例中是
@SpringBootApplication
)可以应用于哪些Java元素。ElementType.TYPE
表示这个注解只能应用于类、接口(包括注解类型)或枚举声明。
- 这是一个元注解,用于指定它所修饰的注解(在本例中是
- @Retention(RetentionPolicy.RUNTIME):
- 这也是一个元注解,用于指定它所修饰的注解(
@SpringBootApplication
)的生命周期。RetentionPolicy.RUNTIME
表示这个注解不仅被保留在编译后的class文件中,而且在运行时可以通过反射获取到。
- 这也是一个元注解,用于指定它所修饰的注解(
- @Documented:
- 这个注解表示被它修饰的注解(
@SpringBootApplication
)应该被Javadoc和类似的工具记录。换句话说,如果你在类上使用了@SpringBootApplication
注解,并且为该类生成了Javadoc文档,那么这个注解也会出现在Javadoc文档中。
- 这个注解表示被它修饰的注解(
- @Inherited:
- 表示如果一个类使用了这个注解(
@SpringBootApplication
),那么它的子类会自动继承这个注解,除非子类明确地使用@SpringBootApplication(false)
来关闭继承。然而,在@SpringBootApplication
这个场景下,由于它通常用于主应用类,并且这个类通常不会被子类继承,所以@Inherited
在这里的实际作用并不明显。
- 表示如果一个类使用了这个注解(
- @SpringBootConfiguration:
- 表明该类是一个Spring Boot的配置类,即它使用Java配置的方式来替代传统的XML配置文件。在Spring Boot中,这个注解其实就是一个
@Configuration
注解的特化版本,专门用于Spring Boot应用。
- 表明该类是一个Spring Boot的配置类,即它使用Java配置的方式来替代传统的XML配置文件。在Spring Boot中,这个注解其实就是一个
- @EnableAutoConfiguration:
- 这是Spring Boot的核心注解之一。它告诉Spring Boot根据添加的jar包依赖(即Starters)来自动配置你的应用。例如,如果你添加了
spring-boot-starter-web
依赖,Spring Boot会自动配置嵌入式Tomcat、Spring MVC等。此外,你还可以通过spring.factories
文件和@SpringBootApplication
的exclude
属性来定制或关闭某些自动配置。
- 这是Spring Boot的核心注解之一。它告诉Spring Boot根据添加的jar包依赖(即Starters)来自动配置你的应用。例如,如果你添加了
- @ComponentScan(excludeFilters = {...}):
-
告诉Spring Boot在哪些包中查找被
@Component
、@Service
、@Repository
、@Controller
等注解标记的类,并把它们注册为Spring Bean。excludeFilters
属性用于指定要排除的类或包。在这个例子中,TypeExcludeFilter
和AutoConfigurationExcludeFilter
是自定义的过滤器,用于在组件扫描时排除特定的类或包。 -
TypeExcludeFilter
通常用于排除某些特定类型的组件(如某些接口的实现类)。 -
AutoConfigurationExcludeFilter
则是Spring Boot特有的过滤器,用于排除Spring Boot的自动配置类。这是通过spring.autoconfigure.exclude
属性或在@SpringBootApplication
注解中指定的排除类来实现的。
-
上述注解中,前四个都是元注解,与自动配置无关。@ComponentScan
注解用于包扫描,也与自动配置无关;@SpringBootConfiguration
注解用于声明是一个配置类,也与springboot的自动配置无关。所以与springboot自动配置相关的注解就是@EnableAutoConfiguration
了。其实从注解的名称也可以看出来,@EnableAutoConfiguration
注解是用于启用自动配置的注解。点进@EnableAutoConfiguration
查看该注解。
前面四个注解为元注解,不用看,下面将先讲解@AutoConfigurationPackage
注解和@Import注解的使用。
@AutoConfigurationPackage
注解在Spring Boot中扮演着重要的角色,其作用主要可以归纳为以下几点:
- 自动配置包:
@AutoConfigurationPackage
注解的主要作用是将主程序类(通常是带有@SpringBootApplication
注解的类)所在的包及所有子包下的组件扫描到Spring容器中。这意味着这些包下的类如果使用了如@Service
、@Controller
、@Repository
等注解,它们将会被自动注册为Spring的Bean。- 作为组合注解的一部分:
@AutoConfigurationPackage
注解通常不是直接使用的,而是作为@SpringBootApplication
注解的一部分。具体来说,@SpringBootApplication
注解包含了@EnableAutoConfiguration
注解,而@EnableAutoConfiguration
又进一步包含了@AutoConfigurationPackage
。因此,当开发者在Spring Boot的主类上添加@SpringBootApplication
注解时,就间接地启用了@AutoConfigurationPackage
的功能。- 包路径的确定:
- Spring Boot会自动确定
@SpringBootApplication
(或带有@EnableAutoConfiguration
的类)所在的包路径,并将其作为自动配置的基准包。然后,这个基准包及其所有子包下的组件都会被扫描并注册到Spring容器中。- 附加其他路径:
- 虽然
@AutoConfigurationPackage
默认扫描主程序类所在的包及所有子包,但开发者也可以通过额外的配置来指定其他的包路径。不过,这通常不是通过直接修改@AutoConfigurationPackage
来实现的,而是通过其他方式,如@ComponentScan
注解或Spring Boot的配置文件。- 与Spring Boot的自动配置机制相结合:
@AutoConfigurationPackage
与Spring Boot的自动配置机制紧密结合。当Spring Boot启动时,它会根据添加的依赖和其他配置信息来自动配置应用程序。这个过程中,@AutoConfigurationPackage
确保了需要的组件都能够被Spring容器正确地管理和使用。
@AutoConfigurationPackage
注解的主要作用是将主程序类所在的包及所有子包下的组件扫描到Spring容器中,也与自动配置关系不大。下面我们了解一下。
@Import注解在Spring框架中主要用于将指定的类、配置类或组件导入到Spring的Inversion of Control(IOC)容器中,以实现这些类、配置类或组件的自动配置和管理。其作用具体可以归纳为以下几点:
- 导入配置类或组件:
- @Import注解允许开发者直接导入一个或多个配置类(带有@Configuration注解的类)或组件类到Spring IOC容器中。这样,这些类中的Bean定义或组件就能被Spring容器管理和自动装配到其他Bean中。
- 支持多种导入方式:
@Import注解支持多种导入方式,包括直接导入类、通过实现ImportSelector接口动态选择类进行导入,以及通过实现ImportBeanDefinitionRegistrar接口自定义Bean的注册过程。
直接导入类:使用@Import的value属性,传入一个或多个Class<?>类型的数组,Spring将自动将这些类作为Bean注册到IOC容器中。
使用ImportSelector:开发者可以实现ImportSelector接口,并重写selectImports方法,根据需求动态返回需要导入的类的全类名数组。
使用ImportBeanDefinitionRegistrar:开发者可以实现ImportBeanDefinitionRegistrar接口,并重写registerBeanDefinitions方法,以自定义Bean的注册逻辑。
- 支持导入普通类:
- 在Spring 4.2及之后的版本中,@Import注解还支持导入普通的Java类(非配置类)。这些普通类在导入后也会被Spring容器管理,并可以通过@Autowired等方式进行自动装配。
- 与@Configuration结合使用:
- @Import注解经常与@Configuration注解结合使用,用于在配置类中导入其他配置类或组件。这使得配置类能够模块化地组织和管理Bean定义,提高代码的可读性和可维护性。
- 自定义Bean名称:
- 当使用ImportBeanDefinitionRegistrar方式导入类时,开发者可以自定义Bean在容器中的名称。这对于需要控制Bean名称的场景非常有用。
- 与Spring Boot自动配置结合:
- 在Spring Boot中,@Import注解与自动配置机制结合紧密。许多自动配置类都通过@Import注解来导入其他配置类或组件,以实现自动配置的功能
@Import注解允许开发者直接导入一个或多个配置类或组件类到Spring IOC容器中。所以我们查看一下导入的类AutoConfigurationImportSelector.class
该类实现了DeferredImportSelector接口,而DeferredImportSelector又是ImportSelector的子接口
ImportSelector接口:里面有一个String[] selectImports(AnnotationMetadata var1);方法
所以 AutoConfigurationImportSelector类必然也实现了这个方法
selectImports
方法允许开发者根据自定义的逻辑来动态选择配置类。它返回一个字符串数组,数组中的每个元素都是配置类的全限定类名。
该类返回StringUtils.toStringArray(autoConfigurationEntry.getConfigurations())
所以我们查看autoConfigurationEntry是如何获取的,即查看getAutoConfigurationEntry方法
该方法返回new AutoConfigurationEntry(configurations, exclusions);
所以我们查看configurations是如何来的,即查看getCandidateConfigurations方法
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.
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.");
翻译这段英文
在META-INF/spring.factores中找不到自动配置类。如果你
正在使用自定义包装,请确保该文件正确无误。
所以 META-INF/spring.factores里面装的就是需要springboot自动配置的类
我们找一个依赖查看下面有没有META-INF/spring.factores这个文件,这个文件里面的内容是类的全限定名。
随便找一个类点进去,这个类是一个配置类,里面有方法上面使用@Bean注解,使用了@Bean注解,方法的返回值就会交给Spring IOC管理
提问:META-INF/spring.factores文件里面所有的类都交给Spring IOC了吗
答案:不是
比如上图中和Bean一起使用的还有注解@ConditionalOnMissingBean,需要满足条件才会交给Spring管理。
- 当在配置类或组件类上标注了@ConditionalOnMissingBean注解时,Spring Boot会检查容器中是否已存在指定类型的Bean。
- 如果不存在,则会根据@ConditionalOnMissingBean的配置来决定是否创建一个新的Bean实例并加入到容器中。