Является ли ThreadLocal предпочтительным для HttpServletRequest.setAttribute («ключ», «значение»)?

Спецификация сервлета (см. Мой предыдущий вопрос) гарантирует, что один и тот же поток выполнит все фильтры и связанный сервлет. Учитывая это, я не вижу никакого смысла для передачи данных с использованиемHttpServletRequest.setAttribute если есть возможность использоватьThreadLocal (при условии, что вы правильно очистите). Я чувствую, что есть два преимущества использованияThreadLocal: безопасность типов и лучшая производительность, потому что не используются строковые ключи или карты (за исключением, вероятно, в коллекции потоков по (не строковому) идентификатору потока).

Может ли кто-нибудь подтвердить, если я прав, чтобы я мог отказаться отsetAttribute?

 necromancer10 апр. 2012 г., 23:22
Я не использую JSP (& quot; если есть возможность использоватьThreadLocal& quot;), поэтому этот вопрос касается их использования по другим причинам.
 Matt Ball10 апр. 2012 г., 23:11
Атрибуты запроса, в первую очередь, для рендеринга JSP.
 Matt Ball11 апр. 2012 г., 05:29
EntityManager являетсяvery обычно получается с DI - вы должны серьезно рассмотреть этот подход.
 Matt Ball10 апр. 2012 г., 23:24
Так где / как именно вы собираетесь использовать эти значения? Этоsounds как вы просто используете переменные global-ish вместо DI или регулярной передачи аргументов.
 necromancer10 апр. 2012 г., 23:31
Мне в первую очередь нужно перевезтиUser а такжеEntityManager объекты от пользователя и базы данных Фильтры к сервлету. Я также обнаружил, что они часто и неожиданно необходимы в коде, находящемся дальше, и я испытываю желание использовать их далеко за пределами сервлета (то есть во вложенном коде, вызываемом doGet). Я чувствую, что может быть лучший способ для более глубокого кода - предложения? Но я не чувствую, что есть лучший способ для фильтра к сервлету - предложения? У меня нет зависимостей JSP / JSF / other-framework, и я использую чистую Java с сервлетами и фильтрами. Я контролирую весь код. Спасибо!

Ответы на вопрос(4)

Решение Вопроса

Is ThreadLocal preferable to HttpServletRequest.setAttribute(“key”, “value”)?

Зависит от конкретного функционального требования.

JSF, например, хранитFacesContext вThreadLocal, Это позволяет получить доступ ко всем артефактам JSF, включая & quot; raw & quot;HttpServletRequest а такжеHttpServletResponse в любом месте кода, который выполняетсяFacesServletтакие как управляемые бобы. Большинство других основанных на Java средах MVC следуют тому же примеру.

Согласно вашему комментарию,

I primarily need to transport the User and EntityManager objects from the user and database Filters to the Servlet. I also find that these are frequently and unexpectedly needed in code further down the line and I am tempted to use them well beyond the Servlet (i. e. in nested code called by doGet). I feel there may be a better way for deeper code - suggestions?

Что касаетсяUser В качестве примера, для которого я предполагаю, что это атрибут сеанса, я скорее следую тому же подходу, что и JSF. СоздатьThreadLocal<Context> гдеContext Ваш пользовательский класс-оболочка, содержащий ссылки на текущийHttpServletRequest и, возможно, такжеHttpServletResponse так что вы можете получить к ним доступ в любом месте вашего кода. При необходимости предоставьте удобные методы, чтобы получить среди другихUser прямо изContext учебный класс.

Что касаетсяEntityManager Например, вы можете использовать тот же подход, но я лично не включил его вsame ThreadLocal<Context>, а скорее другой. Или, лучше, просто получить его из JNDI на уровне сервиса, что позволит вам более точно контролировать транзакции. В любом случае, пожалуйста, убедитесь, что вы правильно обрабатываете коммит / закрытие. Принятие сохранения и управления транзакциями из контейнера должно быть сделано с особой осторожностью. Я действительно пересматриваю отвращение к использованию существующих и хорошо спроектированных API / сред, таких как EJB / JPA, в противном случае вы рискуете потратить впустую время на переосмысление всех уже стандартизированных API и прочего.

