SpringBoot集成Redis实现缓存处理(Spring AOP实现)
第一章 需求分析
计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的AOP(面向切面编程)。
开源项目:https://github.com/u014427391/jeeplatform
欢迎star(收藏)
第二章 SpringBoot简介
Spring框架作为JavaEE框架领域的一款重要的开源框架,在企业应用开发中有着很重要的作用,同时Spring框架及其子框架很多,所以知识量很广。
SpringBoot:一款Spring框架的子框架,也可以叫微框架,是2014年推出的一款使Spring框架开发变得容易的框架。学过Spring框架的都知识,Spring框架难以避免地需要配置不少XMl,而使用SpringBoot框架的话,就可以使用注解开发,极大地简化基于Spring框架的开发。SpringBoot充分利用了JavaConfig的配置模式以及“约定优于配置”的理念,能够极大的简化基于SpringMVC的Web应用和REST服务开发。
第三章 Redis简介
3.1 Redis安装部署(Linux)
Redis安装部署的可以参考我的博客(Redis是基于C编写的,所以安装前先安装gcc编译器):http://blog.csdn.net/u014427391/article/details/71210989
3.2 Redis简介
Redis如今已经成为Web开发社区最火热的内存数据库之一,随着Web2.0的快速发展,再加上半结构数据比重加大,网站对高效性能的需求也越来越多。
而且大型网站一般都有几百台或者更多Redis服务器。Redis作为一款功能强大的系统,无论是存储、队列还是缓存系统,都有其用武之地。
SpringBoot框架入门的可以参考我之前的博客:http://blog.csdn.net/u014427391/article/details/70655332
第四章 Redis缓存实现
4.1下面结构图
项目结构图:
4.2 SpringBoot的yml文件配置
添加resource下面的application.yml配置,这里主要配置mysql,druid,redis
spring:
datasource:
# 主数据源
shop:
url: jdbc:mysql://127.0.0.1:3306/jeeplatform?autoReconnect=true&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 连接池设置
druid:
initial-size: 5
min-idle: 5
max-active: 20
# 配置获取连接等待超时的时间
max-wait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
# Oracle请使用select 1 from dual
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
use-global-data-source-stat: true
jpa:
database: mysql
hibernate:
show_sql: true
format_sql: true
ddl-auto: none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
#Jedis配置
jedis :
pool :
host : 127.0.0.1
port : 6379
password : password
timeout : 0
config :
maxTotal : 100
maxIdle : 10
maxWaitMillis : 100000
编写一个配置类启动配置JedisConfig.java:
package org.muses.jeeplatform.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
//@ConfigurationProperties(prefix = JedisConfig.JEDIS_PREFIX )
public class JedisConfig {
//public static final String JEDIS_PREFIX = "jedis";
@Bean(name= "jedisPool")
@Autowired
public JedisPool jedisPool(@Qualifier("jedisPoolConfig") JedisPoolConfig config,
@Value("${spring.jedis.pool.host}")String host,
@Value("${spring.jedis.pool.port}")int port,
@Value("${spring.jedis.pool.timeout}")int timeout,
@Value("${spring.jedis.pool.password}")String password) {
return new JedisPool(config, host, port,timeout,password);
}
@Bean(name= "jedisPoolConfig")
public JedisPoolConfig jedisPoolConfig (@Value("${spring.jedis.pool.config.maxTotal}")int maxTotal,
@Value("${spring.jedis.pool.config.maxIdle}")int maxIdle,
@Value("${spring.jedis.pool.config.maxWaitMillis}")int maxWaitMillis) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMaxWaitMillis(maxWaitMillis);
return config;
}
}
4.3 元注解类编写
编写一个元注解类RedisCache.java,被改注解定义的类都自动实现AOP缓存处理
package org.muses.jeeplatform.annotation;
import org.muses.jeeplatform.common.RedisCacheNamespace;
import java.lang.annotation.*;
/**
* 元注解 用来标识查询数据库的方法
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
// RedisCacheNamespace nameSpace();
}
JDK 5提供的注解,除了Retention以外,还有另外三个,即Target 、Inherited 和 Documented。基于这个,我们可以实现自定义的元注解
我们设置RedisCache基于Method方法级别引用。
1.RetentionPolicy.SOURCE 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
2.RetentionPolicy.CLASS 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
3.RetentionPolicy.RUNTIME 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
4.4 调用JedisPool实现Redis缓存处理
package org.muses.jeeplatform.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import javax.annotation.Resource;
@Component("redisCache")
public class RedisCache {
@Autowired
private JedisPool jedisPool;
private JedisPool getJedisPool(){
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool){
this.jedisPool = jedisPool;
}
/**
* 从Redis缓存获取数据
* @param redisKey
* @return
*/
public Object getDataFromRedis(String redisKey){
Jedis jedis = jedisPool.getResource();
byte[] byteArray = jedis.get(redisKey.getBytes());
if(byteArray != null){
return SerializeUtil.unSerialize(byteArray);
}
return null;
}
/**
* 保存数据到Redis
* @param redisKey
*/
public String saveDataToRedis(String redisKey,Object obj){
byte[] bytes = SerializeUtil.serialize(obj);
Jedis jedis = jedisPool.getResource();
String code = jedis.set(redisKey.getBytes(), bytes);
return code;
}
}
对象序列化的工具类:
package org.muses.jeeplatform.cache;
import java.io.*;
public class SerializeUtil {
/**
* 序列化对象
* @param obj
* @return
*/
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 byteArray
* @return
*/
public static Object unSerialize(byte[] byteArray){
ByteArrayInputStream bais = null;
try {
//反序列化为对象
bais = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
这里记得Vo类都要实现Serializable
例如菜单信息VO类,这是一个JPA映射的实体类
package org.muses.jeeplatform.core.entity.admin;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @description 菜单信息实体
* @author Nicky
* @date 2017年3月17日
*/
@Table(name="sys_menu")
@Entity
public class Menu implements Serializable {
/** 菜单Id**/
private int menuId;
/** 上级Id**/
private int parentId;
/** 菜单名称**/
private String menuName;
/** 菜单图标**/
private String menuIcon;
/** 菜单URL**/
private String menuUrl;
/** 菜单类型**/
private String menuType;
/** 菜单排序**/
private String menuOrder;
/**菜单状态**/
private String menuStatus;
private List<Menu> subMenu;
private String target;
private boolean hasSubMenu = false;
public Menu() {
super();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int getMenuId() {
return this.menuId;
}
public void setMenuId(int menuId) {
this.menuId = menuId;
}
@Column(length=100)
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
@Column(length=100)
public String getMenuName() {
return this.menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
@Column(length=30)
public String getMenuIcon() {
return this.menuIcon;
}
public void setMenuIcon(String menuIcon) {
this.menuIcon = menuIcon;
}
@Column(length=100)
public String getMenuUrl() {
return this.menuUrl;
}
public void setMenuUrl(String menuUrl) {
this.menuUrl = menuUrl;
}
@Column(length=100)
public String getMenuType() {
return this.menuType;
}
public void setMenuType(String menuType) {
this.menuType = menuType;
}
@Column(length=10)
public String getMenuOrder() {
return menuOrder;
}
public void setMenuOrder(String menuOrder) {
this.menuOrder = menuOrder;
}
@Column(length=10)
public String getMenuStatus(){
return menuStatus;
}
public void setMenuStatus(String menuStatus){
this.menuStatus = menuStatus;
}
@Transient
public List<Menu> getSubMenu() {
return subMenu;
}
public void setSubMenu(List<Menu> subMenu) {
this.subMenu = subMenu;
}
public void setTarget(String target){
this.target = target;
}
@Transient
public String getTarget(){
return target;
}
public void setHasSubMenu(boolean hasSubMenu){
this.hasSubMenu = hasSubMenu;
}
@Transient
public boolean getHasSubMenu(){
return hasSubMenu;
}
}
4.5 Spring AOP实现监控所有被@RedisCache注解的方法缓存
先从Redis里获取缓存,查询不到,就查询MySQL数据库,然后再保存到Redis缓存里,下次查询时直接调用Redis缓存
package org.muses.jeeplatform.cache;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* AOP实现Redis缓存处理
*/
@Component
@Aspect
public class RedisAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisAspect.class);
@Autowired
@Qualifier("redisCache")
private RedisCache redisCache;
/**
* 拦截所有元注解RedisCache注解的方法
*/
@Pointcut("@annotation(org.muses.jeeplatform.annotation.RedisCache)")
public void pointcutMethod(){
}
/**
* 环绕处理,先从Redis里获取缓存,查询不到,就查询MySQL数据库,
* 然后再保存到Redis缓存里
* @param joinPoint
* @return
*/
@Around("pointcutMethod()")
public Object around(ProceedingJoinPoint joinPoint){
//前置:从Redis里获取缓存
//先获取目标方法参数
long startTime = System.currentTimeMillis();
String applId = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
applId = String.valueOf(args[0]);
}
//获取目标方法所在类
String target = joinPoint.getTarget().toString();
String className = target.split("@")[0];
//获取目标方法的方法名称
String methodName = joinPoint.getSignature().getName();
//redis中key格式: applId:方法名称
String redisKey = applId + ":" + className + "." + methodName;
Object obj = redisCache.getDataFromRedis(redisKey);
if(obj!=null){
LOGGER.info("**********从Redis中查到了数据**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
return obj;
}
long endTime = System.currentTimeMillis();
LOGGER.info("Redis缓存AOP处理所用时间:"+(endTime-startTime));
LOGGER.info("**********没有从Redis查到数据**********");
try{
obj = joinPoint.proceed();
}catch(Throwable e){
e.printStackTrace();
}
LOGGER.info("**********开始从MySQL查询数据**********");
//后置:将数据库查到的数据保存到Redis
String code = redisCache.saveDataToRedis(redisKey,obj);
if(code.equals("OK")){
LOGGER.info("**********数据成功保存到Redis缓存!!!**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
}
return obj;
}
}
然后调用@RedisCache实现缓存
/**
* 通过菜单Id获取菜单信息
* @param id
* @return
*/
@Transactional
@RedisCache
public Menu findMenuById(@RedisCacheKey int id){
return menuRepository.findMenuByMenuId(id);
}
登录系统,然后加入@RedisCache注解的方法都会实现Redis缓存处理
可以看到Redis里保存到了缓存
项目代码:https://github.com/u014427391/jeeplatform,欢迎去github上star(收藏)
SpringBoot集成Redis实现缓存处理(Spring AOP实现)的更多相关文章
- SpringBoot | 集成Redis
Windows下安装: https://github.com/MicrosoftArchive/redis/releases zip下就解包到自定义目录下,msi就跟着步骤安装 进入安装目录下运行命令 ...
- (35)Spring Boot集成Redis实现缓存机制【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] 本文章牵涉到的技术点比较多:Spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对 ...
- Spring Boot从入门到精通(六)集成Redis实现缓存机制
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言 ...
- 【springBoot】springBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- SpringBoot集成redis的key,value序列化的相关问题
使用的是maven工程 springBoot集成redis默认使用的是注解,在官方文档中只需要2步; 1.在pom文件中引入即可 <dependency> <groupId>o ...
- springboot集成redis(mybatis、分布式session)
安装Redis请参考:<CentOS快速安装Redis> 一.springboot集成redis并实现DB与缓存同步 1.添加redis及数据库相关依赖(pom.xml) <depe ...
- Windows环境下springboot集成redis的安装与使用
一,redis安装 首先我们需要下载Windows版本的redis压缩包地址如下: https://github.com/MicrosoftArchive/redis/releases 连接打开后如下 ...
- springBoot集成Redis遇到的坑(择库)源码分析为什么择库失败
提示: springboot提供了一套链接redis的api,也就是个jar包,用到的连接类叫做LettuceConnectionConfiguration,所以我们引入pom时是这样的 <de ...
- springboot集成redis使用redis作为session报错ClassNotFoundException类RememberMeServices
springboot 集成redis使用redis作为缓存,会报错的问题. 错误信息: java.lang.IllegalStateException: Error processing condit ...
随机推荐
- Java 数据结构与算法分析学习
由于之前面试android的时候考到了很多关于java的知识,所以这次重温数据结构知识就打算用java来学习,毕竟android是以java为基础的,而且我现在学习的j2ee架构也是以java为基础的 ...
- 基于netfilter和LVM的密码窃取
一:要求: 编写一个基于netfilter的模块,该模块的功能是捕获如mail.ustc.edu.cn等使用明文传输用户名和密码的网站的用户名和密码:并在接收到特定的ICMP数据包之后将捕获的用户名和 ...
- POJ 3468 A Simple Problem with Integers(树状数组区间更新) 续
这个题刚开始的时候是套模板的,并没有真的理解什么树状数组的区间更新,这几天想了一下,下面是总结: 区间更新这里引进了一个数组delta数组,delta[i]表示区间 [i, n] 的共同增量,每次你需 ...
- c++学习笔记---01---C++语言与OO思想介绍
C++语言与OO思想介绍 C++的特点与OO思想 C语言有一个优点,即它的速度可以很快.写出来的程序可以很精练.简单.小巧,不用为了解决某个问题环绕太平洋一大圈. 但如果将C和C++相比较,C++就经 ...
- Jumpserver部署与安装
jumpserver特点: 完全开源,GPL授权 Python编写,容易再次开发 实现了跳板机基本功能,认证.授权.审计 集成了Ansible,批量命令等 支持WebTerminal Bootstra ...
- C#中split分隔字符串的应用
.用单个字符来分隔:string str="aaajbbbjccc";string[] sArray=str.Split('j');foreach(string i in sArr ...
- [转载] 谷歌技术"三宝"之MapReduce
转载自http://blog.csdn.net/opennaive/article/details/7514146 江湖传说永流传:谷歌技术有"三宝",GFS.MapReduce和 ...
- Python进阶---面向对象第三弹(进阶篇)
Python对象中一些方法 一.__str__ class Teacher: def __init__(self,name,age): self.name=name self.age=age self ...
- 微信小程序开发《三》:微信小程序请求不能使用session的原因及解决办法
本人在前面的微信小程序开发<二>中提到要想在服务端保持状态需要在客户端第一次请求服务器的时候给客户端返回一个sessionid,由客户端在本地保存,下次请求的时候在header里面带上这个 ...
- 一步一步带你实现virtual dom(二) -- Props和事件
很高兴我们可以继续分享编写虚拟DOM的知识.这次我们要讲解的是产品级的内容,其中包括:设置和DOM一致性.以及事件的处理. 使用Babel 在继续之前,我们需要弥补前一篇文章中没有详细讲解的内容.假设 ...