SpingMVC注解开发的高级知识总结
数据回显
- 数据回显的需求:表单提交出现错误,重新回到表单,同时用户重新填写数据,刚才提交的参数在页面上回显。
- 执行的流程:点击修改连接---->携带id进入修改的controller---->修改页面.jsp---->提交修改好的页面—>失败的话将刚刚填写的数据进行重新显示
简单数据类型
-
为了效果的展示,我们将隐藏的数据id显示出来。简单的数据回显,我们使用model.addAttribute方法设置key一致。
-
首先进入修改页面的controller
-
数据在修改.jsp得到展示
-
点击提交---->错误之后数据进行回显—>修改提交的controller中
pojo数据类型的回显
- 方法一:仍然使用model.addAttribute
- 方法二:使用@ModelAttribute,作用于将请求pojo数据放到Model中回显到页面
在ModelAttribute方法指定的名称就是要填充Model中的key,在页面中就要通过key取数据。
@ModelAttribute将方法返回值传到页面
- 需求:商品类别信息在商品信息页面显示。
比如商品通过筛选(根据商品的类型)
页面显示
使用@ModelAttribute将公用的取数据的方法返回值传到页面,不用在每一个controller方法通过Model将数据传到页面。
参数绑定集合类型
绑定数组
-
需求:在商品查询列表页面,用户选择要删除的商品,批量删除商品。
-
在controller方法中如何将批量提交的数据绑定成数组类型。
-
页面传递数组的参数,使用 js 给普通的按钮组件添加批量删除(转跳到批量删除的controller)
-
Item商品的controller接收:
绑定List<Object>
-
需求:批量修改商品信息提交
先进入批量修改商品页面,填写信息,点击提交。
-
在列表添加批量修改按钮,点击转跳到批量修改controller,
-
通过查询所有将结果展示批量修改.jsp
itemsList:controller方法形参包装类型中list的属性名。itemsList[0]或itemsList[1]。。,[]中是序号,从0开始。
itemsList[].name:name就是controller方法形参包装类型中list中pojo的属性名
-
商品完成修改之后进行提交(接收集合类型的参数)使用包装类型。
商品的图片上传
-
需求:在商品修改页面,增加图片上传的功能。
操作流程:
用户进入商品修改页面
上传图片
点击提交(提交的是图片和商品信息)
再次进入修改页面,图片在商品修改页面展示
图片存储问题
切记:不要把图片上传到工程 目录 ,不方便进行工程 维护。
实际电商项目中使用专门图片服务器(http,比如apache、tomcat)。
本教程使用图片虚拟目录,通过虚拟目录 访问硬盘上存储的图片目录 。
图片目录中尽量进行目录分级存储,提高访问速度(提交i/o)。
虚拟 目录 设置:
配置图片上传解析器
- 依赖于:两个jar包
springmvc使用commons-fileupload进行图片上传。
commons-fileupload对应的springmvc的图片上传解析器:
在springmvc.xml的配置上传图片的解析器<!-- 文件上传,上传图片 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 设置上传文件的最大尺寸为5MB --> <property name="maxUploadSize"> <value>5242880</value> </property> </bean>
编写上传图片的页面
-
配置提交的action中的
enctype="multipart/form-data"
-
修改页面:
-
编写controller
json数据的交互
-
springmvc解析json依赖的jar包:
-
json数据配置的方式有两种:
-
第一种:Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换
在处理器适配器中注入MappingJacksonHttpMessageConverter
-
第二种:使用:mvc:annotation-driven 对json数据响应提供支持
<!-- 配置注解驱动,相当于同时使用最新处理器映射跟处理器适配器,对json数据响应提供支持 --> <mvc:annotation-driven conversion-service="conversionService" />
@RequestBody和@ResponseBody
-
@RequestBody:将请求的json数据转成java对象
-
@ResponseBody:将java对象转成json数据输出。
请求json相应json
-
请求页面
function requestJson() { $.ajax({ url : "${pageContext.request.contextPath }/requestJson.action", type : "post", contentType : "application/json;charset=utf-8", //请求json数据,使用json表示商品信息 data : '{"name":"手机","price":1999}', success : function(data) { alert(data.name); } }); }
-
controller方法接收并返回:
// 传入Json响应Json @RequestMapping("/requestJson") public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom, HttpServletRequest request) { return itemsCustom; }
请求key/value相应json
-
请求页面:
function responseJson() { $.ajax({ url : "${pageContext.request.contextPath }/responseJson.action", type : "post", // contentType : "application/json;charset=utf-8", //请求json数据,使用json表示商品信息 data : "name=手机&price=3999", success : function(data) { alert(data.name); } }); }
-
controller方法接收并返回:
// 传入key/value响应Json @RequestMapping("/responseJson") public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) { return itemsCustom }
统一异常处理
-
需求:一般项目中都需要作异常处理,基于系统架构的设计考虑,使用统一的异常处理方法。
-
系统中异常类型有哪些?
包括预期可能发生的异常、运行时异常(RuntimeException),运行时异常不是预期会发生的。
针对预期可能发生的异常,在代码手动处理异常可以try/catch捕获,可以向上抛出。
针对运行时异常,只能通过规范代码质量、在系统测试时详细测试等排除运行时异常。
自定义异常
-
针对预期可能发生的异常,定义很多异常类型,这些异常类型通常继承于Exception。
这里定义一个系统自定义异常类:
/** * 系统自定义的异常类型,实际开发中可能要定义多种异常类型 * @author SYJ */ public class CustomException extends Exception { // 异常信息 private String message; public CustomException() { super(); } public CustomException(String message) { super(); this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
统一异常分析
-
要在一个统一异常处理的类中要处理系统抛出的所有异常,根据异常类型来处理。
-
前端控制器DispatcherServlet在进行HandlerMapping、调用HandlerAdapter执行Handler过程中,如果遇到异常,进行异常处理。
-
DispatcherServlet的源码分析
-
在系统中自定义统一的异常处理器,写系统自己的异常处理代码。
定义统一异常处理器类
-
定义统一异常处理器类
-
根据不同的异常类型进行异常处理。
-
系统自定义的异常类是CustomException,在controller方法中、service方法中手动抛出此类异常。
-
针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常处理在错误页面展示
-
针对非CustomException异常,对这类重新构造成一个CustomException,异常信息为“系统发生异常了,请联系管理员”,此类错误需要在系统测试阶段去排除。
public class CustomExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 输出异常(方便调试) ex.printStackTrace(); // 定义异常信息在指定页面显示 String message = null; CustomException customException = null; // 统一处理异常代码 // 针对系统自定义的CustomException异常,就可以直接从异常类中获取异常信息,将异常信息在页面展示 // 异常信息 if (ex instanceof CustomException) { customException = (CustomException) ex; } else { // 针对非CustomException异常,我们对这类的信息重新尽心异常类型的构造,异常信息为“系统发生异常了,请联系管理员” customException = new CustomException("系统发生异常了,请联系管理员"); } message = customException.getMessage(); request.setAttribute("message", message); try { request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response); } catch (ServletException | IOException e) { e.printStackTrace(); } return new ModelAndView(); } }
-
将定义好的统一异常处理类在springmvc.xml中进行配置(因为实现了HandlerExceptionResolver接口所以不需要加bean的id)
<!-- 配置统一异常处理器 --> <bean class="com.syj.ssm.exception.CustomExceptionResolver"></bean>
-
测试:
-
图解分析异常的统一处理
RESTful支持
-
RESTful软件开发理念,RESTful对http进行非常好的诠释。
RESTful即Representational State Transfer的缩写。
url的RESTful实现
-
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&…
RESTful的url是简洁的:http:// localhost:8080/items/editItems/1
参数通过url传递,rest接口返回json数据
-
需求:根据id查看商品信息,商品信息查看的连接使用RESTful方式实现,商品信息以json返回。
-
第一步更改DispatcherServlet配置
<!-- RESTful的配置 --> <servlet> <servlet-name>springmvc_rest</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 加载SpringMVC的配置 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc_rest</servlet-name> <!-- RESTful的配置是 / --> <url-pattern>/</url-pattern> </servlet-mapping>
-
第二步参数通过url传递
配置页面:
在controller中进行接收:// RESTful测试 @RequestMapping("/viewItems/{id}") // 根据商品id查看商品信息rest接口 // @RequestMapping中指定restful方式的ur1中的参教,参教需要用 { } 包起来 // @PathVariable将ur1中的 { } 包起参数和形参进行绑定 public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception { // 调用 service查询商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); return itemsCustom; }
-
设置静态资源解析
当DispatcherServlet拦截/开头的所有请求,对静态资源的访问就报错:
需要通过设置对静态资源进行解析.
访问/js/**的url从工程下/js/下解析。
<!-- 静态资源的解析 --> <!-- 访问/js/**的url从工程下/js/下解析 --> <mvc:resources location="/js/" mapping="/js/**" /> <mvc:resources location="/img/" mapping="/img/**" />
SpringMVC拦截器
-
用户请求到DispatherServlet中,DispatherServlet调用HandlerMapping查找Handler,HandlerMapping返回一个拦截的链儿(多个拦截),springmvc中的拦截器是通过HandlerMapping发起的。
在企业开发,使用拦截器实现用户认证(用户登陆后进行身份校验拦截),用户权限拦截。
springmvc拦截器方法
-
需要实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor { // 执行Handler之后执行 // 作为系统的统一异常处理,进行方法的执行性能的监控,在preHandler中设置一个时间点,在afterCompletion设置一个时间点,两个时间的差值 // 实现系统统一对日志的处理 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor1...afterCompletion..."); } // 在执行handler返回ModelAndView之前执行 // 如果需要向页面提供数据或者配配置视图信息,可以使用此方法从ModelAndView执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyInterceptor1...postHandle..."); } // 在handler之前执行 // 主要用于用户认证的校验、用户的权限校验 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor1...preHandle..."); // 如果为true表示放行,如果返回false表示拦截之后不继续执行handler return true; } }
配置拦截器
-
配置全局的拦截器,DispatcherServlet将配置的全局拦截器加载到所有的HandlerMapping。
在springmvc.xml中配置:
<!-- 拦截器的配置 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.syj.ssm.interceptor.MyInterceptor1"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.syj.ssm.interceptor.MyInterceptor2"></bean> </mvc:interceptor> </mvc:interceptors>
拦截器应用(用户认证拦截)
-
需求:用户访问系统的资源(url),如果用户没有进行身份认证,进行拦截,系统跳转登陆页面,如果用户已经认证通过,用户可以继续访问系统
的资源。 -
登录页面的开发:
<title>用户的登录</title> </head> <body> <form action="${pageContext.request.contextPath }/login.action" method="post"> 用户名:<input type="text" name="usercode" /><br> 密 码:<input type="password" name="password" /><br> <input type="submit" value="登录" > </form> </body> </html>
-
登录和退出的controller的开发:
@Controller public class Login { @RequestMapping("/login") // 用户的登录提交的方法 public String login(HttpServletRequest request, HttpServletResponse response, HttpSession session) { // 调用service通过查询数据库,验证账号和密码的正确性 // 执行各种操作 // 获取用户名 String usercode = (String) request.getParameter("usercode"); // 将用户名存储到session中 session.setAttribute("usercode", usercode); // 验证成功之后转跳到查询的列表 return "redirect:/item/queryItems.action"; } // 用户的退出 @RequestMapping("/logout") public String logout(HttpSession session) { // session失效 session.invalidate(); return "redirect:/login.jsp"; } }
-
拦截实现思路:
-
登录拦截器的开发:
public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("LoginInterceptor ... afterCompletion ..."); } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("LoginInterceptor ... postHandle ..."); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginInterceptor ... preHandle ..."); // 1、如果url是公开地址(比如网站的首页人人都可以查看) // 得到请求的地址 StringBuffer url = request.getRequestURL(); if (url.indexOf("login.action") >= 0) { return true; } // 2、判断session中是否存在该用户 HttpSession session = request.getSession(); String usercode = (String) session.getAttribute("usercode"); // 如果存在就放行 if (usercode != null && usercode.length() > 0 && !"".equals(usercode.trim())) { return true; } // 执行到这里说明该用户没有登录或者不存在 request.getRequestDispatcher("/login.jsp").forward(request, response); return false; } }
-
在springmvc.xml中配置拦截器:
SpringMVC和Struts2的区别
-
springmvc是通过方法的形参接收参数,在使用时可以以单例方式使用,建议使用单例。
struts是通过成员变量接收参数,在使用时必须以多例方式使用。
-
springmvc是基于方法开发,struts基于类开发。
springmvc将一个请求的Method和Handler进行关联绑定,一个method对应一个Handler。
-
springmvc开发以方法为单位进行开发,方法更帖进service(业务方法)。
-
经过实际测试,发现struts标签解析速度比较慢,建议在实际开发时使用jstl。