1. 程式人生 > >Spring-boot(二)WebMvcConfigurerAdapter詳解

Spring-boot(二)WebMvcConfigurerAdapter詳解

定義

WebMvcConfigurationAdapter是一個配置類,該配置類主要利用@Bean方式來配置,該配置類裡的配置主要功能是針對Http請求作統一處理。我們想要使用這樣的配置方式,需要自定義一個類取繼承這個配置類。

具體的API方法功能

以下的API中WebConfig 類也是我自己定義的,它是繼承WebMvcConfigurerAdapter類,並且裡邊什麼也沒做。

public void addFormatters(FormatterRegistry registry)

該方法的功能是新增一個例項,這個例項的功能在請求到達之前將資料進行格式化或者轉換,以及請求之後對返回給前端的資料進行轉換與格式化

@Configuration
public class SysWebConfig extends WebConfig {

    /**
     * 全域性處理http請求,主要用於用於物件與字串之間的轉換
     *
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new TestForamtter());
    }
}


/**
 * 該類是執行緒安全的,類似於PropertyEditer功能,用於物件與字串之間的轉換
 *
 */

public class TestForamtter implements Formatter<Test> {

    /**
     * 將請求中的字串轉換成物件
     * Test實體是自己瞎定義的物件,用於測試,這裡就不貼了
     * @param s
     * @param locale
     * @return
     * @throws ParseException
     */
    @Override
    public Test parse(String s, Locale locale) throws ParseException {
        Test test = new Test();
        test.setA("i am  is a");
        test.setB("i am is b ");
        return test;
    }

    /**
     * 將請求中的物件轉換成字串類
     *
     * @param test
     * @param locale
     * @return
     */
    @Override
    public String print(Test test, Locale locale) {
        return "this is test!";
    }
}

public void addInterceptors(InterceptorRegistry registry)

該方法功能是新增攔截器例項,這個例項需要實現HandlerInterceptor介面

/**
 * WEB配置類
 */
@Configuration
public class SysWebConfig extends WebConfig {
    /**
     * 新增攔截器例項。該例項需要實現HandlerInterceptor
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	 //註冊一個攔截器,addPathPatterns指定攔截的RUl,預設除了/login之外的對所有請求進行攔截
        registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**").excludePathPatterns("/login");
        super.addInterceptors(registry);
    }
}


/**
 * 這是一個攔截器,用於處理請求
 */

public class TestInterceptor implements HandlerInterceptor {

    /**
     * 這個方法是在請求controller之前執行的
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
            Exception {
        String token = request.getHeader("Authorization");
        System.err.println("請求controller之前獲取Token:" + token);
        return true;
    }

    /**
     * 這個方法只在請求controller之後,還未薰染檢視執行的
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView
            modelAndView) throws Exception {
        System.err.println("請求controller之後");
    }

    /**
     * 這個方法是在渲染檢視之後執行的
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception
            ex) throws Exception {
    }
}

public void configurePathMatch(PathMatchConfigurer configurer)

該個方法的功能是設定匹配路由請求規則


/**
 * WEB配置類
 */
@Configuration
public class SysWebConfig extends WebConfig {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 系統對外暴露的 URL 不會識別和匹配 .* 字尾,例如你請求的是/user.*,那麼系統
        //不會匹配到/user.html
        configurer.setUseSuffixPatternMatch(false) 
                .setUseTrailingSlashMatch(true); // 系統不區分 URL 的最後一個字元是否是斜槓 /
    }
}

public void addCorsMappings(CorsRegistry registry)

該方法的功能是配置哪些請求可以跨域請求,以及哪些機器可以訪問跨域請求。所謂的跨域大部分是針對前後端分離的專案,並且前端跟後端沒有部署在同一臺伺服器上


/**
 * WEB配置類
 */
@Configuration
public class SysWebConfig extends WebConfig {

