基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存

  如何能更简洁的利用aop实现redis缓存,话不多说,上demo

  需求:
    数据查询时每次都需要从数据库查询数据,数据库压力很大,查询速度慢,
    因此设置缓存层,查询数据时先从redis中查询,如果查询不到,则到数据库中查询
    然后将数据库中查询的数据放到redis中一份,下次查询时就能直接从redis中查到,不需要查询数据库了

  实现过程:

      先搭建ssm的架子,引入redis,编写redis 缓存方法 RedisCache.java以及序列化用到的工具类

      自定义注解 getCache 目的:
            被这个注解标记的方法实现aop
            防止redis key重复

     编写切面
      @Aspect

        @Pointcut("@annotation(com.spring_redis.cache.GetCache)")
        切入点为自定义注解 即每个被该注解标记的方法实现通知

        @Around("getCache()")
        利用环绕通知
          过程: 查询时,先查询redis 如果存在key-value,则返回不查询
          如果不存在,则查询数据库,之后将查询到的数据存入到redis缓存中
          redis key格式:为了防止key冲突,创建的key格式为:
          包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(integer).123"

  目录结构

      

  maven依赖:

    

 <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- spring版本号 -->
<spring.version>4.0.6.RELEASE</spring.version>
<!-- mybatis版本号 -->
<mybatis.version>3.2.7</mybatis.version>
</properties>
<dependencies> <!-- spring核心包 -->
<!-- springframe start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.0.6.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springframe end --> <!-- aop注解 --> <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency> <!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency> <!-- dbcp2连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.0.1</version>
</dependency> <!-- json数据 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency> <!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency> <dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency> <!-- servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!-- servlet-api end --> <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>

  这里只给出redis 的相关配置

    在applicationContext-dao.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
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/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <!-- 加载db.properties文件中的内容,db.properties文件中key命名要有一定的特殊规则 -->
<context:property-placeholder location="classpath:properties/db.properties" /> <!-- 配置数据源 ,dbcp --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="dataSource" p:configLocation="classpath:mybatis/sqlMapConfig.xml"
></bean>
<!-- Redis和缓存配置开始 -->
<!-- jedis 配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
<property name="maxIdle" value="100" />
<property name="maxWaitMillis" value="1000" />
<property name="testOnBorrow" value="true" />
</bean > <!-- redis连接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
<constructor-arg name="poolConfig" ref="poolConfig"/>
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6379"/>
</bean> <!-- redis服务器中心 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<property name="poolConfig" ref="poolConfig" />
<property name="port" value="6379" />
<property name="hostName" value="127.0.0.1" />
<!-- <property name="password" value="${redis.password}" /> -->
<property name="timeout" value="10000" ></property>
</bean >
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean > <!-- cache配置 -->
<bean id="putCache" class="com.spring_redis.cache.PutCacheAOP" >
<property name="redisTemplate" ref="redisTemplate" />
</bean> <!-- cache配置 -->
<bean id="getCache" class="com.spring_redis.cache.GetCacheAOP" >
<property name="redisTemplate" ref="redisTemplate" />
</bean> <!-- Redis和缓存配置结束 --> <!-- mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描包路径,如果需要扫描多个包,中间使用半角逗号隔开 -->
<property name="basePackage" value="com.spring_redis.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean> <bean id="roomservice" class="com.spring_redis.service.impl.RoomServiceImpl" > </bean> </beans>

  springmvc.xml

 <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-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/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd "> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 可以扫描controller、service、... 这里让扫描controller,指定controller的包 com.ssm.controlle -->
<context:component-scan base-package="com.spring_redis">
</context:component-scan>
<!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置 mvc:annotation-driven默认加载很多的参数绑定方法 -->
<mvc:annotation-driven />
<!-- 视图解析器 解析jsp解析,默认使用jstl标签,classpath下的得有jstl的包 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置jsp路径的前缀 <property name="prefix" value="/jsp/"/> -->
<!-- 配置jsp路径的后缀 -->
<property name="suffix" value=".jsp" />
</bean> </beans>

