# spring-showcase **Repository Path**: thinwonton/spring-showcase ## Basic Information - **Project Name**: spring-showcase - **Description**: spring的演示案例 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2017-06-21 - **Last Updated**: 2022-06-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #springdemo [TOC] ##1. springmvc-restful 该工程演示springmvc提供rest服务。 **解决PUT/DELETE方法在controller中不能获取参数的问题** 因为HTML表单提交中没有PUT/DELETE方法,所以需要通过POST方式模拟PUT/DELETE方法。 (a) web.xml添加过滤器 HiddenHttpMethodFilter ``` HiddenHttpMethodFilter org.springframework.web.filter.HiddenHttpMethodFilter HiddenHttpMethodFilter /* ``` (b) 通过POST表单提交方式模拟PUT/DELETE方法请求,需要借助隐藏字段_method,_method将会在HiddenHttpMethodFilter过滤器中提取,具体查看HiddenHttpMethodFilter过滤器源码。 ```
``` (c) controller中指定接收method ``` /** * 更新用户 * * @return */ @RequestMapping(method = RequestMethod.PUT, produces = { "application/json;charset=UTF-8" }) public ResponseEntity updateUser(User user) { System.out.println("PUT: "+user.toString()); } /** * 删除用户 * * @return */ @RequestMapping(method = RequestMethod.DELETE, produces = { "application/json;charset=UTF-8" }) public ResponseEntity deleteById(@RequestParam(value = "id", defaultValue = "0") Long id) { System.out.println("delete ---> ID=" + id); } ``` ##2. springmvc-jsonp springmvc-jsonp-user 该工程演示ajax jsonp跨域请求时,springmvc返回jsonp格式数据。 springmvc-jsonp工程提供门户页面,部署在127.0.0.1:80域名;springmvc-jsonp-user提供用户服务,部署在127.0.0.1:8080域名,它为门户页面提供获取用户资料的服务。门户页面如果直接访问用户服务提供的API获取用户资料,将会被浏览器提示为跨域请求拒绝访问,此时需要利用JSONP访问用户模块的API。 (1)AJAX JSONP ``` //加载用户资料 $.ajax({ url : "http://127.0.0.1:8080/api/user", type : "get", dataType : "jsonp", jsonp : "callback", success : function(json) { $('#userInfo').html(JSON.stringify(json)); }, error : function() { alert('error'); } }); ``` (2)controller返回JSONP格式的数据 ``` @RequestMapping(value = { "", "/" }, method = RequestMethod.GET, produces = { "application/json;charset=UTF-8" }) @ResponseBody public Object getUser(@RequestParam(value = "callback", required = false) String callback) { User user = new User(); user.setId(100L); user.setAge(20); user.setName("hugo"); if (StringUtils.isBlank(callback)) { return user; } // 返回jsonp格式 MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(user); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; } ``` ##3. springmvc-interceptor 该工程演示springmvc拦截器的执行顺序。其中,正常流程涉及MyHandlerInterceptor1、MyHandlerInterceptor2、MyHandlerInterceptor3三个拦截器,中断流程涉及MyHandlerInterceptor4、MyHandlerInterceptor5、MyHandlerInterceptor6三个拦截器,MyHandlerInterceptor5将会在preHandle中返回false。 **正常流程** ``` MyHandlerInterceptor1--->preHandle MyHandlerInterceptor2--->preHandle MyHandlerInterceptor3--->preHandle HelloController---->hello MyHandlerInterceptor3--->postHandle MyHandlerInterceptor2--->postHandle MyHandlerInterceptor1--->postHandle MyHandlerInterceptor3--->afterCompletion MyHandlerInterceptor2--->afterCompletion MyHandlerInterceptor1--->afterCompletion ``` **中断流程** ``` MyHandlerInterceptor4--->preHandle MyHandlerInterceptor5--->preHandle MyHandlerInterceptor4--->afterCompletion ``` (1)拦截器注册 ``` ``` (2)实现拦截器 ``` public class MyHandlerInterceptor4 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(MyHandlerInterceptor4.class.getSimpleName() + "--->preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(MyHandlerInterceptor4.class.getSimpleName() + "--->postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(MyHandlerInterceptor4.class.getSimpleName() + "--->afterCompletion"); } } ``` ##4. springmvc-multiple-submit 演示防止重复提交表单 在表单中隐藏字段token,springmvc对比session中的token与表单中的token是否一致判断是否重复提交表单 (1)定义注解,在controller中增加注解指示在拦截器中添加或者移除session中的token ``` /** *

