Several methods of getting request in spring and its thread security analysis

osc_ ow92sntn 2020-11-20 07:23:56
methods getting request spring thread


Spring In order to get request Several ways to , And thread security analysis

Preface

This article will introduce you to Spring MVC Developed web In the system , obtain request Several methods of object , And discusses its thread safety .

Originality is not easy. , If you think the article will help you , Welcome to thumb up 、 Comment on . There are omissions in the article , Welcome criticism and correction .

Welcome to reprint , Reprint please indicate the original link :http://www.cnblogs.com/kismetv/p/8757260.html

Catalog

summary

How to test thread security

Method 1:Controller China Canada parameters

Method 2: Automatic injection

Method 3: Automatically inject

Method 4: Manual call

Method 5:@ModelAttribute Method

summary

summary

In the use of Spring MVC Development Web System time , You often need to use request object , For example, get the client ip Address 、 Requested url、header Properties in ( Such as cookie、 Authorization information )、body Data in, etc . Because in Spring MVC in , request-processing Controller、Service The objects are all singleton , So get request The most important thing to pay attention to when object is , That is request Whether the object is thread safe : When there are a large number of concurrent requests , Can you guarantee different requests / Different... Are used in threads request object .

There is another problem to be noted here : Previously mentioned “ While processing the request ” Use request object , Where is it used ? Consider getting request There are slight differences in the way objects are used , It can be roughly divided into two categories :

1)      stay Spring Of Bean Use in request object : Both include Controller、Service、Repository etc. MVC Of Bean, It also includes Component And so on. Spring Bean. For the sake of illustration , Later Spring Medium Bean It is abbreviated to Bean.

2)      In Africa Bean Use in request object : As ordinary Java Object method , Or in the static method of a class .

Besides , The discussion in this paper revolves around the representative request request Object expanded , But the method used also applies to response object 、InputStream/Reader、OutputStream/ Writer etc. ; among InputStream/Reader The data in the request can be read ,OutputStream/ Writer You can write data to the response .

Last , obtain request Object method and Spring And MVC It also matters ; This article is based on Spring4 Have a discussion , And all the experiments were done with 4.1.1 edition .

How to test thread security

since request The thread safety of objects needs special attention , In order to facilitate the later discussion , Here's how to test request Whether the object is thread safe .

The basic idea of testing , Is to simulate a large number of concurrent client requests , The server then determines whether these requests use the same request object .

Judge request Whether the objects are the same , The most intuitive way is to print out request Address of the object , If it is the same, the same object is used . However , In almost all web The implementation of the server , They all use thread pools , This leads to two requests that arrive successively , It may be handled by the same thread : After the previous request has been processed , Thread pool reclaims the thread , And the thread is reassigned to subsequent requests . And in the same thread , The use of request The object is likely to be the same ( The address is the same , Different properties ). Even if it's safe for threads , Different requests use request The object address may also be the same .

To avoid this problem , One way is to sleep the thread for a few seconds during request processing , This allows each thread to work long enough , In order to avoid the same thread assigned to different requests ; Another way , It's using request Other properties of ( Such parameters 、header、body etc. ) As request Whether thread safety depends on , Because even though different requests use the same thread successively (request The object address is the same ), Just construct it twice with different properties request object , that request The use of objects is thread safe . This article uses the second method to test .

