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

Очевидно, что для простых сервлетов проще всего просто передать объекты, но в сложных сценариях иногда полезно иметь один статический, но поточно-безопасный метод получения.

Назначение ThreadLocal?

когда у вас есть какой-то объект, который не является потокобезопасным, но вы хотите избежать синхронизации доступа к этому объекту ( 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 никогда не должны появляться ни на каком другом уровне, кроме уровня представления приложения.

Другие вопросы по тегам