Springboot之Spring MVC

Springboot之Spring MVC

1.DispatcherServlet在springboot中的配置

1.1 在配置类中配置

@Bean  
 public ServletRegistrationBean dispatcherRegistration(
 					DispatcherServlet dispatcherServlet) {  
     return new ServletRegistrationBean(
     			dispatcherServlet,"/api/*"
     );  
}

1.2在application.properties中配置

spring.mvc.pathmatch.use-suffix-pattern=true
server.servlet.context-path=/   ->是项目访问根目录:例如:/vms

2.参数校验

数据的校验的重要性就不用说了,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。
JSR(Java Specification Requests) 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,这样就可以在需要校验的时候进行校验了,非常方便!
校验的时候我们实际用的是 Hibernate Validator框架。Hibernate Validator 是 Hibernate 团队最初的数据校验框架,Hibernate Validator 4.xBean Validation 1.0(JSR 303)的参考实现,Hibernate Validator 5.xBean Validation 1.1(JSR 349)的参考实现,目前最新版的 Hibernate Validator 6.xBean Validation 2.0(JSR 380)的参考实现。
SpringBoot 项目的 spring-boot-starter-web 依赖中已经有 hibernate-validator包,不需要引用相关依赖。如下图所示(通过 idea 插件—Maven Helper 生成)

2.1. 一些常用的字段验证的注解

@NotEmpty被注释的字符串的不能为 null 也不能为空
@NotBlank被注释的字符串非 null,并且必须包含一个非空白字符
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
@Email被注释的元素必须是 Email 格式。
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期

2.1.1验证请求体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {    
@NotNull(message = "classId 不能为空")    
private String classId;    
@Size(max = 33)    
@NotNull(message = "name 不能为空")    
private String name;    
@Pattern(regexp = "((^Man$|^Woman$|^UGM$))", message = "sex 值不在可选范围")    
@NotNull(message = "sex 不能为空")    
private String sex;    
@Email(message = "email 格式不正确")    
@NotNull(message = "email 不能为空")    
private String email;
}

需要在前端@RequestMapping注解的方法中加上@Valid

@RestController
@RequestMapping("/api")
public class PersonController {    
	@PostMapping("/person")    
	public ResponseEntity<Person> getPerson(@RequestBody @Valid Person person) {        
		return ResponseEntity.ok().body(person);   
	}
}

如果验证不通过就会抛出MethodArgumentNotValidException异常,我们可以在全局异常处理类中处理抛出的异常,如2.2.

2.2 @ControllerAdvice和@ExceptionHandler

@ControllerAdvice在全局处理类上使用

@ExceptionHandler在全局异常处理类的方法上使用

spring通过@ControllerAdvice@ExceptionHandler来处理全局异常

