浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)
第一章 需求分析
计划在team的开源项目里加入redis实现缓存处理,因为业务功能已经实现了一部分,通过写redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了spring框架的aop(面向切面编程)。
开源项目:
第二章 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编译器):
3.2 redis简介
redis如今已经成为web开发社区最火热的内存数据库之一,随着web2.0的快速发展,再加上半结构数据比重加大,网站对高效性能的需求也越来越多。
而且大型网站一般都有几百台或者更多redis服务器。redis作为一款功能强大的系统,无论是存储、队列还是缓存系统,都有其用武之地。
springboot框架入门的可以参考之前的文章:
第四章 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里保存到了缓存
项目代码:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)
-
浅谈SpringCache与redis集成实现缓存解决方案
-
详解SpringBoot集成Redis来实现缓存技术方案
-
SpringBoot2.0 基础案例(08):集成Redis数据库,实现缓存管理
-
SpringBoot专题学习Part26:Spring Cache整合Redis实现缓存及SpringBoot 2.x新版本自定义CacheManager
-
SpringBoot2.0 基础案例(08):集成Redis数据库,实现缓存管理
-
Spring Boot集成Redis实现缓存机制的实例详解
-
Spring Boot集成Redis实现缓存机制的实例详解