文章转载自:https://www.jiweichengzhu.com/article/f3dbfa6f56b542feb6be6801317374ea
如果还有问题,加群交流:686430774
案例代码下载,请移步原文链接!
SSM还有一种框架是springmvc + spring + mybatis,在我个人看来,其实应该叫做SM更合适(就是这个名字实在是太骚了),毕竟springmvc也是spring衍生出来的产品,在这个框架中,springmvc取代了struts作为控制层,spring依旧跟以前一样作为中间层负责解耦和数据源、事务的处理,mybatis依旧是负责dao层的处理。
当时从ssh和ssi转过来的使用它的时候,就给我一种感觉:“这个框架突然就火了,struts突然就不行了”,真的是特别的迅速,这么牛逼的框架,当时也是做了一番笔记,将之前整理的笔记分享给大家。
框架版本:
Spring:4.1.5.RELEASE
Mybatis:3.3.0
在springmvc框架中,controller层也是有spring来管理的,一些请求的映射和转发都是它来完成,跟struts使用filter来处理请求相比,它使用的是servlet来处理,入口是DispatcherServlet,所以在web.xml需要单独为其制定一个spring的配置文件来做一些特殊的处理,如:controller扫描路径、静态资源文件映射、返回数据增强处理、视图解析器,具体的点我们来看一下配置文件:
spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <!-- 使用非默认方式扫描注解 --> <context:component-scan base-package="com.ssm" use-default-filters="false"> <!-- 只扫描@Controller注解,其他的在spring-context.xml中扫描 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 启用注解驱动 --> <!-- 自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter --> <!-- 是springMVC为@Controller分发请求所必须用到的 --> <mvc:annotation-driven/> <!-- spring升级到4.X版本之后,会对默认请求后缀进行处理,导致不能随意指定如html之类的后缀,否则response中的content-type会出问题,返回不了json --> <!-- 这个玩意儿很有用,有时候想模拟静态化页面的时候,就是想用.html作为controller的后缀,否则就只能使用rest风格的无后缀url了 --> <!--<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <!– Turn off working out content type based on URL file extension, should fall back to looking at the Accept headers –> <property name="favorPathExtension" value="false"/> </bean>--> <!-- 增强处理,配置json和字符的返回,可以指定想用的json转换器 --> <!--<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/plain;charset=UTF-8"/> </bean> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"> <property name="supportedMediaTypes" value="application/octet-stream;charset=UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> <value>text/json;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>--> <!-- 在web.xml中配置DispatcherServlet拦截了所有的url,所以这里需要做一个映射才能访问到静态文件,如果依旧拦截的是.do/.action这种,这里就不需要配置映射 --> <mvc:resources location="/resource/" mapping="/resource/**"/> <!-- 每个想要对外开放的静态目录,都需要配置映射,也可以使用上面的偷懒方式,放开整个目录 --> <!-- <mvc:resources location="/resource/css/" mapping="/resource/css/**" /> <mvc:resources location="/resource/js/" mapping="/resource/js/**" /> --> <!-- 配置视图解析器,最终输出到浏览器上的内容就是从这里渲染出去的 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
spring-context.xml
再来看一下controller的写法:
package com.ssm.controller; import com.ssm.entity.Student; import com.ssm.service.SchoolService; import com.ssm.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @Controller @RequestMapping("/student")// 加上映射注解,自定义path public class StudentController { @Autowired private SchoolService schoolService; @Autowired private StudentService studentService; // 日期格式处理 @InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception { // 日期格式 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 是否严格按照格式来解析 dateFormat.setLenient(true); // 注册自定义的格式转换器 binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); } @RequestMapping(value = "/list", method = RequestMethod.GET)// 定义path,并且指定请求方式,只有get能访问到 public String list(ModelMap modelMap, String text) { modelMap.put("list", studentService.list(text)); return "/student_list"; } @RequestMapping(value = "/json_list", method = RequestMethod.GET)// 定义path,并且指定请求方式,只有get能访问到 @ResponseBody// 如果不是返回页面,一定要加上此注解,否则返回不了json数据 public List<Student> json_list() { return studentService.list(null); } @RequestMapping("/toAdd")// 如果不定义请求方式,get和post都可以访问 public String toAdd(ModelMap modelMap, String type) { modelMap.put("type", type); modelMap.put("schoolList", schoolService.list(null)); return "/student_maintain"; } @RequestMapping("/add") public String add(Student student) { studentService.add(student); return "redirect:/student/list"; } @RequestMapping("/toUpdate") public String toUpdate(ModelMap modelMap, String type, Integer id) { modelMap.put("type", type); modelMap.put("student", studentService.getById(id)); modelMap.put("schoolList", schoolService.list(null)); return "/student_maintain"; } @RequestMapping("/update") public String update(Student student) { studentService.update(student); return "redirect:/student/list"; } @RequestMapping("/delete") public String delete(Integer id) { studentService.delete(id); return "redirect:/student/list"; } }
讲真,不要太清爽,终于不要写一堆get、set了,就可以直接从方法上接收参数了,而且控制器上的注解语义也很清晰,返回json也方便的一批,大家自己感受一下~
最后再提一点,关于spring-servlet.xml中提到4.x版本的后缀问题,大家可能不是很明白,我来个大家做个json返回的演示,大家就能看懂了。
在StudentController中有一个json_list方法是以json格式返回所有的学生列表,而且我已经加上了@ResponseBody注解,但是我直接在浏览器上访问的时候,却提示我406,如下图:
根据响应头信息,我们可以看到spring返回的是html,因为它对我的后缀做了分析,看到我是html后缀,就默认返回text/html类型,而不再是application/json了,也就是说,如果我们想要返回json,那么我们不能使用html做后缀,如:/student/json_list.json、/student/json_list.do、/student/json_list.action,或者什么后缀都不写,如:/student/json_list。
为了解决上面这个问题,我们将配置修改一下,加一个negotiation-manager来处理:
<!-- 启用注解驱动 --> <!-- 自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter --> <!-- 是springMVC为@Controller分发请求所必须用到的 --> <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/> <!-- spring升级到4.X版本之后,会对默认请求后缀进行处理,导致不能随意指定如html之类的后缀,否则response中的content-type会出问题,返回不了json --> <!-- 这个玩意儿很有用,有时候想模拟静态化页面的时候,就是想用.html作为controller的后缀,否则就只能使用rest风格的无后缀url了 --> <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <!-- Turn off working out content type based on URL file extension, should fall back to looking at the Accept headers --> <property name="favorPathExtension" value="false"/> </bean>
再次访问/student/json_list.html请求,就能正常的返回json了,如下图:
整体来说,SpringMVC还是比Struts简单很多,不管是从代码上还是从配置上来看,都很简洁、清爽!