文章目录
1.SpringBoot 简介
简化Spring应用开发的框架,是整个Spring技术栈的一个大整合,J2EE开发的一站式解决方案
2.从简单的Hello Word入手
新建一个SpringBoot有两种方法:
-
Spring官网教程,新建一个HelloWorld的SpringBoot项目
-
新建一个Maven项目,pom.xml如下
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- 添加插件,可以打包成一个可执行的jar包 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
代码片段如下
/**
* @SpringBootApplication 标注为SpringBoot应用
*/
@SpringBootApplication
public class PracticeApplication {
public static void main(String[] args) {
//SpringBoot应用启动起来
SpringApplication.run(PracticeApplication.class, args);
}
}
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}
直接从main方法启动,访问localhost:8080/hello,页面显示Hello World
3.Hello World 探究
此处以spring-boot 2.1.8.RELEASE为例
1.pom.xml
1.1 父项目:
从pom.xml看到项目的父依赖为 spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
进入spring-boot-starter-parent,可以看到还有一层父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
进入此POM可以看到,该文件中的属性配置了整个项目的所依赖的jar包和版本控制,可以将其视为整个项目的版本仲裁中心。
1.2启动器
在pom.xml父项目下面还可以看到关于web的启动器相关,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
点进去可以看到有关Web开发所必备的jar包依赖,如
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
<scope>compile</scope>
</dependency>
在Spring官网上我们还可以看到很多的启动器,点进去都会看到模块正常运行所需要的jar包依赖。
这样在我们需要某个模块的时候就可以直接导入该模块的stater依赖即可。
2.@SpringBootApplication注解
@SpringBootApplication标注在一个类上,表明该类为Spring Boot的主配置类,此注解为一个组合注解:
@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
2.1@SpringBootConfiguration
表明该类为一个Spring Boot的配置类,底层还是Spring注解@Configuration–表明该类为一个配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//表示配置类也是容器中的一个组件
public @interface Configuration
2.2@EnableAutoConfiguration
启用自动配置,@EnableAutoConfiguration为一个组合注解,源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
2.2.1 @AutoConfigurationPackage 自动配置包
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)//导入组件(用法见Spring注解开发(一)组件注册)
public @interface AutoConfigurationPackage
- AutoConfigurationPackages.Registrar实现了ImportBeanDefinitionRegistrar接口,
实现ImportBeanDefinitionRegistrar接口,我们可以为容器注册组件(Spring注解开发(一)组件注册简单提过这个) - 实现了DeferredImportSelector:与ImportSelector类似,区别在于处理操作被延迟到所有其他配置项都处理完毕再进行。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports
在AutoConfigurationPackages.Registrar类的registerBeanDefinitions方法上加上断点:
进入debug模式,调用链如下图:
可以看到在refresh容器的时候会调用该方法的registerBeanDefinitions方法。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
————————————>
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
此处从metadata中获取的PackageName即为SpringBoot启动类所在的包名,该方法将此包下的所有组件都注册到容器中。
并注册一个name为org.springframework.boot.autoconfigure.AutoConfigurationPackages的bean定义信息,并将包名称设置到bean定义中去
- @Import注解,给容器中导入一个组件。这个注解帮助我们将多个配置文件(可能是按功能分,或是按业务分)导入到单个主配置中,以避免将所有配置写在一个配置中。
2.2.2 导入组件AutoConfigurationImportSelector
- 先看一下SpringApplication创建时的源码:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断是否是Web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//由此处进入下面的方法,并读取"META-INF/spring.factories"中的配置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在SpringFactoriesLoader的loadSpringFactories方法上打上断点
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
上面第十行代码处,读取spring-boot-autoconfigure jar包下的/META-INF/spring-factories路径,并读取其中的K-V,保存到properties中。
具体如下图(此处主要关注EnableAutoConfiguration):
也可以看下spring-boot-autoconfigure jar包下的/META-INF/spring-factories文件:
最终将其保存在了SpringFactoriesLoader中的cache属性中。
- 再看下Import导入的工作
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
AutoConfigurationImportSelector 实现了DeferredImportSelector,DeferredImportSelector是延期导入,当所有的@Configuration都处理过后才会执行。
该类会在refresh容器的时候执行ConfigurationClassParser的parse方法(见Spring注解开发之@Configuration解析),最终会到
AutoConfigurationImportSelecto的process方法
断点图如下:
在下一步的getAutoConfigurationEntry方法中看到:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//调用到了上一点的在SpringFactoriesLoader中的cache属性,获取了EnableAutoConfiguration对应的117个值
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
经过过滤后返回包装的自动配置类,放入了AutoConfigurationImportSelector $ AutoConfigurationGroup的autoConfigurationEntries属性中。
最后在ConfigurationClassParser $ DeferredImportSelectorGroupingHandler类的processGroupImports方法中将集合中的组件导入到容器中。