springboot原理代码图解剖析
1 springboot官方文档网址
https://docs.spring.io/spring-boot/docs/1.5.8.RELEASE/reference/htmlsingle/
2 POM文件
2.1 版本依赖的管理(版本仲裁中心)
parent导入
parent的父项目
dependencies项目管理依赖
2.2 依赖的导入(以web模块为例)
pom.xml
spring-boot-starter:场景启动器,帮我们导入了模块的相关依赖
3 自动配置
3.1 启动类
@SpringBootApplication
public class SpringBootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestApplication.class, args);
}
}
3.2 @SpringBootApplication注解
此注解标注在类上表明此类是SpringBoot的主配置类,SpringBoot应用就应该运行此类的main方法启动应用
@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 {}
3.3 @SpringBootConfiguration注解
标注在类上,表明此类是SpringBoot的配置类,此注解也是一个组合注解,靠的是@Configuration起作用,此注解是Spring的底层注解,标明这是一个配置类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
其实@Configuration就是容器中的一个组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
3.4 @EnableAutoConfiguration 注解
开启自动配置
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
3.4.1 @AutoConfigurationPackage注解
此注解将主配置类(@SpringBootApplication标注的类)所在包同级目录及所有子包里面所有的组件扫描到Spring容器中
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
@Import注解,给容器中导入一个组件
Registrar类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, new String[]{(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()});
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
拿到包名:
new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()
3.4.2 @Import({EnableAutoConfigurationImportSelector.class})注解
给容器中导入选择器(EnableAutoConfigurationImportSelector)组件
选择器将所有需要的组件以全类名的方式返回,这些组件会被添加到容器中
给容器中导入自动配置类(xxxAutoConfiguration),这些配置类会给容器中导入这个场景所需要的组件,并为之配置好
配置类在哪来的?
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
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.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
protected ClassLoader getBeanClassLoader() {
return this.beanClassLoader; //类加载器
}
loadFactoryNames方法
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(ex.hasMoreElements()) {
URL url = (URL)ex.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
此方法从类路径下META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,使其自动配置生效
所有的配置都在spring-boot-autoconfigure-1.5.8.RELEASE.jar包中
4 自动配置原理
4.1 yml配置文件可配属性
4.2 原理
1.SpringBoot启动加载主配置类,开启了自动配置功能@EnableAutoConfiguration
[email protected]
- 使用EnableAutoConfigurationImportSelector给容器中导入组件
- selectImports方法起作用
- loadFactoryNames方法加载配置文件
扫描所有jar包类路径下META-INF/spring.factories配置文件,封装成properties对 象,并从中获取EnableAutoConfiguration.class类名对应的属性值(即每一个自动配 置类的全路径名称),把他们添加到容器中
3.以HttpEncodingAutoConfiguration为例解释自动配置
@Configuration
@EnableConfigurationProperties({HttpEncodingProperties.class})
@ConditionalOnWebApplication
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
}
- @Configuration:表明是配置类,给容器中添加组件
- EnableConfigurationProperties:启用指定类的ConfigurationProperties功能
@ConfigurationProperties(prefix = "spring.http.encoding")
//从配置文件中获取指定前缀的属性值和bean中对应的值进行绑定,配置文件中写什么配置可参考xxxProperties配置文件
public class HttpEncodingProperties {
}
- @ConditionalOnWebApplication
@Conditional({OnWebApplicationCondition.class}) //这是Spring注解
public @interface ConditionalOnWebApplication {
}
此注解根据不同的条件,若满足此条件,此注解的bean配置才会生效
这个注解作用是判断当前应用是否是web应用
- @ConditionalOnClass:判断当前项目有没有这个类
CharacterEncodingFilter是SpringMVC中解决乱码的过滤器 - @ConditionalOnProperty:判断配置文件中是否存在某个配置
配置名称为prefix+value=spring.http.encoding.enabled
matchIfMissing=即使不存在此配置,默认此配置也是生效的
4.3 总结
- SpringBoot启动会加载大量的自动配置类
- 我们需要的功能SpringBoot有没有帮我们配置好
- 看自动配置类中配置了哪些组件,只要组件有,就不需要我们再配置了
- 给容器中的自动配置类添加组件的时候,会从对应的properties对象中获取属性,我们在配置文件中配置这些属性即可
- 记住两个重要的约定吧:
– xxxAutoConfiguration:自动配置类注解,将配置添加到Spring容器中
– xxxProperties:配置类对应的属性封装类
5 SpringMVC的自动配置
5.1 官方文档中有这样一段话
5.2 定制视图解析器
@SpringBootApplication
public class SpringBootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestApplication.class, args);
}
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
private class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
发送请求后查看
5.3 格式化器小结
示例:
@Bean
@ConditionalOnProperty(prefix = "spring.mvc",name = {"date-format"}) //有此配置才注册
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());
}
由下图得知,如若我们自己定制格式化器,只需要创建实现了Converter接口的bean,将其注册到容器中即可
5.4 扩展SpringMVC
编写一个配置类@Configuration,是WebMvcConfigurerAdapter类型,不能标注@EnableWebMvc注解,这是我们自己的配置和SpringBoot的自动配置联合生效
@Configuration
public class MyWebMvc extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/aaa").setViewName("error/404");
}
}
原理(示例)
5.5 接管SpringMvc
如果我们只使用我们自己的配置,不使用SpringBoot的自动配置,可将@EnableWebMvc注解添加到配置类上,使SpringMvc的自动配置失效,全面接管SpringMVC
@Configuration
@EnableWebMvc
public class MyWebMvc extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/aaa").setViewName("error/404");
}
}
原理
5.6 SpringBoot自动配置总结
- SpringBoot在自动配置组件的时候,先看容器中有没有用户自己的配置,若有则使用用户的配置,若没有才自动配置,还有一种情况就是将自动配置和用户配置合并
- SpringBoot里面有xxxConfigurer帮助我们进行扩展配置