See also: Retrieving Web Session from a POJO Outside the Web Container Design Patterns web based applications
 12 апр. 2012 г., 19:43
Откуда вы получаете ThreadLocal, в этом случае это будет одноэлементный (или иным образом статический) класс, поскольку это механизм совместного использования ThreadLocal между фильтрами и сервлетом. Я бы порекомендовал не использовать такой синглтон глубже в коде (если только вы не передаете представление интерфейса синглтона как параметр). Кроме того, если вы не пишете каркас, который инкапсулирует лежащие в основе механизмы, я бы не использовал подход ThreadLocal над атрибутами для соображений производительности, если код приложения не является настолько узким, что это действительно имеет значение.
 necromancer12 апр. 2012 г., 07:30
Спасибо за ваш ответ. Я предпочитаю ваш подход, но для полноты изложения вы не могли бы высказать свое мнение о "синглетонах - это плохо". критика ThreadLocal? (все остальные ответы предлагают эту критику и предпочитают setAttribute, поэтому было бы хорошо, если бы это было возможно). еще раз спасибо!
 12 апр. 2012 г., 14:48
ThreadLocal это не синглтон. Singleton - это статический класс с лениво загруженным статическим содержимым, который всегда возвращает то же самое в течение остатка времени жизни приложения.
 necromancer12 апр. 2012 г., 21:30
@ increment1 - это был опыт обучения. @BalusC прав, что он не одиночный (см. Javadoc, вы можете создавать несколько экземпляров, и нет никакой ленивой загрузки). Это не статический класс в том смысле, что здесь нет экземпляров. Статические методы ThreadLocal являются фабричными и создают несколько экземпляров. Смотрите исходный код дляget(): grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… Нет глобальных / статических структур данных; он внутренне использует непубличнуюthreadLocals членThread класс. (продолжение)
 necromancer12 апр. 2012 г., 21:35
Таким образом, ни на одной фотографии нет глобалов и синглетонов. Уже есть потокThread объект доступен для любой части кода иThreadLocal просто использует поле в потокеThread объект для предоставленияThreadLocals. Поэтому ни одна критика синглетонов или глобалов не применима. Пожалуйста, дайте мне знать, если я ошибаюсь, и если нет, я уверен, что вы сочтете это лучшим ответом. Еще раз спасибо за ваш собственный ответ. Это определенно помогло мне копнуть глубже и лучше понять вещи.

Использование ThreadLocal таким способом подразумевает, что вы полагаетесь на какой-то тип синглтона для поддержания общего состояния. В конечном счете, это обычно считается плохим дизайном, поскольку у него есть ряд недостатков, в том числе сложность инкапсуляции функциональности, знание зависимостей и невозможность замены (или макета) функциональности.

Снижение производительности при использовании атрибутов или сеанса, вероятно, того стоит по сравнению с менее привычным предложением переменных ThreadLocal в одиночном коде. Тем не менее, это, конечно, зависит от вашего конкретного случая использования и проекта. Использование синглетонов в сервлетах в качестве способа поддержания состояния приложения несколько распространено из-за трудности совместного использования состояния приложения с сервлетами (сложно внедрить зависимости), но для этих объектов одноэлементные объекты непривычно поддерживать для состояния пользователя (вне EJBs).

Если вы используете сеанс или устанавливаете объект контейнера в качестве атрибута, вы будете иметь дело только с одной выборкой через String (которая будет O (1)), а затем с одним случаем для вашего типа контейнера (который имеет специальные средства доступа для все значения, которые вы хотите). Вызов кода ниже по линии должен принимать соответствующие параметры и избегать, насколько это возможно, любого типа глобальных или одноэлементных.

