注解类和方法大致如下
@Controller @RequestMapping("/URL跟路径") public class CLassXXXController{ @RequestMapping("/URL子路径1") public ModelAndView Method_1(参数1,参数2,...参数n) ...... ModelAndView modelAndView = new ModelAndView(); // 相当 于request的setAttribut,在jsp页面中通过itemsList取数据 modelAndView.addObject("itemsList", itemsList); modelAndView.setViewName("items/itemsList"); return modelAndView; } @RequestMapping("/URL子路径1") public String Method_2(参数1,参数2,...参数n) ...... //等效上面的 return "items/itemsList"; }
返回值说明
方法1:
上面两种返回都是转发,如果定义了视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置jsp路径的前缀 --> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- 配置jsp路径的后缀 --> <property name="suffix" value=".jsp" /> </bean>
那么转发的路径就是"/WEB-INF/jsp/items/itemsList.jsp",全路径也就是host/项目名//WEB-INF/jsp/items/itemsList.jsp
方法2:
返回"redirect:XXX", 或者"forward:XXX",重定向request不共享,转发request共享。
注意:此时路径是相对于类名上的根路径。比如类上的RequestMapping("hello"),那么会转发到host/项目名/hello/XXX
参数绑定
方法中可以有任意数量的参数,可以是HttpServletResponse,HttpServletRequest,HttpSession,ModelMap,
基本类型,自定义的类。
简单类型
@RequestMapping("/hei")
public String Fun1(Integer id,其他参数)
访问该路径时有一个key名称为id,value会自动绑定。
名称不一致的绑定
public String Fun1(@RequestParam(value="id",required=true) Integer xxxd,其他参数)
POJO绑定
public class OneThing{ private Integer id; private String name; private String place; private Date createDate; private String comment; } @RequestMapping("/onething") public String Fun2(OneThing thing,其他参数){ .... }
访问该路径时,当key/value中key的名称和POJO类属性名称一致,其值会绑定到该pojo对象中。
特殊情况:日期类型。访问页面提供的日期字符需要转换,才能绑定到pojo的Date对象。
创建转换类
public class CustomDateConverter implements Converter<String,Date>{ public Date convert(String source) { //实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { //转成直接返回 return simpleDateFormat.parse(source); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果参数绑定失败返回null return null; } }
在springmvc.xml中配置
<!-- 自定义参数绑定 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <!-- 转换器 --> <property name="converters"> <list> <!-- 日期类型转换 --> <bean class="com.legion.ssm.controller.converter.CustomDateConverter" /> </list> </property> </bean> <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
包装类型pojo绑定
上面的OneThing类添加一个新的属性,
private Item myitem;
这个属性也是一个pojo类,当访问提供的key/value的时候key名称为
myitem.name等等的形式时,其会绑定到OneThing对象中。
数组类型
出现场景:前端使用复选框
用户提交多个记录,此时items_id会有多个记录,
@RequestMapping("/deleteItems") public String deleteItems(int[] items_id) throws Exception {
List绑定
前端形如
<c:forEach items="${itemsList }" var="item" varStatus="status">
<tr>
<td><input name="itemsList[${status.index }].name" value="${item.name }"/></td>
<td><input name="itemsList[${status.index }].price" value="${item.price }"/></td>
<td><input name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
<td><input name="itemsList[${status.index }].detail" value="${item.detail }"/></td>
</tr>
</c:forEach>
http协议数据就是
itemLists[0].name=XXX&itemLists[0].price=XXX&itemLists[0].detail=XX......itemLists[1].name=XXX&itemLists[1].price=XXX.....
@RequestMapping("/onething") public String Fun2(OneThing thing,其他参数){ .... } //OneThing类新增一个List类型的属性 private List<Item> itemsList;
http协议 中的数据会绑定到OneThing对象中去
SpringMVC校验
maven配置,使用了tomcat8
<dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b08</version> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.7.Final</version> </dependency>
在springmvc.xml中配置
<!-- 校验器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <!-- hibernate校验器 --> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 校验错误信息配置文件 --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- 资源文件名 --> <property name="basenames"> <list> <value>classpath:CustomValidationMessages</value> </list> </property> <!-- 资源文件编码格式 --> <property name="fileEncodings" value="utf-8" /> <!-- 对资源文件内容缓存时间,单位秒 --> <property name="cacheSeconds" value="120" /> </bean> <mvc:annotation-driven conversion-service="conversionService" validator="validator"></mvc:annotation-driven>
在Pojo类,比如ItemsCustom的属性上添加注解
@Size(min=3,max=30,message="{items.name.length.error}") private String name; //已经建立CustomValidationMessages.properties文件 //指定了字符串items.name.length.error的值
关键使用:需要验证的pojo类前面加@Validated,后面跟BindingResult对象,用于错误处理
@RequestMapping("/editItemsSubmit") public String editItemsSubmit( @Validated ItemsCustom itemsCustom,BindingResult br) {。。。 }
错误处理
if(br.hasErrors()) { List<ObjectError> ae=br.getAllErrors(); for(ObjectError oe:ae) { System.out.println(oe.getDefaultMessage()); }
分组校验
先建立一个接口VG1,
在pojo类需要验证的属性上这样使用
@Size(min=5,max=30,message="{items.name.length.error}",groups= {VG1.class}) private String name;
验证的时候
@RequestMapping("/editItemsSubmit") public String editItemsSubmit( @Validated(value={VG1.class}) ItemsCustom itemsCustom,BindingResult br) {。。。 }
数据回显
把对象放到request域中
方法1:
在参数中添加Model 对象m
m.addAttribute("id",对象名);
上传图片
配置springmvc.xml
<!-- 文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置上传文件的最大尺寸为5MB --> <property name="maxUploadSize"> <value>5242880</value> </property> </bean>
pom中添加commons-fileupload
创建虚拟目录存储图片
或者修改tomcat的配置
在conf/server.xml文件中添加
上传页面的前端代码注意事项:
Form中添加
enctype="multipart/form-data"
<input type="file" name="items_pic"/>
对应处理handler参数中必须有
MultipartFile items_pic
对象名必须和前端的上传name一样
相关处理代码
String originalFilename = items_pic.getOriginalFilename(); //上传图片 if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){ //存储图片的物理路径 String pic_path = "D:\我的文档\Pictures\"; //新的图片名称 String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(".")); File newFile = new File(pic_path+newFileName); //将内存中的数据写入磁盘 items_pic.transferTo(newFile); //将新图片名称写到itemsCustom中 itemsCustom.setPic(newFileName); }
JSON处理
pom.xml配置
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.6</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.6</version> </dependency>
使用Jquery的配置
静态资源配置,在springmvc.xml中配置
<mvc:resources location="/js/" mapping="/js/**" /> <mvc:resources location="/img/" mapping="/img/**" />
项目中添加js文件
前端代码
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>json交互测试</title> <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script> <script type="text/javascript"> //请求json,输出是json function requestJson(){ $.ajax({ type:'post', url:'${pageContext.request.contextPath }/requestJson', contentType:'application/json;charset=utf-8', //数据格式是json串,商品信息 data:'{"name":"手机","price":999}', success:function(data){//返回json结果 alert(JSON.stringify(data)); } }); } //请求key/value,输出是json function responseJson(){ $.ajax({ type:'post', url:'${pageContext.request.contextPath }/responseJson', //请求是key/value这里不需要指定contentType,因为默认就 是key/value类型 //contentType:'application/json;charset=utf-8', //数据格式是json串,商品信息 data:'name=手机&price=999', success:function(data){//返回json结果 alert(JSON.stringify(data)); } }); } </script> </head> <body> <input type="button" onclick="requestJson()" value="请求json,输出是json"/> <input type="button" onclick="responseJson()" value="请求key/value,输出是json"/> </body> </html>
后端处理代码
@Controller public class JsonTest { @RequestMapping("/requestJson") public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom ic ) { ic.setPic("The legion has returned..."); return ic; } @RequestMapping("/responseJson") public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){ //@ResponseBody将itemsCustom转成json输出 return itemsCustom; } }
拦截器
类的定义,需要实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub System.out.println("拦截器11 之前"); return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub System.out.println("拦截器11 之后"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub System.out.println("拦截器11完成"); } }
在springmvc.xml中配置
<!--拦截器 --> <mvc:interceptors> <!--多个拦截器,顺序执行 --> <mvc:interceptor> <!-- /**表示所有url包括子url路径 --> <mvc:mapping path="/**" /> <bean class="com.legion.ssm.interceptor.HandlerInterceptor1"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.legion.ssm.interceptor.HandlerInterceptor2"></bean> </mvc:interceptor> </mvc:interceptors>
测试总结
两个拦截器都放行
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。
拦截器1放行,拦截器2不放行
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle不会执行。
拦截器1不放行,拦截器2不放行
HandlerInterceptor1...preHandle
拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
拦截器1 preHandle不放行,拦截器2不执行。
使用拦截器实现登录认证
如果请求的url是公开地址(无需登陆即可访问的url),让放行
如果用户session 不存在跳转到登陆页面
如果用户session存在放行,继续操作。
public class LoginInterceptor implements HandlerInterceptor { //进入 Handler方法之前执行 //用于身份认证、身份授权 //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求的url String url = request.getRequestURI(); //判断url是否是公开 地址(实际使用时将公开 地址配置配置文件中) //这里公开地址是登陆提交的地址 if(url.indexOf("login")>=0){ //如果进行登陆提交,放行 return true; } //判断session HttpSession session = request.getSession(); //从session中取出用户身份信息 String username = (String) session.getAttribute("username"); if(username != null){ //身份存在,放行 return true; } //执行这里表示用户身份需要认证,跳转登陆页面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); //return false表示拦截,不向下执行 //return true表示放行 return false; } //进入Handler方法之后,返回modelAndView之前执行 //应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("HandlerInterceptor1...postHandle"); } //执行Handler完成执行此方法 //应用场景:统一异常处理,统一日志处理 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("HandlerInterceptor1...afterCompletion"); } }
@Controller public class LoginController { // 登陆 @RequestMapping("/login") public String login(HttpSession session, String username, String password) throws Exception { // 调用service进行用户身份验证 // ... System.out.println("用户名是:"+username); // 在session中保存用户身份信息 session.setAttribute("username", username); // 重定向到商品列表页面 return "redirect:/items/queryItems"; } // 退出 @RequestMapping("/logout") public String logout(HttpSession session) throws Exception { // 清除session session.invalidate(); // 重定向到商品列表页面 return "redirect:/items/queryItems"; }
额外内容:访问Webapp下的静态资源。
比如在Webapp文件夹下面放了XXX.json。现在访问http://localhost/XXX.json不行,
需要在web.xml中配置如下:
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.json</url-pattern> </servlet-mapping>