SpringMVC学习
1. SpringMVC介绍
- 1. SpringMVC配置(掌握)
- 2. 全注解配置方式(掌握)
- 3. 获取参数、传递属性、上传、下载、操作json;(掌握)
- SpringMVC
2.1. 简介
SpringMVC是一个基于MVC模式的WEB框架,它解决WEB开发中常见的问题(参数接收、文件上传/下载、表单验证、国际化、等等),使用非常简单,SpringMVC作为Spring中的一个模块,可以与Spring无缝集成。
2.2. 为什么要学习SpringMVC
Spring是一个轻量级的Java 开发框架,为了解决企业应用开发的复杂性而创建。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。SpringMVC以Spring框架为核心,为应用程序中的Web层(表现层)提出的一套优秀的解决方案。
目前很多公司都使用SpringMVC,90%的招聘单位要求熟悉使用SpringMVC。
- Hello World入门
3.1. 入门理论
SpringMVC以Spring为核心,而Spring最核心的模块是IOC(控制反转)/DI(依赖注入)容器
所以使用SpringMVC必须先保证Spring IOC容器初始化,简单的说就是让你的项目拥有Spring的运行环境.
使用框架的第一步需要框架的官方网站下载框架相关的文件,
而SpringMVC无需单独下载,因为SpringMVC是作为Spring中的一个模块存在,所以我们只需要下载Spring即可。
查看:spring/libs/目录
beans,contenxt,core,expression,test,aop 依赖的logging
spring-webmvc-4.1.2.RELEASE.jar SpringMVC的jar文件。
spring-web-4.1.2.RELEASE.jar Spring对Web项目运行的支持。
3.2. 核心控制器(前端控制器)
顾名思义核心控制器用于Web层核心功能的处理以及在所有控制器执行之前,所有的WebMvc框架都采用了这种方式,在Struts2中我们使用的是StrutsPrepareAndExecuteFilter作为核心控制器,在SpringMVC中使用的是DispatcherServlet为核心控制器. DispatcherServlet核心控制器会拦截匹配的请求,把拦截下来的请求,依据相应的规则分发到目标Controller来处理。
3.3. 加入相关Spring的jar包
不要使用spring.3的jar和4.x的jar文件混用,危险的很
com.springsource.org.apache.commons.logging-1.1.1.jar
spring-beans-4.1.2.RELEASE.jar
spring-context-4.1.2.RELEASE.jar
spring-core-4.1.2.RELEASE.jar
spring-expression-4.1.2.RELEASE.jar
spring-aop-4.1.2.RELEASE.jar
spring orm、jdbc、tx相关jar根据项目使用自行添加;
加入相关SpringMVC相关jar包。
spring-web-4.1.2.RELEASE.jar spring 对web项目的支持。
spring-webmvc-4.1.2.RELEASE.jar SpringMVC核心包。
3.4. 配置核心控制器
Webmvc框架的心脏就是核心控制器,负责所有请求的公共功能,然后在分发给具体的控制器(我们编写的控制器),完成业务逻辑,响应视图。
3.4.1. Jar文件找
3.4.2. web.xml
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 请求的入口,以.do结尾的请求都会经过DispatcherServlet处理 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
3.5. 新建Controller控制器
SpringMVC中的控制器与以前我们以前学习的action、servlet作用一样,处理用户请求,调用业务逻辑,返回结果视图。
SpringMVC中的控制器有一定规范,要么实现接口,要么使用POJO对象与注解配合使用。
规范要求 |
说明 |
实现Controller接口或子接口。 注意:子接口很多已经过时了。 |
核心方法为handleRequest(req,resp),处理用户请求 |
普通的类(常用的哦) |
使用一个普通的类作为我们的控制器,每一个方法就是一个处理器,这种方式需要配合注解标签才能使用。 |
public class Controller1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return null;
}
}
3.6. 添加处理请求代码
handleRequest方法的两个参数与我们以前的servlet中service方法一样,request和response对象,我们可以使用这个两个对象完成一次请求的所有工作,比如你可以使用request接受参数,或者使用response重定向等等,注意方法除了返回值以外还有一个返回值ModelAndView。
ModelAndView是SpringMVC控制器中特有一个对象,描述一次请求响应的 数据(Model)和 视图(View)。
3.7. 视图页面:
3.8. 配置我们的控制器
SpringMVC是基于Spring,Spring中的核心就是Ioc容器,而Ioc容器中最重要的成员就是<bean>,SpringMVC中的控制器也是一个一个<bean>。
部署项目,启动Tomcat报错
错误信息如下:
没找到/WEB-INF/springmvc-servlet.xml文件,因为在启动Tomcat服务器的时候会初始化SpringMVC中的DispatcherServlet,而这个DispatcherServlet会根据配置文件初始化Spring容器,默认配置文件路径为:/WEB-INF/<servlet-name>-servlet.xml
解决问题方式:
1.可以把我们的配置文件更名为springmvc-servlet.xml,然后拷贝到WEB-INF中。
2.一般spring的配置文件都在放在src中或者resource文件中,我们可以通过以下配置告诉SpringMVC我们的配置文件路径。
访问
http://localhost/helloworld
注意:如果你的配置为<url-pattern>*.do</url-pattern>,并且<bean name="/helloworld.do">,访问路径为: http://localhost/helloworld.do
- Controller控制器实现的3种方式
现在只用第三种,全注解
4.1. 第一种实现Controller接口:
配置
4.2. 第二种实现HttpRequestHandler接口:
配置
4.3. 第三种普通类和注解:建议使用(请看后面的全注解配置)
POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBean
没有继承类,也没有实现类
配置(只需要配置让spring管理这个bean即可,无需指定路径,因为方法上面通过@RequestMapping指定)
- <url-pattern> 可以写那些内容?
5.1. *.do
<!-- 请求的入口,以.do结尾的请求都会经过DispatcherServlet处理 -->
5.2. /(现在最流行的配置方式)
<!-- 以RESTful的访问风格,来访问当前应用,现在用的最多的 -->
<!-- 静态资源被拦截:原因是在tocmat的web.xml里面有一个专门处理静态的资源的servlet,它配置的url-pattern也是/ ,造成图片静态资源不能成功被访问-->
5.2.1. Tomcat/conf/web.xml
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> 处理静态资源,如图片、css等
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
5.2.2. 静态资源访问
由于我们配置的拦截器规则是"/","/"就代表默认控制器,tomcat本身自带有一个默认控制器,这个控制器用于处理静态资源请求,如果我们配置默认控制器,那么tomcat的默认控制器就不生效,导致静态资源无法访问。
解决办法:SpringMVC开启静态资源控制器。
5.3. /*
<!-- /* struts2就是这样的,springmvc不能使用此配置,因为连jsp作为一个控制器拦截处理 -->
- 全注解
SpringMVC中所有控制器,其实在spring中就是一个一个bean,spring管理bean的方法分为两种,第一种是xml,第二种是注解。
6.1. SpringMVC中控制器建议使用注解方式,是官方推荐的,外面公司也是使用此方式。
6.2. SpringMVC开启注解支持
6.3. spring通过注解管理bean
- SpringMVC返回JSON
7.1. 解析json的常用工具包
Jackson http://jackson.codehaus.org/ springmvc默认使用
JSON-lib:http://json-lib.sourceforge.net/ ajax课程学习的时候使用的
JSONSerializer.toJSON(parents).toString()
Gson:http://code.google.com/p/google-gson/
阿里:fastjson
7.2. 出现406状态异常,加入jackson json工具包
7.3. 处理有乱码的问题(主要是提供给ie浏览器)
<!-- 为了处理返回的JSON数据的编码,默认是ISO-8859-1的,设置为UTF-8 -->
<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
7.4. 对日期格式的特殊处理(后面要用到的知识)
7.4.1. 从后台向前台:
默认返回的日期格式为时间戳,而在前台我们希望显示出指定规则的日期字符串。如:
默认:{"name":"小明哥","birthdate":121223223}
期望: {"name":"小明哥","birthdate":"2025-12-12"}
在日期get属性,字段上,添加一个格式化注解
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
7.4.2. 从前台向后台:(异常的状态码是400,类似于struts2转换异常)
在后台模型的setter方法上,添加注解
import org.springframework.format.annotation.DateTimeFormat;
@DateTimeFormat(pattern="yyyy-MM-dd")
7.4.3. JsonController
@Controller
public class JsonController {
@RequestMapping("/json1")
@ResponseBody
public String json1() throws Exception {
return "xxx";
}
@RequestMapping("/json2")
@ResponseBody
public String[] json2() throws Exception {
return new String[] { "A", "B", "C" };
}
@RequestMapping("/json3")
@ResponseBody
public User json3() throws Exception {
return new User("xxx", 20, new Date());
}
@RequestMapping("/json4")
@ResponseBody
public List<User> json4() throws Exception {
List<User> users = new ArrayList<User>();
for (int i = 1; i < 11; i++) {
users.add(new User("xxx" + i, 20 + i, new Date()));
}
return users;
}
}
- 接收参数
8.1. 常用第1,2,4种
8.2. spring内置请求编码过滤器(只能接受post请求)
注意:SpringMVC框架本身没有处理请求编码,需要配置一个spring内置请求编码过滤器。
如果有get请求乱码,tomcat6.x,7.x/conf/server.xml
<Connector port="80" protocol="HTTP/1.1" URIEncoding=”UTF-8”
connectionTimeout="20000"
redirectPort="8443" />
8.3. 通过控制器的执行方法参数来接收表单的参数
8.4. 通过模型对象来接收,做项目经常用
8.5. 3.通过HttpServletRequest接收(不建议使用)
8.6. 接收url中参数的请求,接收用户请求参数值
以后你自己写webservice,然后让其他人来访问
此种方式就是RESTful访问方式
- 传递数据
9.1. 常用第1种
9.2. 通过model对象进行数据传递
9.3. 通过ModelAndView对象传递
9.4. 通过request对象进行数据传递(不建议使用)
9.5. 通过返回值传递数据(更不建议使用)
9.6. 转发
上面4个案例都是转发,转发的值都是放到request请求作用域,下一个页面可以取出
Url地址不会改变
9.7. 重定向
重定向就是发出一个全新的请求,
如果把值放到request请求,下一个页面不能取出
如果把值放到session会话请求,下一个页面可以取出
Url地址会改变
- 文件上传
10.1. 添加jar文件
由于SpringMVC自己没有实现文件上传,它使用是apache.commons.fileupload
com.springsource.org.apache.commons.fileupload-1.2.0.jar
com.springsource.org.apache.commons.io-1.4.0.jar
10.2. jsp页面
enctype="application/x-www-form-urlencoded" post提交默认值
如果上传文件修改enctype=”multipart/form-data”
10.3. 异常,需要配置上传解析器
错误:提示告诉开发者你没有配置文件上传解析器。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<property name="maxUploadSize">
<!-- spring el写法:5MB -->
<value>#{1024*1024*5}</value>
</property>
</bean>
10.4. 后台处理
@Controller
public class UploadController {
// 先发出一个get请求,显示一个上传页面
// localhost/upload get提交
@RequestMapping(name = "upload", method = RequestMethod.GET)
public String get() throws Exception {
return "upload";
}
// <form action="upload" method="post" enctype="multipart/form-data">
// 其他属性<input type="text" name="name"><br />
// 文件属性<input type="file" name="upload"><br />
// <input type="submit"/>
// </form>
// 上面页面一提交,就返回下面post方法
// 发出一个post请求,处理文件的上传
// localhost/upload post提交
@RequestMapping(name = "upload", method = RequestMethod.POST)
public String post(MultipartFile upload, String name, HttpServletRequest request) throws Exception {
System.out.println("其他属性name::" + name);
System.out.println("上传文件的类型:" + upload.getContentType());
System.out.println("上传文件名称:" + upload.getName());
System.out.println("上传文件文件名:" + upload.getOriginalFilename());
// 上传的流
InputStream inputStream = upload.getInputStream();
// 获取webapp路径
String webapp = request.getServletContext().getRealPath("/uploads/");
File file = new File(webapp, upload.getOriginalFilename());
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
// 如果upload文件夹不存在,就创建
parentFile.mkdirs();
}
// 拷贝到webapp一个目录里面
IOUtils.copy(inputStream, new FileOutputStream(file));
return "upload";
}
}
- 文件下载(还是原来servlet的方式)
@RequestMapping("download")
public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 第一行:设置建议的下载文件名
response.setHeader("Content-Disposition", "filename=employee.xlsx");
// 第二行:设置文件名的类型
// struts.xml:<param name="contentType">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</param>
response.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// 准备文件
String webapp = request.getServletContext().getRealPath("/");
File file = new File(webapp, "employee-5.xlsx");
FileInputStream inputStream = new FileInputStream(file);
IOUtils.copy(inputStream, response.getOutputStream());
}
- SpringMVC 执行流程
- SpringMVC工作流程详细描述
控制器=处理器(Handler)
1. 用户向服务器发送请求,请求被SpringMVC前端控制DispatcherServlet捕获;
2. DispatcherServlet通过调用HandlerMapping(处理器映射管理对象)获得该请求对应的Handler对象(包括控制器以及Handler对象对应的拦截器)
HandlerExecutionChain对象(包含:控制器+2个拦截器);
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,来渲染视图(Model+View合成)
8. 将渲染结果返回给客户端。
- SpringMVC工作流程描述(简易版本,面试)
1. 客户端请求提交到DispatcherServlet
2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller。
3. DispatcherServlet将请求转发给到Controller。
4. Controller调用业务逻辑处理后,返回ModelAndView
5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图。
6. 视图负责将结果显示到客户端。
- SpringMVC核心对象
15.1. HanderMapping 处理器映射管理对象
作用:根据不同的请求选择最合适的处理器(自己编写的控制器),处理器映射管理对象可以配置多个,谁最先匹配就执行谁。(通过访问的url地址匹配控制器)
SpringMVC默认:/org/springframework/web/servlet/DispatcherServlet.properties
BeanNameUrlHandlerMapping 处理通过<bean name="/xxx">注册的控制器。控制器需要实现Controller接口。
BeanNameUrlHandlerMapping |
处理通过<bean name="/xxx">注册的控制器。控制器需要实现Controller接口。 |
DefaultAnnotationHandlerMapping |
处理通过注解@Controller(类标签) 及@RequestMapping(方法标签) 注册的控制器。该处理器映射管理对象已经在Spring 3.2 版本过时,替换为RequestMappingHandlerMapping。 |
15.2. spring 中提供的内置处理器映射
15.2.1. 简单url处理器映射(现在不用的)
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
(了解即可,现在已经基本不用了)
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/helloworld(url 地址)=helloworldController(bean id)
/helloworld002=helloworldController
</value>
</property>
</bean>
15.3. 使用<mvc:annotation-driven/>开启新的映射处理器。
上面的配置就是告诉spring
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 采用注解方式请求处理器映射。
15.4. HandlerAdapter 处理器适配
15.4.1. 作用:
支持多种类型的处理器,如何来执行"处理器(控制器)"; 如何执行我们的控制器。
15.4.2. Spring默认处理器适配:
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter 处理实现了HttpRequestHandler接口对应的控制器。
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter 处理实现了Controlle接口对应的控制器。
15.5. 使用<mvc:annotation-driven/>开启新的处理器适配
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter 处理通过注解方式的控制器。 3.2中已过时,替换为org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
15.6. ViewResolver 视图解析器 (struts2视图类型类似)
15.6.1. ViewResolver 视图解析器:
作用:根据不同的视图,响应不同的结果,比如普通的jsp或json.
SpringMVC默认:
InternalResourceViewResolver : 支持默认视图,采用forward,redirect。
视图名规则
不写前缀默认为"转发"
视图名字符串前缀:
forward:/xxx.jsp 采用转发。
redirect:/xxx.jsp 采用重定向。
new ModelAndView("forward:/userList");
new ModelAndView("redirect:/user");
15.6.2. 注册(替换)视图解析器
设置视图路径的前后缀,该配置可以让我们写视图路径的时候更简单。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/" />
<property name="suffix" value=".jsp" />
</bean>
路径简写:
15.6.3. 注意事项:"forward:"与"redirect:"前缀问题
页面不在前缀路径中的情况(或者页面路径不是以.jsp为后缀)
- 课程总结
16.1. 重点
- 全部在代码里面,怎样处理json,接收参数,传递参数,上传,下载
16.2. 难点
- 理解url-pattern的3种配置, 达到不同效果
- 常见异常
- No mapping found for HTTP request with URI [/controller3.do] in DispatcherServlet with name 'dispatcher'
表示在当前spring的配置里面没有找到处理/controller3.do的控制器,springmvc配置不对
- java.lang.ClassNotFoundException: org.springframework.aop.TargetSource
添加aop的jar文件
- HTTP Status 500 - Circular view path [param1]: would dispatch back to the current handler URL [/param1] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
注册(替换)视图解析器
设置视图路径的前后缀,该配置可以让我们写视图路径的时候更简单。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/" />
<property name="suffix" value=".jsp" />
</bean>
- 状态异常
如果出现406状态异常,jackson的jar文件没有导入
如果出现400状态异常,类似于struts2的类型转换异常,
如日期格式输入错误或者是英文浏览器
在后台模型的setter方法上,添加注解
import org.springframework.format.annotation.DateTimeFormat;
@DateTimeFormat(pattern="yyyy-MM-dd")
如年龄字段输入了字符串
- HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: is a MultipartResolver configured?
缺少配置
- 课后练习
- 面试题
- Servlet什么时候被实例化?
Servlet是单例,针对一个tomcat在内存里面只有一个实例
A.第一次访问的时候
B.tomcat启动的时候
<!-- 告诉tomcat启动的时候就进行实例化 -->
<load-on-startup>1</load-on-startup>
- Spring MVC执行流程
- 面试题:SpringMVC与Struts2区别
目前企业中使用SpringMVC的比例已经远远超过Struts2,那么两者到底有什么区别,是很多初学者比较关注的问题,下面我们就来对SpringMVC和Struts2进行各方面的比较:
核心控制器(前端控制器、预处理控制器):
对于使用过mvc框架的人来说这个词应该不会陌生,核心控制器的主要用途是处理所有的请求,然后对那些特殊的请求(控制器)统一的进行处理(字符编码、文件上传、参数接受、异常处理等等),SpringMVC核心控制器是Servlet,而Struts2是Filter。
控制器实例:
SpringMVC会比Struts快一些(理论上)。SpringMVC是基于方法设计,而Sturts是基于对象,每次发一次请求都会实例一个action,每个action都会被注入 属性,而Spring更像Servlet一样,只有一个实例,每次请求执行对应的方法即可(注意:由于是单例实例,所以应当避免全局变量的修改,这样会产生线程安全问题)。
管理方式:
大部分的公司的核心架构中,就会使用到spring,而SpringMVC又是spring中的一个模块,所以spring对于SpringMVC的控制器管理更加简单方便,而且提供了全 注解方式进行管理,各种功能的注解都比较全面,使用简单,而struts2需要采用XML很多的配置参数来管理(虽然也可以采用注解,但是几乎没有公司那样使用)。
参数传递:
Struts2中自身提供多种参数接受,其实都是通过(ValueStack)进行传递和赋值,而SpringMVC是通过方法的参数进行接收。
学习难度:Struts有很多新的技术点,比如拦截器、值栈及OGNL表达式,学习成本较高,SpringMVC 比较简单,很少的时间都能上手。
intercepter的实现机制:
struts有以自己的interceptor机制,SpringMVC用的是独立的AOP方式。这样导致struts的配置文件量还是比SpringMVC大,虽然struts的配置能继承,
所以我觉得理论使用上来讲,SpringMVC使用更加简洁,开发效率SpringMVC确实比struts2高。
SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
spring mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间 也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。
json操作
SpringMVC处理ajax请求,直接通过返回数据,方法中使用注解@ResponseBody,SpringMVC自动帮我们对象转换为JSON数据。
struts2 唯一好处:很多内置的27个插件