背景: 我们现在有很多WEB项目基于SpringBoot开发,其中接口管理是必不可少的,接口中Request对象是核心内容,我们对参数、属性、header等等元数据的获取和配置都离不开它,常用的Filter、拦截器等操作类也都有它的身影。本篇在此基础上,从源码角度讨论获取Request的三种方式:
方式一、接口方法或全局异常处理方法参数注入
通常在开发简单POST接口时,请求参数或Header等信息都可以用*@RequestBody、@RequestParam、@PathVariable或@RequestHeader*注解直接取值,部分场景下当接口方法参数名和请求体参数名值和类型一致时,也可以赋值,如下所示:
当然最原始的也可用Request实例对象,即在此接口上配置HttpServletRequest参数获取,这是最简单也是最直接的方式。(PS: 接口方法也可设置HttpSession参数控制session逻辑)
源码一、方法参数解析入口类
package org.springframework.web.method.support;
public class InvocableHandlerMethod extends HandlerMethod {
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 解析方法所有参数,并将其传给最终的反射方法对象执行invoke操作
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
return this.doInvoke(args);
}
}
源码二、方法参数解析类(选一:RequestResponseBodyMethodProcessor)
// 方法参数解析接口
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
package org.springframework.web.servlet.mvc.method.annotation;
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
......省略......
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
// 通过转换器将Request中请求流数据转换为对应的实体
Object arg = this.readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
// 参数校验
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return this.adaptArgumentIfNecessary(arg, parameter);
}
// 参数校验
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
// 获取参数所有注解,下面将判断是否包含校验类注解
Annotation[] annotations = parameter.getParameterAnnotations();
Annotation[] var4 = annotations;
int var5 = annotations.length;
for(int var6 = 0; var6 < var5; ++var6) {
Annotation ann = var4[var6];
// 此处获取@Valid等校验注解,如果有,binder执行校验并获取校验结果
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
}
}
}
......省略......
}
HandlerMethodArgumentResolver默认有31种,覆盖绝大部分类型参数的解析,使我们从Request的API的操作中解放出来,从而关注业务逻辑的开发和管理,以下仅列出核心解析器:
1、RequestResponseBodyMethodProcessor // 支持@RequestBody注解参数解析
2、ServletRequestMethodArgumentResolver //支持ServletRequest、MultipartRequest及HttpSession等参数解析
3、RequestHeaderMethodArgumentResolver // 支持Header参数解析
方法二:方法体内通过RequestContextHolder全局持久化类获取
HttpServletRequest holderRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
在SpringBoot项目中,有两处地方进行了上下文保存动作:
1、OrderedRequestContextFilter extends RequestContextFilter implements OrderedFilter 设置
public class RequestContextFilter extends OncePerRequestFilter {
// 是否线程间传递,适用于后端存在异步线程池场景,需手工开启
private boolean threadContextInheritable = false;
public void setThreadContextInheritable(boolean threadContextInheritable) {
this.threadContextInheritable = threadContextInheritable;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 请求与响应封装体
ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
// 内存保存
this.initContextHolders(request, attributes);
try {
filterChain.doFilter(request, response);
} finally {
// 清除
LocaleContextHolder.resetLocaleContext();
RequestContextHolder.resetRequestAttributes();
attributes.requestCompleted();
}
}
// 请求&响应上下文具体保存逻辑-核心为ThreadLocal
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
OrderedRequestContextFilter是通过SpringBoot自动配置,只要是Web项目都不用手工设置,自动配置源码:
@AutoConfiguration(
after = {DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
public class WebMvcAutoConfiguration {
@Configuration(
proxyBeanMethods = false
)
@Import({EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
@Bean
@ConditionalOnMissingBean({RequestContextListener.class, RequestContextFilter.class})
@ConditionalOnMissingFilterBean({RequestContextFilter.class})
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
}
}
二、DispatcherServlet执行时设置
public class DispatcherServlet extends FrameworkServlet {
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);
}
}
}
三、定义HttpServletRequest类实例变量,通过Spring容器注入,如@Autowired注解修饰
这种方式比较少见,但既然能够使用,说明此时我们看到的request对象,不再是前面方法里获取的Request对象,而是披了一层羊皮的代理对象,这样每个接口获取时才能找到自己的上下文,那它是怎么实现的呢?
熟悉Spring框架的话,就会知道容器在实例化外部控制器实例时,会在BeanPostProcessor等处优先处理需要注入的实例,当需要注入实例到Request变量时,BeanFactory会自动处理:
// Bean实例化时会执行
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
// beanName为外部类名称,requiredType为HttpServletRequest.class
protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());
Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
// BeanFactory所有缓存的resolvableDependencies,具体见如上截图中8个依赖
Iterator var6 = this.resolvableDependencies.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<Class<?>, Object> classObjectEntry = (Map.Entry)var6.next();
Class<?> autowiringType = (Class)classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
// 此处根据变量类型,获取对应的ObjectFactory工厂,再生成对应的Proxy
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
..........
}
}
abstract class AutowireUtils {
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory)autowiringValue;
if (!(autowiringValue instanceof Serializable) || !requiredType.isInterface()) {
return factory.getObject();
}
// 此处使用JDK动态代理
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(), new Class[]{requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
return autowiringValue;
}
}
// 私有静态内部类封装InvocationHandler具体的反射调用内容,配合JDK动态代理生成代理类
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
private final ObjectFactory<?> objectFactory;
ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
this.objectFactory = objectFactory;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "equals":
return proxy == args[0];
case "hashCode":
return System.identityHashCode(proxy);
case "toString":
return this.objectFactory.toString();
default:
try {
return method.invoke(this.objectFactory.getObject(), args);
} catch (InvocationTargetException var6) {
throw var6.getTargetException();
}
}
}
}
}
ObjectFactoryDelegatingInvocationHandler是处理封装类,具体逻辑为ObjectFactory实现类提供
public abstract class WebApplicationContextUtils {
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) {
beanFactory.registerScope("request", new RequestScope());
beanFactory.registerScope("session", new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope("application", appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
// 此处注册Request执行的工厂类
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
WebApplicationContextUtils.FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
}
Request代理类真实执行逻辑:
public abstract class WebApplicationContextUtils {
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
// ObjectFactory核心就是getObject方法,封装实例创建的过程
public ServletRequest getObject() {
// 实际获取的还是RequestContextHolder缓存的Request对象
return WebApplicationContextUtils.currentRequestAttributes().getRequest();
}
}
}
总结:
1、一般需求场景,直接在接口方法上标注HttpServletRequest对象即可
2、如果需要在任意场景使用HttpServletRequest对象,方法二和方法三都可,方法三使用更加简洁
3、方法二和方法三底层都依赖ThreadLocal,如果后台使用异步线程池,需要手工设置inheritable属性为true
4、从方法一中,我们可以学到接口方法可以定义多个我们需要的上下文参数,如HttpSession;从方法三中我们可以学习到全局使用Request、Response、Session等8个对象。
5、方法三虽然涉及Bean生命周期、JDK动态代理等底层知识,但实际内容还是围绕RequestContextHolder做的封装,使用时更加方便,全局场景推荐使用此方式。