1.Spring MVC 概述
1.1.企业级应用基本架构
企业级应用基本架构(C/S)以及SPRING在架构中的作用。
此架构图中spring要解决什么问题?
1)对象的构建,对象的依赖管理。(重点是从资源使用角度进行分析)
2)扩展业务的动态切入。(基于OCP更好实现功能扩展,同时进行解耦)
3)简化传统web mvc架构中的一些细节处理问题(参数获取,校验,值的注入,响应方式及数据格式的转换)。
问题:
1)我们一般说的解耦是没有耦合吗?不是,所有软件系统中的对象之间
都会存在耦合,只是要把耦合降低。
2)降低耦合的最主要目的是什么?就是要提高系统的可维护性,便于系统进行更好的升级扩展。
3)降低耦合的主要实现方式?
3.1)对象之间的耦合尽量耦合与接口与工厂
3.2)代码的重用尽量使用组合而非继承。(has a 取代 is a)
1.2.Web MVC架构及分析
基于servlet,jsp,javabean技术实现的MVC架构
问题:
1)假如我们没有使用spring mvc,你如何对系统采用mvc思想进行分层?(参考jsp+servlet+javabean技术)
2)传统的web mvc 编程架构中有什么优势,劣势?
优势:结构层次更加清晰,可读性,可维护性相对较好
劣势:参数获取,类型转换,流程调用相对都比较复杂
3)市场上相对比较成熟的mvc框架:spring mvc,struts2,….
1.3.Spring MVC 架构及分析
Spring MVC是MVC架构模式的一种完美实现,它简化了Java WEB 中基于MVC架构的编程过程,是Spring中的WEB应用模块。其官方MVC概要架构图如下:
Spring MVC 底层核心架构图及工作流程(先了解,写完项目案例再重点强化)
Spring MVC 中的核心组件:
1)DispatcherServlet (前端控制器, 处理请求的入口)
2)HandlerMapping (映射器对象, 用于管理url与对应controller的映射关系)
3)Controller (后端控制器, 负责处理请求的控制逻辑)
4)ModelAndView (模型, 封装业务处理结果和视图)
5)ViewResolver(视图解析器,解析对应的视图关系:前缀+view+后缀)
备注:假如希望了解Spring MVC的详细处理流程可以基于断点调试法进行跟踪。
2.Spring MVC 编程基础
2.1.编程基本步骤
Step01:创建maven web 项目并解决项目中的错误问题
Step02:添加项目依赖(spring-webmvc)及spring核心配置文件
Step03:配置前端控制器DispatcherServlet(web.xml)
Step04:创建后端控制器(Controller)及页面
Step05:spring配置文件中配置核心应用组件
Step06:部署及测试springmvc 应用。
2.2.基于xml编程实现
2.2.1.创建Maven WEB 项目
Web项目打包方式为war方式
Web项目的target runtimes为tomcat
Web 项目的编译版本为JDK1.8
可能会遇到的问题:
1)war项目默认不会创建web.xml(需要自己生成)
2)项目创建好需要设置运行时环境tomcat(多个tomcat时选哪个)
3)统一编译版本(版本不统一很有可能会出现项目不编译)
4)统一编码(UTF-8)
5)假如创建的项目还有问题,可对项目进行clean操作
2.2.2.添加Spring MVC 项目依赖及配置文件
打开项目的pom.xml文件,然后添加依赖(选择组groupId为org.springframework)
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
假如添加好了依赖,都没有找到对应的jar依赖,先检测网络是否是通的.假如网络是通的,还没有下载到具体的依赖,此时要右键项目,选择maven/upate maven project/fore update…进行maven强制更新操作.
在项目的resource的目录中添加核心配置文件applicationContext.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" >
</beans>
说明:配置文件的名字需要自己定义,当这个空的配置文件中的某行有相关错误
时,先去检测网络是不是通的。
2.2.3.配置前端控制器
打开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:applicationContext.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的核心,这个控制器一般需要在服务器启动时即初始化。
其中
1)load-on-startup 表示启动时则加载此servlet(正数数值越小,优先级越大,0最大)
2)init-param 中的参数名不能变(此名字在DispatcherServlet父类中定义)
2.2.4.创建后端控制器
后端控制器编写时可以实现Controller接口,然后重写handleRequest方法处理请求
public class HelloController implements Controller{
public ModelAndView handleRequest(HttpServletRequest request) {
ModelAndView mv=new ModelAndView("hello");
mv.addObject("message","helloworld");
return mv;
}
}
其中:
ModelAndView对象为一个模型与视图对象,内置一个map对象,主要用于封装业务数据和视图名。
ModelAndView构造方法中传递的为视图名,addObject方法可以以key/value形式存储数据。
ModelAndView 对象返回时会被spring mvc自动存储到请求作用域,在对应的视图页面可以直接从此作用域获取对应的值。
2.2.5.创建JSP页面
在项目的WEB-INF/pages文件夹下创建hello.jsp文件,然后设置其内容,例如
…
<body>
<h1>${message}</h1>
</body>
…
2.2.6.配置后端控制及视图解析
在spring mvc 核心配置文件中添如下配置。
<bean id="helloController" class="spring.controller.XmlHelloController">
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello.do">跟bean对象同名</prop>
</props>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
2.2.7.部署到tomcat运行
将项目部署到tomcat,然后启动运行,在地址栏输入具体url访问对应controller。
问题:
1)tomcat启动时出现ClassNotFoundException,而这个class又不是我们自己的类,此时要重新maven update,重新发布(右键tomcat 重新publish),多次尝试还是不可以,此时重启eclipse。
2)404异常,一般表示服务端资源没找到,首先检测访问路径是否正确,然后还可以在项目的部署目录中去查找对应的资源,必须确保资源是存在的,假如资源不存在,说明代码没有正常编译。(很常见)
3)如何解决这种项目不编译的问题?
step01) 将tomcat下的项目移除,clean你的tomcat服务器
step02) 将项目的jre从buildpath移除,然后重新添加
step03) 对项目先进行maven clean操作(清除原先编译结构,然后重新编译)
step04) 再次对项目进行clean操作
step05) 重新部署项目,启动tomcat运行
4)运行项目时尽量不要右键运行选在run as /run on server
2.2.8.运行原理分析
本应用的简易处理流程如下:
2.3.基于注解编程实现
2.3.1.创建Maven WEB 项目
Web项目打包方式为war方式
Web项目的target runtimes为tomcat
Web 项目的编译版本为JDK1.8
2.3.2.添加Spring MVC 项目依赖及配置文件
打开项目的pom.xml文件,然后添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
假如添加好了依赖,都没有找到对应的jar依赖,先检测网络是否是通的.假如网络是通的,还没有下载到具体的依赖,此时要右键项目,选择maven/upate maven project/fore update…进行maven强制更新操作.
在项目的resource的目录中添加核心配置文件applicationContext.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" >
</beans>
2.3.3.配置前端控制器
打开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:applicationContext.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的核心。
2.3.4.创建后端控制器
@Controller
@RequestMapping("/")
public class AnnotationHelloController {
@RequestMapping("doSayHello")
public ModelAndView doSayHello() {
ModelAndView mv=new ModelAndView("hello");
mv.addObject("message", "helloworld");
return mv;
}
}
其中
通过@RequestMapping注解定义url到controller具体方法的映射。
2.3.5.创建jsp页面
2.3.6.配置组件扫描与视图解析
在spring mvc 核心配置文件中添加
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.company"/>
<!—启用MVC注解扫描 -->
<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>
2.3.7.部署与运行
将项目部署到tomcat,然后启动运行,在地址栏输入具体url访问对应controller。
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:
3.2.请求方式映射
3.2.1.请求方式限定
项目中Controller层对象的每个方法默认可以处理任意方式的请求,假如要指定控制层方法只能处理GET或只能处理POST请求,那该如何实现呢?
借助@RequestMapping注解中的method属性指定具体的请求处理方式,例如
@RequestMapping(value=”doSaveObj”,
method=RequestMethod.POST)
public String doSaveObject(Object obj){….}
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="request",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 请求一个控制层资源时,可以在对应方法中直接使用参数变量接收参数数据,但参数变量的类型要为对象类型。
@RequestMapping(value="param",method=RequestMethod.GET)
@ResponseBody
public String withParam(@RequestParam String foo) {
return "Obtained 'foo' query parameter value '" + foo + "'";
}
提示:@RequestParam注解用于接收请求参数中名字为foo的参数值,假如请求参数名与方法中的参数名一致,@RequestParam注解可以省略。假如不一致则可以使用@RequestParam注解定义新的参数名直接接收页面数据,然后传递给方法名,例如
@RequestMapping(value="param", method=RequestMethod.GET)
@ResponseBody
public String withParam(
@RequestParam(value="param1",required=false) String foo) {
return "Obtained 'foo' query parameter value '" + foo + "'";
}
提示:required=false表示,参数可以不存在,假如为true,参数不存在时会抛出异常。
3.3.3.Java bean对象
当请求中多个参数时可以通过在方法中定义多个参数接收参数数据,也可以利用一个javabean对象接收多个参数数据以简化多个参数变量的定义。
@RequestMapping(value="group",method=RequestMethod.GET)
@ResponseBody
public String withParamGroup(Message bean) {
return "Obtained parameter group " + bean;
}
提示:当使用javabean接收请求参数数据时,bean中需要有与参数名对应的set方法。
3.3.4.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.5.请求头数据
当服务端要获取客户端请求头中数据信息时,可通过@RequestHeader即可将请求头中的属性值绑定到处理方法的入参中,例如获取请求中Accept属性的值,然后传入到对应方法的参数中。
@RequestMapping(value="header", method=RequestMethod.GET)
@ResponseBody
public String withHeader(@RequestHeader String Accept) {
return "Obtained 'Accept' header '" + Accept + "'";
}
假如希望在此方法中直接从cookie取值,可以定义参数时使用@CookieValue对参数进行修饰,参数名一般要与cookie对象中的key相同
提示:方法中的参数名需要与请求头参数中某个某个参数名相同,具体请求头相关信息可以在浏览器控制台查看。
当应用中要获取请求中所有数据时可以在请求方法中定义一个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.ModelAndView 对象
在对服务端响应数据进行封装时,可以直接在方法参数中定义一个ModelAndView类型的参数,借助ModelAndView对象封装响应数据.
@RequestMapping("doModelAndView")
public ModelAndView doModelAndView(ModelAndView mv) {
mv.addObject("data", "model and view");
mv.setViewName("back");
return mv;
}
提示:ModelAndView 对象由Spring创建,并可以将数据存储到ModelAndView对象的ModalMap类型的属性中(可参考源代码).
4.1.2.Model对象
将响应数据直接封装为model中。
@RequestMapping("doModel")
public String doModel(Model model) {
model.addAttribute("data", "modal");
return "back";
}
当我们返回具体view时,系统底层会自动将model对象存储到request作用域。
4.1.3.Map对象
将响应数据封装到Map中(我建议假如使用map对数据进行封装,可直接采用model对象)。
@RequestMapping("doMap")
public String doMap(Map<String,Object> map) {
map.put("data", "map..");
return "back";
}
说明: 通过map接收页面参数时,需要使用@RequestParam注解声明
@RequestMapping("doMap")
public String doMap(@RequestParam Map<String,Object> map) {
map.put("data", "map..");
return "back";
}
4.2.响应数据转换JSON
4.2.1.JSON 应用概述
JSON(JavaScript Object Notation):一种轻量级数据交换格式,通常用于实现客户端与服务端之间的数据传输.
企业级Java项目数据传输方式:
4.2.2.客户端JSON数据操作
在客户端JSON格式的JavaScript对象:
var o1={id:10,name:’a’,age:20}
var o2=[{id:10,name:’a’,age:20},{id:20,name:’B’,age:30}]
访问json格式的javascript对象中的数据
var id1=o1.id;
var id2=o2[0].id
在客户端JSON格式对象转换为字符串
JSON.parse(json格式的javascript对象)
在客户端将JSON格式的javascript对象转换为json格式的字符串
JSON.stringify(json格式的字符串)
4.2.3.JAVA端JSON数据操作
在实际的项目中,通常会将服务端返回的数据直接转换JSON格式字符串,在客户端将这个字符串再转换为javascript对象,然后从这个对象直接取数据.
在将Java对象转换为JSON串时一般首先要添加第三方的JSONAPI 库,通过这样的库可以很方便的实现实现Java对象到JSON串的转换。
服务端常用JSON库:jackson和阿里的fastjson
Jackson 依赖库(SPRING默认支持此依赖库)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>
Jackson应用案例(jackson应用基础)
public class TestJackson {
@Test
public void testMap()throws Exception{
Map<String,Object> map=
new HashMap<String,Object>();
map.put("id", 100);
map.put("name","name-a");
map.put("age",20);
ObjectMapper om=new ObjectMapper();
String s1=om.writeValueAsString(map);
System.out.println(s1);
Map<?,?> map2=
om.readValue(s1, Map.class);
System.out.println(map2);
}
@Test
public void testList()throws Exception{
List<Map<String,Object>> list=
new ArrayList<Map<String,Object>>();
Map<String,Object> map=
new HashMap<String,Object>();
map.put("id", 100);
map.put("name","AAA");
map.put("age",20);
list.add(map);
map=new HashMap<String,Object>();
map.put("id", 101);
map.put("name","DDD");
map.put("age", 30);
list.add(map);
ObjectMapper om=new ObjectMapper();
String s1=om.writeValueAsString(list);
System.out.println(s1);
}
@Test
public void testUser()throws Exception{
SysUser user=new SysUser();
user.setId(10);
user.setUsername("CCCCC");
user.setPassword("123456");
ObjectMapper om=new ObjectMapper();
String jsonStr=
om.writeValueAsString(user);
System.out.println(jsonStr);
List<SysUser> list=new ArrayList<SysUser>();
list.add(user);
user=new SysUser();
user.setId(20);
user.setUsername("MMM");
user.setPassword("456789");
list.add(user);
jsonStr=om.writeValueAsString(list);
System.out.println(jsonStr);
}
}
fastjson依赖库
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
fastjson 应用案例(学习fastjson的基础)
public class testFastjson {
@Test
public void testMap(){
Map<String,Object> map=
new HashMap<String,Object>();
map.put("id", 10);
map.put("name", "AAA");
String s1=JSON.toJSONString(map);
System.out.println(s1);
Map<?,?> map2=
JSON.parseObject(s1, Map.class);
System.out.println(map2);
}
@Test
public void testUser(){
SysUser user=new SysUser();
user.setId(20);
user.setUsername("CCC");
user.setPassword("123456");
String s2=JSON.toJSONString(user);
System.out.println(s2);
}
@Test
public void testList(){
List<SysUser> list=new ArrayList<SysUser>();
SysUser user=new SysUser();
user.setId(20);
user.setUsername("CCC");
user.setPassword("123456");
list.add(user);
user=new SysUser();
user.setId(30);
user.setUsername("DDD");
user.setPassword("123456");
list.add(user);
String s2=JSON.toJSONString(list);
System.out.println(s2);
}
}
4.2.4.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 SysUser doUser(){
SysUser user=new SysUser();
user.setId(100);
user.setUsername("CCC");
user.setPassword("123456");
return user;
}
将Java List集合转换为JSON串。
@RequestMapping("doList")
@ResponseBody
public List<SysUser> doList(){
List<SysUser> list=new ArrayList<>();
SysUser user=new SysUser();
user.setId(100);
user.setUsername("CCC");
user.setPassword("123456");
list.add(user);
user=new SysUser();
user.setId(200);
user.setUsername("DDD");
user.setPassword("123");
list.add(user);
return list;
}
备注:将来controller中数据来自服务端数据。
4.2.5.Spring 集成fastjson库
spring 中默认不支持fastjson api直接将java对象转换为json串,假如需要使用
fastjson 实现对象的转换,现阶段我们需要手动进行转换。
项目中添加fastjson依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
创建controller,例如ResponseController,然后在类中添加相关方法,手动将java对象转换为json串。
@RequestMapping("doFastMap")
@ResponseBody
public String doFastMap(){
Map<String,Object> map=
new HashMap<>();
map.put("id", 100);
map.put("name", "AAA");
return JSON.toJSONString(map);
}
备注:后续项目中我们还可以采用指定对象转换器的方式集成fastjson,目前先掌握这部分应用。
5.Spring 拦截器应用
5.1.拦截器概述
拦截器是SpringMVC中的一个核心应用组件,主要用于处理多个
Controller的共性问题.当我们的请求由DispatcherServlet派发
到具体Controller之前首先要执行拦截器中一些相关方法,在这些
方法中可以对请求进行相应预处理(例如权限检测,参数验证),这些方法可以决定对这个请求进行拦截还是放行.
通过spring mvc 架构图分析,拦截器在Spring MVC中处理流程中的一个位置
回顾Spring MVC详细架构图
思考:假如对请求数据进行编码,是应在过滤器还是拦截器?
5.2.拦截器编写及基本配置
拦截器如何编写?
我们自己编写Spring MVC拦截器需要实现HandlerInterceptor接口(实现接口需要重写所有的方法)或者继承此接口的实现类 HandlerInterceptorAdapter(继承这个类时可根据需求重写必要的方法)
例如定义一个拦截器,计算controller方法的执行时间
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception {
System.out.println("time.preHandle");
long startTime=System.nanoTime();
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("time.postHandle");
}
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("time.afterCompletion");
long endTime=System.nanoTime();
long startTime=(Long)request.getAttribute("startTime");
System.out.println("time:"+(endTime-startTime));
}
}
这些方法的简易执行流程点如下图所示:
这些方法的详细的执行时间点如下:
我们自己的拦截器编写完成以后需要在spring配置文件中进行配置,,
例如:
<mvc:interceptors>
<mvc:interceptor>
<!-- 指定拦截哪些请求路径 -->
<mvc:mapping path="/**"/>
<!-- 排除要拦截的路径 -->
<mvc:exclude-mapping path="/user/doLogin.do"/>
<!-- 拦截器(这个bean也可以通过ref方式引用,前提是在外面要通过xml或注解方式定义) -->
<bean class="com.spring.TimeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
5.3.拦截器链及配置实现
当我们系统中有多个拦截器时,这些拦截器可以构成一个拦截器链.其原理类似过滤器中的过滤链。在多个拦截器应用中仅当所有匹配的拦截器的preHandle()都执行之后,才会调用Controller中处理请求的方法,然后再执行所有匹配的拦截器的postHandler(),再执行所有匹配的拦截器的afterCompletion()。
在拦截器链中,各拦截器的执行先后顺序取决于配置文件中配置的节点的先后顺序!
例如:再定义一个日志拦截器
@Component
public class LogInterceptor
extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("log.preHandler");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("log.afterCompletion");
}
}
配置日志拦截器
<!-- 配置拦截器 (要注意标签的编写顺序)-->
<mvc:interceptors>
<!-- 当存在多个拦截器时,哪个拦截器的方法先执行,要看拦截器配置的顺序 -->
<mvc:interceptor>
<mvc:mapping path="/user/*"/>
<ref bean="logInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- 拦截所有请求(这些请求要交给拦截器处理) -->
<mvc:mapping path="/**"/>
<!-- 定制对哪些请求不拦截 (这些请求不需要拦截处理)-->
<mvc:exclude-mapping path="/user/doLogin.do"/>
<!-- 拦截器对象 -->
<bean class="com.spring.TimeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
6.Spring MVC异常处理
6.1.Spring MVC 异常概述
实际项目中我们经常会采用分层架构设计程序,每一层都可能会有异常,假如异常信息没有处理,可能会选择抛出,假如这些被抛出的异常与具体业务相关,那到控制层以后我们一般都进行相应的处理(处理方式应该相对友好)
在Spring mvc 项目中,边界出现异常以后我们通常会在Controller中进行处理,常用分层架构中的异常分析:
在分层架构中,异常可能会从DAO层逐步抛出到控制层,可以在控制层对异常进行相关处理。
6.2.Spring MVC 异常处理
在spring中处理异常时,通常会在Controller中定义具体的异常处理方法,这个方法上使用@HandlerException注解进行描述.例如在指定Controller中定义异常处理方法:
@ExceptionHandler(value=Exception.class)
@ResponseBody
public String handleException(Exception e){
System.out.println("局部异常处理");
return e.getMessage();
}
当多个异常处理类中很多异常处理的方式是相同的,还可以将这个异常处理方法提取到父类,以实现方法的重用。
假如不希望将共性的异常处理方法提取到父类,还可以在外部定义一个全局的异常处理类,这个类使用@ControllerAdvice注解进行修饰.然后在这个类中定义具体的异常处理方法,这些方法再使用@HandlerExcpeiton进行修饰,例如
@ControllerAdvice
public class AdviceExceptionHandler {
@ExceptionHandler(Throwable.class)
@ResponseBody
public String handlerException(Throwable e){
System.out.println("全局的异常处理");
return e.getMessage();
}
}
Spring MVC中异常处理机制总结:
1)为什么要进行异常处理?(提高系统的容错能力,让系统表现的更加友好)
2)Spring MVC中异常处理的方式?(局部的,全局的)
3)局部方式异常处理如何实现?(Controller类中直接定义异常处理方法,使用@ExceptionHandler进行修饰)
4)全局方式异常处理如何实现?(1通过继承将共性提取到父类,2通过@ControllerAdvice注解修饰类,然后类中定义异常处理方法)
5)出现异常后,异常处理的具体机制是怎样的(首先会在当前Controller(包含父类)中查找有没有定义异常处理方法,假如有则查看注解中定义的异常类型是否与当前异常类型相匹配,假如匹配则直接处理,假如不匹配则查看当前类中有没有父类类型的异常处理方法与当前异常匹配,假如有则直接处理,没有则找全局异常处理。全局也没有则直接不友好的输出错误信息)
说明:实际项目中在controller的方法中除了要对正确的业务数据进行封装之外,还会对异常信息进行封装(例如封装到JsonResult),然后转换为json格式的字符串,输出到客户端。
7.Spring MVC 文件上传
7.1.文件上传业务场景
在电子商务系统中对具体商品采用图片方式进行描述,这个图片需要上传到服务器等等。
7.2.Spring MVC 文件上传基本步骤
Step01: 添加文件上传相关依赖(jar包:commons-fileupload)
Step02: 配置对上传文件对象的解析(spring的核心配置文件)
Step03: 编写文件上传表单(请求方式为post,enctype必须为multipart/form-data)
Step04: 编写对应的控制层对象处理文件上传(通过mutilpartFile参数接收文件)
7.2.1.添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
7.2.2.配置对象解析
在spring的核心配置文件中添加对象解析
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置文件编码处理 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 配置文件大小限制 -->
<property name="maxUploadSize" value="4096000000"/>
<!-- 配置内存缓冲区大小 -->
<property name="maxInMemorySize" value="1024000"/>
</bean>
说明:配置对象解析时,bean的id一定要指定,而且官方规定必须为multipartResolver
7.2.3.定义upload.jsp
在WEB-INF/pages目录下创建一个upload.jsp,然后创建上传表单
<body>
<h1>文件上传</h1>
<h1>${msg}</h1>
<form action="doUpload.do"
method="post"
enctype="multipart/form-data">
<input type="file" name="upfile"/>
<br/>
<input type="submit" value="upload">
</form>
</body>
说明:定义表单时,提交方式必须为post,enctype必须为multipart/form-data
7.2.4.定义UploadController
@RequestMapping("/upload/")
@Controller
public class UploadController {
@RequestMapping("uploadUI")
public String uploadUI(){
return "upload";
}
@RequestMapping("doUpload")
public ModelAndView doUpload(MultipartFile upfile)
throws IOException{
String fileName=upfile.getOriginalFilename();
long size=upfile.getSize();
System.out.println(fileName+"/"+size);
File dest=new File("D:/SSMWORK/"+fileName);
upfile.transferTo(dest);
ModelAndView mv=new ModelAndView("upload");
mv.addObject("msg", "upload ok");
return mv;
}
}
说明:在控制层方法接收请求中file类型的数据时需要借助MultipartFile 类型的参数对象,其参数名要求与表单中type为file的参数名相同。