`
hiskyrisa
  • 浏览: 36928 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

基于Annotation和AOP实现Service缓存方案

 
阅读更多

简单的使用缓存的方法,就是在Service中嵌入调用缓存的代码。

1.先查询缓存,

2.若缓存命中,则返回

3.从数据库查询,并且往缓存中插入一份

 

这种做法导致许多许多重复代码,所以想到了用注解来调用缓存。并且在注解中设置key和expire

先定义注解

package com.cache;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodCache {
    String prefix();

    int expire();
}

 定义一个简单的Service类,并对需要走缓存的方法加上注解。注意此处注解中的prefix写成这个样子,是为了告诉Aspect要获取的属性,以便能够动态生成key。

package com.cache;

import com.user.vo.User;
import org.springframework.stereotype.Component;

@Component("userService2")
public class UserService {

    @MethodCache(prefix = "'user_'+#userId", expire = 100)
    public User get(User user) {
        System.out.println("Calling Service...");
        user.setName("hisky");
        return user;
    }
}

 定义一个简单的CacheManager类

package com.cache;

import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

@Component
public class CacheManager {
    private ConcurrentHashMap cache = new ConcurrentHashMap();

    public Object get(String key) {
        Object value = cache.get(key);
        return value;
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }
}

 重点来了!定义一个Aspect,截获有注解的方法。

package com.cache;

import com.sun.javafx.binding.StringFormatter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

@org.aspectj.lang.annotation.Aspect
@Component
public class CacheAspect {

    @Autowired
    private CacheManager cacheManager;

    @Pointcut("@annotation(com.cache.MethodCache)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String key = getKey(pjp);
        Object cachedObj = cacheManager.get(key.toString());
        System.out.println(String.format("Find the object from cache, key=%s,value=%s", key, cachedObj));
        if (cachedObj != null) {
            return cachedObj;
        }

        System.out.println("Find nothing from cache, keep call service.");
        cachedObj = pjp.proceed(pjp.getArgs());
        System.out.println(String.format("Put the object to cache, key=%s,value=%s", key, cachedObj));
        cacheManager.put(key.toString(), cachedObj);
        return cachedObj;
    }

    public String getKey(ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        Annotation annotation = method.getAnnotation(MethodCache.class);
        String prefix = ((MethodCache) annotation).prefix();

        Object args[] = pjp.getArgs();
        String key = SpringELParser.parseKey(prefix, args[0]);
        return key;

    }
}

 为了支持动态的生成Key,此处引入Spring EL解析注解的Key

package com.cache;

import com.user.vo.User;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class SpringELParser {

    private static ExpressionParser parser = new SpelExpressionParser();

    //  private static Logger log = Logger.getLogger(SpelParser.class);
    // String prefix = "'Book.'+#bookId";
    // int bookId = 100;
    public static String parseKey(String key, String condition, String[] paramNames, Object[] arguments) {
        try {
            if (!checkCondition(condition, paramNames, arguments)) {
                return null;
            }
            Expression expression = parser.parseExpression(key);
            EvaluationContext context = new StandardEvaluationContext();
            int length = paramNames.length;
            if (length > 0) {
                for (int i = 0; i < length; i++) {
                    context.setVariable(paramNames[i], arguments[i]);
                }
            }
            return expression.getValue(context, String.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static boolean checkCondition(String condition, String[] paramNames, Object[] arguments) {
        if (condition.length() < 1) {
            return true;
        }
        Expression expression = parser.parseExpression(condition);
        EvaluationContext context = new StandardEvaluationContext();
        int length = paramNames.length;
        if (length > 0) {
            for (int i = 0; i < length; i++) {
                context.setVariable(paramNames[i], arguments[i]);
            }
        }
        return expression.getValue(context, boolean.class);
    }

    public static String parseKey(String prefix, Object arg) {
        String key = "";
        try {
            String param = getParam(prefix);
            Object value = getValue(arg, param);
            key = parseKey(prefix, "", new String[]{param}, new Object[]{value});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return key;
    }

    private static Object getValue(Object arg, String param) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String methodName = getMethodName(param);
        Class clzz = arg.getClass();
        Method method = clzz.getMethod(methodName);
        return method.invoke(arg);
    }

    private static String getParam(String prefix) {
        return prefix.substring(prefix.lastIndexOf("#") + 1);
    }

    private static String getMethodName(String param) {
        return "get" + param.substring(0, 1).toUpperCase() + param.substring(1);
    }

    public static void main(String[] args) {
        try {
            String prefix = "'user_'+#userId";

            User user = new User();
            user.setUserId(123);

            String key = parseKey(prefix, user);

            System.out.println(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 Spring的配置文件

<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"
       default-lazy-init="true">

    <context:component-scan base-package="com.*">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>

    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

 测试类

package com.cache;

import com.user.vo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AOPTest {
    public static void main(String args[]) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/cache/spring-test.xml");
        UserService service = (UserService) ctx.getBean("userService2");
        User user = new User();
        user.setUserId(1);
        service.get(user);

        service.get(user);
    }
}

 从控制台可以看出,此处生成的key为user_1。第一次缓存查不到,则调用service获取。第二次则从缓存直接返回了。

Find the object from cache, key=user_1,value=null
Find nothing from cache, keep call service.
Calling Service...
Put the object to cache, key=user_1,value=com.user.vo.User@45667d98[userId=1,name=hisky,password=<null>,type=<null>]
Find the object from cache, key=user_1,value=com.user.vo.User@45667d98[userId=1,name=hisky,password=<null>,type=<null>]

 

分享到:
评论

相关推荐

    SpringAOP结合ehCache实现简单缓存实例

    在IT行业中,Spring AOP(面向切面编程)和EhCache是两个非常重要的概念,它们在提升应用程序性能和管理缓存方面发挥着关键作用。本文将深入探讨如何结合Spring AOP与EhCache实现一个简单的缓存实例,以便优化Java...

    Spring 与Ehcache实现基于方法的缓存

    本篇文章将详细探讨如何在Spring框架中集成并实现基于方法的缓存机制,利用Ehcache来优化数据访问。 首先,我们需要理解Spring的AOP概念,AOP允许我们定义横切关注点,如日志、事务管理或,正如在这个案例中,缓存...

    spring 的AOP 基础

    - **基于注解的AOP(Annotation-based AOP)**:使用`@Aspect`注解定义切面,`@Before`、`@After`、`@Around`等注解定义通知。 **3. 使用步骤** - **配置AOP**:在Spring配置文件中启用AOP支持,如 `&lt;aop:aspectj-...

    自定义注解实现缓存机制

    至此,我们就完成了使用自定义注解实现的Spring Boot和Redis缓存机制。通过这种方式,我们可以灵活地控制哪些方法的结果应该被缓存,并且可以根据业务需求调整缓存策略,如过期时间、键生成规则等。同时,得益于...

    Spring AOP面向方面编程原理:AOP概念

    例如,可以使用引入让任何对象实现`IsModified`接口,从而简化缓存操作。 6. **目标对象(Target Object)**:包含了连接点的对象,即被通知的对象。在Spring AOP中,这个对象通常是业务逻辑实现类。 7. **AOP代理...

    Spring AOP 1.0示例

    Spring AOP 1.0是Spring框架早期的一个版本,它引入了面向切面编程(Aspect Oriented Programming,AOP)的概念,使得开发者可以方便地实现横切关注点,如日志记录、事务管理、性能监控等,从而提高代码的可读性和可...

    Spring AOP简单demo

    **Spring AOP实现方式** 1. **注解驱动(Annotation-based)**:使用`@Aspect`注解定义切面,`@Before`, `@After`, `@AfterReturning`, `@AfterThrowing`, `@Around`定义通知,`@Pointcut`定义切入点表达式。 2. *...

    SpringBoot AOP各种注解、自定义注解、鉴权使用案例(免费下载)

    SpringBoot AOP,即面向切面编程,是Spring框架中的一个重要特性,用于实现代码的横切关注点,如日志记录、事务管理、权限验证等。AOP通过使用代理模式,将这些关注点与核心业务逻辑分离,使得代码更加模块化,更...

    spring aop

    - Spring AOP基于代理,只能在方法调用层面织入,而AspectJ可以在编译时或运行时通过字节码操作实现更细粒度的织入,支持字段和构造器级别的切点。 - AspectJ提供了更强大的切入点表达式和通知模型,但配置和使用...

    16.2 Spring框架-AOP面向切面编程

    JDK动态代理基于接口实现,适用于目标对象实现了接口的情况;而CGLIB代理则是在运行时动态生成一个目标类的子类来实现,当目标对象没有实现接口时,Spring会选择使用CGLIB。 AOP的核心概念包括切面(Aspect)、通知...

    struts1.3+spring2.5+hibernate3.3 组合开发 annotation实现

    Struts1.3、Spring2.5 和 Hibernate3.3 是经典的 Java Web 开发框架组合,它们在企业级应用中广泛...同时,深入研究 Spring 的 AOP、事务管理以及 Hibernate 的缓存策略,将有助于提升开发水平和解决复杂问题的能力。

    Java SSM service层配置文件

    总之,`applicationContext-service.xml`文件是SSM架构中Service层的核心配置,它定义了Service组件的生命周期、依赖关系和行为,是实现业务逻辑的关键部分。理解和熟练配置这个文件,对于提升Java企业级应用的开发...

    SpringMVC Spring Hibernate 框架整合 Annotation Maven Project

    Hibernate还支持二级缓存和事务管理,提高了性能和数据一致性。 **整合过程** 整合SpringMVC、Spring和Hibernate通常涉及以下几个步骤: 1. **配置pom.xml**:引入所需的库依赖,如Spring、SpringMVC、Hibernate、...

    java注解annotation.rar

    2. 框架配置:Spring框架中的`@Component`、`@Service`、`@Repository`和`@Controller`等用于组件扫描和依赖注入。 3. 缓存管理:例如Hibernate的`@Cacheable`用于缓存查询结果。 4. 验证:JSR 303/349的`@Valid`和`...

    struts2+spring2+hibernate3 Annotation的整合

    Struts2、Spring和Hibernate是Java Web开发中的三大框架,它们各自负责不同的职责:Struts2专注于MVC(Model-View-Controller)架构的实现,Spring提供了强大的依赖注入(DI)和面向切面编程(AOP)功能,而...

    rest(jersey)+hibernate+spring+annotation android开发服务器端

    Spring的`@Autowired`注解可以自动装配依赖,`@Service`、`@Repository`和`@Controller`等注解则有助于代码组织和职责划分。 5. **Annotation**:注解是Java语言的一个重要特性,它允许在代码中嵌入元数据,从而...

    sshFrame(struts2.3.4+spring3.1+heibernate4.0+annotation零配置

    **Spring** 是一个全面的Java企业级应用开发框架,它以依赖注入(DI)和面向切面编程(AOP)为核心,提供事务管理、数据访问集成、邮件服务等功能。Spring3.1版本引入了更多改进,如对Java配置的支持,使配置更简洁...

    Struts1.3 + Hibernate3.3 + Spring3.0 Annotation整合

    这三种框架的结合提供了模型-视图-控制器(MVC)架构、对象关系映射(ORM)以及依赖注入(DI)和面向切面编程(AOP)的能力,从而简化了复杂的应用开发流程。 **Struts1.3** 是一个基于MVC设计模式的Java Web框架,...

    SpringBoot 接口访问频率限制(一)

    当然,实际应用中可能需要更复杂的限制策略,如基于IP地址、用户ID、会话ID等,这可能需要结合缓存服务(如Redis)或数据库来存储和查询访问记录。 除了手动实现外,Spring Cloud Gateway和Spring Cloud Zuul等...

    Spring整合EhCache详细教程(史上最全)

    这两种方式都实现了相同的目标——创建一个AOP切面,从而使得带有特定缓存注解的方法能够触发缓存逻辑。 #### 缓存管理器详解 缓存管理器是Spring缓存框架的核心组件之一,负责管理缓存的具体实现以及缓存的生命...

Global site tag (gtag.js) - Google Analytics