Spring MVC 가 Controller 층 에 request 를 주입 하 는 구덩이 에 대한 상세 한 설명

머리말
코드 를 절약 하기 위해 HttpServletRequest 를 방법 체 에 설명 하지 않 고 autowire 로 드릴 의 구 덩이 를 직접 주입 합 니 다.
결론:마음 이 급 한 사람 에 게.Controller 의 구성원 변수 에 직접@Autowire 를 사용 하여 HttpServletRequest 를 설명 합 니 다.이것 은 스 레 드 가 안전 합 니 다!

@Controller
public class TestController{
 @Autowire
 HttpServletRequest request;
 @RequestMapping("/")
 public void test(){
  request.getAttribute("uid"); 
 }
}
결론 은 위 와 같다.
배경
이 렇 습 니 다.프로젝트 에서 저 는 Request 의 머리 에 인증 정 보 를 넣 었 기 때문에 차단 기 에서 정 보 를 캡 처 하고 검증 을 통과 한 후에 현재 사용자 의 신분 을 request 의 Attribute 에 추가 하여 Controller 층 에서 꺼 내 서 재 활용 할 수 있 습 니 다.
의문:왜 Controller 에서@RequestHeader 를 직접 사용 하지 않 습 니까?header 안 은 암호 화 된 데이터 이 고 복잡 한 인증 을 거 쳐 판단 해 야 하기 때문에 이 단 계 를 차단기 에 버 리 고 실행 합 니 다.
그래서 복호화 한 후에 저 는 사용자 정보(예 를 들 어 uid)를request.setAttribute()request 에 설정 하여 Controller 에서 추출 합 니 다.
request 를 사용 하려 면 다음 과 같은 방법 으로 설명 해 야 합 니 다.

public Result save(HttpServletRequest request){
 // dosomething();
}
그러면 저 는 모든 방법 에 uid 를 사용 해 야 합 니 다.모든 방법 이 하나의 request 인 자 를 설명 하고 불필요 한 절 차 를 절약 하기 위해 서 입 니 다.나 는 기 류 를 하나 썼 다.

public class CommonController{
 @Autowire
 HttpServletReqeust request;
 public String getUid(){
  return (String)request.getAttribute("uid");
 }
}
나중에 저 는 contrller 가 단일 사례 이기 때문에 이렇게 쓰 면 뒤의 reqeust 가 앞의 request 를 덮어 쓰 고 병발 조건 에서 스 레 드 안전 문제 가 발생 하지 않 을 까 걱정 했 습 니 다.그래서 저 는 segment Fault 에 질문 을 했 습 니 다.대부분의 네티즌 들 은'스 레 드 문제 가 있 습 니 다!'라 고 말 했 습 니 다segmentFault 문제 주소\#\#검증 과정 은 네티즌 들 의 대부분 관점 이 방법 에 만 설명 할 수 있 기 때문에 저 는 자 연 스 럽 게 그렇게 많은 코드 를 쓰 는 것 을 포기 하고 싶 지 않 습 니 다.그래서 제 검증 과정 을 시작 하 겠 습 니 다.열성 적 인 프로그래머 들 이 저 에 게 여러 가지 해결 방안 을 제공 해 주 었 습 니 다.제 가 힘 을 써 서 증명 한 이상 결 과 를 여기에 두 고 여러분 께 공유 하 겠 습 니 다.
방법
첫 번 째 방법 은 controller 의 방법 에 Http ServletReqeust 를 표시 하 는 것 입 니 다.코드 는 다음 과 같 습 니 다.

@RequestMapping("/test")
@RestController
public class CTest {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/iiii")
 public String test(HttpServletRequest request) {
  logger.info(request.hashCode() + "");
  return null;
 }
}
브 라 우 저 에서 F5 미 친 듯 이 누 르 기
출력

그때 나 는 어 리 석 었 다.**약속 한 스 레 드 는 안전 하 다!**이 건 뭐야?같은 request 잖 아!왜 놀 리 는 거 야!이 때문에 저 는 오랫동안 request 를 찾 았 습 니 다.hashcode()를 다시 썼 습 니까?
아,사실은 그렇습니다.제 가 브 라 우 저 로 F5 를 미 친 듯 이 눌 렀 기 때문에 아무리 눌 러 도 동시 다발 을 모 의 할 수 없습니다.그러면 서버 가 같은 스 레 드 로 제 요청 을 처리 하 는 것 만으로 도 충분 합 니 다.이 request 의 hashcode 에 대해 jdk 의 말 에 따 르 면 obj 가 jvm 에 있 는 가상 주소 에 따라 계산 한 것 입 니 다.뒤의 일 은 제 가 추측 한 것 입 니 다.만약 에 진정 으로 알 고 싶 은 것 이 있다 면 알려 주 십시오!
추측 하 다.
서버 에 있 는 모든 thread 가 신청 한 request 의 메모리 공간 은 이 서버 가 시 작 될 때 고정 되 어 있 습 니 다.그러면 제 가 요청 할 때마다 그 가 신청 한 메모리 공간(배열 과 같은 구조 일 수 있 습 니 다)에 request 를 새로 만 듭 니 다.(배열 과 같은 시작 점 은 항상 같은 메모리 주소 입 니 다)그러면 제 가 요청 을 하 겠 습 니 다.그 는 시작 위치 에 Request 를 새로 만들어 서 servlet 에 전달 하고 처 리 를 시작 합 니 다.처리 가 끝 난 후에 소각 합 니 다.그러면 그 는 다음 요청 으로 새로 만 든 Request 를 이전 request 가 소각 되 었 기 때문에 시작 주소 부터 만 듭 니 다.그러면 모든 것 이 설명 할 수 있 습 니 다!
추측 이 끝나다
검증 추측:
내 가 없 애 버 릴 시간 없 으 면 되 잖 아.테스트 코드.