* 防止重复提交注解,用于方法
* 在新建页面方法上,设置preHandle为true,此时拦截器会在Session中保存一个token, * 同时需要在新建的页面中添加
* 保存方法需要验证重复提交的,设置avoid为true 此时会在拦截器中验证是否重复提交 *

* */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AvoidMultipalSubmit { boolean preHandle() default false; boolean avoid() default false; } ``` (2) 在controller中添加注解 ``` @Controller public class UserController { /** * 保存用户信息 * * @param user * @return */ @AvoidMultipalSubmit(avoid = true) @RequestMapping(value = "/user", method = RequestMethod.POST) public String saveUser(User user) { System.out.println("save user -->" + user.toString()); return "redirect:/page/user/ok"; //返回保存成功的页面 } /** * 返回用户页面 * * @return */ @AvoidMultipalSubmit(preHandle = true) @RequestMapping(value = "/page/user") public String userPage() { return "user"; } /** * 返回保存用户OK页面 * * @return */ @RequestMapping(value = "/page/user/ok") public String okPage() { return "ok"; } } ``` (3)定义和注册拦截器 ``` public class AvoidMultipalSubmitHandlerInterceptor extends HandlerInterceptorAdapter { private static final String TOKEN = "token"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); // 获取方法上的注解 AvoidMultipalSubmit annotation = method.getAnnotation(AvoidMultipalSubmit.class); if (annotation != null) { // 在session保存token if (annotation.preHandle()) { request.getSession().setAttribute(TOKEN, TokenProcessor.getInstance().generate()); } // 防止重复提交 if (annotation.avoid()) { if (isMultipleSubmit(request)) { return false; } request.getSession().removeAttribute(TOKEN); } } } return true; } /** * 判断是否重复提交表单 * * @param request * @return */ private boolean isMultipleSubmit(HttpServletRequest request) { String serverToken = (String) request.getSession().getAttribute(TOKEN); if (serverToken == null) { return true; } String clinetToken = request.getParameter(TOKEN); if (clinetToken == null || !serverToken.equals(clinetToken)) { return true; } return false; } } ``` ``` ``` (4)设置表单隐藏输入框 ```
用户名: 年龄:
``` ## 5. springmvc-exception 该工程演示如何处理特定的异常。举例:抛出auth异常时返回某个页面。参考 http://howtodoinjava.com/spring/spring-mvc/spring-mvc-simplemappingexceptionresolver-example/?utm_source=tuicool&utm_medium=referral ## 6. spring-schema-extend 基于可扩展Schema的特性自定义标签,spring将会把该标签对应的bean注入到spring容器中进行管理。 ## 7. springmvc-ContentNegotiatingViewResolver 内容协商视图解析器 配置: ``` json=application/json xml=application/xml com.github.thinwonton.spring.entity.User ``` ## 8. springmvc-aspectj spring aop 演示 ## 9. springmvc-aop-cache 基于spring aop的cache解决方案,示例包含ehcache和redis缓存 配套教程: https://my.oschina.net/thinwonton/blog/878090 ## 10. springmvc-hibernate-validator 基于hibernate validator的spring mvc后台校验 ## 11. springmvc-handlerMethodArgumentResolver 研究HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler ## 11. springmvc-webDataBinder 研究webDataBinder string -> date会发生下面的错误,所以要自定义转换器 ``` WARN org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type [java.lang.String] to required type [java.util.Date]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '1986-04-11'; nested exception is java.lang.IllegalArgumentException ```