SpringMVC-特性annotation

局部配置


@InitBinder

日期参数绑定
通常我们使用它,来解决日期类型参数绑定的问题:

@Controller
@RequestMapping("/ad")
public class AdapterController {
	private SimpleDateFormat yMd = new SimpleDateFormat("yyyy-MM-dd");
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(Date.class, new CustomDateEditor(yMd,false));
	}
}

参数校验 with @Valid

  • 1.注册一个敏感词Validator,
@Component
public class SensitiveNameValidator implements Validator {
	@Override
	public boolean supports(Class<?> clazz) {
		return String.class == clazz;
	}

	@Override
	public void validate(Object target, Errors errors) {
		String str = (String) target;
		if(str.indexOf("xi") > -1) {
			errors.reject("-0001","包含敏感词");
		}
	}
}
  • 2.在controller中添加validator信息:
@Controller
@RequestMapping("/ad")
public class AdapterController {
	@InitBinder
	public void initBinderValidator(WebDataBinder binder) {
		//设置为sensitiveNameValidator
//		binder.setValidator(sensitiveNameValidator);

		//添加一个validator
		binder.addValidators(sensitiveNameValidator);
	}
}
  • 3.在目标方法中使用validator:
@Controller
@RequestMapping("/ad")
public class AdapterController {
	@RequestMapping(value = "/valid")
	@ResponseBody
	public String index(@Valid String name, BindingResult br){
		if(br.hasErrors()){
			br.getAllErrors().forEach(error ->{
				System.out.println("error===="+error);
			});
		}
		return "hello:" + name;
	}
}

@ModelAttribute

@ModelAttribute方法通常被用来填充一些公共需要的属性或数据。

@Controller
@RequestMapping("/ad")
public class AdapterController {
	//给当前model设置("className",xxx)
	@ModelAttribute("className")
	public String setModel(){
		return this.getClass().getName();
	}

	//给当前model设置("teacher", "老夫子")
	@ModelAttribute
	public void setModel2(Model model2){
		model2.addAttribute("teacher", "老夫子");
	}

	// 给当前model设置("string","execlib")
	@ModelAttribute
	public String setModel3(){
		return "execlib";
	}

	@RequestMapping("/attr")
	@ResponseBody
	public String attr(Model model){
		Map map = model.asMap();
		System.out.println("========="+map.get("className")); //cn.jhs.mvc.controller.AdapterController
		System.out.println("========="+map.get("teacher")); //老夫子
		System.out.println("========="+map.get("string")); //execlib
		return "success";
	}

	@RequestMapping("attr2")
	public String attr2(@ModelAttribute("teacher") String teacher, BindingResult result) {
		if (result.hasErrors()) {
			return "error";
		}
		System.out.println("========="+teacher); //老夫子
		return "success";
	}
}

@SessionAttributes

Spring MVC对session的操作有如下两种方式:

  • 基于HttpSession
  • 基于注解@SessionAttributes

@SessionAttributes
SessionAttributes是只能注解于类或者接口,@SessionAttributes的value代表我们需要把什么样的对象放入session,在我们的方法后当我们把对象放入ModelMap这个对象的时候,根据匹配规则也会自动设置到session中。
@SessionAttributes比较简单,只有两个属性:valuetypes

  • value : 数组。 存储任意modelAttribute中任意 同名的属性。
  • types: 数组。 存储任意modelAttribute中任意 同类型的属性。
  • 注意 :当两个属性同时配置时, 二者是 or 的关系,取二者匹配到属性的合集

demo

@Controller
@RequestMapping("/session")
@SessionAttributes(value={"name","birth","age"},types = {String.class,Date.class})
public class SessionController {
	private final String CUR_USER_NAME_KEY = "cur_user_name";

	@RequestMapping("/set")
	@ResponseBody
	public String set(Model model , HttpSession session) {
		//与@SessionAttributes value匹配 或者属于 types中任意类型即可
		model.addAttribute("name", "Trump");
		model.addAttribute("birth", new Date());
		model.addAttribute("age", 31);

		//传统session方式
		session.setAttribute(CUR_USER_NAME_KEY,"X-MAN"); 
		return "set session success!";
	}

	@RequestMapping("/get")
	@ResponseBody
	public String error(HttpSession session){
		System.out.println("======"+session.getAttribute(CUR_USER_NAME_KEY)); // X-MAN
		
		System.out.println("======"+session.getAttribute("name")); // Trump
		System.out.println("======"+session.getAttribute("birth")); //当前date
		System.out.println("======"+session.getAttribute("age")); //31 
		return "success";
	}
}