В конечном итоге, прежде чем рассматривать несколько необычное (хотя и умное) решение во имя производительности, всегда сначала тестируйте простую реализацию, чтобы убедиться, что ее производительность адекватна. 2ns, которые могут быть сохранены здесь, скорее всего, незначительны по сравнению с 20 + мс, потраченными на запросы базы данных и установление соединения tcp.

 necromancer11 апр. 2012 г., 01:13
Разве JVM не скрывает все это от вас? Посмотри пожалуйстаdocs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html
 necromancer11 апр. 2012 г., 01:16
хм ... в примере в документе есть синглтон. я все еще не убежден, является ли это плохой вещью или даже если это необходимо. но твой ответ определенно зарабатывает на моем голосе :)
 11 апр. 2012 г., 01:11
@agksmehx Синглтон, на который я ссылаюсь, будет объектом или статическим классом, в котором вы храните глобально доступную переменную ThreadLocal (она должна существовать каким-то глобальным статически доступным способом, чтобы вы могли использовать его, как указано).
 necromancer11 апр. 2012 г., 00:14
Я не вижу синглтона. Объект для каждого потокаThreadLocalкоторый имеет отношение 1 к 1 для каждого запросаset/getAttribute, Вариант использования - для транспортировки объекта User, который просматривается фильтром, в сервлет. В фильтре я мог сказатьsetAttribute("user", user); или жеThreadStore.setUser(user); и в сервлете соответственно:User user = (User)getAttribute("user"); или жеThreadStore.getUser() где ThreadStore - это удобный слой вокруг ThreadLocal. Я не вижу синглтона.
 11 апр. 2012 г., 22:37
@agksmehx Сам объект String кэширует свой собственный хэш-код (и даже литералы String в кавычках имеют фактический экземпляр объекта String). В моем случае я обычно использовал бы один объект в атрибутах или сеансе, который содержит все другие объекты, поэтому мне нужно беспокоиться только об одном приведении. (например, UserContext context = (UserContext) request.getAttribute (USER_CONTEXT_KEY); int someInt = context.getSomeInt ();). Затем более глубокий код принимает в качестве параметра объект UserContext (вместо передачи запроса или чего-то в этом роде). Бонусные баллы, если UserContext является интерфейсом.

Я бы посоветовал против ThreadLocal.

Я понимаю, почему вы рассматриваете это, но я думаю, что вы попали в ловушку преждевременной оптимизации. ThreadLocals - это по сути глобальные переменные - редко хороший дизайн. Экономия времени будет незначительной. Я гарантирую, что это никогда не будет узким местом в пропускной способности вашего сервера.

Использование ThreadLocal также может вызвать проблемы, если вы хотите начать использовать асинхронные ответы на некоторые запросы (например, для поддержки длинного опроса), поскольку модель потоков очень отличается.

 necromancer11 апр. 2012 г., 21:12
безопасность типов - моя более важная проблема, и переключение с карты со строковыми ключами на прямой доступ не относится к категории преждевременной оптимизации. Вроде как переход от строкового представления целых к байтовому представлению не будет преждевременной оптимизацией. Таким образом, основной компромисс между безопасностью типов и «хорошим дизайном» (синглтон, глобальный и т. д.). конкретный вариант использования - это передача данных от фильтра к сервлету, и я не могу увидеть конкретную / конкретную демонстрацию того, почему это не «хороший дизайн». Я ценю хороший дизайн, но с помощью кода я склоняюсь к тому, что подходит для конкретных случаев использования.

локальный поток работает лучше, если вы пытаетесь установить & quot; глобальный & quot; переменные для кода "позади" все, что имеет дело с HttpServletRequest. Если вы используете страницы JSP / JSF или какой-либо другой компонент веб-интерфейса, который читает из HttpServletRequest, то вам в конечном итоге придется самостоятельно извлекать информацию из ThreadLocal. 2 не эквивалентны для большинства веб-программирования.

 10 апр. 2012 г., 23:39
