1.Spring MVC 概述
1.1 企业级应用基本架构
企业级应用中的软件的分层的基本架构(参考阿里巴巴开发手册)
说明:
分层架构的本质是分而治之,已达到分解问题复杂性的目的,从而更好的进行设计与实现。
1.2 Web MVC架构及分析
基于servlet,jsp,javabean技术实现的MVC架构,具体架构图如下:
1.3 Spring MVC 架构及分析
Spring MVC是MVC架构模式的一种完美实现,它简化了Java WEB 中基于MVC架构的编程过程,是Spring中的WEB应用模块。
Spring MVC 底层核心架构图及工作流程(先了解,写完项目案例再重点强化)
Spring MVC 中的核心组件:
- DispatcherServlet (前端控制器, 处理请求的入口)
- HandlerMapping (映射器对象, 用于管理url与对应controller的映射关系)
- Interceptors(拦截器,实现请求响应的共性处理)
- Controller (后端控制器, 负责处理请求的控制逻辑)
- ViewResolver(视图解析器,解析对应的视图关系:前缀+view+后缀)
备注:
假如希望了解Spring MVC的详细处理流程可以基于断点调试法进行跟踪。
2. Spring MVC 编程基础
2.1 Spring MVC 编程基本步骤
Step01:创建maven web 项目并解决项目中的错误问题
Step02:添加Spring MVC项目核心依赖
Step03:配置Spring MVC项目核心组件
Step04:创建Spring MVC 后端控制器及页面
Step05:部署及测试spring mvc 项目应用。
2.2 Spring MVC 编程基础实现(重点)
2.2.1 创建Maven WEB 项目并添加Spring MVC 依赖
Step01:创建maven web项目
- 项目名 CGB-SPRING-MVC-01
- Web项目打包方式为war方式
Step02:配置maven web项目
- 生成web.xml(项目视图)
- Web项目的target runtimes为tomcat
- Web 项目的编译版本为JDK1.8
Step03: 添加Spring MVC项目依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
2.2.2 添加Spring MVC配置文件并进行基本配置
在项目的resources的目录中添加核心配置文件(例如spring-configs.xml)并进行基本配置
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd" >
<!--配置组件扫描 -->
<context:component-scan base-package="com.jt"/>
<!—启用MVC默认配置 (@RequestMapping) -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
说明:
配置文件的名字可以自己指定。
2.2.3 配置Spring MVC前端控制器
打开web.xml,配置DispatcherServlet对象
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-configs.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
前端控制器是spring mvc处理请求的入口,是springmvc的核心,这个控制器一般需要在服务器启动时即初始化。
其中
- load-on-startup 表示启动时则加载此servlet,数字越小优先级越高.
- init-param 中的参数名不能变(此名字在DispatcherServlet父类中定义)
2.2.4 创建并配置Spring MVC后端控制器
编写Spring MVC后端控制器
@Controller
@RequestMapping("/")
public class HelloController{
@RequestMapping("doSayHello")
public ModelAndView doSayHello() {
ModelAndView mv=new ModelAndView("hello");
mv.addObject("message", "helloworld");
return mv;
}
}
其中:
- 通过@RequestMapping注解定义url到controller具体方法的映射,这个映射信息会被存储,一般是存储到一个HandlerMapping对象中.
- ModelAndView对象为一个模型与视图对象,内置一个map对象,主要用于封装业务数据和视图名。
- ModelAndView构造方法中传递的为视图名,addObject方法可以以key/value形式存储数据。
- ModelAndView 对象返回时会被spring mvc自动存储到请求作用域,在对应的视图页面可以直接从此作用域获取对应的值。
2.2.5 创建JSP页面对象
在项目的WEB-INF/pages文件夹下创建hello.jsp文件,然后设置其内容,例如
<body>
<h1>${message}</h1>
</body>
说明:
WEB-INF目录下的资源不能允许通过浏览器地址栏直接访问。
2.2.6 部署并运行项目以及请求响应流程分析
将项目部署到tomcat,然后启动运行,在地址栏输入具体url访问对应controller对象。
问题分析:
1)tomcat启动时出现ClassNotFoundException,而这个class又不是我们自己的类,此时要重新maven update,重新发布(右键tomcat 重新publish),多次尝试还是不可以,此时重启eclipse。
2)404异常,一般表示服务端资源没找到,首先检测访问路径是否正确,然后还可以在项目的部署目录中去查找对应的资源,必须确保资源是存在的,假如资源不存在,说明代码没有正常编译。(很常见)
3)如何解决这种项目不编译的问题?
- 停止tomcat将tomcat下的项目移除,并clean你的tomcat服务器(两个clean)
- 对项目进行maven clean操作(清除原先编译结构,然后重新编译)
- 再次对项目进行clean操作(菜单栏中的project clean)
- 重新部署项目,启动tomcat运行
- 假如经历了以上几个步骤,还没解决此问题,重启eclipse再试
说明:
假如你的eclipse经常出现类似问题,换jdk。
4)运行项目时尽量不要右键运行选在run as /run on server
Tomcat 启动及对象加载流程分析:
项目的请求处理流程结构及过程解析:
Step01:客户端向服务服务端发请求
Step02:服务端对请求信息进行过滤(Filter)
Step03:请求到达前端控制DispatcherServlet
Step04:前端控制器基于url在HandlerMapping中的映射找请求执行链
Step05:执行执行链中的拦截器(Interceptor)方法
Step06:执行执行链中的控制器(Controller)方法
Step07:对象控制层返回的视图进行解析
Step08:向客户端返回一个响应结果。
3. Spring MVC 请求处理
3.1 请求路径映射
实际项目中我们要借助@RequestMapping注解定义映射路径。其注解应用位置
- 类定义处: 提供初步的请求映射信息。
- 方法定义处: 提供进一步的细分映射信息
3.1.1 普通url映射
@RequestMapping(value={"/doSayHello", "/user/doSayWelcome"}):
多个URL路径可以映射到同一个处理器的功能处理方法。
3.1.2 Rest风格url映射
REST即表述性状态传递(英文:Representational State Transfer,简称REST),是一种软件架构编码风格,是基于网络应用进行设计和开发的编码方式。可以降低开发的复杂度,提高程序的可伸缩性。
例如:
@RequestMapping("/msg/{xxx}")
请求的URL可以是“/msg/hello”或“/msg/welcome”
@RequestMapping("/msg/{id}/create"):
请求的URL可以是“/users/1/create”。
@RequestMapping("/msg/{mId}/topics/{tId}")
这样也是可以的,请求的URL可以是“/users/10/topics/12”。
说明:
通过@PathVariable可以提取URI模板模式中的{×××}中的×××变量。
http://localhost:8080/项目名/doUpdate/1.do
3.2 请求方式映射
3.2.1 请求方式限定
项目中Controller层对象的每个方法默认可以处理任意方式的请求,假如要指定控制层方法只能处理GET或只能处理POST请求,那该如何实现呢?
借助@RequestMapping注解中的method属性指定具体的请求处理方式
例如:
@RequestMapping(value=”doSaveObj”,
method=RequestMethod.POST)
public String doSaveObject(Object obj){….}
知识点扩展:
1)@GetMapping 注解应用(定义的映射只能处理get请求)
2)@PostMapping 注解应用(定义的映射只能处理post请求)
3.2.2 请求方式组合
项目中还可在控制层方法上借助@RequestMapping注解中的method属性指定使用哪几种方式处理请求。
@RequestMapping(value=”doSaveObj”,method={RequestMethod.POST,RequestMethod.GET})
public String doSaveObject(Object obj){….}
提示:一般浏览器只支持GET或POST方式。
3.3 请求参数映射(重点)
3.3.1 标准Servlet API(了解)
请求映射方法中可以直接使用ServletAPI 中的对象获取参数数据,例如
HttpServletRequest,HttpSession对象等
例如:
@RequestMapping(value="withRequest",method=RequestMethod.GET)
@ResponseBody
public String withRequest(HttpServletRequest request){
System.out.println(request.getRequestURI());
return "Obtainer 'foo' query parameter value '"+request.getParameter("gid")+"'";
}
提示:
@ResponseBody注解作用:
该注解作用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区
使用情况:
返回的数据不是Html标签的页面,而是其他数据格式的数据时,(如Json、xml,普通文本等)使用;
3.3.2 直接量对象(重点)
SpringMVC 请求一个控制层资源时,可以在对应方法中直接使用参数变量接收参数数据,但参数变量的类型建议为对象类型。
使用String类型变量接受请求参数的值:
@RequestMapping(value="withStringParam",method=RequestMethod.GET)
@ResponseBody
public String withStringParam(@RequestParam String foo) {
return "Obtained 'foo' query parameter value '" + foo + "'";
}
提示:
@RequestParam注解用于接收请求参数中名字为foo的参数值
假如请求参数名与方法中的参数名一致,@RequestParam注解可以省略;
假如不一致则可以使用@RequestParam注解定义新的参数名直接接收页面数据,然后传递给方法名
还有就是请求参数中包含特殊字符时,需要借助@RequestParam注解对参数进行声明
例如:
@RequestMapping(value="withStringParam", method=RequestMethod.GET)
@ResponseBody
public String withStringParam(
@RequestParam(value="param-01",required=false) String foo) {
return "Obtained 'foo' query parameter value '" + foo + "'";
}
提示:
required=false表示,参数可以不存在,假如为true(默认),参数不存在时会抛出异常(400异常)。
使用Date类型变量接受请求日期参数的值:
@RequestMapping(value="withDateParam")
@ResponseBody
public String withDateParam(Date birthday) {
return "Obtained date parameter value '" + birthday + "'";
}
Spring MVC 默认支持yyyy/MM/dd格式日期转换,假如日期格式不匹配会报400异常
使用Integer类型的可变参数或数组接收请求数据
@RequestMapping(value="withVarParam")
@ResponseBody
public String withVarParam(Integer… ids) {
return "Obtained ids parameter value '" + ids + "'";
}
3.3.3 Java bean对象(重点)
当请求中多个参数时可以通过在方法中定义多个参数接收参数数据,也可以利用一个javabean对象接收多个参数数据以简化多个参数变量的定义。
@RequestMapping(value="withParamGroup",method=RequestMethod.GET)
@ResponseBody
public String withParamGroup(SysLog entity) {
return "Obtained javabean parameter group " + entity;
}
提示:
当使用javabean接收请求参数数据时,bean中需要有与参数名对应的set方法。
3.3.4 集合Map对象对象(了解)
说明:
通过map接收页面参数时,需要使用@RequestParam注解声明
@RequestMapping("doMap02")
public String withParamGroup (
@RequestParam Map<String,Object> map) {
return "Obtained map parameter group " + map;
}
提示:
此时的map不能再作为响应数据的封装对象
3.3.5 Rest url数据(重点)
SpringMVC请求资源路径的URL可以通过{XXX}形式指定动态的URL,动态URL中的这个可变参数的值可以直接注入到方法对应的参数中。
@RequestMapping(value="path/{var}",method=RequestMethod.GET)
@ResponseBody
public String withPathVariable(@PathVariable String var) {
return "Obtained 'var' path variable value '" + var + "'";
}
通过@PathVariable注解指定参数变量var获取请求url中{var}数据
3.3.6 请求头数据(了解)
当服务端要获取客户端请求头中数据信息时,可通过@RequestHeader即可将请求头中
的属性值绑定到处理方法的入参中,例如获取请求中Accept属性的值,然后传入到对应方法的参数中。
@RequestMapping(value="header", method=RequestMethod.GET)
@ResponseBody
public String withHeader(@RequestHeader String Accept) {
return "Obtained 'Accept' header '" + Accept + "'";
}
假如希望在此方法中直接从cookie取值,可以定义参数时使用@CookieValue对参数进行修饰,参数名一般要与cookie对象中的key相同
@RequestMapping(value="withCookie")
@ResponseBody
public String withCooke(
@CookieValue String JSESSIONID) {
return "Obtained COOKIE Value '" + JSESSIONID + "'";
}
提示:
方法中的参数名需要与请求头参数中某个参数名相同,具体请求头相关信息可以在浏览器控制台查看。
当应用中要获取请求中所有数据时可以在请求方法中定义一个HttpEntity<String>参数,通过此参数获取请求头及请求体中数据
例如:
@RequestMapping(value="entity", method=RequestMethod.POST)
public @ResponseBody String withEntity(
HttpEntity<String> entity) {
return "Posted request body " + entity.getBody() + " headers = " + entity.getHeaders();
}
如上写法:了解
4. Spring MVC 响应处理
4.1 响应数据封装
4.1.1 Servlet API 对象(了解)
将请求数据直接封装到Request 对象
@RequestMapping("doResponse01")
public String doResponse01(HttpServletRequest request) {
request.setAttribute("data", "hello..");
return "response";
}
在response.jsp页面可以直接借助${data}方式获取数据。
当方法中直接返回一个页面时,默认执行的是请求转发,假如需要实现重定向,
可以在返回的地址后添加redirect
例如:
return "redirect:responseUI.do"; 其中responseUI.do对应一个请求url.
@RequestMapping("doResponse02")
public String doResponse02(HttpServletRequest request) {
request.setAttribute("data", "hello..");
return "redirect:responseUI.do";
}
在如上方法中可以重定向到一个responseUI对应的新的URL。
@RequestMapping("responseUI")
public String responseUI() {
return "response";
}
在新的请求中不能直接获取上一个请求作用域的数据。
回顾请求转发与重定向:
1)请求转发(forward)
2)重定向(redirect)
4.1.2 ModelAndView 对象(重点)
在对服务端响应数据进行封装时,可以直接在方法参数中定义一个ModelAndView类型的参数,借助ModelAndView对象封装响应数据.
@RequestMapping("doModelAndView")
public ModelAndView doModelAndView(ModelAndView mv) {
//ModelAndView mv=new ModelAndView();
mv.addObject("data", "model and view");
mv.setViewName("response");//view
return mv;
}
提示:
ModelAndView 对象由Spring创建,并可以将数据存储到ModelAndView对象的ModalMap类型的属性中(可参考源代码).
4.1.3 Model对象(重点)
将响应数据直接封装为model中。
@RequestMapping("doModel")
public String doModel(Model model) {
model.addAttribute("data", "modal");
return "response";
}
当我们返回具体view时,系统底层会自动将model对象存储到request作用域。
4.1.4 Map对象(了解)
将响应数据封装到Map中(我建议假如使用map对数据进行封装,可直接采用model对象)。
@RequestMapping("doMap01")
public String doMap01(Map<String,Object> map) {
map.put("data", "map..");
return "response";
}
4.2 响应数据转换JSON(重点)
4.2.1 JSON 应用概述
JSON(JavaScript Object Notation):一种轻量级数据交换格式,通常作为客户端与服务端进行数据交互的一种标准。
企业级Java项目数据传输方式:
reponse.getWriter().write(jsonStr);
客户端访问服务端时,服务器从数据库取出数据进行封装,然后再将对象转换为json串,通过网络传输到客户端。
4.2.2 Spring 集成jackson库
spring 中默认支持jackson应用的,但使用时需要添加jackson依赖,例如
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>
创建Controller中例如ResponseController,然后在类中添加对应方法。例如
将Map对象内容转换为字符串(spring底层会直接访问jackson api将对象转换为字符串)
@RequestMapping("doMap")
@ResponseBody
public Map<String,Object> doMap(){
Map<String,Object> map=
new HashMap<>();
map.put("id", 100);
map.put("name", "AAA");
return map;
}
将JavaBean对象转换为JSON串
@RequestMapping("doUser")
@ResponseBody
public SysLog doLog(){
SysLog log=new SysLog ();
log.setId(100);
log.setUsername("CCC");
log.setIP("192.168.1.12");
return log;
}
将Java List集合转换为JSON串。
@RequestMapping("doList")
@ResponseBody
public List<SysLog> doList(){
List<SysLog> list=new ArrayList<>();
SysLog log=new SysLog ();
log.setId(100);
log.setUsername("CCC");
log.setIP("192.168.1.12");
list.add(log);
log=new SysLog ();
log.setId(100);
log.setUsername("CCC");
log.setIP("192.168.1.12");
list.add(log);
return list;
}
备注:将来controller中数据来自服务端数据。