您现在的位置: 首页 > 网站导航收录 > 百科知识百科知识
怎样系统学习Spring boot?
容器,注解,加载怎样系统学习Spring boot?
发布时间:2020-12-06加入收藏来源:互联网点击:
使用 @Value 注解注入的属性通常都比较简单,如果同一个配置在多个地方使用,也存在不方便维护的问题(考虑下,如果有几十个地方在使用某个配置,而现在你想改下名字,你改怎么做?)。对于更为复杂的配置,Spring Boot 提供了更优雅的实现方式,那就是 @ConfigurationProperties 注解。我们可以通过下面的方式来改写上面的代码:
@ConfigurationProperties 对于更为复杂的配置,处理起来也是得心应手,比如有如下配置文件:
可以定义如下配置类来接收这些属性
@EnableConfigurationProperties 注解表示对 @ConfigurationProperties 的内嵌支持,默认会将对应 Properties Class 作为 bean 注入的 IOC 容器中,即在相应的 Properties 类上不用加 @Component 注解。
三、削铁如泥:SpringFactoriesLoader 详解
JVM 提供了 3 种类加载器: BootstrapClassLoader、 ExtClassLoader、 AppClassLoader 分别加载 Java 核心类库、扩展类库以及应用的类路径( CLASSPATH)下的类库。JVM通过双亲委派模型进行类的加载,我们也可以通过继承 java.lang.classloader 实现自己的类加载器。
何为双亲委派模型?当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的 BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。
采用双亲委派模型的一个好处是保证使用不同类加载器最终得到的都是同一个对象,这样就可以保证 Java 核心库的类型安全。查看 ClassLoader 的源码,对双亲委派模型会有更直观的认识:
但双亲委派模型并不能解决所有的类加载器问题,比如,Java 提供了很多服务提供者接口,允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JNDI、JAXP 等,这些 SPI 的接口由核心类库提供,却由第三方实现,这样就存在一个问题:
SPI 的接口是 Java 核心库的一部分,是由 BootstrapClassLoader 加载的;SPI 实现的 Java 类一般是由 AppClassLoader 来加载的。BootstrapClassLoader 是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给 AppClassLoader,因为它是最顶层的类加载器。也就是说,双亲委派模型并不能解决这个问题。
线程上下文类加载器( ContextClassLoader)正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是 AppClassLoader。在核心类库使用 SPI 接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。
线程上下文类加载器在很多 SPI 的实现中都会用到。但在 JDBC 中,你可能会看到一种更直接的实现方式,比如,JDBC 驱动管理 java.sql.Driver 中的 loadInitialDrivers()方法中,你可以直接看到 JDK 是如何加载驱动的:
其实讲解线程上下文类加载器,最主要是让大家在看到 Thread.currentThread().getClassLoader()和 Thread.currentThread().getContextClassLoader()时不会一脸懵逼,这两者除了在许多底层框架中取得的 ClassLoader 可能会有所不同外,其他大多数业务场景下都是一样的,大家只要知道它是为了解决什么问题而存在的即可。
类加载器除了加载 class 外,还有一个非常重要功能,就是加载资源,它可以从jar包中读取任何资源文件,比如, ClassLoader.getResources(Stringname)方法就是用于读取 jar 包中的资源文件,其代码如下:
是不是觉得有点眼熟,不错,它的逻辑其实跟类加载的逻辑是一样的,首先判断父类加载器是否为空,不为空则委托父类加载器执行资源查找任务,直到 BootstrapClassLoader,最后才轮到自己查找。而不同的类加载器负责扫描不同路径下的 jar 包,就如同加载 class 一样,最后会扫描所有的 jar 包,找到符合条件的资源文件。
类加载器的 findResources(name)方法会遍历其负责加载的所有 jar 包,找到 jar 包中名称为 name 的资源文件,这里的资源可以是任何文件,甚至是 .clas 文件,比如下面的示例,用于查找 Array.class 文件:
运行后可以得到如下结果:
根据资源文件的 URL,可以构造相应的文件来读取资源内容。
看到这里,你可能会感到挺奇怪的,你不是要详解 SpringFactoriesLoader 吗?上来讲了一堆 ClassLoader 是几个意思?看下它的源码你就知道了:
有了前面关于 ClassLoader 的知识,再来理解这段代码,是不是感觉豁然开朗:从 CLASSPATH 下的每个 Jar 包中搜寻所有 META-INF/spring.factories 配置文件,然后将解析 properties 文件,找到指定名称的配置后返回。
需要注意的是,其实这里不仅仅是会去 ClassPath 路径下查找,会扫描所有路径下的 Jar 包,只不过这个文件只会在 Classpath 下的 jar 包中。来简单看下 spring.factories 文件的内容吧:
执行 loadFactoryNames(EnableAutoConfiguration.class,classLoader)后,得到对应的一组 @Configuration 类,我们就可以通过反射实例化这些类然后注入到 IOC 容器中,最后容器里就有了一系列标注了 @Configuration 的 JavaConfig 形式的配置类。
这就是 Spring Boot 启动流程的上半部分,其核心就是在 Spring 容器初始化并启动的基础上加入各种扩展点,这些扩展点包括:ApplicationContextInitializer、ApplicationListener 以及各种 BeanFactoryPostProcessor 等。
四、另一件武器:Spring 容器的事件监听机制
过去,事件监听机制多用于图形界面编程,比如:点击按钮、在文本框输入内容等操作被称为事件,而当事件触发时,应用程序作出一定的响应则表示应用监听了这个事件,而在服务器端,事件的监听机制更多的用于异步通知以及监控和异常处理。
Java提供了实现事件监听机制的两个基础类:自定义事件类型扩展自 java.util.EventObject、事件的监听器扩展自 java.util.EventListener。
五、启动引导:Spring Boot 应用启动的秘密
5.1 SpringApplication 初始化
SpringBoot 整个启动流程分为两个步骤:初始化一个 SpringApplication 对象、执行该对象的 run 方法。看下 SpringApplication 的初始化流程, SpringApplication 的构造方法中调用 initialize(Object[] sources)方法,其代码如下:
5.2 Spring Boot 启动流程
Spring Boot 应用的整个启动流程都封装在 SpringApplication.run 方法中,其整个流程真的是太长太长了,但本质上就是在 Spring 容器启动的基础上做了大量的扩展,按照这个思路来看看源码:
这就是 Spring Boot 的整个启动流程,其核心就是在 Spring 容器初始化并启动的基础上加入各种扩展点
回答于 2019-09-11 08:43:50
我假设你已经了解Spring framework。觉得第一需要了解它为什么自动加载你需要东西,比如你加spring boot starter web在dependance里面,它就知道启动tomcat或jetty。你可以spring boot starter的jar里面找到很多AutoConfiguration的类,我觉得它是实现这个机制的关键。
上一篇:特朗普称沙特失踪记者“看上去确实”已经遇害,美国将准备制裁沙特吗?
下一篇:返回列表
相关链接 |
||
网友回复(共有 0 条回复) |