springmvc[1]-HandlerInterceptor、ResponseBodyAdvice

  1. 对部分接口做使用说明
  2. 实现 public String user(@UserInfo String userName) 可获取用户信息

获取用户信息实现流程

  1. CurrentUserMethodArgumentResolver
  2. UserInfo
  3. AppMvcConfig.addArgumentResolvers
  4. TestController.user(@UserInfo String userName)

拦截器执行顺序

  • HandlerInterceptor.preHadnle
  • TestController.test
  • ResponseBodyAdvice.supports
  • ResponseBodyAdvice.beforeBodyWrite
  • HandlerInterceptor.postHandle
  • HandlerInterceptor.afterCompletion

在这里插入图片描述

代码如下 :

  • TestController.java : 测试controller
  • AppMvcConfig.java : 配置文件
  • MvcClient.java : 测试类
  • MyHandlerExceptionResolver.java : 异常处理
  • MyHandlerInterceptor.java : HandlerInterceptor
  • MyResponseBodyAdvice.java : ResponseBodyAdvice
  • MyWebApplicationInitializer.java : springmvc项目启动类
  • CurrentUserMethodArgumentResolver.java : 用户信息
  • UserInfo.java : 注解类

TestController

package com.web.controller;

import com.web.user.UserInfo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

/**
 * 测试controller
 */
@Controller
public class TestController {

	@GetMapping("/test")
	@ResponseBody
	public String test() {
		System.err.println(" TestController.test ");
		return "hello world!";
	}

	@GetMapping("/map")
	@ResponseBody
	public Map<String, Object> map() {
		Map<String, Object> map = new HashMap<>();
		map.put("name", "anyf");
		return map;
	}

	@GetMapping("/user")
	@ResponseBody
	public String user(@UserInfo String userName) {
		System.err.println(" TestController.user = " + userName);
		return userName;
	}

}


AppMvcConfig

package com.web;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.web.user.CurrentUserMethodArgumentResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 配置文件
 */
@ComponentScan("com.web")
@EnableWebMvc
public class AppMvcConfig implements WebMvcConfigurer {

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		/**
		 * 添加 HandlerInterceptor
		 */
		registry.addInterceptor(new MyHandlerInterceptor());
	}

	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
		/**
		 * 添加 HandlerMethodArgumentResolver,拦截添加了@UserInfo注解的controller,并对用户信息赋值
		 */
		resolvers.add(myHandlerMethodArgumentResolver());
	}

	@Bean
	public CurrentUserMethodArgumentResolver myHandlerMethodArgumentResolver() {
		return new CurrentUserMethodArgumentResolver();
	}

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		/**
		 * 解决 : org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.HashMap
		 * 返回map报错
		 */
		converters.add(0, fastJsonHttpMessageConverter());
		converters.add(1, responseBodyStringConverter());
	}

	@Bean
	public HttpMessageConverter<String> responseBodyStringConverter() {
		return new StringHttpMessageConverter(StandardCharsets.UTF_8);
	}

	@Bean
	public HttpMessageConverter<?> fastJsonHttpMessageConverter() {
		return new FastJsonHttpMessageConverter();
	}

}

MvcClient

package com.web;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.ServletException;

/**
 * 测试代码
 * 解决 : java.lang.ClassNotFoundException: org.apache.jasper.servlet.JspServlet
 * gradle 增加 : optional("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.50")
 *
 * http://localhost:8080/app/test
 *
 */
public class MvcClient {

	public static void main(String[] args) throws ServletException, LifecycleException {
		Tomcat tomcat = new Tomcat();
		tomcat.setBaseDir(".");
		tomcat.setPort(8080);
		/**
		 * 说明是web项目
		 */
		tomcat.addWebapp("/app", "D:\\Program Files\\logs\\");
		tomcat.start();
		tomcat.getServer().await();
	}

}

MyHandlerExceptionResolver

package com.web;

