在开发JavaWeb项目中,我们经常使用HttpServletRequest获取请求参数、请求头等信息。在Spring项目,我们通常会使用Spring提供的注解获取参数,如RequestParam、RequestHeader。 不过在某些场景下,我们可能需要从HttpServletRequest对象中取得更多的能力,如获取请求IP,获取请求域名等。这篇我们来学习如何在SpringMVC环境下获取HttpServletRequest,以及它们的实现方式,做到知其所以然。 Controller方法参数 使用注解后的SpringMVCcontroller方法可以作为handler处理请求,如果想获取request对象,只需要在方法中添加ServletRequest或HttpServletRequest类型参数即可。代码如下RestControllerpublicclassUserController{GetMapping(getUser)publicStringgetUser(HttpServletRequestrequest){returnrequestipis:request。getRemoteHost();}} 扩展:如何要获取reponse,同例只要在方法中增加ServletResponse或HttpServletResponse类型参数即可。Controller方法参数实现原理 通过上面的代码我们很容易就实现了,那spring是怎么帮我们搞定的呢? 在springmvc中,所有浏览器发起的请求都会先交给DispatcherServlet处理。DispatcherServlet根据用户或默认的配置使用HandlerMapping查找可处理请求的处理器。DispatcherServlet拿到HandlerMapping返回的处理器链HandlerExecutionChain。整个处理器链包含拦截器和处理。DispatcherServlet将处理器适配为HandlerAdapter。DispatcherServlet使用拦截器进行请求前置处理。DispatcherServlet使用处理器进行请求处理。DispatcherServlet使用拦截器进行请求后置处理。DispatcherServlet从拦截器或处理器中提取到模型及视图ModelAndView。DispatcherServlet使用视图解析器ViewResolver解析视图出视图View。DispatcherServlet渲染视图,响应请求返回给浏览器。 在上面第6步【DispatcherServlet使用处理器进行请求处理】时,在调用我们自己的controller方法之前,Spring通过HandlerMethodArgumentResolver向我们的controller方法注入对应的参数。 静态方法 除了通过controller方法参数获取HttpServletRequest对象,Spring还允许通过其提供的工具类的静态方法来获取HttpServletRequest。示例如下。HttpServletRequestrequest((ServletRequestAttributes)RequestContextHolder。currentRequestAttributes())。getRequest();静态方法实现原理 上面的示例中,RequestContextHolder表示一个请求上下文的持有者,内部将每个请求上下文信息存储到ThreadLocal中。publicabstractclassRequestContextHolder{线程上下文RequestAttributesprivatestaticfinalThreadLocalRequestAttributesrequestAttributesHoldernewNamedThreadLocal(Requestattributes);支持继承的线程上下文RequestAttributesprivatestaticfinalThreadLocalRequestAttributesinheritableRequestAttributesHoldernewNamedInheritableThreadLocal(Requestcontext);} DispatcherServlet处理请求前会将request存至ServletRequestAttributes,然后放到RequestContextHolder中,具体可见DispatcherServlet的父类FrameworkServlet。processRequest()。直接注入 还可以将HttpServletRequest当做普通的bean注入。代码如下RestControllerpublicclassUserController{AutowiredprivateHttpServletRGetMapping(getIP)publicStringgetIP(){returnrequestipis:request。getRemoteHost();}}直接注入分析 通过Autowired的方式引入request也很简单,想想这里会有问题吗?。。。。。。。 Controller不是一个单例bean对象吗?在一个Spring容器内只有一个实例,而每次请求都对应一个request对象,Spring是怎样做到使用一个request表示多个请求的? 经过仔细分析,我们可以发现Spring注入bean时使用了底层的DefaultListableBeanFactory获取bean实例,相关代码如下。publicclassDefaultListableBeanFactoryextendsAbstractAutowireCapableBeanFactoryimplementsConfigurableListableBeanFactory,BeanDefinitionRegistry,Serializable{不依赖关系privatefinalMapC?,ObjectresolvableDependenciesnewConcurrentHashMap(16);查找候选beanprotectedMapString,ObjectfindAutowireCandidates(NullableStringbeanName,C?requiredType,DependencyDescriptordescriptor){部分代码省略MapString,ObjectresultnewLinkedHashMap(candidateNames。length);for(Map。EntryC?,ObjectclassObjectEntry:this。resolvableDependencies。entrySet()){C?autowiringTypeclassObjectEntry。getKey();if(autowiringType。isAssignableFrom(requiredType)){ObjectautowiringValueclassObjectEntry。getValue();解析ObjectFactoryautowiringValueAutowireUtils。resolveAutowiringValue(autowiringValue,requiredType);if(requiredType。isInstance(autowiringValue)){result。put(ObjectUtils。identityToString(autowiringValue),autowiringValue);}}}部分代码省略}} DefaultListableBeanFactory查找候选bean时会先从resolvableDependencies中查找,找到后调用AutowireUtils。resolveAutowiringValue方法再次解析。 resolvableDependencies中对象是Spring中特殊的存在,不属于Spring管理的bean,需要手动注册到DefaultListableBeanFactory。 我们继续跟踪源码。abstractclassAutowireUtils{publicstaticObjectresolveAutowiringValue(ObjectautowiringValue,C?requiredType){if(autowiringValueinstanceofObjectFactory!requiredType。isInstance(autowiringValue)){ObjectFactory类型值和所需类型不匹配,创建代理对象ObjectF?factory(ObjectF?)autowiringVif(autowiringValueinstanceofSerializablerequiredType。isInterface()){创建代理对象,可用于处理HttpServletRequest注入等问题autowiringValueProxy。newProxyInstance(requiredType。getClassLoader(),newC?〔〕{requiredType},newObjectFactoryDelegatingInvocationHandler(factory));}else{returnfactory。getObject();}}returnautowiringV}} 当resolvableDependencies中对象是ObjectFactory类型,并且与所需的类型不匹配,Spring使用ObjectFactory创建了一个JDK代理对象:privatestaticclassObjectFactoryDelegatingInvocationHandlerimplementsInvocationHandler,Serializable{privatefinalObjectF?objectFpublicObjectFactoryDelegatingInvocationHandler(ObjectF?objectFactory){this。objectFactoryobjectF}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{try{returnmethod。invoke(this。objectFactory。getObject(),args);}catch(InvocationTargetExceptionex){throwex。getTargetException();}}} 代理的实现简单,每当所需类型的方法调用时,就调用ObjectFactory中获取的实例对象的对应方法。 那怎么与HttpServletRequest关联启来呢? Spring在启动时会注册Web环境相关的依赖对象publicabstractclassWebApplicationContextUtils{publicstaticvoidregisterWebApplicationScopes(ConfigurableListableBeanFactorybeanFactory,NullableServletContextsc){beanFactory。registerScope(WebApplicationContext。SCOPEREQUEST,newRequestScope());beanFactory。registerScope(WebApplicationContext。SCOPESESSION,newSessionScope());if(sc!null){ServletContextScopeappScopenewServletContextScope(sc);beanFactory。registerScope(WebApplicationContext。SCOPEAPPLICATION,appScope);RegisterasServletContextattribute,forContextCleanupListenertodetectit。sc。setAttribute(ServletContextScope。class。getName(),appScope);}ServletRequest类型对应ObjectFactory注册beanFactory。registerResolvableDependency(ServletRequest。class,newRequestObjectFactory());beanFactory。registerResolvableDependency(ServletResponse。class,newResponseObjectFactory());beanFactory。registerResolvableDependency(HttpSession。class,newSessionObjectFactory());beanFactory。registerResolvableDependency(WebRequest。class,newWebRequestObjectFactory());if(jsfPresent){FacesDependencyRegistrar。registerFacesDependencies(beanFactory);}}} 可以看到:Spring为ServletRequest注入的是RequestObjectFactory类型,那再看看它的实现:publicabstractclassWebApplicationContextUtils{privatestaticclassRequestObjectFactoryimplementsObjectFactoryServletRequest,Serializable{OverridepublicServletRequestgetObject(){returncurrentRequestAttributes()。getRequest();}ReturnthecurrentRequestAttributesinstanceasServletRequestAttributes。seeRequestContextHoldercurrentRequestAttributes()privatestaticServletRequestAttributescurrentRequestAttributes(){RequestAttributesrequestAttrRequestContextHolder。currentRequestAttributes();if(!(requestAttrinstanceofServletRequestAttributes)){thrownewIllegalStateException(Currentrequestisnotaservletrequest);}return(ServletRequestAttributes)requestA}OverridepublicStringtoString(){returnCurrentHttpServletR}}} 可以看到,和前面介绍的【静态方法】思路一样。 以上就是3种在spring场景中,获取request的方法,get到了吗?