欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?web開(kāi)發(fā)教程之Request獲取3種方式

 更新時(shí)間:2023年11月22日 16:06:23   作者:lzzyok  
這篇文章主要給大家介紹了關(guān)于Spring?web開(kāi)發(fā)教程之Request獲取3種方式的相關(guān)資料,request對(duì)象是從客戶端向服務(wù)器發(fā)出請(qǐng)求,包括用戶提交的信息以及客戶端的一些信息,需要的朋友可以參考下

前言

在開(kāi)發(fā) Java Web 項(xiàng)目中,我們經(jīng)常使用 HttpServletRequest 獲取請(qǐng)求參數(shù)、請(qǐng)求頭等信息。在Spring項(xiàng)目,我們通常會(huì)使用 Spring 提供的注解獲取參數(shù),如 @RequestParam、@RequestHeader。

不過(guò)在某些場(chǎng)景下,我們可能需要從 HttpServletRequest 對(duì)象中取得更多的能力,如獲取請(qǐng)求 IP,獲取請(qǐng)求域名等。這篇我們來(lái)學(xué)習(xí)如何在 Spring MVC 環(huán)境下獲取 HttpServletRequest,以及它們的實(shí)現(xiàn)方式,做到知其所以然。

Controller 方法參數(shù)

使用注解后的 Spring MVC controller 方法可以作為 handler 處理請(qǐng)求,如果想獲取 request 對(duì)象,只需要在方法中添加 ServletRequest 或 HttpServletRequest 類(lèi)型參數(shù)即可。代碼如下

@RestController
public class UserController {

    @GetMapping("/getUser")
    public String getUser(HttpServletRequest request) {
        return "request ip is : " + request.getRemoteHost();
    }

}

擴(kuò)展:如何要獲取reponse,同例只要在方法中增加 ServletResponse 或 HttpServletResponse 類(lèi)型參數(shù)即可。

Controller 方法參數(shù)實(shí)現(xiàn)原理

通過(guò)上面的代碼我們很容易就實(shí)現(xiàn)了,那spring是怎么幫我們搞定的呢?

  • 在spring mvc中,所有瀏覽器發(fā)起的請(qǐng)求都會(huì)先交給DispatcherServlet 處理。
  • DispatcherServlet 根據(jù)用戶或默認(rèn)的配置使用 HandlerMapping 查找可處理請(qǐng)求的處理器。
  • DispatcherServlet 拿到 HandlerMapping 返回的處理器鏈 HandlerExecutionChain。整個(gè)處理器鏈包含攔截器和處理。
  • DispatcherServlet 將處理器適配為 HandlerAdapter。
  • DispatcherServlet 使用攔截器進(jìn)行請(qǐng)求前置處理。
  • DispatcherServlet 使用處理器進(jìn)行請(qǐng)求處理。
  • DispatcherServlet 使用攔截器進(jìn)行請(qǐng)求后置處理。
  • DispatcherServlet 從攔截器或處理器中提取到模型及視圖 ModelAndView。
  • DispatcherServlet 使用視圖解析器 ViewResolver 解析視圖出視圖 View。
  • DispatcherServlet 渲染視圖,響應(yīng)請(qǐng)求返回給瀏覽器。

在上面第6步【DispatcherServlet 使用處理器進(jìn)行請(qǐng)求處理】時(shí),在調(diào)用我們自己的controller方法之前,Spring通過(guò)
HandlerMethodArgumentResolver向我們的controller方法注入對(duì)應(yīng)的參數(shù)。

靜態(tài)方法

除了通過(guò) controller 方法參數(shù)獲取 HttpServletRequest 對(duì)象,Spring 還允許通過(guò)其提供的工具類(lèi)的靜態(tài)方法來(lái)獲取 HttpServletRequest。示例如下。

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

靜態(tài)方法實(shí)現(xiàn)原理

上面的示例中,RequestContextHolder 表示一個(gè)請(qǐng)求上下文的持有者,內(nèi)部將每個(gè)請(qǐng)求上下文信息存儲(chǔ)到 ThreadLocal 中。

public abstract class RequestContextHolder {

  /**
   * 線程上下文 RequestAttributes
   */
  private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
      new NamedThreadLocal<>("Request attributes");

  /**
   * 支持繼承的線程上下文 RequestAttributes
   */
  private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
      new NamedInheritableThreadLocal<>("Request context");

}

DispatcherServlet 處理請(qǐng)求前會(huì)將 request 存至 ServletRequestAttributes,然后放到 RequestContextHolder 中,具體可見(jiàn)DispatcherServlet的父類(lèi)
FrameworkServlet.processRequest()。

