Spring AOP & Ehcache

Caching ist ja bekanntlich eine Querschnitts-Anforderung (cross cutting concern), was man eigentlich nicht direkt in die Business-Logik integrieren möchte/sollte. Hier bietet es sich an, die Cache-Funktionalität mittels AOP (Aspektorientierte Programmierung) zu realisieren. Dieser Beitrag erklärt das exemplarisch realisieren der Cache-Funktionalität mittels Spring AOP und Ehcache. Einführung Vorweg. Es gibt natürlich bei den springmodules ein Cache-Modul. Dieses wird, wenn überhaupt, nur sehr langsam weiterentwickelt. Das neue Spring Extensions Projekt beinhaltet leider kein Cache-Modul. Hinzu kommen noch fehlende Funktionalitäten wie z.B. das Löschen von nur einem Objekt aus dem Cache. Falls man jedoch doch Springmodules Cache nutzen will, kann man sich mein altes Beispiel anschauen.

Technologie In der Lösung wird nur Spring AOP und Ehcache direkt verwendet. Bei der AOP Implementierung wird nicht AspectJ verwendet um die Anzahl der abhängigen Bibliotheken gering zu halten. Dies ist auch möglich, da die benötigte Funktionalität durch Spring AOP bereitgestellt wird. Mittels der Spring-Klasse EhCacheManagerFactoryBean wird der Ehcache integriert.

Bibliotheken In diesem Fall braucht man mindestens (und die entsprechende Abhängigkeiten):

  • spring 2.5.6
  • ehcache

XML Konfiguration Die XML Konfiguration für Spring sieht wie folgt aus:

 <!-- ...some other spring configuration... -->

 <!-- Ehcache bean which gets the path the ehcache.xml config file -->
 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml" />
    </bean>

 <!-- Interceptor which handels the cross cutting concerns (here: caching) -->
 <bean id="coInterceptor" class="de.ic.jee.aop.COCacheInterceptor">
		<constructor-arg index="0" ref="cacheManager" />
  		<!-- Set the name of the cache which will be used from the cache manager -->
		<constructor-arg index="1" value="coCache" />
	</bean>

  <!-- Advisor for the relevant methods -->
    <bean id="coAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
        	<ref local="coInterceptor"/>
		</property>
		<property name="patterns">
                  <!-- only the get and update methods -->
        	  <list>
        		<value>.\*update.\*</value>
        		<value>.\*get.\*</value>
        	  </list>
		</property>
	</bean>

  <!-- Proxy factory for aop proxy  -->
	<bean id="coServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target">
                        <!-- The target bean object -->
			<ref bean="coService"/>
		</property>
		<property name="interceptorNames">
			<list>				
				<value>coAdvisor</value>
			</list>
		</property>	
                <!-- Name of this new generated proxy, to identify this object
                       in the autowired process.
                -->	
		<qualifier value="coServiceProxy"/>
	</bean>

Spring AOP ist proxy-basiert. Hierfür wird deswegen ein ProxyFactoryBean definiert, was den Advisor (coAdvisor) auf ein bestimmtes Bean einsetzt, somit entsteht dann eine neue Proxy-Klasse (coServiceProxy).

Mit RegexpMethodPointcutAdvisor kann definiert werden, welche Methoden beobachtet werden. In unserem Fall alle update und get Methoden.

Der Advice bzw. Interceptor (coInterceptor), welcher die Querschnitts-Funktionalität beinhaltet, kriegt eine Referenz auf den CacheManager (hier: cacheManager). Mit dieser Referenz kann beim Methoden-Aufruf vorher im Cache geprüft werden, ob die Antwort schon bekannt ist, wenn nicht, wird die Methode weiter ausgeführt und das Ergebnis (Rückgabewert) dann im Cache gespeichert.

public class COCacheInterceptor implements MethodInterceptor {

    private final CacheManager cacheManager;
    public COCacheInterceptor(final CacheManager cm, final String cn) {
		this.cacheManager = cm;
		this.cacheName = cn;
	}

     public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
	
          /\*
           \* Cache cache = cacheManager.getCache(this.cacheName);
           \* String cacheKey = .... generate cache key, maybe the arguments of the method invocation
           \* get element from the cache
           \* Element e = cache.get(cacheKey);
           \* If element found:
           \* - return e.getObjectValue()
           \* otherwise:
           \* - proceed the method, methodResult = methodInvocation.proceed()
           \* - put the result in the cache, cache.put(new Element(cacheKey, methodResult));
           \* - return methodResult; 
           \*/
         
     } // invoke()
}

Mit wenig Konfiguration und Programmierung kann man sehr einfach Cache-Funktionalität einfügen, ohne die Business Logik zu verändern.

Links: - Spring AOP: http://static.springsource.org/spring/docs/2.5.x/reference/aop-api.html - Ehcache: http://ehcache.org/ - Spring 2.5.x API: http://static.springsource.org/spring/docs/2.5.x/api/index.html

comment

Comments

arrow_back

Previous

.NET: XmlSerializer ohne dynamische Code-Generierung

Next

Spec 2.0: JSF 2 Überblick
arrow_forward