接下来就是Spring的各个处理细节了,不管框架如何疯转其实咱们处理请求的流程是不变的,设计到的操做也是固定的,举个例子,当咱们要实现一个登录功能时:html
建立一个用于处理登陆请求的Servletjava
实现doget等其余http方法(一些状况可能根据业务须要限制请求方法)web
从request对象中取出数据spring
处理编码sql
验证参数是否符合要求数据库
对参数数据类型进行转换(须要时)apache
开始业务逻辑处理(登陆)json
可能须要操做session来完成数组
组织响应给数据,多是html多是json,tomcat
Header与cookie的处理
整个SpringMVC其实就是帮咱们对上面的操做进行封装,固然了SpringMVC也提供了更多的功能,如国际化..
在handler方法中咱们能够添加一下参数,用于获取一些特殊的对象:
model 是框架帮咱们建立好的的Model对象,若使用该参数做为返回的model,则须要修改方法返回值为String用于指定视图名称;
案例:
@RequestMapping("/courseList2.action") public String courseList(Model model) { model.addAttribute("courses", courseService.selectCourseList()); return "courses.jsp"; } @RequestMapping("/courseList3.action") public String courseList(ModelMap model, HttpSession session,HttpServletRequest request,HttpServletResponse response) { System.out.println(request); System.out.println(response); System.out.println(session.getId()); model.addAttribute("courses", courseService.selectCourseList()); return "courses.jsp"; }
在使用Servlet开发的过程当中咱们会频繁的调用request.getAttribute来获取请求参数,参数较少时还没什么,一旦参数较多的时候就会产生大量的冗余代码,SpringMVC提供了多种以简化获取参数的过程的方法
在handler方法中添加与前台参数名称和类型匹配的参数,框架会自动解析参数传入handler方法中;
案例:
假设咱们要修改课程信息的功能,首先要获取原始信息
@RequestMapping("/edit.action") public String edit(Model model,Integer id) { Course course = courseService.selectCourseByID(id); model.addAttribute("course",course); return "edit.jsp"; }
支持的参数类型:
整形:Integer、int 字符串:String 单精度:Float、float 双精度:Double、double 布尔型:Boolean、boolean
当先后台参数名称不匹配时能够@RequestParam注解进行自定义映射;
注解参数:
案例:
@RequestMapping("/edit.action") public String edit(Model model,@RequestParam("id") Integer iid) { Course course = courseService.selectCourseByID(iid); model.addAttribute("course",course); return "edit.jsp"; }
注意:参数类型能够是基础类型也能够是包装类型,建议使用包装类型,这样能够保证为获取到参数时不会由于null没法转换为基础类型而致使的异常;
edit.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head></head> <form action="updateCourse.action" method="post"> <input name="id" value="${course.id}" hidden="hidden"/> <input name="name" value="${course.name}"/> <input name="teachName" value="${course.teachName}"/> <%-- <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/>--%> <input name="score" type="number" value="${course.score}"/> <input name="hours" type="number" value="${course.hours}"/> <input type="submit"> </form> <body> </body> </html>
当参数个数很是多时上面的方法就显得麻烦了,SpringMVC支持将参数映射到一个实体类;
在handler方法中添加任意类型实体类做为参数; 一样的只有参数名称和实体属性一致时才能映射成功;
咱们继续完善修改功能,如今要获取修改后的内容了:
@RequestMapping("/updateCourse.action") public String update(Course course) { courseService.updateCourse(course); return "/courseList.action"; }
上面的例子中出现了中文乱码问题,请求方法为post, 只须要在request中设置编码方式便可,可是此时参数已经被框架解析了,咱们在handler中经过request设置以及不生效了,因此咱们须要在请求到达SpringMVC以前就进行处理,这就用到了之前学过的过滤器了;
好消息是SpringMVC以及提供了过滤器,咱们只须要配置到web.xml中便可
<!-- 编码过滤器--> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <!-- 是否对响应设置编码 --> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 编码过滤器 END-->
须要注意的是,该过滤器只对post生效,若是是get乱码则仍是须要修改tomcat的server.xml或是经过代码从ISO-8859-从新编码为UTF-8
String username = request.getParameter("username"); username = new String(username.getBytes("iso8859-1"), "utf-8"); //从新编码
咱们将edit.jsp中的开课标签取消注释,而后测试会发现系统给出了400异常,查看控制台能够看到如下信息:
意思是框架没法将String类型的请求参数转换为须要的Date类型,这就须要,这是由于日期格式多种多样,每一个地区不一样,因此这须要咱们本身来实现转换;
实现convert接口便可做为转换器,该接口的两个两个泛型表示输入源类型和输出目标类型;
public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String s) { SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd"); try { return sm.parse(s); } catch (ParseException e) { e.printStackTrace(); } return null; } }
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.kkb.converter.StringToDateConverter"/> </set> </property> </bean> <mvc:annotation-driven conversion-service="conversionService"/>
使用@DateTimeFormat能够实现上面xml相同的配置:
public class Course { private Integer id; private String name; private String teachName; @DateTimeFormat(pattern = "yyyy-MM-dd") private Date startTime; }
注意:注解做用在实体类对应的属性上,且配置文件必须添加<mvc:annotation-driven>
当须要将参数映射到实体类的关联对象中时,也称为包装类型;
例如:在课程对象中有一个用户对象,表示这是某个用户的课程;前台须要同时传递课程对象的属性,和用户对象的属性,后台就须要要用一个包装类型来接收,即一个包装了用户对象的课程对象; 再说的简单点,即课程对象中包含一个用户对象;
在前台须要指出关联对象的属性名称,如:用户.name
实体:
public class Course { private Integer id; private String name; private String teachName; private Date startTime; private Integer score; private Integer hours; private User user;//新添加的User类属性 set/get....
handler:
@RequestMapping("/updateCourse.action") public String update(Course course) { courseService.updateCourse(course); return "/courseList.action"; }
jsp:
<form action="updateCourse.action" method="post"> <input name="id" value="${course.id}" hidden="hidden"/> <input name="name" value="${course.name}"/> <input name="teachName" value="${course.teachName}"/> <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/> <input name="score" type="number" value="${course.score}"/> <input name="hours" type="number" value="${course.hours}"/> <input name="user.username"/> <!-- 新添加的参数--> <input type="submit"> </form>
一些状况下,某一参数可能会有多个值,例如要进行批量删除操做,要删除的id会有多个,那就须要将参数映射到一个数组中;HttPServletRequest本来就支持获取数组参数,SpringMVC仅是帮咱们作了一个类型转换;
1.修改页面添加多选框:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> </head> <body> <form action="deleteCourses"> <table border="1"> <tr> <th>选择</th> <th>名称</th> <th>讲师</th> <th>开课日期</th> <th>学分</th> <th>课时</th> <th>操做</th> </tr> <c:forEach items="${courses}" var="course"> <tr> <td> <input type="checkbox" name="ids" value="${course.id}"> </td> <td>${course.name}</td> <td>${course.teachName}</td> <td> <fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/> </td> <td>${course.score}</td> <td>${course.hours}</td> <td> <a href="editCourse?id=${course.id}">修改</a> </td> </tr> </c:forEach> </table> <input type="submit" value="批量删除"> </form> </body> </html>
2.handler方法:
@RequestMapping("/deleteCourses") public String deleteCourses(Integer[] ids){ courseService.deleteCourses(ids); return "/getCourses"; }
3.service方法:
public void deleteCourses(Integer[] ids) { for (Integer id : ids){ courseMapper.deleteByPrimaryKey(id); } }
当请求参数包含多个对象的属性数据,是须要使用list来接收,一般用在批量修改批量添加等;
list映射要求参数名称为对象属性[下标].属性名称
,同时handler中使要用包装类型来接收;
如下是实现一个批量修改课程信息的功能;
1.修改页面中的td,使得每个td均可以编辑:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <html> <head> </head> <body> <form action="deleteCourses" id="fm"> <table border="1"> <tr> <th>选择</th><th>名称</th><th>讲师</th><th>开课日期</th><th>学分</th><th>课时</th><th>操做</th> </tr> <c:forEach items="${courses}" var="course" varStatus="status"> <tr> <td><input type="checkbox" name="ids" value="${course.id}"></td> <td><input value="${course.name}" name="courses[${status.index}].name"/></td> <td><input value="${course.teachName}" name="courses[${status.index}].teachName"/></td> <td><input value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>' name="courses[${status.index}].startTime"/></td> <td><input value="${course.score}" name="courses[${status.index}].score"/></td> <td><input value="${course.hours}" name="courses[${status.index}].hours"/></td> <input hidden="hidden" name="courses[${status.index}].id" value="${course.id}"> <td><a href="editCourse?id=${course.id}">修改</a></td> </tr> </c:forEach> </table> <input type="submit" value="批量删除"> <input type="button" onclick='function updates() { document.getElementById("fm").action = "updateCourses" document.getElementById("fm").submit() } updates()' value="批量修改"> </form> </body> </html>
2.包装类型:
public class RequestPack { //用于接收参数列表的list private List<Course> courses; public List<Course> getCourses() { return courses; } public void setCourses(List<Course> courses) { this.courses = courses; } public RequestPack(List<Course> courses) { this.courses = courses; } }
3.handler方法:
@RequestMapping("/updateCourses") public String updateCourses(RequestPack data){ courseService.updateCourses(data.getCourses()); return "/getCourses"; }
4.service方法:
public void updateCourses(List<Course> courses) { for (Course course:courses){ courseMapper.updateByPrimaryKey(course); } }
强调:list只能映射到包装类型中,没法直接映射到handler参数上
错误案例:
@RequestMapping("/updateCourses") public String updateCourses(RequestPack data, ArrayList<Course> courses){ courseService.updateCourses(data.getCourses()); return "/getCourses"; }
文件上传是web项目中很是常见的需求,SpringMVC使用了apache开源的两个库用于处理文件上传,因此在编写代码前咱们须要先导入下面两个依赖包:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
假设须要实现一个上传图片的功能,须要如今数据库中添加一个字段用于存储图片的路径,同时不要忘记修改pojo以及mapper文件,使之与数据库字段对应
1.页面增长input 用于提交文件,并修改表单的enctype为multipart/form-data
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head></head> <form action="updateCourse" method="post" enctype="multipart/form-data"> <input name="id" value="${course.id}" hidden="hidden"/> 名称:<input name="name" value="${course.name}"/> <br/> 讲师:<input name="teachName" value="${course.teachName}"/> <br/> <input name="startTime" type="date" value='<fmt:formatDate value="${course.startTime}" pattern="yyyy-MM-dd"/>'/> <br/> 学分:<input name="score" type="number" value="${course.score}"/> <br/> 课时:<input name="hours" type="number" value="${course.hours}"/> <br/> <c:if test="${course.pic != null}"> <img src="${pageContext.servletContext.contextPath}${course.pic}" style="width: 100px;height: 100px"> </c:if> 图片:<input name="picFile" type="file"/><br/> <!-- 新增input--> <br/> <input type="submit"> </form> <body> </body> </html>
2.在mvc配置文件中添加multipart解析器,(页面上传文件都是以,multipart编码方式)
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
3.handler方法中添加MultipartFile类型的参数
@RequestMapping("/updateCourse") public String updateCourse(Course course, MultipartFile picFile) throws IOException { /*** * 1.得到文件,取出文件后缀 * 2.生成惟一标识, * 3.写入文件到指定路径 * 4.存储文件路径到数据库 */ System.out.println(picFile.getName()); String suffix = picFile.getOriginalFilename().substring(picFile.getOriginalFilename().lastIndexOf(".")); String fileName = UUID.randomUUID() + suffix; String basepath = getClass().getClassLoader().getResource(".").getPath(); System.out.println(basepath); picFile.transferTo(new File(basepath+"../../images/"+fileName)); course.setPic("/images/"+fileName); courseService.update(course); return "/getCourses"; }
注意:实际开发中都是存储到文件服务器,不会放在项目里
4.静态资源处
若web.xml中DispatcherServlet的URLmapping 为/ 则还须要在SpringMVC中添加静态资源配置
<mvc:resources mapping="/images/**" location="/images/"/> <!--当请求地址为/images/开头时(不管后面有多少层目录),做为静态资源 到/images/下查找文件-->
若URLMapping为*.action 或相似其余的时则无需处理,由于Tomcat会直接查找webapp下的资源,不会交给DispatcherServlet
一些状况下咱们可能须要对请求进行限制,好比仅容许POST,GET等...
RequestMapping注解中提供了多个参数用于添加请求的限制条件
案例:
@RequestMapping(value = "/editCourse",method = RequestMethod.POST,headers = {"id"},params = {"name"},consumes = {"text/plain"})
为了简化书写,MVC还提供了集合路径和方法限制的注解,包括常见的请求方法:
PostMapping GetMapping DeleteMapping PutMapping 例: @PostMapping("/editCourse")