背景:最近在研究Spring源码,那么当然要研究一下SpringBoot的自动配置的模块,那么SpringBoot的自动配置到底是怎么做的呢?
正文:SpringBoot的自动配置聊起来大家都并不陌生,都知道SpringBoot想对于SpringMvc来说是可以不用配置web.xml,application.xml
也就是说不用配置servlet的信息,不用配置一些引入的依赖的配置信息,那么这些其实都是依赖SpringBoot的自动配置,那么SpringBoot的
自动配置主要是依赖于DI,IOC就是Spring的容器原理以及注解的功能,那么重SpringBoot源码的入口开始看起,SpringBoot的入口当然就是
SpringBootApplication了,一个简单的代码如下:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
这是一个最简单的SpringBoot的入口,配置好最小的配置的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.cloud</groupId>
<artifactId>config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>config-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
后通过运行SpringApplication的run方法就可以启动一个SpringBoot的web程序,在国内大多的SpringBoot的程序都是为了作为web服务器,因此我们也分析作为web服务器的SpringBoot,因此SpringBoot的进程启动的入口就是对应的这个main方法,当然里面启动了Tomcat的容器,Tomcat也查找了SpringBoot的某个特定路径下的配置文件来制定启动配置类用来加载替代web.xml的配置,这里我们先来分析
@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 {
/**
* 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 {};
另外一个注解就是@EnableConfiguration
这个注解内容如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
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
*/
String[] excludeName() default {};
重点的内容是@Import注解
我们为了能继续的向下看先来学习一下注解@Import顾名思义引入类加载到SpringBean的容器里面去,这个注解主要的功能是,作用在类上,通过快速导入的方式把实例加入到Spring的IOC容器中去,@Bean的方式也可以,但是@Bean的注解可以用于导入第三方的包,@Impoet的注解快速导入方式更便捷,主要分为如下三种方法
- 直接填写class数组方式
- ImportSelector方式【重点】
- ImportBeanDefinitionRegistrar方式
第一种方法
@Import({ 类名.class , 类名.class... })
public class TestDemo {
}
第二种方法
引入一个ImportSelector的实现类
public class Myclass implements ImportSelector {
//既然是接口肯定要实现这个接口的方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[0];
}
}
第三种方法
public class Myclass2 implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//指定bean定义信息(包括bean的类型、作用域...)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
//注册一个bean指定bean名字(id)
beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
}
}
总结如下
第一种用法:@Import({ 要导入的容器中的组件 } ):容器会自动注册这个组件,id默认是全类名
第二种用法:ImportSelector:返回需要导入的组件的全类名数组,springboot底层用的特别多【重点 】
第三种用法:ImportBeanDefinitionRegistrar:手动注册bean到容器
好了 接下来我们要来进行自动配置的理解啦
了解自动配置
自动配置:
- 基于添加的JAR依赖自动对Spring Boot应用程序进行配置
- spring-boot-autoconfiguration
开启自动配置:
- @EnableAutoConfiguration
- exclude = Class<?>[]
- @SpringBootApplication
详细解读:
SpringBoot的自动配置的开启是需要配置@EnableAutoConfiguration的但是你如果细看自己的工程项目的时候会没有在Application.class启动类上面看到对应的代码处看到这个注解的,那是因为这个注解已经被@SpringBootApplication给引入了。
@SpringBootApplication这个注解的exclude这个参数可以主动的控制忽略一些配置类的自动加载,利用@AliasFor(annotation = EnableAutoConfiguration.class)来进行参数值传递
@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration这个注解的,会帮我们自动加载META-INF/spring.factories
自定义配置其实就三步
- 编写 Java Config
@Configuration
@ConditionalOnClass(GreetingApplicationRunner.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GreetingApplicationRunner.class)
@ConditionalOnProperty(name = "greeting.enabled", havingValue = "true", matchIfMissing = true)
public GreetingApplicationRunner greetingApplicationRunner() {
return new GreetingApplicationRunner();
}
}
@Slf4j
public class GreetingApplicationRunner implements ApplicationRunner {
public GreetingApplicationRunner() {
log.info("Initializing GreetingApplicationRunner.");
}
public GreetingApplicationRunner(String param) {
log.info("Initializing GreetingApplicationRunner." + param);
}
public void run(ApplicationArguments args) throws Exception {
log.info("Hello everyone! We all like Spring! ");
}
}
- 定位自动配置
处理在配置类的classpath下面的META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
geektime.spring.hello.greeting.GreetingAutoConfiguration