Ну, безопасность типа это хорошо. Производительность, ну, преждевременная оптимизация - корень всех зол. Потеря способности легко взаимодействовать в будущем, на мой взгляд, не стоит лишних хлопот.
 necromancer11 апр. 2012 г., 01:13
Разве JVM не скрывает все это от вас? Посмотри пожалуйстаdocs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html
 11 апр. 2012 г., 22:37
@agksmehx Сам объект String кэширует свой собственный хэш-код (и даже литералы String в кавычках имеют фактический экземпляр объекта String). В моем случае я обычно использовал бы один объект в атрибутах или сеансе, который содержит все другие объекты, поэтому мне нужно беспокоиться только об одном приведении. (например, UserContext context = (UserContext) request.getAttribute (USER_CONTEXT_KEY); int someInt = context.getSomeInt ();). Затем более глубокий код принимает в качестве параметра объект UserContext (вместо передачи запроса или чего-то в этом роде). Бонусные баллы, если UserContext является интерфейсом.
 necromancer10 апр. 2012 г., 23:27
Благодарю. Вы, кажется, подразумеваетеrequest.setAttribute лучше для телаdoGet или жеdoPost, но я не понимаю, почему лучше перевозить объект изFilter вdoGet с помощьюsetAttribute/getAttribute? Я чувствую, что если отправитель и получатель полностью мой собственный код без каких-либо зависимостей, то я никогда не должен использоватьsetAttribute даже если код не "позади" все, что имеет дело с HttpServletRequest. Я что-то пропустил?
 necromancer10 апр. 2012 г., 23:35
Вы говорите, что это "не имеет большого значения". Как насчет моей мысли, что ThreadLocal лучше: «Преимущества использования ThreadLocal: безопасность типов и лучшая производительность, потому что не используются строковые ключи или карты»? Мне определенно не нужно будет интегрировать его, или, если бы я это сделал, его было достаточно, чтобы изменить его на этом этапе.
 11 апр. 2012 г., 01:11
@agksmehx Синглтон, на который я ссылаюсь, будет объектом или статическим классом, в котором вы храните глобально доступную переменную ThreadLocal (она должна существовать каким-то глобальным статически доступным способом, чтобы вы могли использовать его, как указано).
 10 апр. 2012 г., 23:32
Если это ваш собственный код, это, вероятно, не имеет большого значения. Однако вам будет сложно интегрироваться с другими библиотеками и кодом.
 necromancer11 апр. 2012 г., 01:16
хм ... в примере в документе есть синглтон. я все еще не убежден, является ли это плохой вещью или даже если это необходимо. но твой ответ определенно зарабатывает на моем голосе :)
 necromancer11 апр. 2012 г., 21:12
безопасность типов - моя более важная проблема, и переключение с карты со строковыми ключами на прямой доступ не относится к категории преждевременной оптимизации. Вроде как переход от строкового представления целых к байтовому представлению не будет преждевременной оптимизацией. Таким образом, основной компромисс между безопасностью типов и «хорошим дизайном» (синглтон, глобальный и т. д.). конкретный вариант использования - это передача данных от фильтра к сервлету, и я не могу увидеть конкретную / конкретную демонстрацию того, почему это не «хороший дизайн». Я ценю хороший дизайн, но с помощью кода я склоняюсь к тому, что подходит для конкретных случаев использования.
 necromancer11 апр. 2012 г., 00:14
Я не вижу синглтона. Объект для каждого потокаThreadLocalкоторый имеет отношение 1 к 1 для каждого запросаset/getAttribute, Вариант использования - для транспортировки объекта User, который просматривается фильтром, в сервлет. В фильтре я мог сказатьsetAttribute("user", user); или жеThreadStore.setUser(user); и в сервлете соответственно:User user = (User)getAttribute("user"); или жеThreadStore.getUser() где ThreadStore - это удобный слой вокруг ThreadLocal. Я не вижу синглтона.

Ваш ответ на вопрос