总览:
上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行、SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块。
及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要功能。
启动:
每个SpringBoot程序都有一个主入口,也就是main方法,main里面调用SpringApplication.run()启动整个spring-boot程序,该方法所在类需要使用@SpringBootApplication注解。
以及@ImportResource注解(if need),@SpringBootApplication包括三个注解,功能如下:@EnableAutoConfiguration:SpringBoot根据应用所声明的依赖来对Spring框架进行自动配置。
@SpringBootConfiguration(内部为@Configuration):被标注的类等于在spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境。
@ComponentScan:组件扫描,可自动发现和装配Bean,默认扫描SpringApplication的run方法里的Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下。
最近在使用Spring MVC过程中遇到了一些问题,网上搜索不少帖子后虽然找到了答案和解决方法,但这些答案大部分都只是给了结论,并没有说明具体原因,感觉总是有点不太满意。
更重要的是这些所谓的结论大多是抄来抄去,基本源自一家,真实性也有待考证。
那作为程序员怎么能知其所以然呢?
此处请大家内心默读三遍。
用过Spring 的人都知道其核心就是IOC和AOP,因此要想了解Spring机制就得先从这两点入手,本文主要通过对IOC部分的机制进行介绍。
在开始阅读之前,先准备好以下实验材料。
IDEA 是一个优秀的开发工具,如果还在用Eclipse的建议切换到此工具进行。
IDEA有很多的快捷键,在分析过程中建议大家多用Ctrl+Alt+B快捷键,可以快速定位到实现函数。
Spring bean的加载主要分为以下6步:
查看源码第一步是找到程序入口,再以入口为突破口,一步步进行源码跟踪。
Java Web应用中的入口就是web.xml。
在web.xml找到ContextLoaderListener ,此Listener负责初始化Spring IOC。
contextConfigLocation参数设置了bean定义文件地址。
下面是ContextLoaderListener的官方定义:
翻译过来ContextLoaderListener作用就是负责启动和关闭Spring root WebApplicationContext。
具体WebApplicationContext是什么?开始看源码。
从源码看出此Listener主要有两个函数,一个负责初始化WebApplicationContext,一个负责销毁。
继续看initWebApplicationContext函数。
在上面的代码中主要有两个功能:
进入CreateWebAPPlicationContext函数
进入determineContextClass函数。
进入configureAndReFreshWebApplicaitonContext函数。
WebApplication Context有很多实现类。 但从上面determineContextClass得知此处wac实际上是XmlWebApplicationContext类,因此进入XmlWebApplication类查看其继承的refresh()方法。
沿方法调用栈一层层看下去。
获取beanFactory。
beanFactory初始化。
加载bean。
读取XML配置文件。
XmlBeanDefinitionReader读取XML文件中的bean定义。
继续查看loadBeanDefinitons函数调用栈,进入到XmlBeanDefinitioReader类的loadBeanDefinitions方法。
最终将XML文件解析成Document文档对象。
上一步完成了XML文件的解析工作,接下来将XML中定义的bean注册到webApplicationContext,继续跟踪函数。
用BeanDefinitionDocumentReader对象来注册bean。
解析XML文档。
循环解析XML文档中的每个元素。
下面是默认命名空间的解析逻辑。
不明白Spring的命名空间的可以网上查一下,其实类似于package,用来区分变量来源,防止变量重名。
这里我们就不一一跟踪,以解析bean元素为例继续展开。
解析bean元素,最后把每个bean解析为一个包含bean所有信息的BeanDefinitionHolder对象。
接下来将解析到的bean注册到webApplicationContext中。接下继续跟踪registerBeanDefinition函数。
跟踪registerBeanDefinition函数,此函数将bean信息保存到到webApplicationContext的beanDefinitionMap变量中,该变量为map类型,保存Spring 容器中所有的bean定义。
Spring 实例化bean的时机有两个。
一个是容器启动时候,另一个是真正调用的时候。
相信用过Spring的同学们都知道以上概念,但是为什么呢?
继续从源码角度进行分析,回到之前XmlWebApplication的refresh()方法。
可以看到获得beanFactory后调用了 finishBeanFactoryInitialization()方法,继续跟踪此方法。
预先实例化单例类逻辑。
获取bean。
doGetBean中处理的逻辑很多,为了减少干扰,下面只显示了创建bean的函数调用栈。
创建bean。
判断哪种动态代理方式实例化bean。
不管哪种方式最终都是通过反射的形式完成了bean的实例化。
我们继续回到doGetBean函数,分析获取bean的逻辑。
上面方法中首先调用getSingleton(beanName)方法来获取单例bean,如果获取到则直接返回该bean。方法调用栈如下:
getSingleton方法先从singletonObjects属性中获取bean 对象,如果不为空则返回该对象,否则返回null。
那 singletonObjects保存的是什么?什么时候保存的呢?
回到doGetBean()函数继续分析。如果singletonObjects没有该bean的对象,进入到创建bean的逻辑。处理逻辑如下:
下面是判断容器中有没有注册bean的逻辑,此处beanDefinitionMap相信大家都不陌生,在注册bean的流程里已经说过所有的bean信息都会保存到该变量中。
如果该容器中已经注册过bean,继续往下走。先获取该bean的依赖bean,如果镩子依赖bean,则先递归获取相应的依赖bean。
依赖bean创建完成后,接下来就是创建自身bean实例了。
获取bean实例的处理逻辑有三种,即Singleton、Prototype、其它(request、session、global session),下面一一说明。
如果bean是单例模式,执行此逻辑。
获取单例bean,如果已经有该bean的对象直接返回。如果没有则创建单例bean对象,并添加到容器的singletonObjects Map中,以后直接从singletonObjects直接获取bean。
把新生成的单例bean加入到类型为MAP 的singletonObjects属性中,这也就是前面singletonObjects()方法中获取单例bean时从此Map中获取的原因。
Prototype是每次获取该bean时候都新建一个bean,因此逻辑比较简单,直接创建一个bean后返回。
从相应scope获取对象实例。
判断scope,获取实例函数逻辑。
在相应scope中设置实例函数逻辑。
以上就是Spring bean从无到有的整个逻辑。
从源码角度分析 bean的实例化流程到此基本接近尾声了。
回到开头的问题,ContextLoaderListener中初始化的WebApplicationContext到底是什么呢?
通过源码的分析我们知道WebApplicationContext负责了bean的创建、保存、获取。其实也就是我们平时所说的IOC容器,只不过名字表述不同而已。
本文主要是讲解了XML配置文件中bean的解析、注册、实例化。对于其它命名空间的解析还没有讲到,后续的文章中会一一介绍。
希望通过本文让大家在以后使用Spring的过程中有“一切尽在掌控之中”的感觉,而不仅仅是稀里糊涂的使用。
简单来讲,spring为java应用程序提供了全面基础支持
提供了一些依赖注入和开箱即用的模块,如下,这些模块减少了开发负担,提高了效率
使用Spring创建一个web application,依赖如下
Spring使用pring Test, JUnit, Hamcrest, and Mockito libraries都需要添加
举栗子:jsp 的web应用
Spring需要定义 dispatcher servlet, mappings 和其他 支持配置,一般是在 web.xml 文件或 Initializer 文件中配置
在配置文件中添加 @EnableWebMvc 注解
需要 thymeleaf-spring5 依赖以及配置view resolver
Spring需要 spring-security-web和spring-security-config依赖
Spring还需要继承WebSecurityConfigurerAdapter类,添加@EnableWebSecurity注解
Spring和SpringBoot中应用程序引导的基本区别在于servlet。Spring使用 Web.xml 或 SpringServletContainerInitiators 作为它的引导入口点。
Spring支持 Web.xml 引导方式以及最新的Servlet 3+方法。
Web.xml 分步骤处理:
1Servlet容器(服务器)读取 Web.xml
2这个 DispatcherServlet 中定义的 Web.xml 由容器实例化。
3DispatcherServlet 创造 WebApplicationContext 通过阅读 Web-INF/{servletName}-servlet.xml
4最后, DispatcherServlet 注册应用程序上下文中定义的bean。
以下是Spring引导程序如何使用Servlet 3+方法:
1容器搜索实现的类。 ServletContainerInitiators 并执行
2这个 SpringServletContainerInitiators 查找实现的所有类 WebApplicationInitiator
3这个 WebApplicationInitiator 使用xml或 @配置 班
4这个 WebApplicationInitiator 创建 DispatcherServlet 使用先前创建的上下文。
Spring和SpringBoot两个框架都支持像Maven和Gradle这样的通用包管理技术
Spring Boot Maven插件 在Maven中提供SpringBoot支持。它还允许打包可执行的JAR或WAR实现
在部署上下文中,SpringBoot相对于Spring的一些优点包括:
1提供嵌入式容器支持
2使用命令独立运行JAR的规定 爪哇-JAR
3选项以排除依赖项,以避免在外部容器中部署时可能发生的JAR冲突。
4选项可以在部署时指定活动配置文件。
5用于集成测试的随机端口生成
SpringBoot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的样板配置
SpringBoot构建了一个更快、更有效地开发生态系统
starter依赖简化了应用构建和配置
嵌入式服务器以避免应用程序部署的复杂性
度量指标、Helth检查和外部化配置
Spring功能的自动配置
使用Springboot创建web application的依赖如下
只需要一个starter 依赖,就把spring里面的testing libraries自动添加了
Spring提供了不同模块的常见starter如下
其他starter自定查看
地址:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter
举栗子:jsp 的web应用
Springboot省去了上面spring的配置,我只要添加 web starter 和如下配置即可
web starter 自动配置了spring 很多配置,如下
地址:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-auto-configuration
SpringBoot只需要 spring-boot-starter-thymeleaf ,就可以让web application支持 Thymeleaf
Thymeleaf3.0,新特性需要添加thymeleaf-layout-dialect 依赖
添加依赖后需要添加templates 到src/main/resources/templates文件下就会自动加载
SpringBoot只需要添加 spring-boot-starter-security 即可(这个依赖中包含 spring-security-web和spring-security-config )
SpringBoot只使用Servlet 3特性来引导应用程序
SpringBoot应用程序的入口点是用 @SpringBootApplication :
默认情况下,SpringBoot使用嵌入式容器运行应用程序。在本例中,SpringBoot使用 public static void main 启动嵌入式Web服务器的入口点。
此外,它还负责处理 Servlet,过滤器, 和 ServletContextInitiator 从应用程序上下文到嵌入式servlet容器的bean。
SpringBoot的另一个特性是它会自动扫描主类或主类的子包中的所有类以获取组件。
SpringBoot还提供了将其部署为外部容器中的Web存档的选项。在这种情况下,我们必须扩展 SpringBootServletInitiator :
Spring和SpringBoot两个框架都支持像Maven和Gradle这样的通用包管理技术
Spring Boot Maven插件 在Maven中提供SpringBoot支持。它还允许打包可执行的JAR或WAR实现
在部署上下文中,SpringBoot相对于Spring的一些优点包括:
1提供嵌入式容器支持
2使用命令独立运行JAR的规定 爪哇-JAR
3选项以排除依赖项,以避免在外部容器中部署时可能发生的JAR冲突。
4选项可以在部署时指定活动配置文件。
5用于集成测试的随机端口生成
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)