web項目根據不同情況中對request對象的獲取方式

使用request對象,究竟是在哪裏使用呢?考慮到獲取request對象的方法有微小的不同,大體可以分爲兩類:

1、在Spring的Bean中使用request對象:既包括Controller、Service、Repository等MVC的Bean,也包括了Component等普通的Spring Bean。爲了方便說明,後文中Spring中的Bean一律簡稱爲Bean。

2、在非Bean中使用request對象:如普通的Java對象的方法中使用,或在類的靜態方法中使用。

此外,本文討論是圍繞代表請求的request對象展開的,但所用方法同樣適用於response對象、InputStream/Reader、OutputStream/ Writer等;其中InputStream/Reader可以讀取請求中的數據,OutputStream/Writer可以向響應寫入數據。

最後,獲取request對象的方法與Spring及MVC的版本也有關係;本文基於Spring4進行討論,且所做的實驗都是使用4.1.1版本。

方式1:Controller中加參數

@Controller
public class TestController {
  @RequestMapping("/test")
  public void test(HttpServletRequest request) throws  InterruptedException{
         ....
	}  
}

方法實現的原理是,在Controller方法開始處理請求時,Spring會將request對象賦值到方法參數中。除了request對象,可以通過這種方法獲取的參數還有很多,具體可以見:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
Controller中獲取request對象後,如果要在其他方法中(如service方法、工具類方法等)使用request對象,需要在調用這些方法時將request對象作爲參數傳入。
優缺點:
這種方法的主要缺點是request對象寫起來冗餘太多,主要體現在兩點:
(1)如果多個controller方法中都需要request對象,那麼在每個方法中都需要添加一遍request參數
(2) request對象的獲取只能從controller開始,如果使用request對象的地方在函數調用層級比較深的地方,那麼整個調用鏈上的所有方法都需要添加request參數。
實際上,在整個請求處理的過程中,request對象是貫穿始終的;也就是說,除了定時器等特殊情況,request對象相當於線程內部的一個全局變量。而該方法,相當於將這個全局變量,傳來傳去。

方法2:自動注入

@Controller
public class TestController {
  @Autowired
  HttpServletRequest request;
  @RequestMapping("/test")
  public void test() throws  InterruptedException{
         ....
	}  
}

在Spring中,Controller的scope是singleton(單例),也就是說在整個web系統中,只有一個TestController;但是其中注入的request卻是線程安全的,原因在於:
使用這種方式,當Bean(本例的TestController)初始化時,Spring並沒有注入一個request對象,而是注入了一個代理(proxy);當Bean中需要使用request對象時,通過該代理獲取request對象。
優缺點
(1)注入不侷限於Controller中:在方法1中,只能在Controller中加入request參數。而對於方法2,不僅可以在Controller中注入,還可以在任何Bean中注入,包括Service、Repository及普通的Bean。
(2)注入的對象不限於request:除了注入request對象,該方法還可以注入其他scope爲request或session的對象,如response對象、session對象等;並保證線程安全。
(3)減少代碼冗餘:只需要在需要request對象的Bean中注入request對象,便可以在該Bean的各個方法中使用,與方法1相比大大減少了代碼冗餘。
但是,該方法也會存在代碼冗餘。考慮這樣的場景:web系統中有很多controller,每個controller中都會使用request對象(這種場景實際上非常頻繁),這時就需要寫很多次注入request的代碼;如果還需要注入response,代碼就更繁瑣了。下面說明自動注入方法的改進方法,並分析其線程安全性及優缺點。

方法3:基類中自動注入

@Controller
public class BaseController {
  @Autowired
  HttpServletRequest request; 
}
@Controller
public class TestController extends BaseController{
  @RequestMapping("/test")
  public void test() throws  InterruptedException{
      String value = request.getParameter("key");
         ....
	}  
}

分析:在理解了方法2的線程安全性的基礎上,很容易理解方法3是線程安全的:當創建不同的派生類對象時,基類中的域(這裏是注入的request)在不同的派生類對象中會佔據不同的內存空間,也就是說將注入request的代碼放在基類中對線程安全性沒有任何影響
優缺點
與方法2相比,避免了在不同的Controller中重複注入request;但是考慮到java只允許繼承一個基類,所以如果Controller需要繼承其他類時,該方法便不再好用。
無論是方法2和方法3,都只能在Bean中注入request;如果其他方法(如工具類中static方法)需要使用request對象,則需要在調用這些方法時將request參數傳遞進去。下面介紹的方法4,則可以直接在諸如工具類中的static方法中使用request對象(當然在各種Bean中也可以使用)。點擊此處查看公衆號全套Spring系列免費技術教程。

方法4:手動調用

@Controller
public class TestController {

  @RequestMapping("/test")
  public void test() throws  InterruptedException{
     HttpServletRequest request = ((ServletRequestAtrributes)RequestContextHolder.currentRequestAtrributes()).getRquest();
         ....
	}  
}

優缺點
優點:可以在非Bean中直接獲取。缺點:如果使用的地方較多,代碼非常繁瑣;因此可以與其他方法配合使用。

總結
綜上所述,Controller中加參數(方法1)、自動注入(方法2和方法3)、手動調用(方法4)都是線程安全的,都可以用來獲取request對象。如果系統中request對象使用較少,則使用哪種方式均可;如果使用較多,建議使用自動注入(方法2 和方法3)來減少代碼冗餘。如果需要在非Bean中使用request對象,既可以在上層調用時通過參數傳入,也可以直接在方法中通過手動調用(方法4)獲得。

覺得可以記得點個贊,非常感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章