Ehcache学习(三)ehcache与springAOP拦截器实例

 本次我们使用springAOP+ehcache结合来实现数据的缓存,我们可以 Cache 系统中 Service 或则 DAO 层的 get/find 等方法返回结果,如果数据更新( 使用Create/update/delete 方法), 则刷新 cache 中相应的内容。

 Aop中最常见的就是拦截器,那么我们首先需要创建的就是一个拦截器:

 ?  MethodCacheInterceptor

<span style="font-family:Microsoft YaHei;font-size:14px;">public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
    private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
    private Cache cache;
    public void setCache(Cache cache) {
        this.cache = cache;
    }
    public MethodCacheInterceptor() {
        super();
    }

    /**
     *  public Object invoke(MethodInvocation invocation) 中,
     完成了搜索 Cache/新建 cache 的功能。
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke( MethodInvocation invocation ) throws Throwable {
        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] arguments = invocation.getArguments();
        Object result;
        logger.debug("Find object from cache is " + cache.getName());
        String cacheKey = getCacheKey(targetName, methodName, arguments);
//        搜索cache功能,如果存在直接返回,否则的话将创建之后进行存储
        Element element = cache.get(cacheKey);
        if (element == null) {
            logger.debug("Hold up method , Get method result and create cache........!");
            result = invocation.proceed();   //处理结果,这句代码的作用是获取所拦截方法的返回值
            element = new Element(cacheKey, (Serializable) result);
            cache.put(element);
        }
        return element.getValue();
//        return null;
    }

    /**
     * implement InitializingBean, 检查 cache 是否为空
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");

    }

    /**
     * 获得 cache key 的方法, cache key 是 Cache 中一个 Element 的唯一标识
     * cache key 包括 包名+类名+方法名, 如 com.co.cache.service.UserServiceImpl.getAllUser
     */
    private String getCacheKey(String targetName, String methodName, Object[] arguments) {
        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(arguments[i]);
            }
        }
        return sb.toString();
    }
}</span>

 ?  MethodCacheAfterAdvice

<span style="font-family:Microsoft YaHei;font-size:14px;">public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean {

    private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);
    private Cache cache;
    public void setCache(Cache cache) {
        this.cache = cache;
    }
    public MethodCacheAfterAdvice() {
        super();
    }
    @Override
    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);
                logger.debug("remove cache " + cacheKey);
            }
        }
    }

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

  随后,再建立一个拦截器MethodCacheAfterAdvice,作用是在用户进行create/update/delete操作时来刷新/remove 相关 cache 内容, 这个拦截器实现了AfterReturningAdvice 接口,将会在所拦截的方法执行后执行在

public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3)方法中所预定的操作

 ?  配置文件:

  Application_spring_cache.xml

