Как инициализировать API в среде сервлетов
Очевидно, я хочу написать разделенные компоненты. Одна часть - это движок форм. Я не хочу, чтобы он зависел от API сервлета, но я должен инициализировать его для каждого запроса (или, по крайней мере, для каждого сеанса).
В приложении я бы использовал что-то вроде
public static void setLocale(Locale l);
тогда мои отдельные классы могли бы получить это со статическим получателем. Это невозможно в среде сервлетов (сервлетам не хватает даже static getServletContext()
метод, через который можно эмулировать статическое поведение).
Я абсолютно не хочу использовать фабрику (у меня будет более 10 классов, каждый из которых будет использовать некоторую конфигурацию, по крайней мере, Locale) или что-то еще хуже: создайте каждый объект с помощью блока параметров (который содержит Locale
и другие настройки).
Я хотел бы знать, что является лучшим практическим в этой ситуации. Может ли статическое поведение эмулироваться удобным для использования способом или у API сервлета есть ответ на эту проблему?
Если любая другая возможность не удалась, я подумал об использовании чего-то вроде
class MyParameters {
private Map<Thread, MyParameters> threadParameters = new Map<Thread, MyParameters>();
public static void setParameters(MyParameters parameters) {
threadParameters.put(Thread.getCurrentThread(), parameters);
}
public static MyParameters getParameters() {
return threadParameters.get(Thread.getCurrentThread());
}
}
... но это создает некоторые проблемы безопасности (сервлет может не инициализировать его и использовать значения, установленные во время предыдущего запроса, обслуживаемого тем же потоком). - Хотя использование локали другого пользователя не представляет особой угрозы.
2 ответа
но я должен инициализировать его для каждого запроса (или, по крайней мере, для каждого сеанса).
Использовать Filter
, HttpServlet
или же ServletRequestListener
(или HttpSessionListener
).
но это создает некоторые проблемы безопасности
Кроме того, потоки объединяются в контейнер. Один и тот же поток может быть повторно использован более одного раза для различных последующих запросов. Когда вы помещаете что-то в поток и не удаляете это к концу запроса, вы получите в результате потокобезопасный код.
Лучше всего создать ThreadLocal<T>
учебный класс. Предполагая, что вы хотите, чтобы он основывался на запросах / ответах, вот следующий пример:
public final class Context {
private static ThreadLocal<Context> instance = new ThreadLocal<Context>();
private HttpServletRequest request;
private HttpServletResponse response;
private Context(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
public static Context getInstance() {
return instance.get();
}
public static Context newInstance(HttpServletRequest request, HttpServletResponse response) {
Context context = new Context(request, response);
instance.set(context);
return context;
}
public void release() {
instance.remove();
}
// ...
}
Получить и установить его в Filter
,
Context context = null;
try {
context = Context.newInstance(request, response);
chain.doFilter(request, response);
} finally {
if (context != null) context.release();
}
(обратите внимание, что очень важно выпустить контекст в finally
блок из try
блок, где вы его получаете, иначе он не будет освобожден, когда обработка запроса-ответа выдает исключение)
Наконец, вы можете получить его везде в своем коде следующим образом:
Context context = Context.getInstance();
context.setLocale(locale);
Locale foo = context.getLocale();
// ...
где вы делегируете методы местному request
и / или response
переменные.
Обратите внимание, что подобная конструкция уже существует в некоторых средах MVC, таких как JSF с ее FacesContext
, Вместо того, чтобы выращивать один дом, вы бы хотели посмотреть, не там ли трава зеленее.
Вы также можете рассмотреть возможность использования моего подхода, который обсуждается здесь: Вопрос о дизайне JSP/ сервлета - Сделать запрос / ответ глобально доступным через JNDI
В моем подходе объекты запроса и ответа хранятся в JNI вместо ThreadLocal. Кроме того, их настройка и очистка выполняются в фильтре, как описано выше...