2.2.1示例:
// Target all Controllers annotated with @RestController
//表示所有被@RestControlle注解的controller都会使用全局异常处理
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
//表示指定包下的所有controller都会使用全局异常处理
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
//表示指定的controller会使用该全局异常处理
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    /**
     * 处理自定义的业务异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BizException.class)  
    @ResponseBody  
    public  ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){
        logger.error("发生业务异常!原因是:{}",e.getErrorMsg());
        return ResultBody.error(e.getErrorCode(),e.getErrorMsg());
    }

    /**
     * 处理空指针的异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){
        logger.error("发生空指针异常!原因是:",e);
        return ResultBody.error(CommonEnum.BODY_NOT_MATCH);
    }
	/**
	 *处理MethodArgumentNotValidException.class异常
	 *
	 **/
	@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Object requestMethodArgumentNotValidException(HttpServletRequest request,MethodArgumentNotValidException exception){
        Map<String,String> map = new HashMap<>();
        map.put("code", ErrorCodeEnum.FAILURE.errCodeString());
        map.put("message", "");
        if (exception != null){
            List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
            for (FieldError error : fieldErrors){
                error.getDefaultMessage();
                map.put("message", exception.getMessage());
                break;
            }
        }
        map.put("data", new Object().toString());
        return map;
    }
    
    /**
        * 处理其他异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, Exception e){
        logger.error("未知异常!原因是:",e);
        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }
}

2.2.2全局异常处理类的特点

1.全局异常处理只能处理被捕获的异常,如果在全局异常处理之前,异常被捕获,那么就无法在全局异常处理中的进行处理

2.全局异常处理类中定了多种异常处理方法,会优先处理子类异常,即如果同时定义了处理Exception和NullPointerException异常的方法,则会优先处理子类异常,也就是NullPointerException。如果只catch了子类异常,抛出更高级异常时,需在全局异常处理中定义处理方法;如果catch住了Exception异常,则所有的异常都不会被送到全局异常处理类

3.自义定全局异常处理除了可以处理上述的数据格式之外,也可以处理页面的跳转,只需在新增的异常方法的返回处理上填写该跳转的路径,并且使用ResponseBody 注解即可 。

4.RestControllerAdviceControllerAdvice的功能大致一样,但是会将返回的数据自动转换为Json格式

5.@ExceptionHandler@RequestMapping方法之后执行,而@ModelAttributehe,@InitBinder@RequestMapping方法之前执行。

3.拦截器(Interceptor和过滤器(Filter)的区别

3.1 相似之处

Spring的Interceptor(拦截器)与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等

3.2 区别

FilterInterceptorSummary
Filter 接口定义在 javax.servlet 包中接口 HandlerInterceptor 定义在org.springframework.web.servlet 包中
Filter 定义在 web.xml 中,现在可以在spring中的配置类中声明FilterRegistrationBeanspringboot实现WebMvcConfigurer,重写addInterceptors(InterceptorRegistry registry) 方法
Filter在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑servlet 的实现。拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期,在请求过程中获取信息,Interceptor 通常和请求更加耦合。在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情, interceptor 都能够轻松的实现
Filter 是 Servlet 规范规定的。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。使用范围不同
Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring容器内的,是Spring框架支持的。规范不同
Filter 不能够使用 Spring 容器资源拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可Spring 中使用 interceptor 更容易
Filter 是被 Server(like Tomcat) 调用Interceptor 是被 Spring 调用因此Filter总是优先于Interceptor执行

4.在springboot中注册Filter

1.通过配置类中声明FilterRegistrationBean来注册

@Bean
public FilterRegistrationBean<Filter> getFilter(){
	FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
    	registration.setFilter(new Filter());
    	registration.addUrlPatterns("/ui/*");
}

2.通过@WebFilter注解,但是通过注解无法设置优先级

5.消息转换器

Message Converters

可以在配置类中实现WebMvcConfigurer,重写 configureMessageConverters() 或者重写 extendMessageConverters() 方法来配置自己的http消息转换器(替代springMvc默认的)

@Configuration
@EnableWebMvc ->在springboot中不需要添加该注解
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(
    			List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(
                new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}

xml的配置方式

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http
      			.converter.json
       			.MappingJackson2HttpMessageConverter">	
            <property name="objectMapper" 													ref="objectMapper"/>
        </bean>
        <bean class="org.springframework.http
        		.converter.xml
        		.MappingJackson2XmlHttpMessageConverter">
            <property name="objectMapper" ref="xmlMapper"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
<bean id="objectMapper" class="org.springframework
				.http.converter.json
				.Jackson2ObjectMapperFactoryBean"
      p:indentOutput="true"
      p:simpleDateFormat="yyyy-MM-dd"
      p:modulesToInstall=
 				"com.fasterxml.jackson
 				.module.paramnames.ParameterNamesModule"/>

<bean id="xmlMapper" parent="objectMapper" 													p:createXmlMapper="true"/>

6.设置静态资源的访问路径

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                    .addResourceLocations("/public", "classpath:/static/")
            .setCachePeriod(31556926);
    }
}

8.异步请求的流程

1.控制器返回Callable
2.Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
3.DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
4.Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
5.根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。

可以定义自己的TaskExecutor,并在实现了WebMvcConfigure的配置类中重写

/**
     * <p>设置web异步请求callable的执行TaskExecutor</p>
     * @params [configurer]
     * @returns void
     * @author lijun58 2020/2/20 17:09
     */
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setTaskExecutor(getExecutor());
    }

9.如何在Service层获取当前Request

HttpServletRequest是何时放入上下文中的

在spring的DispatchServlet extends FrameworkServlet中有个方法

 void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
			
			...
			initContextHolders(request, localeContext, requestAttributes);
			}

其中initContextHolders(request, localeContext, requestAttributes);

private void initContextHolders(HttpServletRequest request,@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(
			localeContext, this.threadContextInheritable
			);
		}
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(
				requestAttributes, 
				this.threadContextInheritable
			);
		}
	}

requestAttribute放入了RequestContextHolder

ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<>("Request attributes");

在Service层调用方法

在使用的时候就可以直接调用

RequestAttribute attribute = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRquest)attribute.getRequest();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值