    /**
     * 增加跨域支援
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")//對哪些請求支援跨域訪問
                .allowedOrigins("*")//哪些機器可以跨域訪問
                .allowCredentials(true)//是否支援使用者憑證
                .allowedMethods("", "", "", "")//支援跨域請求的請求方式
                .maxAge(3600);//準備響應前的快取持續的最大時間(以秒為單位)。
        super.addCorsMappings(registry);
    }
}

public void addResourceHandlers(ResourceHandlerRegistry registry)

該方法的功能就是新增靜態路徑,這個方法中的靜態路徑會覆蓋application.yml裡的配置

public class SysWebConfig extends WebConfig {
   
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //請求的地址
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");//對映的專案中的路徑,這個路徑也可以是額外路勁,例如E://file路徑
        super.addResourceHandlers(registry);
    }
}

public void addViewControllers(ViewControllerRegistry registry)

該方法的功能用於自定義檢視控制器

public class SysWebConfig extends WebConfig {
   
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //對於/hello的請求 重定向到/home
        registry.addRedirectViewController("/test/test", "/test/redirect");
        //對於/loginou請求,響應200狀態碼
        registry.addStatusController("/loginout", HttpStatus.OK);
        //對於/home請求,返回home檢視
        registry.addViewController("/home").setViewName("home");
        super.addViewControllers(registry);
    }
}

public void configureViewResolvers(ViewResolverRegistry registry)

該方法功能是配置檢視解析器的

public class SysWebConfig extends WebConfig {
   
    /**
     * 配置檢視解析器
     * @param registry
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //註冊一個Jsp解析器
        registry.jsp("/static/jsp", ".jsp");/
        //檢視解析器會根據返回的檢視名稱在Spring容器中找到檢視名字對應的Bean,這個Bean是View.class型別的,如果找到就直接返回
        registry.beanName();
        //用於註冊各種各樣的檢視解析器的包括自己定義的檢視解析器
        registry.viewResolver();
        //開啟檢視解析器裁決功能
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        super.configureViewResolvers(registry);
    }
     @Bean
    public TestViewResolver testViewResolver() {
        return new TestViewResolver(testView());
    }
    @Bean
    public TestView testView() {
        return new TestView();
    }
}


public class TestView  implements View {

    @Override
    public String getContentType() {
        return null;
    }

    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

    }
}

public class TestViewResolver implements ViewResolver {

    private View view;

    public TestViewResolver(View view) {
        this.view = view;
    }

    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        return view;
    }
}

public void addResourceHandlers(ResourceHandlerRegistry registry)

該的方法功能是處理靜態檔案用的,由於DispatcherSevelet對所有請求都進行攔截,當我們請求靜態檔案的時候,因為後端沒有對應的Controller,那麼這個請求就會交由DefaultServletHandler去處理。

public class SysWebConfig extends WebConfig {
   
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //請求的地址
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");//對映的專案中的路徑,這個路徑也可以是額外路勁,例如E://file路徑
        super.addResourceHandlers(registry);
    }
}

public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer)

該方法的功能用於自定義檢視控制器

public class SysWebConfig extends WebConfig {
   
     /**
     * 這個方法的的作用註冊一個Handler,當請求靜態資源的時候,如果沒有找到相應Controller,就會把這個請求交由defaultservlethandler處理,
     * 其實就是對DispatcherServlet的一種增強
     *
     * @param configurer
     */
    @Override
    public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
        // 等價於<mvc:default-servlet-handler />, 對靜態資原始檔的訪問, 將無法 mapping 到 Controller 的 path 交給 default servlet handler 處理
        configurer.enable();
    }
}

public void addArgumentResolvers(List argumentResolvers)

該方法的功能用於註冊Controller的引數解析器,並且這個引數可以註冊多個引數解析器,將前端傳過來的引數根據業務需求進行引數的解析,解析成我們想要的型別或者格式。

public class SysWebConfig extends WebConfig {
   
  /**
     * 新增引數解析器
     * @param argumentResolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(testArgumentResolver());
        super.addArgumentResolvers(argumentResolvers);
    }

    /**
     * 配置引數解析器的例項
     * @return
     */
    @Bean
    public TestArgumentResolver testArgumentResolver() {
        return new TestArgumentResolver();
    }
}

public class TestArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        //判斷Controller上是否存在該註解,如果存在就之下下邊的resovleArgument方法進行引數解析
        return methodParameter.hasParameterAnnotation(TestParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory)
            throws Exception {

        //獲取引數上的對應的主註解
        TestParam testParamAnn = methodParameter.getParameterAnnotation(TestParam.class);
        //獲取註解上的引數
        String paramName = testParamAnn.name();
        if (paramName.equals("")) {
            paramName = methodParameter.getParameterName();
        }

        //獲取request請求中的引數,
        Object parameter = nativeWebRequest.getNativeRequest(HttpServletRequest.class).getParameter(paramName);

        //可以根據具體的業務在此進行具體的引數轉換邏輯。。。。。
        return parameter == null || parameter.equals("") ? testParamAnn.defaultVaule() : parameter;
    }
}

