SpringMVC 利用注解获取 Session 中的数据

SpringMVC 利用注解获取 Session 中的数据

1. HandlerMethodArgumentResolver 的使用

  通常来说,我们获取获取 Session 中的数据,比如 登录用户 LoginUserVO,都是在方法中注入 HttpSession,然后执行
LoginUserVO attribute = (LoginUserVO) session.getAttribute(“login_user_key”);
来获取,因为用到的地方比较多,每次都得写重复的代码还得进行数据的转换就会很恶心;

  SpringMVC 可以让我们通过 HandlerMethodArgumentResolver 接口实现 利用注解或不需要注解 直接从方法中注入 Session 中的数据,就像 HttpServletRequest 一样,比如下面的效果:

// 首先在登录时候将当前用户的信息放入 session 中;
request.getSession().setAttribute("login_user_key", loginUser);

// 直接通过自定义标签 @LoginUserBody 获取 session 中的 登录用户数据;
@PutMapping(value = "operation")
public boolean testArgument(@LoginUserBody LoginUserVO loginUser) {
    
    
    System.out.println(loginUser.getTrueName() + " 这里的 loginUser 已经获取");
    return true;
}

  那么具体怎么实现呢?
  首先,先定义自定义注解

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

  然后实现 HandlerMethodArgumentResolver 接口

public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
    
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    
    
        // 判断方法的这个参数是否是否使用了 @LoginUserBody
        return parameter.hasParameterAnnotation(LoginUserBody.class);
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
            throws Exception {
    
    
        // 返回 session 中的用户数据
        return webRequest.getNativeRequest(HttpServletRequest.class).getSession().
            getAttribute("login_user_key");
    }
}

  最后,就是将自定义的 HandlerMethodArgumentResolver 注册到 Spring 中,这里主要有两种方式,一种针对传统的 spring 项目,另一种针对 springboot 项目,springboot 是通过实现 WebMvcConfigurer 的方式来进行配置,

@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
    
    
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    
    
        resolvers.add(new LoginUserArgumentResolver());
    }
}

  传统 spring 项目则是在 XML 中配置:

<mvc:annotation-driven>
	<mvc:argument-resolvers>
	    <bean class="com.xxx.LoginUserArgumentResolver"/>
	</mvc:argument-resolvers>
<mvc:annotation-driven>

2. 实现原理

  首先我们从 DispatcherServlet 开始进行源码阅读,DispatcherServlet 在接受到 请求之后,会通过 doDispatch() 进行整个业务的处理,所以这里从 doDispatch() 进行代码的跟踪;
  首先 DispatcherServlet 会 调用 HandlerAdapter 实现对 MVC 接口的调用,HandlerAdapter 是一个处理接口,MVC 默认使用的是 AbstractHandlerMethodAdapter

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
    ModelAndView mv = null;
	...
    // 调用 HandlerAdapter 实现对 MVC 接口的调用
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    ...
}

  在 AbstractHandlerMethodAdapter.handle() ,请求被转发到了 handleInternal() 方法

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    
    
    return handleInternal(request, response, (HandlerMethod) handler);
}

  handleInternal() 是一个留给子类实现的函数,默认实现为 RequestMappingHandlerAdapter,其在 handleInternal() 调用了 invokeHandlerMethod() 来实现方法的执行和返回值的处理,流程如下所示:

protected ModelAndView handleInternal(HttpServletRequest request,
                                      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    
	...
    // 调用 handlerMethod
    mav = invokeHandlerMethod(request, response, handlerMethod);
	...
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    
	......
    // 执行 invokeAndHandle 调用接口
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
	......
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
	Object... providedArgs) throws Exception {
    
    
	// 调用方法并获取到返回值
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    ......
}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {
    
    
	// 获取参数,也就是说,参数的处理都是这里完成的
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	...
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
    
    
	// 获取接口参数
    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
    
    
       ...
       // 这里的argumentResolvers 实际上是 HandlerMethodArgumentResolverComposite
        if (this.argumentResolvers.supportsParameter(parameter)) {
    
    
            args[i] = this.argumentResolvers.resolveArgument(
                parameter, mavContainer, request, this.dataBinderFactory);
        }   
    }
    return args;
}

  通过上面的流程我们可以知道,Spring 会在 HandlerMethodArgumentResolverComposite 中进行判断和处理参数,其主要代码如下所示:

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    
    
	private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
    
    
		return (getArgumentResolver(parameter) != null);
	}
	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
	@Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    
    
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
    
    
			for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
    
    
				if (methodArgumentResolver.supportsParameter(parameter)) {
    
    
					result = methodArgumentResolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
}

  HandlerMethodArgumentResolverComposite 封装了一个 HandlerMethodArgumentResolver 集合,里面放着所有的 HandlerMethodArgumentResolver,选择 HandlerMethodArgumentResolver 的时候就循环判断,然后选出符合条件的一个,进行执行;

猜你喜欢

转载自blog.csdn.net/qq_36845919/article/details/108352039
今日推荐