全局配置

上面列出的@InitBinder、@ModelAttribute都仅在Controller内部有效,那么有没有能够一处配置,所有的@Controller都起作用的配置方法呢,答案是有的

@ControllerAdvice

//org.springframework.web.bind.annotation.ControllerAdvice ;
public @interface ControllerAdvice {
	//basePackages属性的别名
	String[] value() default {};
	
	//扫描basePackages下的所有Controller
	String[] basePackages() default {};
	
	//相对于basePackages更细粒度,到具体的classes
	Class<?>[] basePackageClasses() default {};
	
	//Controller 具有指定的父类或实现指定的接口
	Class<?>[] assignableTypes() default {};
	
	//Controller 具有指定类型注解
	Class<? extends Annotation>[] annotations() default {};
}

全局属性配置

@ControllerAdvice
public class MyControllerAdvice {
	@ModelAttribute("globalAttr")
	public String setModelGlobal(){
		return "全局属性";
	}
}

全局异常处理 with @ExceptionHandler

@ControllerAdvice
public class MyControllerAdvice {
	public static final String DEFAULT_ERROR_VIEW = "/error";
	
	@ExceptionHandler(value = Exception.class)
	public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e)  throws Exception {
			ModelAndView mav = new ModelAndView();
			mav.addObject("exception", e);
			mav.addObject("url", req.getRequestURL());
			mav.setViewName(DEFAULT_ERROR_VIEW);
			return mav;
		}
}

RestControllerAdvice

RestControllerAdvice,作用于@RestController注解下的bean中。用法同ControllerAdvice类似。


@ResponseBodyAdvice

@ResponseBodyAdvice可以修改返回值,比如数据脱敏、数据加密等。
返回值类型限制
它只能对如下两种返回值的方法起作用:

  • 返回值为HttpEntity类型
  • 返回值或处理器方法上注释了 @ResponseBody

类定义限制
ResponseBodyAdvice必须

  1. register到RequestMappingHandlerAdapter 或者ExceptionHandlerExceptionResolver
  2. 它的定义上有@ControllerAdvice注解。

demo

@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<String> {

	//判断是否为手机号方法,逻辑省略,直接返回true
	private boolean isTelNo(MethodParameter returnType){
		return true;
	}

	@Override
	public boolean supports(MethodParameter returnType, Class converterType) {
		return isTelNo(returnType);
	}


	@Override
	public String beforeBodyWrite(String telNo, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
		//手机号脱敏
		return telNo.replaceAll("(\\d{3})(\\d{4})(\\d{3})","$1****$3") ;
	}
}


参数绑定

@MatrixVariable

MatrixVariable也是url中的一部分,不过它不是url的一个值,而是值的一些属性,它通过分号和逗号配合使用。
SpringMvc使用
<mvc:annotation-driven enable-matrix-variables="true"> </mvc:annotation-driven>
开启@MatrixVariable功能。

基本用法

	//GET /phone/xiaomi;color=red
	@RequestMapping("/phone/{factory}")
	public String matrix(@PathVariable  String factory,@MatrixVariable String color){
		System.out.println(factory);//xiaomi
		System.out.println(color); //red
		return factory + ":" +color;
	}

多个@MatrixVariable

	//GET /phone/xiaomi;size=64g;color=red
	@RequestMapping("/phone/{factory}")
	public String multi(@PathVariable  String factory,@MatrixVariable String size,@MatrixVariable String color){
		System.out.println(factory);//xiaomi
		System.out.println(size); //64g
		System.out.println(color); //red
		return factory + ":" +size + ":" +color;
	}

多个@MatrixVariable转换Map
使用map时,map的value是List

	//GET /phone/xiaomi;size=64g;color=red
	@RequestMapping("/phone/{factory}")
	public String map(@PathVariable  String factory,@MatrixVariable Map map){
		//返回的value是List
		System.out.println(factory);//xiaomi
		System.out.println(map.get("size")); // [11]
		System.out.println( map.get("color")); // [red],
		return factory + ":" +map;
	}

传递Set属性

	//GET /phone/xiaomi;size=64g;color=red,black
	@RequestMapping("/phone/{factory}")
	public String set(@PathVariable  String factory,@MatrixVariable String size,@MatrixVariable Set color){
		System.out.println(factory);//xiaomi
		System.out.println(size); //64g
		System.out.println(color); //[red, black]
		return factory + ":" +size + ":" +color;
	}