import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * HandlerExceptionResolver : 异常处理
 * HandlerExceptionResolver接口中定义了一个resolveException方法,用于处理Controller中的异常。
 * Exception ex参数即Controller抛出的异常。返回值类型是ModelAndView,可以通过这个返回值来设置异常时显示的页面。
 */
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		String requestType = request.getHeader("X-Requested-With");
		String contentType = request.getContentType() == null ? "" : request.getContentType().toLowerCase();

		String message;
		if (ex instanceof GeneralException) {
			// 用户自定义异常
			message = ((GeneralException) ex).getSimpleMessage();
		} else { // 通用异常
			message = "系统异常!";
		}

		// 判断是否是Ajax 请求
		if (contentType.contains("application/json") || !StringUtils.isEmpty(requestType) && ("XMLHttpRequest".equalsIgnoreCase(requestType))) {
			Assert.notNull(response);
			response.setContentType("text/json;charset=utf-8");
			ModelAndView modelAndView = new ModelAndView();
			// 返回异常信息(JSON)
			try  {
				PrintWriter writer = response.getWriter();
				writer.write(message);
				writer.flush();
			} catch (IOException ignore) {
			} finally {
				modelAndView.clear();
				return modelAndView;
			}
		} else {
			// 页面跳转
			Map<String, Object> modal = new HashMap<>();
			modal.put("exceptionMessage", message);
			modal.put("ex", ex);
			return new ModelAndView("error/error", modal);
		}
	}
}

class GeneralException extends Exception{

	public String getSimpleMessage() {
		return getMessage();
	}

}

MyHandlerInterceptor

package com.web;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * HandlerInterceptor
 */
public class MyHandlerInterceptor implements HandlerInterceptor {

	/**
	 * 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		System.err.println(" HandlerInterceptor.preHadnle ");
		return true;
	}

	/**
	 * 在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView;
	 *
	 * 对于@ResponseBody和ResponseEntity方法,postHandle不太有用,因为它们的响应是在handleAdapter内和postHandle之前写入和提交的。
	 * 这意味着对响应进行任何更改都为时已晚,例如添加额外的头。
	 * 对于这样的场景,您可以实现ResponseBodyAdvice并将其声明为控制器通知bean,或者直接在RequestMappingHandlerAdapter上配置它。
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		System.err.println(" HandlerInterceptor.postHandle ");
	}

	/**
	 * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		System.err.println(" HandlerInterceptor.afterCompletion ");
	}
}

MyResponseBodyAdvice

package com.web;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * ResponseBodyAdvice
 */
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice {

	@Override
	public boolean supports(MethodParameter returnType, Class converterType) {
		System.err.println(" ResponseBodyAdvice.supports ");
		return true;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
		System.err.println(" ResponseBodyAdvice.beforeBodyWrite " + body);
		return body;
	}

}

MyWebApplicationInitializer

package com.web;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

/**
 * springmvc项目启动类
 * 利用了servlet3.0特性,替代web.xml配置文件
 * 启动web服务后,会调用 onStartup
 */
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) {
		System.err.println("=============onStartUp=============");
        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppMvcConfig.class);

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/*");
    }

}

CurrentUserMethodArgumentResolver

package com.web.user;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 * HandlerMethodArgumentResolver,用户信息
 */
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {

	/**
	 * 判断参数是否添加了@UserInfo注解
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(UserInfo.class);
	}

	/**
	 * 获取用户信息,并将用户信息进行赋值
	 */
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		/**
		 * 可以改为从request获取数据,或者从redis获取数据
		 */
		return "anyf";
	}

}

UserInfo

package com.web.user;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserInfo {

}

spring-ayf-test.gradle

plugins {
    id 'java'
}

group 'org.springframework'
version '5.1.9.RELEASE'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile(project(":spring-context"))
    compile(project(":spring-web"))
    compile(project(":spring-webmvc"))
    optional("javax.servlet:javax.servlet-api:3.1.0")
    optional("org.apache.tomcat.embed:tomcat-embed-core:8.0.50")
    optional("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.50")
    optional("com.alibaba:fastjson:1.2.75")
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小安灬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值