CDI ile JCache Notasyonlarını Kullanalım
JCache (JSR 107 – Java Temporary Caching API) Java EE 8 altında yer alacak, caching çözümlerine (Hazelcast, EhCache, Coherence vb.) çatı kuran bir standarttır. JCache Java EE 8 kullanıcıları anketinde %9.5 (turuncu) ile önemli bir oran alarak beklenilen bir standart olduğunu ortaya koymuştur.
JCache aslında Java EE 7 içerisinde yer alacaktı, fakat yetişmediği için Java EE 8’e ötelenmişti.( Bkz. JCache to Miss Java EE 7)
JCache Başlangıç
JCache için daha önce iki yazı hazırlamıştım. Dilerseniz onlardan başlayabilirsiniz. Meselenin iç yüzünü bu şekilde daha iyi anlayabilirsiniz.
JCache Notasyonları
Java EE ekosisteminde anotasyonlardan sıkça faydalanıyoruz. JCache içerisindeki kullanımda ise konfigürasyon ve kullanım detaylarını arka plana atarak sade bir kullanım elde ediyoruz.
Anotasyon nedir diyorsanız şu yazıyı öneririm. https://kodedu.com/2014/09/java-8-tekrarli-notasyonlar-nasil-kullanilir/ |
Şimdi JCache standart notasyonlarını tanıyalım;
CacheDefaults
JCache @CacheDefaults
notasyonu sınıf başına uygulanır. Tüm alt metodlar bunda tanımlı bilgileri kullanır. Örneğin CacheDefaults notasyonuyla isme göre eşsiz bir cache alanı tanımlayabiliriz. Bu notasyonu işleyecek olan DI (Dependency Injection) çözümü, tanımlı isim bilgisine göre arka planda javax.cache.Cache
nesnesi oluşturacaktır.
@CacheDefaults(cacheName = "personCacher")
public class CacheBean {
///
}
CachePut
Bir metodun parametresinde yer alan key
bilgisine göre value
bilgisini cache alanına ekler. key, @CacheKey
notasyonu ile belirtilirken, value @CacheValue
ile belirtilir.
@CacheDefaults(cacheName = "personCacher")
public class CacheBean {
@CachePut
//@CachePut(cacheName = "<cache_name>")
public void put(@CacheKey Long id, @CacheValue Person person) {
//
}
}
Yukarıdaki örnekte Long
türündeki key bilgisine göre, Person
sınıfı türündeki bir nesne, personCacher
içine eklenir. Metod içi boş olabilir veya cache içine eklenen veri kalıcı bir ortama da yazılabilir. Tamamen sizin ihtiyacınıza bağlı bir durum.
CacheResult
Belirli bir cache alanından daha önce eklenen cache verisi elde edilir. Talep edilen veri cache içindeyse metod içine girilmez. Cache dışındaysa metod içerisine girilir ve metod dönüşü cache alanına eklenir.
@CacheDefaults(cacheName = "personCacher")
public class CacheBean {
@CacheResult
public Person get(@CacheKey Long id) {
Person person = //
return person;
}
@CachePut
//@CachePut(cacheName = "<cache_name>")
public void put(@CacheKey Long id, @CacheValue Person person) {
//
}
}
Örneğin yukarıdaki CacheBean#get
metodu, key
bilgisine göre cache değerini döndürmektedir.
CacheRemove
key
bilgisine göre daha evvel eklenen bir girdi cache alanından temizlenir.
@CacheDefaults(cacheName = "personCacher")
public class CacheBean {
@CacheRemove
public void invalidate(@CacheKey Long id) {
//
}
}
Tüm cache girdilerini silmek için @CacheRemoveAll
kullanabiliriz.
@CacheDefaults(cacheName = "personCacher")
public class CacheBean {
@CacheRemoveAll
public void invalidateAll() {
//
}
}
Uygulama zamanı
javax.cache.annotation.*
notasyonları dizayn edilmişlerdir, bundan dolayı bir DI çözümüyle (Spring, CDI, Guice vb.) işlenmelidir. Bu örnekte CDI tercih ettim, ama standart üzeri gittiğimiz için hangi DI teknolojisi kullandığımızın çok önemi yok!
CDI konteyneri bir JavaEE uygulamasında hazır olarak kullanabiliriz veya varsayılan uygulayıcısı JBoss Weld ile bir Java SE uygulamasında da başlatabiliriz. Ben ikincisini tercih ettim.
Proje maven
yapısında hazırlanmıştır.
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>jar</packaging> <groupId>CDI-JCache</groupId> <artifactId>CDI-JCache</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> (1) <groupId>org.jboss.weld.se</groupId> <artifactId>weld-se-core</artifactId> <version>3.0.0.Alpha13</version> </dependency> <dependency> (2) <groupId>org.jsr107.ri</groupId> <artifactId>cache-annotations-ri-cdi</artifactId> <version>1.0.0</version> </dependency> <dependency> (3) <groupId>org.jsr107.ri</groupId> <artifactId>cache-ri-impl</artifactId> <version>1.0.0</version> </dependency> </dependencies> </project>
1 Weld Java SE bağımlılığı 2 JSR 107 notasyonlarını işleyen CDI bağımlılığı (Bkz. maven.search.org) 3 JCache varsayılan uygulayıcısı -
beans.xml
CDI aktifleştirilmesi ve konfigürasyonu için şart olan
beans.xml
dosyası. Burada ayrıca javax.cache.annotation.* notasyonlarını işleyecek CDI kesicilerini (interceptor) tanımlamalıyız.<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all"> <interceptors> <class>org.jsr107.ri.annotations.cdi.CacheResultInterceptor</class> (1) <class>org.jsr107.ri.annotations.cdi.CacheRemoveEntryInterceptor</class> (2) <class>org.jsr107.ri.annotations.cdi.CacheRemoveAllInterceptor</class> (3) <class>org.jsr107.ri.annotations.cdi.CachePutInterceptor</class> (4) </interceptors> </beans>
1 @CacheResult
sahibi bir metod çağrıldığında işleme geçer2 @CacheRemove
sahibi bir metod çağrıldığında işleme geçer3 @CacheRemoveAll
sahibi bir metod çağrıldığında işleme geçer4 @CachePut
sahibi bir metod çağrıldığında işleme geçer -
com.kodcu.*
-
com.kodcu.Person
public class Person implements Serializable { private Long id; private String name; private String status; public Person() { } public Person(Long id, String name) { this.id = id; this.name = name; } public Person(long id) { this(id, "Person-" + id); } // getter, setter metodları public void updateStatus() { setStatus("Cached " + ThreadLocalRandom.current().nextLong(1, 10000)); } }
-
com.kodcu.CacheBean
@CacheDefaults(cacheName = "personCacher") public class CacheBean { @CachePut public void put(@CacheKey Long id, @CacheValue Person person) { person.updateStatus(); } @CacheResult public Person get(@CacheKey Long id) { final Person person = new Person(id); person.updateStatus(); return person; } @CacheRemove public void invalidate(@CacheKey Long id) { } @CacheRemoveAll public void invalidateAll() { } }
-
com.kodcu.App
public class App { @Inject private CacheBean cacheBean; (3) public static void main(String[] args) throws Exception { CDI<Object> cdi = CDI.getCDIProvider().initialize(); (1) final App app = cdi.select(App.class).get(); (2) final CacheBean cacheBean = app.cacheBean; print("Put first object in cache"); // Put cahce id:1,name:Rahman Usta cacheBean.put(1L, new Person(1L)); // Get id:1 final Person p1 = cacheBean.get(1L); print(p1); // Get id:1 final Person p2 = cacheBean.get(1L); print(p2); // Get id:1 final Person p3 = cacheBean.get(1L); print(p3); print("\n******\n"); print("Put second object in cache"); cacheBean.put(2L, new Person(2L)); // Get id:2 final Person p4 = cacheBean.get(2L); print(p4); // Get id:2 final Person p5 = cacheBean.get(2L); print(p5); print("\n******\n"); // Invalidate one print("Invalidate first object in cache"); cacheBean.invalidate(1L); print(cacheBean.get(1L)); print(cacheBean.get(2L)); print("\n******\n"); // Invalidate all print("Invalidate all in cache"); cacheBean.invalidateAll(); print(cacheBean.get(1L)); print(cacheBean.get(2L)); } private static void print(Object object) { System.out.println(object); } }
1 CDI konteyner başlatılıyor. 2 App türünden CDI nesnesi elde ediliyor. 3 CacheBean CDI nesnesi enjekte ediliyor. App#main
metodu çalıştırıldığında aşağıdaki çıktıyı elde ederiz.INFO: WELD-ENV-002003: Weld SE container STATIC_INSTANCE initialized Kas 02, 2015 11:37:44 PM org.jsr107.ri.annotations.DefaultCacheResolverFactory getCacheResolver WARNING: No Cache named 'personCacher' was found in the CacheManager, a default cache will be created. Put first object in cache (1) Person{id=1, name='Person-1', status='Cached 5859'} Person{id=1, name='Person-1', status='Cached 5859'} Person{id=1, name='Person-1', status='Cached 5859'} ****** Put second object in cache (2) Person{id=2, name='Person-2', status='Cached 3832'} Person{id=2, name='Person-2', status='Cached 3832'} ****** Invalidate first object in cache (3) Person{id=1, name='Person-1', status='Cached 19'} Person{id=2, name='Person-2', status='Cached 3832'} ****** Invalidate all in cache (4) Person{id=1, name='Person-1', status='Cached 9648'} Person{id=2, name='Person-2', status='Cached 9072'} Weld SE container STATIC_INSTANCE shut down by shutdown hook
1 İlk Person
nesnesi cache alanına ekleniyor ve her seferinde cache içindeki aynı nesne döndürülüyor.2 İkinci Person
nesnesi cache alanına ekleniyor ve her seferinde cache içindeki aynı nesne döndürülüyor.3 İlk nesne cahce alanından çıkarılıyor, yeni bir talepte bir Person nesnesi elde edilerek cahce tazeleniyor. 4 İki nesne birden cahce alanından çıkarılıyor, yeni bir talepte birer Person nesnesi elde edilerek cahce tazeleniyor.
-
Örnek uygulamaya https://github.com/Adopt-a-JSR/JCache-CDI adresinden erişebilirsiniz.
Tekrar görüşmek dileğiyle.