ThreadLocal для хранения ServletRequest и Response в сервлете: зачем?
Однажды я наткнулся на образец, где ServletRequest
и ответные объекты помещаются в локальный сервлет ThreadLocal
переменные. Класс сервлета также имеет методы для получения текущих объектов запроса и ответа. Таким образом, чтобы получить эти объекты, вам все равно нужно работать с объектом сервлета.
Какой смысл иметь эти ThrealLocal
локальные переменные?
7 ответов
Суть в том, чтобы иметь объекты запроса и ответа в классах, которые в противном случае не имели бы их (например, они не являются сервлетами). Одним из примеров являются управляемые компоненты JSF - их методы не принимают HttpServletRequest
параметры, и поэтому вы можете получить запрос через FacesContext
который имеет их в ThreadLocal
переменные.
Это работает потому, что каждый запрос обрабатывается отдельным потоком (контейнером сервлета). Так что поток = запрос. Но есть одна оговорка - контейнеры, как правило, используют пулы потоков. Поэтому необходимо всегда устанавливать новый запрос в локальном потоке и предпочтительно очищать его впоследствии (например, в Filter
). В противном случае вы можете получить неожиданное поведение.
Но вы должны действительно избегать этого в своем коде. Если вам нужно что-то из запроса или ответа, передайте это как аргумент метода. В противном случае вы рискуете нарушить границы уровня (например, если у вас возникнет желание использовать запрос на уровне службы)
Они позволяют вам получить доступ к HttpServletRequest и HttpServletResponse из других классов в вашем проекте без необходимости передавать ссылки на эти объекты другим классам. Это не тот шаблон, который мне особенно нравится, поскольку он имеет тенденцию смешивать код веб-уровня с вашей бизнес-логикой и усложняет модульное тестирование.
Другие в значительной степени указали, как использовать Thread Locals в представленном вами сценарии. Но, тем не менее, следует помнить, что реализации, основанные на локальных потоках, зависят от "потока" и ломаются, когда все отходит от одного потока на модель запроса. Примером могут служить серверы на основе событий, в которых несколько потоков используются одновременно для множества пользовательских запросов.
Поскольку объекты запроса и ответа хранятся в локальных переменных потока, вы получаете потокобезопасный доступ к этим объектам без необходимости передавать их в качестве параметров метода.
Пример 1: Без локального потока
public class MyServlet extends Servlet {
private MyObject myObject = new MyObject();
public void service(ServletRequest request, ServletResponse response) {
myObject.doSomething(request, response);
}
}
public class MyObject {
private MyOtherObject myOtherObject = new MyOtherObject();
public void doSomething(ServletRequest request, ServletResponse response) {
// I do nothing with request/response, but need to accept them in order
// to pass them to myOtherObject
myOtherObject.doSomethingElse(request, response);
}
}
public class MyOtherObject {
public void doSomethingElse(ServletRequest request, ServletResponse response) {
// Do something else with request / response
}
}
Пример 2: с локальным потоком
public class MyServlet extends Servlet {
private MyObject myObject = new MyObject();
private static ThreadLocal<ServletRequest> currentRequest = new ThreadLocal<ServletRequest>();
public static ServletRequest getCurrentRequest() {
return currentRequest.get();
}
private static ThreadLocal<ServletResponse> currentResponse = new ThreadLocal<ServletResponse>();
public static ServletResponse getCurrentResponse() {
return currentResponse.get();
}
public void service(ServletRequest request, ServletResponse response) {
...
currentRequest.set(request);
currentResponse.set(response);
...
myObject.doSomething();
}
}
public class MyObject {
private MyOtherObject myOtherObject = new MyOtherObject();
public void doSomething() {
// I do not need to know about request / response as I do nothing with them
myOtherObject.doSomethingElse();
}
}
public class MyOtherObject {
public void doSomethingElse() {
// Now I can get the current request / response in a thread safe
// manner and without having to accept them as parameters
ServletRequest request = MyServlet.getCurrentRequest();
ServletResponse response = MyServlet.getCurrentResponse();
// Do something with request / response
}
}
Очевидно, что для простых сервлетов проще всего просто передать объекты, но в сложных сценариях иногда полезно иметь один статический, но поточно-безопасный метод получения.
когда у вас есть какой-то объект, который не является потокобезопасным, но вы хотите избежать синхронизации доступа к этому объекту ( SimpleDateFormat). Вместо этого дайте каждому потоку свой экземпляр объекта.
Вы должны быть очень осторожны при очистке любых ThreadLocals, которые вы get()
или же set()
используя ThreadLocal's remove()
метод.
Я не уверен на 100%, каково было намерение автора кода, с которым вы когда-то сталкивались, но я предполагаю, что идея заключается в том, что ServletRequest
Экземпляр доступен из любого метода в коде без передачи его в качестве параметра или установки в качестве переменной экземпляра. Обычно ThreadLocal
переменная является статической и есть метод, который позволяет получить экземпляр ServletRequest
в статически. Например, вы можете получить доступ ServletRequest
В Struts FromBeans легко использовать эту технику.
Я думаю, что лучший случай может быть как..
Объект соединения, созданный когда-либо в слое Service, помещается в ThreadLocal, затем вызывается слой DAO, получается объект соединения из Threadlocal.
Это действительно ужасно. Вы должны получить нужные значения из HTTP-запроса / сеанса, как только сможете. Вы можете передать эти значения в вызовах методов или Transfer Object. Вы должны стремиться писать методы / классы без технологий. если ваш метод / класс получает http-запрос от ThreadLocal, это бесполезный класс - он больше не используется ни в каком контексте, кроме http.
Меня особенно шокирует то, что люди вытягивают http-запросы из ThreadLocal в BO (Business Objects) или DAO. Запросы HTTP никогда не должны появляться ни на каком другом уровне, кроме уровня представления приложения.