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("");
}
}