<span style="font-family:Microsoft YaHei;font-size:14px;"><?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:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/cache
        http://www.springframework.org/schema/cache/spring-cache-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--支持缓存注解-->
    <cache:annotation-driven cache-manager="cacheManager"/>
    <context:component-scan base-package="ehcache.*" />
    <!-- 一些@RequestMapping 请求和一些转换 -->
    <mvc:annotation-driven />

    <!-- 前后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--  静态资源访问 的两种方式  -->
    <!-- <mvc:default-servlet-handler/>   -->
    <!--<mvc:resources location="/*" mapping="/**" />-->
    <mvc:view-controller path="/" view-name="forward:/index.jsp"/>
    <!-- Spring自己的基于java.util.concurrent.ConcurrentHashMap实现的缓存管理器(该功能是从Spring3.1开始提供的) -->
    <!--
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean name="myCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"/>
            </set>
        </property>
    </bean>
     -->
    <!-- 若只想使用Spring自身提供的缓存器,则注释掉下面的两个关于Ehcache配置的bean,并启用上面的SimpleCacheManager即可 -->
    <!-- Spring提供的基于的Ehcache实现的缓存管理器 -->
    <!--  缓存  属性-->
    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:/ehcache.xml" />
    </bean>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager"  ref="cacheManagerFactory"/>
    </bean>

    <!--为了测试拦截器缓存-->
    <!-- 定义 ehCache 的工厂, 并设置所使用的 Cache name -->
    <bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager">
            <ref local="cacheManagerFactory"/>
        </property>
        <!--如果 cacheName 属性内设置的 name 在 ehCache.xml 中无法找到, 那么将使用默认的-->
        <!--cache(defaultCache 标签定义)-->
        <property name="cacheName">
            <value>DEFAULT_CACHE</value>
        </property>
    </bean>

    <!-- find/create cache 拦截器 -->
    <bean id="methodCacheInterceptor"
          class="ehcache.CacheInterceptor.MethodCacheInterceptor">
        <property name="cache">
            <ref local="ehCache" />
        </property>
    </bean>

    <!-- flush cache 拦截器 -->
    <bean id="methodCacheAfterAdvice"
          class="ehcache.CacheInterceptor.MethodCacheAfterAdvice">
        <property name="cache">
            <ref local="ehCache" />
        </property>
    </bean>
    <!--上面的代码最终创建了两个"切入点", methodCachePointCut 和-->
    <!--methodCachePointCutAdvice, 分别用于拦截不同方法名的方法, 可以根据需要任意增加-->
    <!--所需要拦截方法的名称。-->
    <bean id="methodCachePointCut"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref local="methodCacheInterceptor"/>
        </property>
        <property name="patterns">
            <list>
                <value>.*find.*</value>
                <value>.*get.*</value>
            </list>
        </property>
    </bean>

    <bean id="methodCachePointCutAdvice"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref local="methodCacheAfterAdvice"/>
        </property>
        <property name="patterns">
            <list>
                <value>.*create.*</value>
                <value>.*update.*</value>
                <value>.*delete.*</value>
            </list>
        </property>
    </bean>
    <!--方法测试-->
    <!--如果缓存的配置与spring拦截在一个方法中就可以直接使用不用引入,否则的话需要引入-->
    <!--<import resource="cacheContext.xml"/>-->
    <bean id="testServiceTarget" class="ehcache.Service.TestServiceImpl"/>
    <bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
            <ref local="testServiceTarget"/>
        </property>
        <property name="interceptorNames">
            <list>
                <value>methodCachePointCut</value>
                <value>methodCachePointCutAdvice</value>
            </list>
        </property>
    </bean>
</beans></span>

  Ehcache.xml

<span style="font-family:Microsoft YaHei;font-size:14px;"><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!--当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)。-->
    <diskStore path="java.io.tmpdir"/>
    <!--:用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index。-->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="DEFAULT_CACHE"
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="300000"
           timeToLiveSeconds="600000"
           overflowToDisk="true"
    />
</ehcache></span>

 ?  测试用例

  接口TestService

<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.Iservice;

import java.util.List;

/**
 * Created by xiaona on 2016/6/14.
 */
public interface TestService {
    public List getAllObject();
    public void updateObject(Object Object);
}
</span>

  实现TestServiceImpl

<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.Service;

import ehcache.Iservice.TestService;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Created by xiaona on 2016/6/14.
 */
@Service
public class TestServiceImpl implements TestService {
    @Override
    public List getAllObject() {
        System.out.println("---TestService: Cache 内不存在该 element, 查找并放入 Cache! ");
        return null;
    }

    @Override
    public void updateObject( Object Object ) {
        System.out.println("---TestService:更新了对象,这个 Class 产生的 cache 都将被 remove");
    }
}
</span>

  方法调用

  两种方式:getBean方式和使用注解读取

   第一种方式:

<span style="font-family:Microsoft YaHei;font-size:14px;">package ehcache.controller;

import ehcache.Iservice.TestService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by xiaona on 2016/6/14.
 */
public class TestMain {

    public static void main( String[] args ) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application_spring_cache.xml");
        TestService testService=(TestService)context.getBean("testService");
        System.out.println("1--第一次查找并创建 cache");
        testService.getAllObject();
        System.out.println("2--在 cache 中查找");
        testService.getAllObject();
        System.out.println("3--remove cache");
        testService.updateObject(null);
        System.out.println("4--需要重新查找并创建 cache");
        testService.getAllObject();
    }

}
</span>

   第二种方式:

<span style="font-family:Microsoft YaHei;font-size:14px;">@Resource
private TestService testService;
    @RequestMapping(value = "/testInterceptor", method = RequestMethod.GET)
    public String testInterceptor() {
        System.out.println("1--第一次查找并创建 cache");
        testService.getAllObject();
        System.out.println("2--在 cache 中查找");
        testService.getAllObject();
        System.out.println("3--remove cache");
        testService.updateObject(null);
        System.out.println("4--需要重新查找并创建 cache");
        testService.getAllObject();

        return root + "/" + "testInterceptor";
    }
