springboot笔记-1.自动化配置的关键
最近发现看过的东西容易忘,但是写一遍之后印象倒是会深刻的多。
总所周知springboot极大的简化了java开发繁琐性,而其最大的优势应该就是自动化配置了。比如要使用redis,我们直接引入相关的包,将redis连接信息配置下即可使用。本文主要分析下springboot自动化配置的关键。
本文分析核心过程的代码,无关性的代码略过。
1.注解与组合注解
再说自动化配置前肯定要先说下注解,java5开始引入了注解这么个东西,可以对类,成员方法,成员变量加上标记,而这些标记可以在类加载,编译运行时被读取,基本可以做到无侵入性。同时java也支持组合注解,就是注解之上可以再添加其他注解,例如我们使用springmvc框架时,使用@RestController,即可代替@Controller以及@ResponseBody注解。是因为@RestController就是一个组合注解。其已经组合了两种注解。注解和组合注解在springboot的自动化配置中起了非常大的作用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController { @AliasFor(annotation = Controller.class)
String value() default ""; }
2.@SpringBootApplication
相信用springboot开发的同学对这个注解不会陌生,其是springboot开发的入口注解。可以看下这个注解的内容
@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 {}; }
我们可以看到其组合了很多注解,而这些注解当然又加载了很多注解,下面是这个注解的注解组合。