<aop:aspectj-autoproxy proxy-target-class="true"/>  开启注解这个一定要写到springmvc.xml里,否则注解会不起作用

  那重点开始了

  创建自定义注解

 /**
* 自定义注解,对于查询使用缓存的方法加入该注解
* @author Chenth
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface GetCache {
String name() default "";
String value() default "";
}  

  被这个自定义注解所标记的方法将实现下面的切面

  配置切面

 package com.spring_redis.cache;

 import java.io.Serializable;
import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import com.spring_redis.util.RedisCache; @Component
@Aspect
public class GetCacheAOP { @Autowired
private RedisTemplate<Serializable, Object> redisTemplate; private RedisCache redisCache = new RedisCache(); @Pointcut("@annotation(com.spring_redis.cache.GetCache)")
public void getCache(){
System.out.println("我是一个切入点");
} /**
* 在所有标注@getCache的地方切入
* @param joinPoint
*/
@Around("getCache()")
public Object beforeExec(ProceedingJoinPoint joinPoint){ //前置:到redis中查询缓存
System.out.println("调用从redis中查询的方法..."); //redis中key格式: id
String redisKey = getCacheKey(joinPoint); //获取从redis中查询到的对象
Object objectFromRedis = redisCache.getDataFromRedis(redisKey); //如果查询到了
if(null != objectFromRedis){
System.out.println("从redis中查询到了数据...不需要查询数据库");
return objectFromRedis;
} System.out.println("没有从redis中查到数据..."); //没有查到,那么查询数据库
Object object = null;
try {
object = joinPoint.proceed();
} catch (Throwable e) { e.printStackTrace();
} System.out.println("从数据库中查询的数据..."); //后置:将数据库中查询的数据放到redis中
System.out.println("调用把数据库查询的数据存储到redis中的方法..."); redisCache.setDataToRedis(redisKey, object);
System.out.println("redis中的数据..."+object.toString());
//将查询到的数据返回
return object;
} /**
* 根据类名、方法名和参数值获取唯一的缓存键
* @return 格式为 "包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(int).123"
*/ @SuppressWarnings("unused")
private String getCacheKey(ProceedingJoinPoint joinPoint) { MethodSignature ms=(MethodSignature) joinPoint.getSignature();
Method method=ms.getMethod();
String ActionName = method.getAnnotation(GetCache.class).name();
String fieldList = method.getAnnotation(GetCache.class).value();
//System.out.println("签名是"+ms.toString());
for (String field:fieldList.split(","))
ActionName +="."+field; //先获取目标方法参数
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
} ActionName += "="+id;
String redisKey = ms+"."+ActionName;
return redisKey;
} public void setRedisTemplate(
RedisTemplate<Serializable, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}

  

 @Pointcut("@annotation(com.spring_redis.cache.GetCache)") 这个切入点的作用是
                                      在所有标注@getCache的地方切入 
 @Around("getCache()")这里用的是后置通知,即查询之前先查询redis,如果有数据就返回数据,没有就穿透的数据库查询数据,之后再缓存到redis中

  这里并没有太多的讲解配置ssm框架,可能后续会写关于spring+springmvc+mybatis的框架整合

  编写mapper层,service层,controller层

  mapper

/**
*
* @author cmy
* @date 2016-10-22
* @description 持久化
*/ public interface RoomMapper { @Insert("insert into room(roomName,address) values(#{roomName},#{addRess})")
int insert(Room room); @Select("select * from room where id=#{id}")
public Room selectByPrimaryKey(@Param("id")Integer id); }

  service

 /**
*
* @author cmy
* @date 2016-10-22
* @description test
*/
public interface RoomService { int insert(Room room)throws Exception; Room selectByPrimaryKey(Integer id)throws Exception; } // 实现
/**
* @author cmy
* @date 2016-10-22
* @description test 实现
*/
public class RoomServiceImpl implements RoomService{ @Autowired
private RoomMapper mapper; @Override
public int insert(Room room) throws Exception { return mapper.insert(room);
} @Override
public Room selectByPrimaryKey(Integer id) throws Exception { return mapper.selectByPrimaryKey(id);
} }

   controller

