Подтвердить что ты не робот

Как ScopedProxy решает, какой сеанс использовать?

Синглтон не может autowire SessionBean, но ScopedProxy может.

Предполагая, что 100 пользователей имеют действительный сеанс одновременно в одном приложении, как ScopedProxy решает, что такое сеанс?

Я не думаю, что ScopedProxy выбирает любой случайный сеанс, это было бы бессмысленно, на мой взгляд.

  • Как ScopedProxy решает, какой сеанс использовать?
  • Что делать, если 0 пользователей имеют сеанс? Возникает ли NullPointerException?
  • A @Async - это другой поток, чем вызывающий запрос-процесс-Thread, как ввести HttpRequest-Context в задачу Async?
4b9b3361

Ответ 1

ThreadLocal - это в значительной степени ответ, который вы ищете.

Этот класс предоставляет локальные переменные потока. Эти переменные различаются от их обычных коллег в том, что каждый поток, который обращается к одному (посредством метода get или set) имеет свой собственный, независимо инициализированный копия переменной.

Spring имеет RequestContextHolder

Класс владельца, чтобы выставить веб-запрос в виде связанного с потоком Объект RequestAttributes. Запрос будет унаследован любым ребенком потоки, порожденные текущим потоком, если установлен флаг наследования к true.

Внутри класса вы увидите следующее:

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

И вот фактический сеттер (обратите внимание, что он статичен):

/**
     * 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 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 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() выполняет поиск на Карте. Надеюсь, теперь вы видите всю картину.

Относительно потенциального исключения NullPointer

В принципе, вы можете вызывать методы прокси только в том случае, если область действия активна, что означает, что выполнение потока должно быть запросом сервлета. Таким образом, любые асинхронные задания, команды и т.д. Не срабатывают при таком подходе.

Я бы сказал, это довольно большая проблема для ScopedProxy. Он разрешает некоторые проблемы прозрачно (упрощает цепочку вызовов, для справки), но если вы не будете следовать правилам, вы, вероятно, получите java.lang.IllegalStateException: No thread-bound request found

(Spring Справочная документация по платформам) говорит следующее:

DispatcherServlet, RequestContextListener и RequestContextFilter все сделать то же самое, а именно привязать объект HTTP-запроса к Тема, которая обслуживает этот запрос. Это делает beans, которые доступ к запросам и сеансам доступен далее по цепочке вызовов.

Вы также можете проверить следующий вопрос: Доступ к области запроса beans в многопоточном веб-приложении

@Async и ввод атрибутов запроса

Вообще говоря, нет простого способа решить проблему. Как показано ранее, у нас есть связанные с потоком RequestAttributes.

Потенциальное решение состоит в том, чтобы передать требуемый объект вручную и убедиться, что логика, стоящая за @Async, учитывает это.

Немного более умное решение (предложенное Евгением Кулешовым) должно сделать это прозрачно. Я скопирую код, чтобы упростить чтение и поместить ссылку под блок кода.

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();
} 

Вот этот вопрос: Доступ к ограниченному прокси beans в потоках

Как вы можете видеть, это решение основывается на том, что конструктор фактов будет выполнен в соответствующем контексте, поэтому можно кэшировать соответствующий контекст и позже вставлять его.

Вот еще одна, довольно интересная тема @аннотированный метод Async, зависающий на bean в сеансе

Ответ 2

Синглтон не может autowire SessionBean, но ScopedProxy может.

Это утверждение несколько путано. Он должен быть изменен как

Spring не может вводить beans в область с ограниченной видимостью beansесли только первые не определены как (облачные) прокси.

Другими словами, Spring не сможет вставить не проксированный сеанс bean в однооконный bean. Это будет успешным при инъекции scied-scoped bean в однооконном bean.

Предполагая, что 100 пользователей имеют действительный сеанс одновременно с тем же приложение, как ScopedProxy решает, что означает сеанс?

Первое, что нужно прояснить, - это то, что сеанс является компонентом контейнера Servlet, представленным HttpSession. Spring (и Spring MVC) абстрагирует его с помощью beans (и других вещей, таких как флэш-атрибуты).

HttpSession объекты обычно ассоциируются с пользователем через соответствующие файлы cookie. HTTP-запрос предоставляет cookie с идентификационным значением пользователя, а контейнер Servlet извлекает или создает связанный HttpSession. Другими словами, сеанс идентифицируется из информации в запросе. Вам или Spring нужен доступ к запросу.

Spring MVC, очевидно, имеет доступ к запросу через DispatcherServlet, даже если он обычно не выставляет его методам обработчика (помните, что Spring MVC пытается скрыть API сервлета от вас).

Ниже приведено более или менее подробное описание реализации. Spring MVC вместо того, чтобы распространять объект запроса (HttpServletRequest) до конца вызова, сохранит его в RequestContextHolder.

Класс владельца, чтобы выставить веб-запрос в виде связанного с потоком RequestAttributes объект.

Он может это сделать, потому что контейнеры Servlet обычно (т.е. не async) обрабатывают запросы в одном потоке. Если вы выполняете код в потоке обработчика запроса, у вас есть доступ к запросу. И если у вас есть доступ к запросу, у вас есть доступ к HttpSession.

Фактическая реализация довольно длинная. Если вы хотите войти в него, начните с SessionScope и проведите свой путь.

Все это означает, что Spring не вводит объект конкретного типа bean, он вводит прокси. Следующий пример, используя JDK proxies (только интерфейсы), - это то, что похоже на прокси-серверы с сеансом. Учитывая,

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
            }
        });

и вставьте этот объект proxy в ваш singleton beans (предположительно, контроллер). Когда ваш singleton bean собирается использовать bean для сеанса, он фактически проходит через прокси, а прокси-сервер извлекает и делегирует вызов фактическому объекту.

Что делать, если 0 пользователей имеют сеанс? Возникает ли NullPointerException?

Spring вводит прокси. Прокси не является null. Это объект. Он знает, как извлекать и использовать фактическую цель. С областью сеанса, если цель не существует, она создается и сохраняется в сеансе (и затем используется).


Опасность здесь заключается в том, чтобы использовать прокси-серверы, зависящие от сеанса, вне контекста сеанса. Как было сказано ранее, весь этот трюк работает, потому что контейнеры Servlet работают, обрабатывая один запрос в пределах одного потока. Если вы попытаетесь получить доступ к узлу bean с сеансом связи в потоке, где запрос не связан, вы получите исключение.

Как таковые, не пытайтесь передавать beans с помощью сеанса через границы потоков. Спецификация Servlet позволяет использовать Async Processing и Spring MVC поддерживает его с помощью DefferedResult и Callable. Там в блоге об этом, здесь. Вы по-прежнему не можете обойти охват сеанса bean. Однако, если у вас есть ссылка на AsyncContext, вы можете получить HttpServletRequest и получить доступ к HttpSession самостоятельно.

Если вы контролируете, как вы отправляете потоки (точнее, Runnable s), существуют методы, которые вы можете использовать для копирования контекста запроса как описано здесь.


Вот некоторые связанные сообщения о (сеансах) областей и прокси:

Ответ 3

Я сделаю очень простое объяснение

@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