可以看到一个@SpringBootApplication注解后有很多组合的注解。而这些注解在springboot进行自动化配置的时候起了很大的作用。在后面用到的时候这些注解的作用会一一说明
3.从main方法开始
相信大多数人的springboot项目都是从如下的函数代码开始,那我们就从这个代码开始入手
@SpringBootApplication
public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args);
}
}
跟着代码一路走,会发现其主要分为两步。创建SpringApplication对象,和执行run方法。这儿两步我们分别看
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
4.从创建SpringApplication看spring如何完成自动化可拔插配置
这是SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
...//省略代码
//设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
...//省略代码
}
我们看setInitializers方法,即查找并设置初始化工具接口ApplicationContextInitializer.class的所有实现类。 我们可以看下spring的查找方法即getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//当前的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//1.获取满足条件的类的全限定名
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//2.根据全限定名进行反射生成实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances; }
上面的方法主要由两步,第一步是获取系统配置的全限定名,第二步是根据全限定名反射生成实例,这儿我们主要看第一步。接着往下看
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//缓存 防止重复加载
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//1.找到所有 META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories"));
result = new LinkedMultiValueMap<>();
//2.对spring.factories进行迭代
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//3.解析每个spring.factories 并将内容存入result
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
//这儿result中存的则是spring.factories中的东西
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
//存入缓存
cache.put(classLoader, result);
return result;
}
}
其实这个方法可以说就揭示了springboot进行自动化配置的一个关键,其会搜寻所有的META-INF/spring.factories。然后将信息以key,values的形式存起来。我们可以看下这种文件长什么样,这儿我们看spring-boot-autoconfigure包下spring.factories文件。由于文件太长我截取部分。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition # Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
.....................//省略
这儿可以看到我们之前要查找的ApplicationContextInitializer,在这儿就有对应的两个SharedMetadataReaderFactoryContextInitializer与ConditionEvaluationReportLoggingListener。当然这儿还有很多其他类型的配置类就不一一说明了。springboot会将这些spring.factories文件一一读取,并将其中的信息存储到系统中的cache。既然已经知道了类的全限定名,那我们如果需要加载的话直接反射生成实例即可。我们常见的redis自动配置,或者mongoDB自动配置,在该文件中都可以找到
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...//省略
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
相信到了这儿对springboot如何做到自动化配置肯定已经有了一个初步的认识了。springboot提供了一个插件的方式来供第三方主动集成。按照其规则只要我们写出对应的文件内容并放在项目的META-INF/spring.factories中。在springboot项目中引入该依赖,spring就会去自动读取我们的配置。这儿可以举个简单的例子,比如说我们想要实现一个插件,加入了我们这个插件的依赖后springmvc中json转换由jackson换为fastjson(springmvc默认使用jackson)。那我们就可以创建项目。并创建如下类
@Configuration
@AutoConfigureBefore({WebMvcAutoConfiguration.class})
public class WebMvcConfig {
public WebMvcConfig() {
} @Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = fastJsonHttpMessageConverter.getFastJsonConfig();
SerializeFilter[] serializeFilters = fastJsonConfig.getSerializeFilters();
fastJsonConfig.setSerializerFeatures(new SerializerFeature[]{SerializerFeature.PrettyFormat});
List<MediaType> fastMediaTypes = new ArrayList();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
return fastJsonHttpMessageConverter;
} @Bean
public HttpMessageConverters customConverters(FastJsonHttpMessageConverter fastJsonHttpMessageConverter) {
return new HttpMessageConverters(new HttpMessageConverter[]{fastJsonHttpMessageConverter});
} }
然后在项目的resouce下新建META-INF/spring.factories,并加入上面的配置类信息
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.test.framework.web.mvc.config.WebMvcConfig
这样我们的springboot项目如果引入了这个插件并且引入了fastjson相关的依赖。那么系统就会自动进行替换。想下是不是基本做到了无代码侵入,可拔插。这也就是springboot自动发现并配置的关键。
这个时候当然会有人有疑惑,那我如果引入了这个插件,但是却没引入fastjson的包。那系统不是就会启动报错了吗。能不能做到,我引入了插件所依赖的包才让他生效,否则就不生效呢? spring当然考虑到了这点儿,当然这是后面在分析spring进行配置类解析的时候会讲到的。
到此其实我们就迈出了spring自动发现配置的关键了,当然了,springboot加载自动配置的原理就是如此但时机并不在这儿,这个后面的文章会讲到。
springboot笔记-1.自动化配置的关键的更多相关文章
- springboot笔记-2-.核心的上下文以及配置扫描解析(上)
前言 上一节中说明了springboot是如何做到自动发现配置的,那么本节看下spring如何创建上下文并解析这些配置,加载我们注册到容器管理中的类.上节已经成功的创建了SpringApplicati ...
- 【使用篇二】Quartz自动化配置集成(17)
出处:https://www.jianshu.com/p/49133c107143 定时任务在企业项目比较常用到,几乎所有的项目都会牵扯该功能模块,定时任务一般会处理指定时间点执行某一些业务逻辑.间隔 ...
- SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition
自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...
- SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理
springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...
- 让SpringBoot自动化配置不再神秘
本文若有任何纰漏.错误,还请不吝指出! 注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory.关于BeanFactory, ...
- SpringBoot多重属性文件配置方案笔记
SpringBoot多重属性文件配置方案笔记 需要重写PropertyPlaceholderConfigurer 同时要忽略DataSourceAutoConfiguration @SpringBoo ...
- Spring Boot 探索系列 - 自动化配置篇
26. Logging Prev Part IV. Spring Boot features Next 26. Logging Spring Boot uses Commons Logging f ...
- Linux实战教学笔记25:自动化运维工具之ansible (一)
第二十五节 ansible之文件的批量分发 标签(空格分隔): Linux实战教学笔记-陈思齐 ---本教学笔记是本人学习和工作生涯中的摘记整理而成,此为初稿(尚有诸多不完善之处),为原创作品,允许转 ...
- springboot情操陶冶-web配置(八)
本文关注应用的安全方面,涉及校验以及授权方面,以springboot自带的security板块作为讲解的内容 实例 建议用户可直接路由至博主的先前博客spring security整合cas方案.本文 ...
随机推荐
- flask 路由规划(blueprint)
# 统一路由蓝牙规划 # file:blueprint_route.py from flask import Blueprint route_test = Blueprint("home&q ...
- ubuntu 安装谷歌
下载ubuntugoogle官网:http://www.ubuntuchrome.com/ 我的是ubuntuLTS稳定版: 上传Ubuntu: 执行解压操作: sudo dpkg -i goo ...
- codeforces 1285E. Delete a Segment
链接:https://codeforces.com/problemset/problem/1285/E 题意:给一个数轴上有n个线段集,线段集若有相交,则合并为一个新的合并线段集,比如[1,6]和[2 ...
- mybatis-plus - 初识
一. 集成 pom.xml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid& ...
- Python + Selenium +Chrome 批量下载网页代码修改【新手必学】
Python + Selenium +Chrome 批量下载网页代码修改主要修改以下代码可以调用 本地的 user-agent.txt 和 cookie.txt来达到在登陆状态下 批量打开并下载网页, ...
- Css3 里的弹性盒的比例关系
前两天朋友出去面试遇到了尴尬的问题,原题是:"在一个盒子里包裹着三个子元素,让子元素的宽度以1:1.1:2.1:3的关系依次展示" 这就尴尬了啊.................. ...
- 每天进步一点点------ORCAD Capture CIS 快捷键
ORCAD Capture CIS 快捷键 I: 放大O: 缩小C: 以光标所指为新的窗口显示中心 W: 画线On/OffP: 快速放置元件R: 元件旋转90°H: 元件 ...
- 【网易官方】极客战记(codecombat)攻略-地牢-囚犯
关卡连接: https://codecombat.163.com/play/level/the-prisoner 解放囚犯,你会得到盟友. 简介 敬请期待! 默认代码 # 释放囚犯,击败守卫并夺取宝石 ...
- sql server2008用ip远程连接
sql server2008用ip远程连接 转载 weixin_34167819 发布于2017-09-14 15:23:00 阅读数 84 收藏 展开 1,2005的外围应用配置器在2008中换了地 ...
- 整体单改,单局部改,整体局部改,ListSerializer类
复习 """ 1.ModelSerializer序列化类 models.py class BaseModel(models.Model): is_delete = mod ...