/**
*
* @author cmy
* @date 2016-10-22
* @description test controller
*/ @Controller
@RequestMapping("room")
public class RoomController { @Autowired
private RoomService roomService; @GetCache(name="room",value="id")
@RequestMapping("selectByPrimaryKey")
public @ResponseBody Object roomList(Integer id) throws Exception{
System.out.println("已查询到数据,准备缓存到redis... "+roomService.selectByPrimaryKey(id).getRoomName());
return roomService.selectByPrimaryKey(id);
} }

  缓存要用到的工具类  RedisCache

 public class RedisCache {

     @Autowired
private JedisPool jedisPool = new JedisPool(); //从redis缓存中查询,反序列化
public Object getDataFromRedis(String redisKey){
//查询
Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(redisKey.getBytes()); //如果查询没有为空
if(null == result){
return null;
} //查询到了,反序列化
return SerializeUtil.unSerialize(result);
} //将数据库中查询到的数据放入redis
public void setDataToRedis(String redisKey, Object obj){ //序列化
byte[] bytes = SerializeUtil.serialize(obj); //存入redis
Jedis jedis = jedisPool.getResource();
String success = jedis.set(redisKey.getBytes(), bytes); if("OK".equals(success)){
System.out.println("数据成功保存到redis...");
}
}
}

  缓存要用到的序列化和反序列化工具

 /**
*
* @Description: 序列化反序列化工具
*/
public class SerializeUtil {
/**
*
* 序列化
*/
public static byte[] serialize(Object obj){ ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null; try {
//序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos); oos.writeObject(obj);
byte[] byteArray = baos.toByteArray();
return byteArray; } catch (IOException e) {
e.printStackTrace();
}
return null;
} /**
*
* 反序列化
* @param bytes
* @return
*/
public static Object unSerialize(byte[] bytes){ ByteArrayInputStream bais = null; try {
//反序列化为对象
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject(); } catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

 以上就是利用aop+自定义注解实现 redis缓存的过程了

      有不对之处,还望指出 欢迎留言

有参考到的文章:http://www.cnblogs.com/mrlinfeng/p/5857775.html

        http://blog.csdn.net/chentian610/article/details/51012789

ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存的更多相关文章

  1. SpringCloud微服务实战——搭建企业级开发框架(三十九):使用Redis分布式锁(Redisson)+自定义注解+AOP实现微服务重复请求控制

      通常我们可以在前端通过防抖和节流来解决短时间内请求重复提交的问题,如果因网络问题.Nginx重试机制.微服务Feign重试机制或者用户故意绕过前端防抖和节流设置,直接频繁发起请求,都会导致系统防重 ...

  2. java 编程基础:【注解】 提取注解信息,利用自定义注解编写测试类,注解绑定事件

    提取注解信息 使用注解修饰了类.方法.成员变量等成员之后,这些注解不会自己生效,必须由开发者提供相应工具来提取并处理注解信息.   Java使用java.lang.annotation.Annotat ...

  3. Spring Boot + Redis实战-利用自定义注解+分布式锁实现接口幂等性

    场景 不管是传统行业还是互联网行业,我们都需要保证大部分操作是幂等性的,简单点说,就是无论用户点击多少次,操作多少遍,产生的结果都是一样的,是唯一的.而今次公司的项目里,又被我遇到了这么一个幂等性的问 ...

  4. 【Redis】redis异步消息队列+Spring自定义注解+AOP方式实现系统日志持久化

    说明: SSM项目中的每一个请求都需要进行日志记录操作.一般操作做的思路是:使用springAOP思想,对指定的方法进行拦截.拼装日志信息实体,然后持久化到数据库中.可是仔细想一下会发现:每次的客户端 ...

  5. Redis 源码简洁剖析 11 - 主 IO 线程及 Redis 6.0 多 IO 线程

    Redis 到底是不是单线程的程序? 多 IO 线程的初始化 IO 线程运行函数 IOThreadMain 如何推迟客户端「读」操作? 如何推迟客户端「写」操作? 如何把待「读」客户端分配给 IO 线 ...

  6. Java利用自定义注解、反射实现简单BaseDao

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先 ...

  7. SpringBoot利用自定义注解实现通用的JWT校验方案

    利用注解开发一个通用的JWT前置校验功能 设计的预期: 系统中并不是所有的应用都需要JWT前置校验,这就需要额外设计一个注解Annotation来标识这个方法需要JWT前置校验.例如: @GetMap ...

  8. 【Spring】5、利用自定义注解在SpringMVC中实现自定义权限检查

    转自:http://www.tuicool.com/articles/6z2uIvU 先描述一下应用场景,基于Spring MVC的WEB程序,需要对每个Action进行权限判断,当前用户有权限则允许 ...

  9. Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的 ...

随机推荐

  1. sklearn 增量学习 数据量大

    问题 实际处理和解决机器学习问题过程中,我们会遇到一些"大数据"问题,比如有上百万条数据,上千上万维特征,此时数据存储已经达到10G这种级别.这种情况下,如果还是直接使用传统的方式 ...

  2. Final阶段用户调查报告

    组名称:nice! 项目名称:约跑 小组成员:李权(组长).刘芳芳.于淼.宫丽君.韩媛媛 产品下载地址:http://pan.baidu.com/s/1mhIjaS4 问卷时间:2016年12月2号1 ...

  3. mysql 增删改查最基本用法小结

    目录: 1.新建数据库 2.新建数据表 3.查看表结构 4.增删改查 建立一个数据库students 建立一块数据表class1 内容包括: id 主键 自动编号 无符号位 SMALLINT类型 na ...

  4. (三)SQL入门 数据库规格化简介

    什么是数据库的规格化呢,说白了就是为了去除数据库冗余.为了数据库更加容易管理而将大表按照逻辑划分为小表的过程. 什么是数据库的去规格化呢,就是规格化的反面.那么你可能就会问,既然上面说了数据库的规格化 ...

  5. Oracle Partition By 的使用

    1.概述 Parttion by 关键字是Oracle中分析性函数的一部分,它和聚合函数不同的地方在于它能够返回一个分组中的多条记录,儿聚合函数一般只有一条反映统计值的结果. 2.使用方式 场景:查询 ...

  6. Fuzz的那些事

    Fuzz这个词汇行内的都懂,fuzz工具我就不多说了. 今天,说说fuzz前的准备工作--如何对软件进行修改实现可自动化和无人值守fuzz. 很多软件会有试用期.弹窗.覆盖提示.人机交互等等,这些都会 ...

  7. python查找空格和中文

    前言 图片或者文件夹下,命名不规范,有中文或者有空格.这个脚本批处理查找,并输出到 txt中方便修改,也可以扩展为 直接脚本删除空格等.目前只用在Windows上,mac没有测试,不知道能不能行,有需 ...

  8. MySQL免安装数据库配置-Windows8.1

    1. 解压Mysql压缩包,将包中my-default.ini复制一份,改名为my.ini,添加一下配置信息到my.ini: [client] port=3306 default-character- ...

  9. Java SE 基础:常用关键字

    Java SE 基础:常用关键字 常用关键字表

  10. javascript各种宽高

    参考: http://www.w3cschool.cc/jsref/dom-obj-all.html http://www.cnblogs.com/wen12128/archive/2012/05/2 ...