@RequestMapping("/test")
@RestController
public class CTest {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/oooo")
 public String testA(HttpServletRequest request) throws Exception {
  Thread.sleep(3000);
  logger.info(request.hashCode() + "");
  logger.info(reqeust.getHeader("uid");
  return null;
 }
 @RequestMapping("/iiii")
 public String test(HttpServletRequest request) {
  logger.info(request.hashCode() + "");
  logger.info(reqeust.getHeader("uid");
  return null;
 }
}
위 와 같이 인터페이스/ooo 에서 3 초 동안 휴면 합 니 다.만약 그 가 reqeust 를 함께 사용한다 면 뒤의 요청 은 이 휴면 중의 reqeust 를 덮어 씁 니 다.들 어 오 는 uid 는 인터페이스 주소 입 니 다.먼저 시작/ooo 후 시작/iii
출력

controller.CTest:33 - 364716268
controller.CTest:34 - iiii
controller.CTest:26 - 1892130707
controller.CTest:27 - oooo
결론:1.나중에 시 작 된/ii 는 앞/ooo 의 데 이 터 를 덮어 쓰 지 않 았 고 스 레 드 안전 문제 가 없습니다.2.request 의 hashcode 는 다 릅 니 다./ooo 의 차단 으로 인해 다른 스 레 드 를 처리 해 야 하기 때문에 그 는 이전 과 같은 모든 hashcode 가 아 닌 request 를 새로 만 들 었 습 니 다.
이차 검증

public class HttpTest {
 public static void main(String[] args) throws Exception {
  for (int i = 300; i > 0; i--) {
   final int finalI = i;
   new Thread() {
    @Override
    public void run() {
     System.out.println("v###" + finalI);
     HttpRequest.get("http://localhost:8080/test/iiii?").header("uid", "v###" + finalI).send();
    }
   }.start();
  }
 }
}
아 날로 그 병행 조건 에서 header 의 uid 300 개 는 완전히 받 아들 여 덮어 쓰 지 않 았 습 니 다.
그래서 이런 방식 은 라인 안전 문제 가 없다.
방법
CommonController 에서@ModelAttribute 로 처리 합 니 다.

public class CommonController {

// @Autowired
 protected HttpServletRequest request;
 @ModelAttribute
 public void bindreq(HttpServletRequest request) {
  this.request = request;
 }
 protected String getUid() {
  System.out.println(request.toString());
  return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid");
 }
}
이렇게 하 는 것 은 라인 안전 문제 가 있다!뒤의 request 는 이전의 것 을 덮어 쓸 수 있 습 니 다!
인증 코드

@RestController
@RequestMapping("/test")
public class CTest extends CommonController {
 Logger logger = LoggerFactory.getLogger(getClass());
 @RequestMapping("/iiii")
 public String test() {
  logger.info(request.getHeader("uid"));
  return null;
 }
}

public class HttpTest {
 public static void main(String[] args) throws Exception {
  for (int i = 100; i > 0; i--) {
   final int finalI = i;
   new Thread() {
    @Override
    public void run() {
     System.out.println("v###" + finalI);
     HttpRequest.get("http://localhost:8080/test/iiii").header("uid", "v###" + finalI).send();
    }
   }.start();
  }
 }
}
일부 출력 결 과 를 캡 처 하 였 습 니 다.

controller.CTest:26 - v###52
controller.CTest:26 - v###13
controller.CTest:26 - v###57
controller.CTest:26 - v###57
controller.CTest:26 - v###21
controller.CTest:26 - v###10
controller.CTest:26 - v###82
controller.CTest:26 - v###82
controller.CTest:26 - v###93
controller.CTest:26 - v###71
controller.CTest:26 - v###71
controller.CTest:26 - v###85
controller.CTest:26 - v###85
controller.CTest:26 - v###14
controller.CTest:26 - v###47
controller.CTest:26 - v###47
controller.CTest:26 - v###69
controller.CTest:26 - v###22
controller.CTest:26 - v###55
controller.CTest:26 - v###61
57,71,85,47 이 덮 여 있 고 일부 request 를 잃 어 버 렸 습 니 다!
이렇게 하 는 것 은 라인 이 안전 하지 않다!
방법
CommonController 를 기본 클래스 로 사용 하여 request Autowire 를 사용 합 니 다.

public class CommonController {
 @Autowired
 protected HttpServletRequest request;
 protected String getUid() {
  System.out.println(request.toString());
  return request.getAttribute("uid") == null ? null : (String) request.getAttribute("uid");
 }
}
테스트 인터페이스 가 같 아서 결과 가 반 갑 습 니 다!100 개의 request 는 덮어 쓰 지 않 았 습 니 다.저 는 범 위 를 넓 혀 서 대여섯 번 을 측 정 했 습 니 다.수천 번 의 요청 은 덮어 쓰 지 않 았 습 니 다.이런 쓰기 방법 은 스 레 드 안전 문제 가 없다 는 것 을 증명 할 수 있 습 니 다!
또 하나의 재 미 있 는 것 은 아무리 많은 병발 을 사용 하 더 라 도 request 의 hashcode 는 항상 같 습 니 다.또한 같은 Controller 의 서로 다른 인 터 페 이 스 를 테스트 하 는 것 도 마찬가지 입 니 다.sleep 를 사용 하여 강제로 막 고 hashcode 도 마찬가지 입 니 다.그러나 서로 다른 contrller 를 방문 하면 hashcode 는 다 릅 니 다.구체 적 으로 어떻게 실현 하 는 지 저도 계속 깊이 파고 들 지 않 았 습 니 다.
그러나 결론 은 글 이 처음 말 한 것 처럼 나 왔 다.
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기