The client test code is as follows ( establish 1000 Threads send requests separately ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  class  Test {
     public  static  void  main(String[] args)  throws  Exception {
         String prefix = UUID.randomUUID().toString().replaceAll( "-" "" ) +  "::" ;
         for  ( int  i =  0 ; i <  1000 ; i++) {
             final  String value = prefix + i;
             new  Thread() {
                 @Override
                 public  void  run() {
                     try  {
                         CloseableHttpClient httpClient = HttpClients.createDefault();
                         HttpGet httpGet =  new  HttpGet( "http://localhost:8080/test?key="  + value);
                         httpClient.execute(httpGet);
                         httpClient.close();
                     catch  (IOException e) {
                         e.printStackTrace();
                     }
                 }
             }.start();
         }
     }
}

Server Controller The code is as follows ( For the time being, the acquisition is omitted request Object code ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller
public  class  TestController {
 
     // Store existing parameters , Used to determine whether the parameter is repeated , To determine whether the thread is safe
     public  static  Set<String> set =  new  ConcurrentSkipListSet<>();
 
     @RequestMapping ( "/test" )
     public  void  test()  throws  InterruptedException {
         
         // ………………………… In some way you get request object ………………………………
 
         // Judging thread safety
         String value = request.getParameter( "key" );
         if  (set.contains(value)) {
             System.out.println(value +  "\t Recurring ,request Concurrency is not secure !" );
         else  {
             System.out.println(value);
             set.add(value);
         }
         
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

Add : The above code was originally used HashSet To judge value Whether to repeat , After netizens criticized and corrected , It is improper to use thread unsafe collection classes to verify thread security , Now it's changed to ConcurrentSkipListSet.

If request Object thread safe , The results are as follows :

If there is a thread safety problem , The results printed on the server might look like this :

Unless otherwise specified , The test code will be omitted later in this article .

Method 1:Controller China Canada parameters

Code example

This method is the easiest to implement , Go straight up Controller Code :

1
2
3
4
5
6
7
8
@Controller
public  class  TestController {
     @RequestMapping ( "/test" )
     public  void  test(HttpServletRequest request)  throws  InterruptedException {
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

The principle of this method is , stay Controller Method begins to process the request ,Spring Will request Object assignment to method parameters . except request object , There are many more parameters that can be obtained in this way , For details, please refer to :https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

Controller In order to get request After the object , If you want to do something else ( Such as service Method 、 Tools, methods, etc ) Use request object , You need to put request The object is passed in as a parameter .

Thread safety

test result : Thread safety

analysis : here request Objects are method parameters , Equivalent to local variable , There's no doubt that it's thread safe .

Advantages and disadvantages

The main drawback of this method is request Objects are too redundant to write , It is mainly reflected in two aspects :

1)      If more than one controller All methods need request object , Then you need to add it to each method request Parameters

2)      request Objects can only be obtained from controller Start , If you use request The object is in the deeper level of function call , Then all methods in the call chain need to be added request Parameters

actually , Throughout the processing of the request ,request The object is all the way through ; in other words , Except for special cases like timers ,request Object is equivalent to a global variable inside the thread . And the method , It's equivalent to changing this global variable , Come and go .

Method 2: Automatic injection

Code example

On the first code :

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public  class  TestController{
     
     @Autowired
     private  HttpServletRequest request;  // Automatic injection request
     
     @RequestMapping ( "/test" )
     public  void  test()  throws  InterruptedException{
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

Thread safety

test result : Thread safety

analysis : stay Spring in ,Controller Of scope yes singleton( Single case ), That is to say, in the whole web In the system , only one TestController; But it's infused with request It's thread safe , The reason lies in :

In this way , When Bean( In this case TestController) On initialization ,Spring There was no injection of request object , It's a proxy injection (proxy); When Bean Required in request Object time , Get through this agent request object .

 

The following is a specific code to illustrate this implementation .

Add breakpoints to the above code , see request Object properties , As shown in the figure below :

As can be seen in the figure ,request It's actually an agent : For the implementation of the proxy, see AutowireUtils The inner class of ObjectFactoryDelegatingInvocationHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
  * Reflective InvocationHandler for lazy access to the current target object.
  */
@SuppressWarnings ( "serial" )
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 {
         // …… Omit irrelevant code
         try  {
             return  method.invoke( this .objectFactory.getObject(), args);  // Proxy implementation core code
         }
         catch  (InvocationTargetException ex) {
             throw  ex.getTargetException();
         }
     }
}

in other words , When we call request Methods method when , It's actually called by objectFactory.getObject() Of the generated object method Method ;objectFactory.getObject() The generated object is really request object .

Keep looking at the image above , Find out objectFactory The type of WebApplicationContextUtils The inner class of RequestObjectFactory; and RequestObjectFactory The code is as follows :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
  * Factory that exposes the current request object on demand.
  */
@SuppressWarnings ( "serial" )
private  static  class  RequestObjectFactory  implements  ObjectFactory<ServletRequest>, Serializable {
     @Override
     public  ServletRequest getObject() {
         return  currentRequestAttributes().getRequest();
     }
     @Override
     public  String toString() {
         return  "Current HttpServletRequest" ;
     }
}

among , To get request Object needs to call currentRequestAttributes() Methods to get RequestAttributes object , The implementation of this method is as follows :

1
2
3
4
5
6
7
8
9
10
/**
  * Return the current RequestAttributes instance as ServletRequestAttributes.
  */
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;
}

Generate RequestAttributes The core code of the object is in the class RequestContextHolder in , The relevant codes are as follows ( The irrelevant code in this class is omitted ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  abstract  class  RequestContextHolder {
     public  static  RequestAttributes currentRequestAttributes()  throws  IllegalStateException {
         RequestAttributes attributes = getRequestAttributes();
         // The irrelevant logic is omitted here …………
         return  attributes;
     }
     public  static  RequestAttributes getRequestAttributes() {
         RequestAttributes attributes = requestAttributesHolder.get();
         if  (attributes ==  null ) {
             attributes = inheritableRequestAttributesHolder.get();
         }
         return  attributes;
     }
     private  static  final  ThreadLocal<RequestAttributes> requestAttributesHolder =
             new  NamedThreadLocal<RequestAttributes>( "Request attributes" );
     private  static  final  ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
             new  NamedInheritableThreadLocal<RequestAttributes>( "Request context" );
}

You can see from this code that , Generated RequestAttributes Objects are thread local variables (ThreadLocal), therefore request Objects are also thread local variables ; That's the guarantee request Object thread security .

Advantages and disadvantages

The main advantages of this method :

1)      Injection is not limited to Controller in : In the method 1 in , Only in Controller Add request Parameters . And for the method 2, Not only in Controller In the injection , It can also be in any Bean In the injection , Include Service、Repository And ordinary Bean.

2)      The injected objects are not limited to request: Except for injection request object , This method can also inject other scope by request or session The object of , Such as response object 、session Object etc. ; And thread safety .

3)      Reduce code redundancy : Just need to be request Object's Bean In the injection request object , You can do it when Bean In each method of , With the method 1 It greatly reduces code redundancy .

however , There will also be code redundancy in this method . Consider a scenario like this :web There are many in the system controller, Every controller You can use request object ( This kind of scene is actually very frequent ), At this point, you need to write many injections request Code for ; If you still need to inject response, The code is even more cumbersome . The following is an improvement of the automatic injection method , And analyzes its thread security and advantages and disadvantages .

Method 3: Automatically inject

Code example

With the method 2 comparison , Put the injected code into the base class .

The base class code :

1
2
3
4
public  class  BaseController {
     @Autowired
     protected  HttpServletRequest request;     
}

Controller The code is as follows ; It's listed here BaseController Two derived classes of , Because the test code will be different at this time , So the server-side test code is not omitted ; The client also needs to be modified accordingly ( Simultaneous direction 2 individual url Send a large number of concurrent requests ).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Controller
public  class  TestController  extends  BaseController {
 
     // Store existing parameters , Used to judge the parameters value Whether to repeat , To determine whether the thread is safe
     public  static  Set<String> set =  new  ConcurrentSkipListSet<>();
 
     @RequestMapping ( "/test" )
     public  void  test()  throws  InterruptedException {
         String value = request.getParameter( "key" );
         // Judging thread safety
         if  (set.contains(value)) {
             System.out.println(value +  "\t Recurring ,request Concurrency is not secure !" );
         else  {
             System.out.println(value);
             set.add(value);
         }
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}
 
@Controller
public  class  Test2Controller  extends  BaseController {
     @RequestMapping ( "/test2" )
     public  void  test2()  throws  InterruptedException {
         String value = request.getParameter( "key" );
         // Judging thread safety ( And TestController Use one set Judge )
         if  (TestController.set.contains(value)) {
             System.out.println(value +  "\t Recurring ,request Concurrency is not secure !" );
         else  {
             System.out.println(value);
             TestController.set.add(value);
         }
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

Thread safety

test result : Thread safety

analysis : In understanding the method 2 On the basis of thread safety , It's easy to understand the method 3 It's thread safe : When creating different derived class objects , Fields in the base class ( Here's the injection request) It takes up different memory space in different derived class objects , That is to say, it will inject request There is no impact on thread safety when the code is placed in the base class ; The test results also prove this .

Advantages and disadvantages

With the method 2 comparison , Avoided in different Controller Repeat the injection of request; But considering that java Only one base class is allowed to inherit , So if Controller When you need to inherit other classes , This method is no longer easy to use .

No matter the method 2 And methods 3, Only in Bean In the injection request; If other methods ( For example, in the tool class static Method ) Need to use request object , You need to call these methods with request Parameters are passed in . The method described below 4, It can be used directly in tools such as static Method used in request object ( Of course, in all kinds of Bean Can also be used in ).

Method 4: Manual call

Code example

1
2
3
4
5
6
7
8
9
@Controller
public  class  TestController {
     @RequestMapping ( "/test" )
     public  void  test()  throws  InterruptedException {
         HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

Thread safety

test result : Thread safety

analysis : The method and method 2( Automatic injection ) similar , It's just the way 2 Through automatic injection , This method is implemented by manual method call . Therefore, this method is also thread safe .

Advantages and disadvantages

advantage : It can be in Africa Bean Medium direct acquisition . shortcoming : If there are more places to use , The code is very tedious ; So it can be used in conjunction with other methods .

Method 5:@ModelAttribute Method

Code example

The following method and its variants ( variant : take request and bindRequest In subclasses ) We often see :

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public  class  TestController {
     private  HttpServletRequest request;
     @ModelAttribute
     public  void  bindRequest(HttpServletRequest request) {
         this .request = request;
     }
     @RequestMapping ( "/test" )
     public  void  test()  throws  InterruptedException {
         // The simulation program ran for a while
         Thread.sleep( 1000 );
     }
}

Thread safety

test result : Thread unsafe

analysis :@ModelAttribute Annotations are used in Controller In the modification method , Its effect is Controller Each of the @RequestMapping Before method execution , This method will be implemented . So in this case ,bindRequest() The function is in test() Before execution request Object Assignment . although bindRequest() Parameters in request Thread safe in itself , But because of TestController Is a singleton ,request As TestController A domain of , Thread safety cannot be guaranteed .

summary

in summary ,Controller China Canada parameters ( Method 1)、 Automatic injection ( Method 2 And methods 3)、 Manual call ( Method 4) It's all thread safe , Can be used to get request object . If in the system request Objects are used less , Either way can be used ; If you use more , Automatic injection is recommended ( Method 2 And methods 3) To reduce code redundancy . If you need to be in Africa Bean Use in request object , It can be called in the upper layer through parameters , You can also call... Directly in a method ( Method 4) get .

Besides , This article is about getting request Object method , Focus on the thread safety of this method 、 Code complexity and so on ; In the actual development process , You must also consider the specifications of your project 、 Code maintenance and other issues ( Thank you for your comments ).

reference

https://docs.spring.io/spring/docs/4.1.x/spring-framework-reference/html/beans.html#beans-factory-scopes-other-injection

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

https://stackoverflow.com/questions/10541934/spring-aop-and-aspect-thread-safety-for-an-autowired-httpservletrequest-bean

http://www.phpchina.com/portal.php?mod=view&aid=40966

https://stackoverflow.com/questions/22674044/inject-httpservletrequest-into-controller

https://stackoverflow.com/questions/3320674/spring-how-do-i-inject-an-httpservletrequest-into-a-request-scoped-bean

https://my.oschina.net/sluggarddd/blog/678603?fromerr=XhvpvVTi

https://stackoverflow.com/questions/8504258/spring-3-mvc-accessing-httprequest-from-controller

 

It's not easy to create , If it helps you , Just like it 、 A theory of bai ~

It's not easy to create , If it helps you , Just like it 、 A theory of bai ~

It's not easy to create , If it helps you , Just like it 、 A theory of bai ~

 

classification : Spring

label : springmvcrequestcontroller Thread safety thread safeautowire,injectRequestContextHolder@ModelAttribute

A good article is the best   Pay attention to me   Collect the article   


版权声明
本文为[osc_ ow92sntn]所创,转载请带上原文链接,感谢

  1. redis 监控的一些构思
  2. Some ideas of redis monitoring
  3. Spring中获取request的几种方法,及其线程安全性分析
  4. 20分钟带你掌握JavaScript Promise和 Async/Await
  5. Mybatis - introduction, simple entry program
  6. Java Concurrent Programming: concurrent HashMap
  7. Mybatis: one to one association query
  8. Java IO
  9. MySQL user and authorization management
  10. Object oriented [day07]: review of knowledge points (11)
  11. Network Programming TCP / IP to realize client to client chat
  12. Chapter 2 spring
  13. Process of building minicube in local k8s environment
  14. Interpretation of jQuery source code
  15. Making web full screen effect with jquery
  16. Java 7 exception handling new features - address suppressed () method
  17. Spring 4 uses websocket
  18. Using openldap to answer questions and using java to complete LDAP authentication
  19. Implementation of MySQL high availability cluster with corosync + pacemaker and DRBD
  20. Java production of a simple tank war
  21. Design pattern day02
  22. Hadoop pseudo Distributed installation and running test example
  23. Java Concurrent Programming: callable, future and futuretask
  24. Understanding the three characteristics of Java encapsulation
  25. Understanding closure of JavaScript
  26. Java set excel worksheet to read only
  27. Detailed explanation of PID and socket in MySQL
  28. Distributed Java service platform baratine
  29. Java barcode generation technology barcode4j
  30. Use springboot + MySQL + JPA to add, delete, change, query and page the database
  31. Distributed services framework Dubbo / dubbox getting started example
  32. Zookeeper notes (1) installation, deployment and hello world
  33. Oracle database trigger Usage Summary
  34. MySQL master replication with slave server