關於spring與EHcache的集成

ehcache.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="ehcache.xsd">
	<diskStore path="java.io.tmpdir" />

	<defaultCache maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false" />
	<!-- 配置自定義緩存 maxElementsInMemory:緩存中允許創建的最大對象數 eternal:緩存中對象是否爲永久的,如果是,超時設置將被忽略,對象從不過期。 
		timeToIdleSeconds:緩存數據的鈍化時間,也就是在一個元素消亡之前, 兩次訪問時間的最大時間間隔值,這只能在元素不是永久駐留時有效, 
		如果該值是 0 就意味着元素可以停頓無窮長的時間。 timeToLiveSeconds:緩存數據的生存時間,也就是一個元素從構建到消亡的最大時間間隔值, 
		這只能在元素不是永久駐留時有效,如果該值是0就意味着元素可以停頓無窮長的時間。 overflowToDisk:內存不足時,是否啓用磁盤緩存。 memoryStoreEvictionPolicy:緩存滿了之後的淘汰算法。 -->

	<cache name="mobileCache" maxElementsInMemory="10000" eternal="false"
		overflowToDisk="true" timeToIdleSeconds="1800" timeToLiveSeconds="3600"
		memoryStoreEvictionPolicy="LFU" />

</ehcache> 


spring配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:annotation-config />
	<context:component-scan base-package="com.wallet.*" />
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="wallet-web"></property>
		<!-- value 對應persistence.xml中的 persistence-unit name -->
	</bean>

	<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>


	<span style="color:#FF9966;"><strong><!-- 配置eh緩存管理器 -->
	<!--配置緩存管理器 -->
	<bean id="cacheManager"
		class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
		<property name="configLocation">
			<value>classpath:ehcache.xml</value>
		</property>
	</bean>

	<!-- 創建緩存的工廠的應用 -->
	<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
		<property name="cacheManager">
			<ref local="cacheManager" />
		</property>
		<property name="cacheName">
			<value>com.easyway.MethodCache</value>
		</property>
	</bean>

	<!-- 自定義緩存攔截器 -->
	<bean id="methodCacheInterceptor" class="com.wallet.core.intercepter.MethodCacheInterceptor">
		<property name="cache">
			<ref local="methodCache" />
		</property>
	</bean>

	<!-- 自定義攔截器 -->
	<bean id="methodCachePointCut"
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
			<ref local="methodCacheInterceptor" />
		</property>
		<!-- 下面的配置就使得在數據訪問時,cache將攔截從數據庫獲取的數據,與cache數據比較,如有就不放入cache,沒有就放入,更新到數據庫去,也是先存入cache,再更新到數據庫中去 -->
		<property name="patterns">
			<list>
				<value>.*findAll</value>
				<value>.*selectByCondis</value>
				<value>.*selectAll</value>
			</list>
		</property>
	</bean>

	<!-- flush cache攔截器 -->
	<bean id="methodCacheAfterAdvice" class="com.wallet.core.intercepter.MethodCacheAfterAdvice">
		<property name="cache">
			<ref local="methodCache" />
		</property>
	</bean>

	<bean id="methodCachePointCutAdvice"
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice">
			<ref local="methodCacheAfterAdvice" />
		</property>
		<property name="patterns">
			<list>
				<value>.*add</value>
			</list>
		</property>
	</bean>

	<!-- 聲明一個服務 -->

	<bean id="cascadeEventServiceTarget"
		class="com.wallet.myWallet.service.impl.CascadeEventServiceImpl" />

	<bean id="fixedExpensesAccountServiceTarget"
		class="com.wallet.myWallet.service.impl.FixedExpensesAccountServiceImpl" />

	<!-- 相關的服務 -->
	<bean id="cascadeEventService" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target">
			<ref local="cascadeEventServiceTarget" />
		</property>
		<property name="interceptorNames">
			<list>
				<value>methodCachePointCut</value>
			</list>
		</property>
	</bean>

	<bean id="fixedExpensesAccountService" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target">
			<ref local="fixedExpensesAccountServiceTarget" />
		</property>
		<property name="interceptorNames">
			<list>
				<value>methodCachePointCut</value>
				<value>methodCachePointCutAdvice</value>
			</list>
		</property>
	</bean></strong></span>

	<tx:annotation-driven transaction-manager="txManager" />

