В этом посте объясняется (на простом примере), как вы можете использовать производителей CDI, чтобы немного упростить использование семантики управления кэшем в ваших сервисах RESTful.
Заголовок Cache-Control был добавлен в HTTP 1.1 как очень необходимое улучшение по сравнению с заголовком Expires, доступным в HTTP 1.0. Веб-сервисы RESTful могут использовать этот заголовок для масштабирования своих приложений и повышения их эффективности, например, если вы можете кэшировать ответ на предыдущий запрос, то вам, очевидно, нет необходимости повторять тот же запрос к серверу, если вы уверены в тот факт, что ваши кэшированные данные не устарели!
Как помогает JAX-RS?
JAX-RS поддерживает заголовок Cache-Control начиная с его начальной версии (1.0). Класс CacheControl представляет реальный HTTP-заголовок Cache-Control и предоставляет возможность конфигурировать заголовок с помощью простых методов установки. Подробнее о классе CacheControl в javadocs JAX-RS 2.0
Так как же использовать класс CacheControl ?
Просто верните объект Response, вокруг которого вы можете обернуть экземпляр класса CacheControl.
01
02
03
04
05
06
07
08
09
10
|
@Path ( "/testcache" ) public class RESTfulResource { @GET @Produces ( "text/plain" ) public Response find(){ CacheControl cc = new CacheControl(); cc.setMaxAge( 20 ); return Response.ok(UUID.randomUUID().toString()).cacheControl(cc).build(); } } |
Хотя это относительно удобно для одного метода, повторное создание и возврат объектов CacheControl может вызывать раздражение для нескольких методов.
Производители CDI на помощь!
Производители CDI могут помочь внедрить экземпляры классов, которые технически не являются bean-компонентами (согласно строгому определению) или для классов, над которыми у вас нет контроля, если их украшать областями видимости и определителями.
Идея состоит в том, чтобы
- Используйте пользовательскую аннотацию ( @CacheControlConfig ) для определения значений по умолчанию для заголовка Cache-Control и обеспечения гибкости в случае, если вы хотите переопределить его
0102030405060708091011121314
@Retention
(RUNTIME)
@Target
({FIELD, PARAMETER})
public
@interface
CachControlConfig {
public
boolean
isPrivate()
default
true
;
public
boolean
noCache()
default
false
;
public
boolean
noStore()
default
false
;
public
boolean
noTransform()
default
true
;
public
boolean
mustRevalidate()
default
true
;
public
boolean
proxyRevalidate()
default
false
;
public
int
maxAge()
default
0
;
public
int
sMaxAge()
default
0
;
}
- Просто используйте CDI Producer для создания экземпляра класса CacheControl с помощью объекта InjectionPoint (с удовольствием вводится CDI!) В зависимости от параметров аннотации
01020304050607080910111213141516171819202122
public
class
CacheControlFactory {
@Produces
public
CacheControl get(InjectionPoint ip) {
CachControlConfig ccConfig = ip.getAnnotated().getAnnotation(CachControlConfig.
class
);
CacheControl cc =
null
;
if
(ccConfig !=
null
) {
cc =
new
CacheControl();
cc.setMaxAge(ccConfig.maxAge());
cc.setMustRevalidate(ccConfig.mustRevalidate());
cc.setNoCache(ccConfig.noCache());
cc.setNoStore(ccConfig.noStore());
cc.setNoTransform(ccConfig.noTransform());
cc.setPrivate(ccConfig.isPrivate());
cc.setProxyRevalidate(ccConfig.proxyRevalidate());
cc.setSMaxAge(ccConfig.sMaxAge());
}
return
cc;
}
}
- Просто внедрите экземпляр CacheControl в свой класс ресурсов REST и используйте его в своих методах
010203040506070809101112
@Path
(
"/testcache"
)
public
class
RESTfulResource {
@Inject
@CachControlConfig
(maxAge =
20
)
CacheControl cc;
@GET
@Produces
(
"text/plain"
)
public
Response find() {
return
Response.ok(UUID.randomUUID().toString()).cacheControl(cc).build();
}
}
Дополнительные мысли
- В этом случае область действия созданного экземпляра CacheControl равна @Dependent, т.е. он будет жить и умирать с классом, который его внедрил . В этом случае сам ресурс JAX-RS является RequestScoped (по умолчанию), поскольку контейнер JAX-RS создает новый экземпляр для каждого клиентского запроса, следовательно, новый экземпляр внедренного экземпляра CacheControl будет создаваться вместе с каждым HTTP-запросом.
- Вы также можете ввести классификаторы CDI для дальнейшего сужения областей и учета угловых случаев.
- Вы можете подумать, что того же можно достичь с помощью фильтра JAX-RS. Это правильно. Но вам нужно будет установить заголовок Cache-Control вручную (в изменяемой переменной MultivaluedMap), и логика не будет достаточно гибкой, чтобы учитывать различные конфигурации Cache-Control для разных сценариев
Результаты эксперимента
Используйте среду IDE NetBeans, чтобы поиграть с этим примером (рекомендуется)
- Разверните WAR и перейдите по адресу http: // localhost: 8080 / JAX-RS-Caching-CDI / testcache
- Случайная строка, которая будет кэшироваться в течение 20 секунд (согласно конфигурации с помощью аннотации @CacheControl)
- Запрос GET на тот же URL не приведет к вызову службы REST на стороне сервера. Браузер вернет кэшированное значение.
Хотя код прост, но если вам лень, вы можете взять отсюда (maven) проект и поиграть
Повеселись!
Ссылка: | Упрощение кэширования JAX-RS с помощью CDI от нашего партнера JCG Абхишека Гупты в блоге Object Oriented .. |