</span>

  前提是TestService的实现TestServiceImpl必须加上@Service注解,这样可以把其交给容器进行管理。什么样的结果表示我们的是成功的从缓存中获取到的呢?可以看到,

  第一步执行getAllObject(),执行 TestServiceImpl 内的方法,并创建了 cache, 在第二次执行getAllObject()方法时,由于 cache 有该方法的缓存,直接从cache 中 get 出方法的结果, 所以没有打印出 TestServiceImpl中的内容, 而第三步, 调用了 updateObject 方法,和TestServiceImpl 相关的
cache 被 remove,所以在第四步执行时,又执行TestServiceImpl 中的方法, 创建 Cache。

 ?  小结

  我们看出结合SpringAop的实例,可以实现我们对于请求方法的拦截,其实很多时候我们用到的页面都是一定时间内不会发生变化的,此时我们需要的是拦截经常被调用的方法从而进行缓存,这样的话可以提高用户对于页面请求的响应速度。

时间: 2024-12-29 23:44:20

Ehcache学习(三)ehcache与springAOP拦截器实例的相关文章

Spring异步调用原理及SpringAop拦截器链原理

一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { /** * Indicate the 'async' annotation type to be detected at

Struts2自己定义拦截器实例—登陆权限验证

版本号:struts2.1.6 此实例实现功能:用户须要指定username登陆,登陆成功进入对应页面运行操作,否则返回到登陆页面进行登陆,当直接訪问操作页面(登陆后才干訪问的页面)时则不同意,须返回登陆页面. 代码例如以下: 一.页面 login.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUB

Struts2学习(三)———— 输入校验和拦截器

一.输入校验 在以前我们写一个登录页面时,并没有限制用户的输入,不管用户输入什么,我们都存入数据库中,很显然这是不行的,我们需要检测用户输入的文本是否合法,是否符合我们需要的文本格式,符合菜放行,而struts2中就有这种功能,能帮我们在服务器段进行判断,比如用户名不能为空,年龄只能在0-100之间等.现在我们就来说说如何使用struts2中的校验功能把. 分为两种,编程式校验和配置校验(XML配置校验) 1.1编程式校验, 对action中所有方法都执行校验 实现Validateable接口,

第三天(1)自定义拦截器

---恢复内容开始--- *一)拦截器 (1)什么是栏截器? 在Struts2中,拦截器就是一个实现了特定接口Interceptor的普通类 (2)拦截器的作用? 根据用户的输入信息,进行判断,然后依据判断的结果,转向不同的资源(该资源可以是Action或jsp) (3)拦截器的执行顺序 和在xml文件中配置的<interceptor-ref/>有关,先配置先执行,后配置后执行, 如果第一个拦截器执行失败,不会执行后一个拦截器. (4)struts2内置拦截器 参见<<PPT第6,

struts2学习笔记(5)拦截器

继承AbstractInterceptor类,在类中完成拦截器的功能,只需实现intercept方法即可,提供了init()和destroy()的空实现 示例:显示执行action所用的时间 ①在src中建包com.lzhc.interceptor,在包中建TimerInterceptor.java,使其继承AbstractInterceptor类 ②在struts.xml中进行配置 在package内注册拦截器,代码如下: <interceptors> <interceptor nam

Java Web学习(十)Java拦截器

一.引言 我们日常开发中,经常会遇到这个场景:在访问系统功能前,需要用户登录,不登陆的话无法使用我们的系统,那么如果在每个方法前都加上登录代码...[emmm....我想应该不会有人这么干吧...],常见的可以使用以下几种方式: 使用AOP切面功能来实现 实现WebMvcConfigurer接口,重写addCorsMappings()方法和addInterceptors()方法 下面我们就一起来看下一下怎么实现吧~ 二.代码实现 AOP切面方式 切面方式配置的话,得配置到包路径下或者每个具体方法

Struts2他们拦截器实例定义—登陆权限验证

版本号:struts2.1.6 这种情况下实现功能:用户需要指定username登陆,进入相应的页面运行成功登陆作战,否则,它返回到着陆的登录页面,当直接进入操作页面(登陆访问页面后的能力)如果不同意,必须返回到登陆页面. 码,如以下: 一.页面 login.jsp <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTM

struts2 注解配置拦截器 实例

/** * */ package com.wonders.stpt.contractReview.action; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.a

SpringAOP拦截器的代理机制

要使用方法名匹配AOP切面编程,需要使用到spring中的org.springframework.aop.support.NameMatchMethodPointcutAdvisor这个类,advice属性定义一个通告类,mappedName定义通告类针对的方法,通告的类需要实现一些特定的接口在特定的时候执行: MethodBeforeAdvice :方法之前执行 AfterReturningAdvice:方法之后执行 继承 ThrowsAdviceInterceptor:抛出异常之后执行 AO