1、常规springmvc的流程
1.1springmvc的启动过程
首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。
1.2spring mvc工作流程图
Spring工作流程描述
1.用户向服务器发送请求,请求被Spring前端控制Servelt DispatcherServlet捕获;
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4.提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等;
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中;
5. Handler执行完成后,向DispatcherServlet返回一个ModelAndView对象;
6.根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet;
7. ViewResolver结合Model和View,来渲染视图
8.将渲染结果返回给客户端。
2springboot搭建
2.1选取ide工具
下图是spring官网提供的ide工具
要创建springboot项目可以下载spring官网提供的sts这款IDE,自动集成了创建boot项目所需的插件,当然对使用eclipse的开发者也可以安装对应系统的插件进行构建boot项目。
2.2创建项目
File> New > Spring Starter Project
Next> Finish
项目创建完成:
可以看出,项目源码就一个Java类,在pom.xml中有spring-boot-starter-web的依赖。
SpringBootSampleApplication.java
packageorg.springboot.sample;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
publicclass SpringBootSampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSampleApplication.class,args);
}
}
pom.xml
4.0.0
org.springboot.sample
spring-boot-sample
0.0.1-SNAPSHOT
jar
spring-boot-sample
Spring Boot Sample WebApplication
org.springframework.boot
spring-boot-starter-parent
1.3.1.RELEASE
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
这样就完成了项目的创建,下面我们创建一个HelloController.java定义3个方法
packageorg.springboot.sample.controller;
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;
importorg.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
publicclass HelloController {
@RequestMapping
public String hello() {
return "Hello Spring-Boot";
}
@RequestMapping("/info")
public Map getInfo(@RequestParamString name) {
Map map = newHashMap<>();
map.put("name", name);
return map;
}
@RequestMapping("/list")
public List>getList() {
List> list= new ArrayList<>();
Map map = null;
for (int i = 1; i <= 5; i++) {
map = new HashMap<>();
map.put("name", "Shanhy-"+ i);
list.add(map);
}
return list;
}
}
然后现在可以直接运行SpringBootSampleApplication的main方法,和执行普通java程序一样。
然后可以看到spring-boot内置server容器(默认为Tomcat),这一切spring-boot都帮我们做好了。
控制台输出内容Started SpringBootSampleApplication in 7.358 seconds (JVM running
for 9.154)表示服务已经启动。
在浏览器输入我们3个请求便可看到结果。http://localhost:8080/hello
输出:HelloSpring-Boothttp://localhost:8080/hello/info?name=shanhy
输出:{“name”:”shanhy”}http://localhost:8080/hello/list
输出:[{“name”:”Shanhy-1”},{“name”:”Shanhy-2”},{“name”:”Shanhy-3”},{“name”:”Shanhy-4”},{“name”:”Shanhy-5”}]
通过我们的Hello实例,相信大家一目了然,可谓spring-boot创建一个项目如此简单,完全可以在几分钟内将服务启动。spring-boot抛弃繁琐的配置,让开发人员更专注与业务逻辑的实现。后面几篇文章将会对spring-boot的多个方面通过实例的方式呈现给大家。
3springboot的运行原理
系统启动类视图如下:
可以发现启动类有个@SpringBootApplication注解,这是一个组合注解,结构如下图:
@SpringBootApplication注解的功能主要由@ EnableAutoCongiguration、@componentScan组成。
@EnableAutoCongiguration负责启动自动配置功能,在main方法中的SpringApplication启动的方法中,如下图:
在SpringApplication启动时,依据@ EnableAutoCongiguration设定的自动配置功能,有一部分功能是去扫描maven内所有jar包下的spring.factories文件,构建成一个配置文件链表,如果系统有引用,则根据条件注解去初始化bean,例如:
@componentScan负责扫描启动类所在package下子package的所有需要实例化的bean的IOC,比如@controller、@service、@Component、@Configuration、@bean等。
boot提供了默认的配置文件:application.xml,集成了各类starter服务的配置属性定制化,简单举几个例子:
日志配置
web服务器配置
模板配置
http编码、json和微服务配置
mvc交互配置
认证配置
还有spring.data.*和spring.datasource.*的异构数据源的配置等等,具体详情参考
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/
4springmvc迁移springboot
4.1零配置
常规springmvc的整体结构如下图:
springboot的整体结构如下图:
综合对比,springmvc要迁移springboot,首当其中的就是springboot是如何去XML(web.xml和applicationcontext.xml)的。
去web.xml:依赖内置web服务器启动初始化应用的上下文;
去applicationcontext.xml:依赖java注解和条件注解技术,使用自动配置功能实现。
4.2web.xml
对于一些springboot
application.xml中没有的服务,有三种解决方式:
1、自定义starter pom
2、原始方式自己编写服务
3、原始引用XML
对于springboot服务不好用,想进行变更的,boot设计中为它的任何一个服务都提供了可扩展性的方式进行变更,比如如何解决web.xml内配置的filter、Listener、Interceptor等,可通过注解方式(比如@Component、@Configuration)自行扩展对应的服务。
5样例
5.1pom文件配置
5.2application.xml配置
环境分流配置
开启shutdown功能
配置内容
5.3平滑升级
6总结
使用springboot,首选需要判断boot是否提供了服务,boot有一些服务是自动配置加载的,有一些服务是需要enable*打开才能用的,比如定时@EnableScheduling、异步@EnableAsync服务等,如果boot目前还无法定制某项目的个性化服务,可自行扩展,扩展方式参考4.2小节描述内容。
对于jar包版本一致性问题也是同理,先判断boot是否提供了对应的jar服务,决定是否需要自行扩展。
springboot可以导出jar包也可以导出war包,war包基本依赖外部应用服务器,可以利用外部应用服务器的shutdown功能平滑升级。但jar包形式依赖linux sh脚本的运行,基本都会使用kill去暴力杀线程,这是会影响线上业务的,所以为了保证线上线程不会突然崩溃影响业务,sringboot也考虑了这一点,在监控Actuator中提供了优雅的shutdown功能,只要我们在sh脚本中访问线上服务的shutdown地址,即可优雅的关闭线上服务,平滑升级了。