直接注入

還可以將 HttpServletRequest 當(dāng)做普通的 bean 注入。代碼如下

@RestController
public class UserController {

    @Autowired
    private HttpServletRequest request;

    @GetMapping("/getIP")
    public String getIP() {
        return "request ip is : " + request.getRemoteHost();
    }

}

直接注入分析

通過(guò) @Autowired 的方式引入 request 也很簡(jiǎn)單,想想這里會(huì)有問(wèn)題嗎?.......

Controller 不是一個(gè)單例 bean 對(duì)象嗎?在一個(gè) Spring 容器內(nèi)只有一個(gè)實(shí)例,而每次請(qǐng)求都對(duì)應(yīng)一個(gè) request 對(duì)象,Spring 是怎樣做到使用一個(gè) request 表示多個(gè)請(qǐng)求的?

經(jīng)過(guò)仔細(xì)分析,我們可以發(fā)現(xiàn) Spring 注入 bean 時(shí)使用了底層的 
DefaultListableBeanFactory 獲取 bean 實(shí)例,相關(guān)代碼如下。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  // 不依賴關(guān)系
  private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
   // 查找候選 bean
  protected Map<String, Object> findAutowireCandidates(
      @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {  
    //部分代碼省略
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
      Class<?> autowiringType = classObjectEntry.getKey();
      if (autowiringType.isAssignableFrom(requiredType)) {
        Object autowiringValue = classObjectEntry.getValue();
        // 解析 ObjectFactory
        autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
        if (requiredType.isInstance(autowiringValue)) {
          result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
          break;
        }
      }
    }
    //部分代碼省略
    }
}

DefaultListableBeanFactory 查找候選 bean 時(shí)會(huì)先從 resolvableDependencies 中查找,找到后調(diào)用 AutowireUtils.resolveAutowiringValue方法再次解析。

resolvableDependencies中對(duì)象是 Spring 中特殊的存在,不屬于 Spring 管理的 bean,需要手動(dòng)注冊(cè)到 
DefaultListableBeanFactory。

我們繼續(xù)跟蹤源碼。

abstract class AutowireUtils {
  public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
    if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
      // ObjectFactory 類(lèi)型值和所需類(lèi)型不匹配,創(chuàng)建代理對(duì)象
      ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
      if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
        // 創(chuàng)建代理對(duì)象,可用于處理 HttpServletRequest 注入等問(wèn)題
        autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
            new Class<?>[]{requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
      } else {
        return factory.getObject();
      }
    }
    return autowiringValue;
  }
}

當(dāng)resolvableDependencies中對(duì)象是ObjectFactory 類(lèi)型,并且與所需的類(lèi)型不匹配,Spring 使用 ObjectFactory 創(chuàng)建了一個(gè) JDK 代理對(duì)象:

private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

    private final ObjectFactory<?> objectFactory;

    public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
      this.objectFactory = objectFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       try {
        return method.invoke(this.objectFactory.getObject(), args);
      } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
      }
    }
  }

代理的實(shí)現(xiàn)簡(jiǎn)單,每當(dāng)所需類(lèi)型的方法調(diào)用時(shí),就調(diào)用 ObjectFactory 中獲取的實(shí)例對(duì)象的對(duì)應(yīng)方法。

那怎么與HttpServletRequest關(guān)聯(lián)啟來(lái)呢?

Spring 在啟動(dòng)時(shí)會(huì)注冊(cè) Web 環(huán)境相關(guān)的依賴對(duì)象

public abstract class WebApplicationContextUtils {

  public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
                          @Nullable ServletContext sc) {

    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
    if (sc != null) {
      ServletContextScope appScope = new ServletContextScope(sc);
      beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
      // Register as ServletContext attribute, for ContextCleanupListener to detect it.
      sc.setAttribute(ServletContextScope.class.getName(), appScope);
    }
    // ServletRequest 類(lèi)型對(duì)應(yīng) ObjectFactory 注冊(cè)
    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) {
      FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
    }
  }
  
}

可以看到:Spring 為 ServletRequest 注入的是 RequestObjectFactory 類(lèi)型,那再看看它的實(shí)現(xiàn):

public abstract class WebApplicationContextUtils {

  private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