指定pathVar
{factory}和{category}都有color属性,可以通过pathVar来分割,将属性存储至不同的map中去。

	//GET /phone/xiaomi;color=red,write/note5;color=black
	@RequestMapping("/phone/{factory}/{category}")
	public String multiMap(@PathVariable  String factory,@MatrixVariable(value="color",pathVar = "factory") Set  colorSet ,@MatrixVariable(pathVar = "category",value = "color") String  color2 ){

		System.out.println(factory);//xiaomi
		System.out.println(colorSet); //[red,write]
		System.out.println(color2); //black
		return factory + ":" +colorSet + ":" +color2;
	}


异常处理

servlet容器默认错误页面

web.xml

  <error-page>
    <error-code>404</error-code>
    <location>/errors/404.jsp</location>
  </error-page>

servlet处理
首先定义servlet

@Controller
public class ErrorController {
	@RequestMapping(value = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public Map<String, Object> handle(HttpServletRequest request) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("status", request.getAttribute("javax.servlet.error.status_code"));
		map.put("reason", request.getAttribute("javax.servlet.error.message"));
		return map;
	}
}

error.jsp

<%@ page contentType="application/json" pageEncoding="UTF-8"%>
{
    status:<%=request.getAttribute("javax.servlet.error.status_code") %>,
    reason:<%=request.getAttribute("javax.servlet.error.message") %>
}

@ResponseStatus

使用@ResponseStatus,当异常被抛出时,ResponseStatusExceptionResolver会设置相应的响应状态码。
自定义异常类

@ResponseStatus(value= HttpStatus.NOT_FOUND, reason="全局 not found exception")
public class MyMvcException extends  RuntimeException {
}

使用:

	@RequestMapping("/status")
	public String status(){
		throw new MyMvcException();
	}

结果如下图:
在这里插入图片描述

定义在method

	@RequestMapping("/status2")
	@ResponseBody
	@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "FORBIDDEN更改状态")
	public String status2(){
		return "success";
	}

结果如下图:
在这里插入图片描述

定义在方法上时,无论有无异常抛出,Http status状态码都会被改写!

结合@ExceptionHandler

	@ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="数组越界异常....")  // 400
	@ExceptionHandler(ArrayIndexOutOfBoundsException.class)
	public void conflict() {
		// Nothing to do
	}


	@RequestMapping("/status3")
	public String status3(){
		throw new ArrayIndexOutOfBoundsException();
	}

处理结果:
在这里插入图片描述

全局页面配置

使用Spring MVC的时候,当添加一个新页面访问总是要新增一个Controller或者方法,后跳转到指定的页面上去。
可以使用如下的全局配置

WebMvcConfigurerAdapter

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/success").setViewName("success");
		registry.addViewController("/error").setViewName("error");
		registry.addViewController("/loginPage").setViewName("login");
	}
}

而在spring5.x之后,WebMvcConfigurerAdapter类被标记为@Deprecated

@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
	@Override
	public void configurePathMatch(PathMatchConfigurer configurer) {
	}
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
	}
}

,原因是JDK8之后,接口支持default方法,所以我们可以直接通过实现了WebMvcConfigurer 来实现诸上功能;

WebMvcConfigurer

@Configuration
public class MvcConfig implements WebMvcConfigurer{

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/success").setViewName("success");
		registry.addViewController("/error").setViewName("error");
		registry.addViewController("/loginPage").setViewName("login");
	}
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册interceptor
	}
}

BasicErrorController

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class ExtendedErrorController extends BasicErrorController implements ErrorController {
	public ExtendedErrorController(ErrorAttributes errorAttributes, ServerProperties properties, List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, properties.getError(), errorViewResolvers);
	}

	@RequestMapping(
			produces = {"text/html"}
	)
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		HttpStatus status = this.getStatus(request);
		response.setStatus(status.value());
		Map<String, Object> errorAttributes = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML));
		ModelAndView modelAndView = this.resolveErrorView(request, response, status, errorAttributes);
		response.setStatus(status.value());
		if (modelAndView == null) {
			modelAndView = new ModelAndView();
			String errorPath = this.errorPath(status);
			InternalResourceView view = new InternalResourceView(errorPath);
			modelAndView.setView(view);
			modelAndView.setStatus(status);
		}

		return modelAndView;
	}

	@RequestMapping(
			produces = {"application/json;charset=UTF-8"}
	)
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		return super.error(request);
	}

	private String errorPath(HttpStatus status) {
		switch(status) {
			case NOT_FOUND:
				return "/404.html";
			case UNPROCESSABLE_ENTITY:
				return "/422.html";
			default:
				return "/500.html";
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值