</beans>

spring與ehcache集成的相關配置主要在上色不分,其中主要用到了兩個攔截器一個是methodCacheInterceptor主要用於對查詢數據進行緩存;methodCacheAfterAdvice主要用於對save/update/delete的方法進行緩存。

methodCacheInterceptor


/**
 * 方法攔截器,主要針對查詢方法進行緩存,
 * 用戶每次查詢會現在緩存中根據cacheKey查找相應的數據,
 * 有的話就只從緩存中取,沒有的話就去數據庫中查找,並將查詢結果存到緩存中 
 */
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {

    private static final Logger log = Logger.getLogger(MethodCacheInterceptor.class);

    private Cache cache;

    public void setCache(Cache cache) {
        this.cache = cache;
    }

    public void afterPropertiesSet() throws Exception {
        log.info(cache + " A cache is required. Use setCache(Cache) to provide one.");
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] arguments = invocation.getArguments();
        Object result;

        String cacheKey = getCacheKey(targetName, methodName, arguments);
        Element element = null;
        synchronized (this) {
            element = cache.get(cacheKey);
            if (element == null) {
                log.info(cacheKey + "加入到緩存: " + cache.getName());
                System.out.println("加入到緩存");
                // 調用實際的方法
                result = invocation.proceed();
                element = new Element(cacheKey, (Serializable) result);
                cache.put(element);
            } else {
                log.info(cacheKey + "使用緩存: " + cache.getName());
                System.out.println("使用緩存");
            }
        }
        return element.getValue();
    }

    /**
     * <b>function:</b> 返回具體的方法全路徑名稱 參數
     * 
     * @author hoojo
     * @createDate 2012-7-2 下午06:12:39
     * @param targetName 全路徑
     * @param methodName 方法名稱
     * @param arguments 參數
     * @return 完整方法名稱
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     */
    private String getCacheKey(String targetName, String methodName, Object[] arguments)
        throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append(targetName).append(".").append(methodName);
        if ((arguments != null) && (arguments.length != 0)) {
            for (int i = 0; i < arguments.length; i++) {
                sb.append(".").append(getArgument(arguments[i]));
            }
        }
        return sb.toString();
    }

    /**
     * 因爲查詢的參數是一個實體類,所以通過反射的方式獲取實體類中的屬性以及屬性值作爲cacheKey的一部分
     * 
     * @param object
     * @return
     * @throws InstantiationException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws NoSuchFieldException
     * @throws SecurityException
     */
    private String getArgument(Object object) throws Exception {

        StringBuffer sb = new StringBuffer();
        if(object!=null && !"".equals(object)){
            System.out.println(object.getClass().getName());
            Method[] methods = object.getClass().getDeclaredMethods();
            for (Method m : methods) {
                if (m.getName().startsWith("get")) {
                    String fieldName = m.getName().substring(3, m.getName().length()).toLowerCase();
                    Object obj1 = m.invoke(object, null);
                    if (obj1 != null && !"".equals(obj1)) {
                        sb.append(fieldName).append(obj1);
                    }
                }
            }   
        }
        return sb.toString();
    }

}

methodCacheAfterAdvice


/**
 * 攔截器MethodCacheAfterAdvice,作用是在用戶進行create/update/delete操作時來刷新/remove相關cache內容,
 * 這個攔截器實現了AfterReturningAdvice接口,將會在所攔截的方法執行後執行在public void afterReturning(Object arg0, Method
 * arg1, Object[] arg2, Object arg3)方法中所預定的操作
 */
public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean {


    private Cache cache;

    public void setCache(Cache cache) {
        this.cache = cache;
    }

    public MethodCacheAfterAdvice() {
        super();
    }

    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3)
        throws Throwable {
        String className = arg3.getClass().getName();
        List list = cache.getKeys();
        for (int i = 0; i < list.size(); i++) {
            String cacheKey = String.valueOf(list.get(i));
            if (cacheKey.startsWith(className)) {
                cache.remove(cacheKey);
            }
        }
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
    }

}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章