    @Override
    public ServletRequest getObject() {
      return currentRequestAttributes().getRequest();
    }
	/**
	 * Return the current RequestAttributes instance as ServletRequestAttributes.
	 * @see RequestContextHolder#currentRequestAttributes()
	 */
	private static ServletRequestAttributes currentRequestAttributes() {
		RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
		if (!(requestAttr instanceof ServletRequestAttributes)) {
			throw new IllegalStateException("Current request is not a servlet request");
		}
		return (ServletRequestAttributes) requestAttr;
	}
    @Override
    public String toString() {
      return "Current HttpServletRequest";
    }
  }
}

可以看到,和前面介紹的【靜態(tài)方法】思路一樣。

以上就是3種在spring場(chǎng)景中,獲取request的方法,get到了嗎?

總結(jié)

到此這篇關(guān)于Spring web開(kāi)發(fā)教程之Request獲取3種方式的文章就介紹到這了,更多相關(guān)Spring web獲取Request內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java web實(shí)現(xiàn)賬號(hào)單一登錄,防止同一賬號(hào)重復(fù)登錄(踢人效果)

    Java web實(shí)現(xiàn)賬號(hào)單一登錄,防止同一賬號(hào)重復(fù)登錄(踢人效果)

    這篇文章主要介紹了Java web實(shí)現(xiàn)賬號(hào)單一登錄,防止同一賬號(hào)重復(fù)登錄,有點(diǎn)類(lèi)似于qq登錄踢人效果,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 關(guān)于Jedis的用法以及Jedis使用Redis事務(wù)

    關(guān)于Jedis的用法以及Jedis使用Redis事務(wù)

    這篇文章主要介紹了關(guān)于Jedis的用法以及Jedis使用Redis事務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java定時(shí)任務(wù)取消的示例代碼

    Java定時(shí)任務(wù)取消的示例代碼

    java定時(shí)任務(wù)如何取消,并比如,我之前想每周二晚上6點(diǎn)自動(dòng)生成一條devops流水線,現(xiàn)在我想停掉,下面給大家分享java定時(shí)任務(wù)取消的示例代碼,演示如何創(chuàng)建一個(gè)每周二晚上6點(diǎn)自動(dòng)生成一條devops流水線的定時(shí)任務(wù),感興趣的朋友一起看看吧
    2024-02-02
  • Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容

    Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容

    QSV是一種加密的視頻文件格式。是愛(ài)奇藝公司研發(fā)的一種視頻文件格式,這篇文章主要為大家介紹了如何利用Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容,感興趣的可以了解一下
    2023-03-03
  • Struts2 使用OGNL遍歷map方法詳解

    Struts2 使用OGNL遍歷map方法詳解

    這篇文章主要介紹了Struts2 使用OGNL遍歷map方法詳解,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • java swing框架實(shí)現(xiàn)貪吃蛇游戲

    java swing框架實(shí)現(xiàn)貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了java swing框架實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • java實(shí)現(xiàn)文件重命名功能

    java實(shí)現(xiàn)文件重命名功能

    這篇文章主要介紹了java實(shí)現(xiàn)文件重命名功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Java數(shù)據(jù)結(jié)構(gòu)之線性表

    Java數(shù)據(jù)結(jié)構(gòu)之線性表

    線性表是其組成元素間具有線性關(guān)系的一種數(shù)據(jù)結(jié)構(gòu),對(duì)線性表的基本操作主要有,獲取元素,設(shè)置元素值,遍歷,插入,刪除,查找,替換,排序等。而線性表可以采用順序儲(chǔ)存結(jié)構(gòu)和鏈?zhǔn)絻?chǔ)存結(jié)構(gòu),本節(jié)主要講解順序表、單鏈表以及雙鏈表的各種基本操作。
    2017-03-03
  • Springboot搭建JVM監(jiān)控(Springboot + Prometheus + Grafana)

    Springboot搭建JVM監(jiān)控(Springboot + Prometheus +&n

    在應(yīng)用開(kāi)發(fā)時(shí),監(jiān)控報(bào)警必不可少,本文主要介紹了Springboot搭建JVM監(jiān)控(Springboot + Prometheus + Grafana),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • Java中HashMap和TreeMap的區(qū)別深入理解

    Java中HashMap和TreeMap的區(qū)別深入理解

    首先介紹一下什么是Map。在數(shù)組中我們是通過(guò)數(shù)組下標(biāo)來(lái)對(duì)其內(nèi)容索引的,而在Map中我們通過(guò)對(duì)象來(lái)對(duì)對(duì)象進(jìn)行索引,用來(lái)索引的對(duì)象叫做key,其對(duì)應(yīng)的對(duì)象叫做value
    2012-12-12

最新評(píng)論