# 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
```