@Target(value = ElementType.PARAMETER)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TestParam {
    String name() default "";
    String value() default "";
    String defaultVaule() default "";
}

addReturnValueHandlers

該方法的功能是一個註冊返回值解析器的,這個返回值解析器可以統一的將資料以一定的格式返回給前端。以下程式碼中有很多注意的地方,請認真檢視,具體的原因我就不一一細說了,百度上有很多這些資料。

@Configuration
public class SysWebConfig extends WebConfig {
	@Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        returnValueHandlers.add(testReturnValueHandler());
        super.addReturnValueHandlers(returnValueHandlers);
    }

    @Bean
    public TestReturnValueHandler testReturnValueHandler() {

        return new TestReturnValueHandler();
    }
}

public class TestReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        //只有帶有TestReturnValue的註解參會相應的Controller的返回值進行轉換或者封裝
        return returnType.hasMethodAnnotation(TestReturnValue.class);
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest) throws Exception {
        //特別注意的是,如果不想返回檢視那麼這裡一定要設定成true,他的作用類似於@ResponseBody的功能
        mavContainer.setRequestHandled(true);
        //統一的返回型別
        ReturnInfo<Object> returnInfo = new ReturnInfo<>();
        returnInfo.OK();
        returnInfo.setT(returnValue);
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

        response.addHeader("Content-type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.getWriter().append(objectMapper.writeValueAsString(returnInfo));
    }
}


@Target ( {ElementType.TYPE, ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TestReturnValue {

}

@Validated
@Controller
@RequestMapping("/test")
public class TestController {

    /**
     * 特別注意的是,這裡的Controller方法一定不能返回一個String型別,因為如果返回的是String型別
     * 他預設的就會去找對應的檢視,早晨請求404
     * @return
     */
    @RequestMapping(value = "/returnValue")
    @TestReturnValue
    public Object returnValue() {
        return "liyuzhi";
    }
}

configureHandlerExceptionResolver

該方法的功能是註冊Controller異常處理器的,其中的異常處理器可以統一處理Controller的異常

@Configuration
public class SysWebConfig extends WebConfig {
	  @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(testExceptionHandler());
        super.configureHandlerExceptionResolvers(exceptionResolvers);
    }

    @Bean
    public TestExceptionHandler testExceptionHandler() {
        return new TestExceptionHandler();
    }
}

public class TestExceptionHandler extends AbstractHandlerExceptionResolver {

    //由於spring自帶了異常處理器,我們想要執行我們自己的異常處理器
    //那麼就需要將我們的異常處理器的處理順序放在前邊,否則該異常處理器不會生效
    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object
            handler, Exception ex) {
        ex.printStackTrace();
        //如果Controller遇到異常就會跳轉到這個檢視
        //具體業務邏輯肯定是很複雜的,可以根據具體的業務來將這一塊細化一下
        return new ModelAndView("liyuzhi");
    }
}


@Validated
@Controller
@RequestMapping("/test")
public class TestController {

    /**
     * 特別注意的是,這裡的Controller方法一定不能返回一個String型別,因為如果返回的是String型別
     * 他預設的就會去找對應的檢視,早晨請求404
     *
     * @return
     */
    @RequestMapping(value = "/returnValue")
    @TestReturnValue
    public Object returnValue() {
        return 1;
    }


    /**
     * 特別注意的是,這裡的Controller方法一定不能返回一個String型別,因為如果返回的是String型別
     * 他預設的就會去找對應的檢視,早晨請求404
     *
     * @return
     */
    @RequestMapping(value = "/exceptionResult")
    @TestReturnValue
    public Object exceptionResult() {
        throw new RuntimeException("");
    }
}