В одном из моих проектов у меня было требование загружать справочные данные из нескольких источников в среде Java EE 6 WebLogic с EclipseLink в качестве платформы ORM . Поскольку я не мог найти аннотацию в мире Java EE, сравнимую со сладким @Cacheable из Spring YET, мне пришлось написать свое «собственное» решение для кэширования. Хотя справочные данные практически не меняются с течением времени, еще одним требованием была возможность очистки кеша от внешних источников. Так что здесь идет …
1. Кеш
Предполагалось, что это будет кэш только для чтения с возможностью очистки его извне. Я хотел, чтобы кэш был своего рода оберткой для службы, предоставляющей фактические справочные данные для приложения — стиль AOP с кодом!
1.1. Интерфейс
Простой интерфейс кеша для справочных данных
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Localpublic interface ReferenceDataCache { /** * Returns all reference data required in the application */ ReferenceData getReferenceData(); /** * evict/flush all data from cache */ void evictAll();} |
Функциональность кэширования определяет два простых метода:
-
getReferenceData()— который кэширует справочные данные, собранные за кулисами из разных источников -
evictAll()— метод, вызываемый для полной очистки кэша
1.2. Реализация
Простая реализация кэша справочных данных с @Singleton
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)@Singletonpublic class ReferenceDataCacheBean implements ReferenceDataCache { private static final String ALL_REFERENCE_DATA_KEY = "ALL_REFERENCE_DATA"; private ConcurrentHashMap<String, Object> refDataCache = null; @EJB ReferenceDataService referenceDataService; @PostConstruct public void initialize(){ this.refDataCache = new ConcurrentHashMap<>(); } @Override @Lock(LockType.READ) public ReferenceData getReferenceData() { if(refDataCache.containsKey(ALL_REFERENCE_DATA_KEY)){ return refDataCache.get(ALL_REFERENCE_DATA_KEY); } else { ReferenceData referenceData = referenceDataService.getReferenceData(); refDataCache.put(ALL_REFERENCE_DATA_KEY, referenceData); return referenceData; } } @Override public void evictAll() { refDataCache.clear(); } ..........} |
Замечания:
-
@Singleton— наверное, самая важная строка кода в этом классе. Эта аннотация указывает, что в приложении будет ровно один синглтон этого типа компонента. Этот бин может быть вызван одновременно несколькими потоками. Он также поставляется с аннотацией@PostConstruct. Эта аннотация используется для метода, который должен быть выполнен после внедрения зависимости для выполнения любой инициализации — в нашем случае это инициализация «кэша» (карта хеша) -
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)объявляет тип управления параллелизмом одноэлементного сессионного компонента. По умолчанию установлено значение «Container. Я использую его здесь только для того, чтобы подчеркнуть его существование. Другой параметрConcurrencyManagementType.BEANуказывает, что разработчик компонента отвечает за управление одновременным доступом к экземпляру компонента. - фактический «кеш» — это
ConcurrentHashMapкоторый имеет ключи на основеStringи хранитObjects. Это держится в памяти из-за синглтоновой природы бобов -
@EJBReferenceDataService— это@Stateless@EJBкоторый за кулисами собирает справочные данные из разных источников - Реализация метода getReferenceData () очень проста: он проверяет, есть ли в
ConcurrentHashMapзапись с ключом String, указанным как константа «ALL_REFERENCE_DATA». Если это так, это будет извлечено из памяти, в противном случае будет загружен компонент службы. -
@Lock(LockType.READ)указывает тип блокировки параллелизма для одноэлементных компонентов с параллелизмом, управляемым контейнером. Когда установлено значениеLockType.READ, он применяет метод для разрешения полного одновременного доступа к нему (при условии, что блокировки записи не удерживаются). Это именно то, что я хотел, так как мне нужно только выполнять операции чтения. Другой более консервативный параметр@Lock(LockType.WRITE), который,@Lock(LockType.WRITE), является DEFAULT, обеспечивает исключительный доступ к экземпляру компонента. Это должно замедлить метод в среде с высокой степенью параллелизма … - метод
evictAll()просто удаляет все элементы из хэш-карты.
2. Очистка кеша
Вторая часть этого поста будет посвящена возможностям очистки кеша. Поскольку реализация кэша является корпоративным Java-бином, мы можем вызвать его из MBean или, почему нет, из веб-службы.
2.1. MBean
Если вы новичок в Java Management Extensions (JMX), который представляет собой технологию Java, которая предоставляет инструменты для управления и мониторинга приложений, системных объектов, устройств (например, принтеров) и сервис-ориентированных сетей. Эти ресурсы представлены объектами, называемыми MBeans (для Managed Bean) , я настоятельно рекомендую вам начать с этого учебного курса: Java Management Extensions (JMX)
2.1.1. Интерфейс
Представленный метод позволит только сброс кеша через JMX:
CacheRest MBean
|
1
2
3
4
|
@MXBeanpublic interface CacheResetMXBean { void resetReferenceDataCache(); } |
«MXBean — это тип MBean, который ссылается только на предопределенный набор типов данных. Таким образом, вы можете быть уверены, что ваш MBean будет использоваться любым клиентом, включая удаленные клиенты, без каких-либо требований, чтобы клиент имел доступ к классам, зависящим от модели, представляющим типы ваших MBean. MXBeans предоставляют удобный способ связать связанные значения вместе, не требуя, чтобы клиенты были специально настроены для обработки пакетов ». [4]
2.1.2. Реализация
MBean реализация CacheReset
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@Singleton@Startuppublic class CacheReset implements CacheResetMXBean { private MBeanServer platformMBeanServer; private ObjectName objectName = null; @EJB ReferenceDataCache referenceDataCache; @PostConstruct public void registerInJMX() { try { objectName = new ObjectName("org.codingpedia.simplecacheexample:type=CacheReset"); platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); //unregister the mbean before registerting again Set<ObjectName> existing = platformMBeanServer.queryNames(objectName, null); if(existing.size() > 0){ platformMBeanServer.unregisterMBean(objectName); } platformMBeanServer.registerMBean(this, objectName); } catch (Exception e) { throw new IllegalStateException("Problem during registration of Monitoring into JMX:" + e); } } @Override public void resetReferenceDataCache() { referenceDataCache.evictAll(); } } |
Замечания:
- как уже упоминалось, реализация вызывает только метод
evictAll()одноэлементного компонента, описанного в предыдущем разделе - боб также определяется как
@Singleton - аннотация
@Startupвызывает экземпляр компонента в контейнере при запуске приложения — нетерпеливая инициализация - Я снова
@PostConstructфункциональность@PostConstruct. Здесь этот bean-компонент зарегистрирован в JMX, прежде чем проверять, используется лиObjectNameдля его удаления, если это так…
2.2. Вызов службы отдыха
Я также встроил возможность очищать кеш, вызывая ресурс REST. Это происходит, когда вы выполняете HTTP POST в (rest-context) / reference-data / flush-cache:
Остальные вызовы в кеш справочных данных
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Path("/reference-data")public class ReferenceDataResource { @EJB ReferenceDataCache referenceDataCache; @POST @Path("flush-cache") public Response flushReferenceDataCache() { referenceDataCache.evictAll(); return Response.status(Status.OK).entity("Cache successfully flushed").build(); } @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public Response getReferenceData(@QueryParam("version") String version) { ReferenceData referenceData = referenceDataCache.getReferenceData(); if(version!=null && version.equals(referenceData.getVersion())){ return Response.status(Status.NOT_MODIFIED).entity("Reference data was not modified").build(); } else { return Response.status(Status.OK) .entity(referenceData).build(); } } } |
Обратите внимание на существование параметра запроса версии в @GET getReferenceData(...) . Это представляет собой хэш справочных данных, и если он не был изменен, клиент получит статус HTTP 304 Not Modified . Это хороший способ сэкономить пропускную способность, особенно если у вас есть мобильные клиенты. См. Мой пост Tutorial — Проектирование и реализация REST API на Java с Джерси и Спрингом , где подробно обсуждается проектирование и реализация сервисов REST.
Замечания:
В кластерной среде вам необходимо вызывать метод resetCache (…) для каждой JVM, в которой развернуто приложение, при изменении справочных данных.
Ну вот и все. В этом посте мы узнали, как создать простой кэш с аннотациями Java EE. Конечно, вы можете легко расширить функциональность кэша, чтобы предложить более детальный доступ / очистку кешированных объектов. Не забудьте использовать LockType.WRITE для методов очистки в этом случае …
| Ссылка: | Как создать и очистить кэш справочных данных с помощью одноэлементных EJB-компонентов и MBeans от нашего партнера по JCG Адриана Матеи в блоге Codingpedia.org . |