복붙노트

[SPRING] ScopedProxy는 사용할 세션을 어떻게 결정합니까?

SPRING

ScopedProxy는 사용할 세션을 어떻게 결정합니까?

싱글 톤은 SessionBean을 autowire 할 수는 없지만 ScopedProxy는 할 수있다.

동일한 응용 프로그램에서 100 명의 사용자가 동시에 유효한 세션을 가지고 있다고 가정하면 ScopedProxy는 어떤 세션을 의미하는지 어떻게 결정합니까?

ScopedProxy가 임의의 세션을 선택하지 않는다고 생각합니다. 이것은 제 의견으로 말도 안 될 것입니다.

해결법

  1. ==============================

    1.ThreadLocal은 당신이 찾고있는 답변입니다.

    ThreadLocal은 당신이 찾고있는 답변입니다.

    Spring에 RequestContextHolder가있다.

    수업 내에서 다음을 볼 수 있습니다 :

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
                new NamedThreadLocal<RequestAttributes>("Request attributes");
    

    실제 setter가 있습니다 (정적 인 점에 유의하십시오).

    /**
         * Bind the given RequestAttributes to the current thread.
         * @param attributes the RequestAttributes to expose,
         * or {@code null} to reset the thread-bound context
         * @param inheritable whether to expose the RequestAttributes as inheritable
         * for child threads (using an {@link InheritableThreadLocal})
         */
        public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {}
    

    그래서, 당신이 볼 수 있듯이 ThreadLocal이 제공하는 스레드 별 변수 만 있습니다.

    만약 당신이 여기에서 충분히 골치가 있다면 ThreadLocal.get 구현 (현재 스레드의이 스레드 로컬 변수 복사본에있는 값을 반환합니다)이 있습니다.

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    

    보시다시피 ThreadLocalMap을 사용합니다.

    /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {
    

    getEntry ()는 Map 내에서 조회를 수행합니다. 나는 지금 전체 그림을 보길 바랍니다.

    잠재적 인 NullPointerException에 관해서

    기본적으로 범위가 활성화되어있을 때만 프록시 메소드를 호출 할 수 있습니다. 즉, 스레드 실행은 서블릿 요청이어야합니다. 따라서 모든 비동기 작업, 명령 등은이 방법으로 실패합니다.

    나는 이것이 ScopedProxy의 뒤에 큰 문제라고 말한다. 투명하게 몇 가지 문제를 해결합니다 (호출 체인 단순화, examaple). 그러나 규칙을 따르지 않으면 아마 java.lang.IllegalStateException가 발생합니다. 스레드 바운드 요청을 찾을 수 없습니다.

    (Spring Framework Reference Documentation)은 다음과 같이 말합니다 :

    다음 질문을 확인할 수도 있습니다. 다중 스레드 웹 응용 프로그램에서 요청 범위가 지정된 Bean 액세스

    @Async 및 요청 특성 주입

    일반적으로 말하자면, 문제를 해결하는 직접적인 방법은 없습니다. 앞에서 설명한 것처럼 우리는 RequestAttributes와 연결된 스레드를가집니다.

    잠재적 인 해결책은 필요한 객체를 수동으로 전달하고 @Async의 논리가이를 고려해야한다는 것입니다.

    더 영리한 해결책 (Eugene Kuleshov에 의해 제안 됨)은 그것을 투명하게 수행하는 것입니다. 읽기를 단순화하고 링크를 코드 블록 아래에두기 위해 코드를 복사합니다.

    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    
    /**
     * @author Eugene Kuleshov
     */
    public abstract class RequestAwareRunnable implements Runnable {
      private final RequestAttributes requestAttributes;
      private Thread thread;
    
      public RequestAwareRunnable() {
        this.requestAttributes = RequestContextHolder.getRequestAttributes();
        this.thread = Thread.currentThread();
      }
    
      public void run() {
        try {
          RequestContextHolder.setRequestAttributes(requestAttributes);
          onRun();
        } finally {
          if (Thread.currentThread() != thread) {
            RequestContextHolder.resetRequestAttributes();
          }
          thread = null;
        }
      }
    
      protected abstract void onRun();
    } 
    

    그 질문은 다음과 같습니다.

    보시다시피,이 솔루션은 fact 생성자가 적절한 컨텍스트에서 실행되므로 적절한 컨텍스트를 캐싱하고 나중에 주입하는 것이 가능합니다.

    세션 스코핑 된 빈에 매달려있는 @Async 주석이 달린 또 다른 흥미로운 주제가 있습니다.

  2. ==============================

    2.나는 아주 간단한 설명을 할 것이다.

    나는 아주 간단한 설명을 할 것이다.

    @Component
    @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
    class YourScopedProxy {
    
    public String dosomething() {
            return "Hello"; 
         }
    
    }
    
    
    @Component
    class YourSingleton {
     @Autowired private YourScopedProxy meScopedProxy;
    
     public String usedosomething(){
      return this.meScopedProxy.dosomething();
     }
    }
    
    
       1. How does the ScopedProxy decide what session to use?
    
    we have 100 users
    
    1 user (http session) call YourSingleton.usedosomething => call meScopedProxy :  
    => meScopedProxy  is not the YourScopedProxy  (original)  but a proxy to the YourScopedProxy
       and this proxy understands the scope 
    =>   in this case : proxy get real 'YourScopedProxy' object from  HTTP Session  
    
    
       2. What if 0 users have a Session? Will a NullPointerException occur?
    No because meScopedProxy is a proxy , when u use it 
    => proxy get real 'YourScopedProxy' object from  HTTP Session  
    
  3. ==============================

    3.이 문장은 다소 혼란 스럽습니다. 그것은 다음과 같이 재사용되어야한다.

    이 문장은 다소 혼란 스럽습니다. 그것은 다음과 같이 재사용되어야한다.

    즉, Spring은 비 프록시 세션 스코프 빈을 싱글 톤 범위 빈에 삽입하지 못한다. 이것은 싱글 톤 범위의 빈에 프록시 된 세션 범위 bean을 주입 할 때 성공할 것이다.

    먼저 세션이 HttpSession으로 표시되는 Servlet 컨테이너의 구성 요소임을 명확히해야합니다. Spring (그리고 Spring MVC)은 session-scoped beans (그리고 flash 속성과 같은 다른 것들)로 추상화한다.

    HttpSession 객체는 일반적으로 적절한 쿠키를 통해 사용자에게 연결됩니다. HTTP 요청은 쿠키를 사용자 식별 값으로 제공하고 Servlet 컨테이너는 연관된 HttpSession을 검색하거나 생성합니다. 즉, 세션은 요청의 정보로 식별 할 수 있습니다. 귀하 또는 Spring은 요청에 대한 액세스 권한이 필요합니다.

    Spring MVC는 분명히 DispatcherServlet을 통해 요청에 접근 할 수있다. 비록 Spring MVC가 핸들러 메소드에 노출되지는 않지만 (Spring MVC가 Servlet API를 숨기려고한다).

    다음은 어느 정도 구현 세부 사항입니다. Spring MVC는 요청 객체 (HttpServletRequest)를 호출 스택 위로 확장하는 대신 RequestContextHolder에 저장합니다.

    서블릿 컨테이너는 일반적으로 단일 스레드에서 요청을 처리하므로 (비동기가 아닌)이 작업을 수행 할 수 있습니다. 요청 처리기 스레드에서 코드를 실행 중이면 요청에 액세스 할 수 있습니다. 요청에 대한 액세스 권한이 있으면 HttpSession에 액세스 할 수 있습니다.

    실제 구현은 다소 길다. 들어가기를 원한다면 SessionScope로 시작해서 나가십시오.

    이 모든 것은 Spring이 구체적인 빈 타입의 객체를 주입하지 않는다고 말하는데, 그것은 프록시를 주입한다. 다음 예제는 JDK 프록시 (인터페이스 만 사용)를 사용하여 세션 범위 프록시의 동작을 보여줍니다. 주어진

    interface SessionScopedBean {...}
    class SessionScopedBeanImpl implements SessionScopedBean {...}
    

    Spring은 SessionScopedBean과 비슷한 (하지만 훨씬 더 정교한) 프록시를 만들 것이다.

    SessionScopedBean proxy = (SessionScopedBean) Proxy.newProxyInstance(Sample.class.getClassLoader(),
            new Class<?>[] { SessionScopedBean.class }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    HttpSession session = ...;// get session through RequestContextHolder
                    SessionScopedBean actual = session.getAttribute("some.bean.identifier");
                    if (actual == null) {
                        // if absent, set it
                        session.setAttribute("some.bean.identifier", actual = new SessionScopedBeanImpl());
                    }
                    return method.invoke(actual, args); // delegate to actual object
                }
            });
    

    이 프록시 객체를 싱글 톤 빈 (아마도 컨트롤러)에 삽입하십시오. 싱글 톤 빈이 세션 범위의 빈을 사용할 때 실제로 프록시를 통과하고 프록시는 실제 객체를 호출하고 호출을 위임합니다.

    Spring은 프록시를 삽입한다. 프록시가 null이 아닙니다. 그것은 대상입니다. 실제 목표를 검색하고 사용하는 방법을 알고 있습니다. 세션 범위에서 대상이 존재하지 않으면 세션에서 만들어 저장됩니다.

    세션의 범위 밖에서 세션 범위 프록시를 사용하려고 시도하는 것이 위험합니다. 앞에서 설명한 것처럼이 모든 트릭은 서블릿 컨테이너가 단일 스레드 내에서 단일 요청을 처리하여 작동하기 때문에 작동합니다. 요청이 바인딩되지 않은 스레드에서 세션 범위 Bean에 액세스하려고하면 예외가 발생합니다.

    따라서 스레드 범위에서 세션 범위 Bean을 전달하지 마십시오. 서블릿 스펙은 Async Processing을 사용할 수있게하며 Spring MVC는 DefferedResult 및 Callable에서이를 지원합니다. 여기에 대한 블로그 시리즈가 있습니다. 세션 범위가있는 bean은 여전히 ​​전달할 수 없습니다. 그러나 AsyncContext에 대한 참조가 있으면 HttpServletRequest를 검색하고 HttpSession에 직접 액세스 할 수 있습니다.

    스레드 (또는 Runnables)를 디스패치하는 방법을 제어하는 ​​경우 여기에 설명 된 것과 같이 요청 컨텍스트를 복사하는 데 사용할 수있는 기술이 있습니다.

    다음은 (세션) 범위 및 프록시에 대한 몇 가지 관련 게시물입니다.

  4. from https://stackoverflow.com/questions/33038856/how-does-a-scopedproxy-decide-what-session-to-use by cc-by-sa and MIT license