最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

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

Controller 方法參數(shù)

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

@RestController
public class UserController {

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

}

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

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

通過上面的代碼我們很容易就實(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通過
HandlerMethodArgumentResolver向我們的controller方法注入對(duì)應(yīng)的參數(shù)。

靜態(tài)方法

除了通過 controller 方法參數(shù)獲取 HttpServletRequest 對(duì)象,Spring 還允許通過其提供的工具類的靜態(tà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 中,具體可見DispatcherServlet的父類
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();
    }

}

直接注入分析

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

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)過仔細(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 類型值和所需類型不匹配,創(chuàng)建代理對(duì)象
      ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
      if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
        // 創(chuàng)建代理對(duì)象,可用于處理 HttpServletRequest 注入等問題
        autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
            new Class<?>[]{requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
      } else {
        return factory.getObject();
      }
    }
    return autowiringValue;
  }
}

當(dāng)resolvableDependencies中對(duì)象是ObjectFactory 類型,并且與所需的類型不匹配,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)簡單,每當(dāng)所需類型的方法調(diào)用時(shí),就調(diào)用 ObjectFactory 中獲取的實(shí)例對(duì)象的對(duì)應(yīng)方法。

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

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 類型對(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 類型,那再看看它的實(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開發(fā)教程之Request獲取3種方式的文章就介紹到這了,更多相關(guān)Spring web獲取Request內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解析SpringCloud簡介與微服務(wù)架構(gòu)

    解析SpringCloud簡介與微服務(wù)架構(gòu)

    這篇文章主要介紹了SpringCloud簡介與微服務(wù)架構(gòu),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • JavaWeb使用mvc模式實(shí)現(xiàn)登錄功能

    JavaWeb使用mvc模式實(shí)現(xiàn)登錄功能

    本文主要介紹了JavaWeb使用mvc模式實(shí)現(xiàn)登錄功能,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • SSH框架網(wǎng)上商城項(xiàng)目第6戰(zhàn)之基于DataGrid的數(shù)據(jù)顯示

    SSH框架網(wǎng)上商城項(xiàng)目第6戰(zhàn)之基于DataGrid的數(shù)據(jù)顯示

    SSH框架網(wǎng)上商城項(xiàng)目第6戰(zhàn)之基于DataGrid的數(shù)據(jù)顯示,提供了豐富的選擇、排序、分組和編輯數(shù)據(jù)的功能支持,感興趣的小伙伴們可以參考一下
    2016-05-05
  • SpringBoot集成WebService(wsdl)實(shí)踐

    SpringBoot集成WebService(wsdl)實(shí)踐

    文章介紹了Spring Boot項(xiàng)目中通過緩存IWebService接口實(shí)現(xiàn)類的泛型入?yún)㈩愋?減少反射調(diào)用提升性能的實(shí)現(xiàn)方案,包含依賴配置、工具類封裝、接口定義及soapUI調(diào)用參數(shù)說明
    2025-09-09
  • 通過MyBatis讀取數(shù)據(jù)庫數(shù)據(jù)并提供rest接口訪問

    通過MyBatis讀取數(shù)據(jù)庫數(shù)據(jù)并提供rest接口訪問

    這篇文章主要介紹了通過MyBatis讀取數(shù)據(jù)庫數(shù)據(jù)并提供rest接口訪問 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-08-08
  • Netty分布式客戶端接入流程初始化源碼分析

    Netty分布式客戶端接入流程初始化源碼分析

    這篇文章主要介紹了Netty分布式客戶端接入流程初始化源碼分析,有關(guān)channelConfig有關(guān)的初始化過程剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Java中InputStream重復(fù)使用問題的幾種解決方案

    Java中InputStream重復(fù)使用問題的幾種解決方案

    在Java開發(fā)中,InputStream是用于讀取字節(jié)流的類,在許多場(chǎng)景下,我們可能需要重復(fù)讀取InputStream中的數(shù)據(jù),這篇文章主要介紹了Java中InputStream重復(fù)使用問題的幾種解決方案,需要的朋友可以參考下
    2025-08-08
  • java實(shí)現(xiàn)多線程文件的斷點(diǎn)續(xù)傳

    java實(shí)現(xiàn)多線程文件的斷點(diǎn)續(xù)傳

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)多線程文件的斷點(diǎn)續(xù)傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring Boot集成Mybatis中如何顯示日志的實(shí)現(xiàn)

    Spring Boot集成Mybatis中如何顯示日志的實(shí)現(xiàn)

    這篇文章主要介紹了Spring Boot集成Mybatis中如何顯示日志的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java多線程ThreadForge的實(shí)現(xiàn)

    Java多線程ThreadForge的實(shí)現(xiàn)

    本文主要介紹了Java多線程ThreadForge的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-05-05

最新評(píng)論

淮安市| 肇东市| 许昌县| 中宁县| 铜梁县| 嘉善县| 高唐县| 合山市| 定兴县| 苍梧县| 上犹县| 富川| 留坝县| 沐川县| 石屏县| 保山市| 北宁市| 肥城市| 庄浪县| 黑龙江省| 尼勒克县| 应城市| 铜梁县| 沙坪坝区| 彝良县| 若羌县| 托克逊县| 东丽区| 札达县| 宁强县| 邹平县| 孙吴县| 安康市| 章丘市| 中山市| 永康市| 庐江县| 武山县| 胶州市| 武川县| 博爱县|