spring mvc 复习整理二(仗剑走天涯) Spring Mvc 核心技术 设定字符集 1 2 3 4 5 6 7 8 9 10 11 12 13 <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class >org .springframework .web .filter .CharacterEncodingFilter </filter -class > <init -param > <param -name >encoding </param -name > <param -value >utf -8</param -value > </init -param > </filter > <filter -mapping > <filter -name >CharacterEncodingFilter </filter -name > <url -pattern >/*</url -pattern > </filter -mapping >
返回JSON数据(序列化) ajax请求后台获取数据,而不需要访问任何的页面,这种场景在前后分离的项目当中尤其重要
将我们的对象转化为json字符串。
将返回的内容直接写入响应体,不走视图解析器。
然后将Content-Type设置为application/json即可
fastjson
1 2 3 4 5 6 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.80</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 <mvc:annotation-driven > <mvc:message-converters > <bean id ="fastjson" class ="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" > <property name ="supportedMediaTypes" > <list > <value > text/html;charset=UTF-8</value > <value > application/json;charset=UTF-8</value > </list > </property > </bean > </mvc:message-converters > </mvc:annotation-driven >
1 2 3 4 5 6 7 8 9 10 11 @RequestMapping(value = "/json1",produces = "application/json;charset=utf-8") @ResponseBody public List<User> json1 () { ArrayList<User> users = new ArrayList<>() {{ add(new User("xiaobo" , "xiaobo" )); add(new User("wangyibo" , null )); }}; return users; }
jackson
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-core</artifactId > <version > 2.13.0</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-annotations</artifactId > <version > 2.13.0</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.13.0</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <mvc:annotation-driven conversion-service ="conversionService" > <mvc:message-converters > <bean class ="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" > <property name ="objectMapper" ref ="customObjectMapper" /> <property name ="supportedMediaTypes" > <list > <value > text/plain;charset=UTF-8</value > <value > application/json;charset=UTF-8</value > </list > </property > </bean > </mvc:message-converters > </mvc:annotation-driven > <bean name ="customObjectMapper" class ="com.dream.xiaobo.config.CustomObjectMapper" /> <bean id ="conversionService" class ="org.springframework.context.support.ConversionServiceFactoryBean" > <property name ="converters" > <set > <bean id ="stringToDateConverter" class ="com.dream.xiaobo.config.DateConvertor" /> </set > </property > </bean >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class CustomObjectMapper extends ObjectMapper { public CustomObjectMapper () { super (); configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false ); setTimeZone(TimeZone.getTimeZone("GMT+8" )); setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" )); configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false ); setSerializationInclusion(JsonInclude.Include.NON_NULL); getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true ); } }
1 2 3 4 5 6 7 @PostMapping(value = "/insert") public String user (@RequestBody UserVO user) { System.out.println(user); return "user" ; }
数据转换 配置文件方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class DateConvertor implements Converter <String , Date > { @Override public Date convert (String source) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd" ); Date parse = null ; try { parse = simpleDateFormat.parse(source); } catch (ParseException e) { e.printStackTrace(); } return parse; } }
1 2 3 4 5 6 7 8 9 <bean id ="conversionService" class ="org.springframework.context.support.ConversionServiceFactoryBean" > <property name ="converters" > <set > <bean id ="stringToDateConverter" class ="com.dream.xiaobo.config.DateConvertor" /> </set > </property > </bean > <mvc:annotation-driven conversion-service ="conversionService" />
注解方式 @DateTimeFormat:当从requestParam中获取string参数并需要转化为Date类型时,会根据此注解的参数pattern的格式进行转化 springmvc提供的
@JsonFormat:当从请求体中获取json字符序列,需要反序列化为对象时,时间类型会按照这个注解的属性内容进行处理 jackson提供的
1 2 3 4 5 6 @JsonFormat( pattern = "yyyy-MM-dd", timezone = "GMT-8" ) @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday;
大致处理过程
数据检验 JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架
Constraint
详细信息
@Null
被注解的元素必须为 null
@NotNull
被注解的元素必须不为 null
@AssertTrue
被注解的元素必须为 true
@AssertFalse
被注解的元素必须为 false
@Min(value)
被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)
被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)
被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)
被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)
被注解的元素的大小必须在指定的范围内
@Digits (integer, fraction)
被注解的元素必须是一个数字,其值必须在可接受的范围内
@Past
被注解的元素必须是一个过去的日期
@Future
被注解的元素必须是一个将来的日期
@Pattern(value)
被注解的元素必须符合指定的正则表达式
@Email
被注解的元素必须是电子邮箱地址
@Length
被注解的字符串的大小必须在指定的范围内
@NotEmpty
被注解的字符串的必须非空
@Range
被注解的元素必须在合适的范围内
spring mvc 数据校验
1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > javax.validation</groupId > <artifactId > validation-api</artifactId > <version > 2.0.1.Final</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-validator</artifactId > <version > 6.0.9.Final</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Data @AllArgsConstructor @NoArgsConstructor @ToString public class Student { @NotNull(message = "用户名不能为空") private String userName; @Pattern(regexp = "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", message = "手机号码不正确") private String phone; @Min(value = 1,message = "年龄不能小于{value}岁") @Max(value = 120,message = "年龄不能大于{value}岁") private Integer age; @DateTimeFormat(pattern = "yyyy-MM-dd") @Past(message = "必须是一个之前的时间") private Date birthday; @Email(message = "邮箱格式不正确") private String email; }
1 2 3 4 5 <bean id ="localValidator" class ="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" > <property name ="providerClass" value ="org.hibernate.validator.HibernateValidator" /> </bean > <mvc:annotation-driven validator ="localValidator" />
视图解析器 默认视图解析器 1 2 3 4 5 6 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/page/" /> <property name ="suffix" value =".jsp" /> </bean >
想要添加新的视图解析器可以删除之前的也可以添加
1 <property name ="order" value ="10" />
order表示视图解析的 优先级 ,数字越小优先级越大(即:0为优先级最高,所以优先进行处理视图)
就是 order为0时,modelandview最先到达,然后返回到前端浏览器,然后才往order大的走
配置Thymeleaf视图解析器模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <bean id ="templateResolver" class ="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver" > <property name ="prefix" value ="classpath:/thymeleafs/" /> <property name ="suffix" value =".html" /> <property name ="templateMode" value ="HTML" /> <property name ="cacheable" value ="true" /> <property name ="characterEncoding" value ="UTF-8" /> </bean > <bean id ="templateEngine" class ="org.thymeleaf.spring4.SpringTemplateEngine" > <property name ="templateResolver" ref ="templateResolver" /> <property name ="enableSpringELCompiler" value ="true" /> </bean > <bean id ="viewResolver" class ="org.thymeleaf.spring4.view.ThymeleafViewResolver" > <property name ="order" value ="1" /> <property name ="characterEncoding" value ="UTF-8" /> <property name ="templateEngine" ref ="templateEngine" /> </bean >
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.thymeleaf</groupId > <artifactId > thymeleaf</artifactId > <version > 3.0.14.RELEASE</version > </dependency > <dependency > <groupId > org.thymeleaf</groupId > <artifactId > thymeleaf-spring4</artifactId > <version > 3.0.14.RELEASE</version > </dependency >
1 <html xmlns:th ="http://www.thymeleaf.org" >
全局异常捕获 HandlerExceptionResolver
一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合
另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛,指导传递给浏览器
所以我们可以用HandlerExceptionResolver统一进行处理
service层尽量不要处理异常,如果自己捕获并处理了,异常就不生效了。特别是不要生吞异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Component public class GlobalExceptionResolver implements HandlerExceptionResolver { ModelAndView modelAndView = new ModelAndView(); @Override public ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (ex instanceof ArithmeticException) { return ArithmeticExceptionResolver(); }else { return modelAndView; } } public ModelAndView ArithmeticExceptionResolver () { modelAndView.setViewName("exception" ); modelAndView.addObject("exception" , "系统维护中" ); return modelAndView; } }
1 <bean class ="com.dream.xiaobo.handler.GlobalExceptionResolver" />
@ControllerAdvice 该注解同样能实现异常的全局统一处理
@ControllerAdvice三个功能
@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @ControllerAdvice @Slf4j public class GlobalExceptionResovlerController { @ExceptionHandler(ArithmeticException.class) public String processGrithmeticException (ArithmeticException exception, Model model) { model.addAttribute("exception" ,"系统繁忙,请稍后重试" ); log.error("发生了数学类的异常" ,exception); return "exception" ; } @ExceptionHandler(BusinessException.class) public String processBusinessException (BusinessException exception,Model model) { model.addAttribute("exception" ,"系统繁忙,请稍后重试" ); log.error("发生了业务类的异常" ,exception); return "exception" ; } @ExceptionHandler(Exception.class) public String processException (Exception exception,Model model) { model.addAttribute("exception" ,"系统繁忙,请稍后重试" ); log.error("发生了普通类的异常" ,exception); return "exception" ; } }
资源处理 1 2 3 4 <mvc:resources mapping ="/js/**" location ="classpath:/static/js/" /> <mvc:resources mapping ="/css/**" location ="classpath:/static/css/" /> <mvc:resources mapping ="/image/**" location ="classpath:/static/image/" />
拦截器 SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的【任意】请求
spring mvc拦截器常见的应用场景
登录认证拦截器
字符过滤器
日志操作拦截器
自定义拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { request.getSession().setAttribute("user" ,"session" ); Object user = request.getSession().getAttribute("user" ); if (null == user) { response.sendRedirect("/thymeleaf/login" ); return false ; }else { System.out.println("========preHandler" ); return true ; } } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("========postHandler" ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("=======afterCompletion" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <mvc:interceptors > <mvc:interceptor > <mvc:mapping path ="/**" /> <mvc:exclude-mapping path ="/thymeleaf/toLogin" /> <mvc:exclude-mapping path ="/thymeleaf/login" /> <bean id ="loginInterceptor" class ="com.dream.xiaobo.interceptor.LoginInterceptor" /> </mvc:interceptor > </mvc:interceptors >
拦截器规则 preHandle
:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行
postHandle
:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方法都返回true时才会调用
afterCompletion
:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true也会调用
spring mvc拦截器流程
Restful restful是一种编程风格
利用@RequestMapping 指定要处理请求的URI模板和HTTP请求的动作类型
利用@PathVariable讲URI请求模板中的变量映射到处理方法参数上
利用Ajax,在客户端发出PUT、DELETE动作的请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @RestController @RequestMapping(value = "/goods") public class RestfulController { @GetMapping(value = "/findAll") public R findAll () { List<Goods> goods = List.of( new Goods("电脑" , new BigDecimal("20000" )), new Goods("电话" ,new BigDecimal("4000" )) ); return R.success(goods); } @GetMapping(value = "/findAll/{id}") public R findAllById (@PathVariable Integer id) { Goods goods = new Goods("电话" , new BigDecimal("4000" )); return R.success(goods); } @PostMapping(value = "/save") public R insert (@RequestBody Goods goods) { Goods goodss = new Goods(goods.getName(), goods.getPrice()); return R.success(goodss); } @PutMapping(value = "/update") public R update (Goods goods) { Goods goods1 = new Goods(goods.getName(), goods.getPrice()); return R.success(goods1); } @DeleteMapping(value = "/delete/{id}") public R deleteById (@PathVariable Integer id) { return R.error(); } }
1 2 3 4 5 6 7 8 9 10 @Data @NoArgsConstructor @AllArgsConstructor public class Goods { private String name; private BigDecimal price; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Data @Builder public class R { private static final Integer CODE_SUCCESS = 1000 ; private static final Integer CODE_ERROR = 2000 ; private static final String MESSAGE_SUCCESS = "操作成功" ; private static final String MESSAGE_ERROR = "操作失败" ; private String message; private Integer code; private Object data; public static R success () { return R.builder().code(CODE_SUCCESS) .message(MESSAGE_SUCCESS).build(); } public static R success (Object data) { return R.builder().code(CODE_SUCCESS) .message(MESSAGE_SUCCESS).data(data).build(); } public static R error () { return R.builder().code(CODE_ERROR) .message(MESSAGE_ERROR).build(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > 商品</title > </head > <body > <script src ="https://unpkg.com/axios/dist/axios.min.js" > </script > <h1 > 商品页面</h1 > <script > axios.get('/goods/findAll' ) .then(function (response ) { console .log(response); }) .catch(function (error ) { console .log(error); }); axios.get('/goods/findAll' , { params : { id : 1 } }) .then(function (response ) { console .log(response); }) .catch(function (error ) { console .log(error); }); axios.post('/goods/save' , { name : '电脑' , price : 10000 }) .then(function (response ) { console .log(response); }) .catch(function (error ) { console .log(error); }); </script > </body > </html >
1 2 3 4 5 @RequestMapping(value = "/toGoods") public String toGoods () { return "goods" ; }
你知道的越多 你不知道的越多 嘿 我是小博 带你一起看我目之所及的世界……