redis结合ssm框架中的使用,以AOP原理做一个redis缓存管理

本来想模仿声明式事物管理写一个声明式缓存管理出来的,但是最后发现自己对xml文件里面的标签一无所知,没有像事务管理一样提供类似的缓存管理标签,自己也不会写,故退而求其次,再以AOP原理写了一个切面缓存后,对dao层了一次封装,事物层调用的实际是自己封装类,再由自己的封装类去调用dao层的方法,在调用dao层方法前判断redis库里是否存在缓存,若存在,则从缓存里取出数据,若不存在,则调用方法从数据库里取数据;闲话不多说,上代码:

1.一个Redis管理工具类,负责redis缓存的增,删,查操作

package com.ddp.cache;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

public class RedisUtil {
private static RedisTemplate redisTemplate;

public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
@Resource
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 删除key对应的value
* @param keys
*/
public void remove(final String… keys){
for(String key : keys){
if(exists(key)){
redisTemplate.delete(key);
}
}
}
/**
* 判断缓存中是否有对应的value
* @param key
* @return
*/
public static boolean exists(final Object key){
return redisTemplate.hasKey(key);
}
/**
* 获取缓存
* @param key
* @return
*/
public static Object getCache(Object key){
Object result = null;
ValueOperations<Object, Object> operations= redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
* 写入缓存
* @param key
* @param value
* @return
*/
public static boolean setCache(final Object key,Object value){
boolean result = false;
try {
ValueOperations<Object, Object> operations= redisTemplate.opsForValue();
operations.set(key, value);
result = true; 
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* 获取key
* @param keys
* @return
*/
public static String getCacheKey(Object… keys){
String key = “”;
for(Object keystr: keys){
if(keystr instanceof Collection){//为集合情况
Collection c = (Collection) keystr;
for(Object object:c){
key = key + object+”-“;
}
}else if(keystr instanceof Map){//为map情况
Map map = (Map) keystr;
for(Object object : map.entrySet()){
Map.Entry<Object, Object> entry = (Entry<Object, Object>) object;
key = key + entry.getKey()+entry.getValue()+”-“;
}
}else{//一般处理
key = key + keystr+”-“;
}
}
key = key.substring(0, key.length()-1);
return key;
}
/**
* 清除本数据库的所有缓存
* @return
*/
public static String clearAllCache(){
return (String) redisTemplate.execute(new RedisCallback() {
           public String doInRedis(RedisConnection connection) throws DataAccessException {
               connection.flushDb();
               return “OK”;
           }
       });
}

}

2.dao层代码,此处没用映射文件,直接用的注解

package com.ddp.mybatis.dao;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.ddp.mybatis.po.Student;

public interface StudentMybatisDao {

@Insert(value=”insert into student values(#{id},#{userName},#{password})”)
public void saveStudent(Student student);
@Delete(value=”delete from student where id=#{id}”)
public void deleteStudent(Student student);
@Select(value=”select * from student where id=#{id}”)
public Student queryStudent(int id);
@Update(value=”update student set username=#{userName},password=#{password} where id=#{id}”)
public void updateStudent(Student student);
}

3.service层代码

package com.ddp.service.spring;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.ddp.aop.DaoProxyUtils;
import com.ddp.aop.MethodCacheInterceptor;
import com.ddp.cache.RedisUtil;
import com.ddp.mybatis.dao.StudentMybatisDao;
import com.ddp.mybatis.po.Student;
@Service
public class StudentService {

private StudentMybatisDao studentMybatisDao;

public StudentMybatisDao getStudentMybatisDao() {
return studentMybatisDao;
}
@Resource
public void setStudentMybatisDao(StudentMybatisDao studentMybatisDao) {
this.studentMybatisDao = studentMybatisDao;
}

public Student getStudent(int id) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Student student = new Student();
Object []args = {id};
student = (Student) DaoProxyUtils.excute(student, studentMybatisDao, “queryStudent”, args);
return student;
}

public void addStudent(Student student){
studentMybatisDao.saveStudent(student);
}

public void deleteStudent(Student student){
studentMybatisDao.deleteStudent(student);
}

public void modifyStudent(Student student){
studentMybatisDao.updateStudent(student);
}

}

3.上面DaoProxyUtils这个类便是自己写的一个封装类,用于封装dao层的调用方法,此举只是为了满足大多数业务类的需求。因为目前我还没有找到像声明式事物管理这样好的办法,直接在xml文件进行配置便能对业务层进行事物管理,故暂时只能封装dao层,这样不同的业务类也可以调用这个方法,代码如下

package com.ddp.aop;

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

public class DaoProxyUtils {
/**

* @param returnObj dao层方法需要返回的类型
* @param daoObj dao类的一个实例
* @param method 调用dao层的方法
* @param args 调用dao层方法的参数
* @return
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
*/
public static Object excute(Object returnObj,Object daoObj,String method,Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
MethodCacheInterceptor aoptest = new MethodCacheInterceptor();
aoptest.setModelObject(returnObj);
aoptest.setTargetObject(daoObj);
Object daoObjProxy = (Object) Proxy.newProxyInstance(daoObj.getClass().getClassLoader(), daoObj.getClass().getInterfaces(), aoptest);
Method []methods = daoObjProxy.getClass().getDeclaredMethods();
for(Method daoMethod : methods){
if(method.equals(daoMethod.getName())){
returnObj = daoMethod.invoke(daoObjProxy, args);
}
}
return returnObj;
}
}

4.上面就是产生一个dao类的动态代理类调用自己写的一个AOP,,使之在调用dao层的时候处理自己写的业务,当然如果xml文件能像配置声明式事物管理那样配置缓存,那上面的DaoProxyUtils 类就没什么作用了,我们在业务层调用dao层的方法的时候也不用DaoProxyUtils 类去生成dao层的动态代理类了,而是直接用dao层的一个实例,其实声明式事务管理本质就是一个AOP,而AOP的本质便是动态代理,如果我们做了声明式事物管理,外界在调用业务层方法的时候,其实并不是用业务层类的一个实例类去调用的,而是直接生成了一个业务层的一个动态代理类去调用的业务层方法,在调用方法的同时,那它就能做自己的事物管理了。扯了这么多,只是想说其实此处所用的缓存管理原理和

声明式事务管理其实是一个性质的。越扯越远,回归正题,下面就是一个模仿AOP写的一个interceptor,代码 如下

package com.ddp.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.xml.ws.BindingType;

import com.ddp.cache.RedisUtil;

public class MethodCacheInterceptor implements InvocationHandler{

public Object modelObject;//Dao层操作数据方法返回对象
public Object targetObject;//Dao对象
public List<Object> parameters = new ArrayList<Object>();//方法参数类型

public List<Object> getParameters() {
return parameters;
}

public void setParameters(List<Object> parameters) {
this.parameters = parameters;
}

public Object getTargetObject() {
return targetObject;
}

public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}

public Object getModelObject() {
return modelObject;
}

public void setModelObject(Object modelObject) {
this.modelObject = modelObject;
}

/**
* 以类名+方法名+方法参数类型作为Redis缓存key,
* targetObject此处类名为sqlSessionFactory产生的代理对象,spring整合mybatis,业务层所用的dao层对象其实都为代理对象
* proxy:外界方法调用时的代理对象
* method:外界调用的方法
* args:外界调用方法时的参数
*/
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
//组装方法参数类型
for(int i=0;i<args.length;i++){
parameters.add(args[i].getClass());
}
//获取key
String key = RedisUtil.getCacheKey(targetObject.getClass(),method.getName(),parameters);
System.out.println(key);
if(RedisUtil.exists(key)){
modelObject = RedisUtil.getCache(key);
System.out.println(“缓存数据”);
}else{
modelObject = method.invoke(targetObject, args);
RedisUtil.setCache(key, modelObject);
System.out.println(“查询数据”);
}
return modelObject;
}

}

5.当然最重要的是spring结合mybatis的配置文件:

<?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:aop=”http://www.springframework.org/schema/aop”
    xmlns:tx=”http://www.springframework.org/schema/tx”
    xmlns:context=”http://www.springframework.org/schema/context”
    xmlns:p=”http://www.springframework.org/schema/p”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd”>
<context:annotation-config />
<context:component-scan base-package=”com.ddp” />
<bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource”>
<property name=”driverClassName” value=”oracle.jdbc.driver.OracleDriver”/>
   <property name=”url” value=”jdbc:oracle:thin:@localhost:1521:orcl”/>
   <property name=”username” value=”scott”/>
   <property name=”password” value=”Ddp19930801″/>
</bean>
<!– <context:property-placeholder location=”classpath:jdbc.properties”/> –>
<bean id=”sqlSessionFactory” class=”org.mybatis.spring.SqlSessionFactoryBean”>
<property name=”dataSource” ref=”dataSource”></property>
<!– <property name=”typeAliasesPackage” value=”com.ddp.mybatis.dao”></property> –>
</bean>
<bean class=”org.mybatis.spring.mapper.MapperScannerConfigurer”>
<property name=”basePackage” value=”com.ddp.mybatis.dao”></property>
<property name=”sqlSessionFactory” ref=”sqlSessionFactory”></property>
</bean>
<bean id=”transactionManager”
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>
<property name=”dataSource” ref=”dataSource” />
</bean>
<!–事物管理的AOP配置  –>
<aop:config>
<aop:pointcut id=”studentServiceAop”
expression=”execution(public * com.ddp.service.spring..*.*(..))” />
<aop:advisor pointcut-ref=”studentServiceAop”
advice-ref=”txAdvice” />
</aop:config>

<tx:advice id=”txAdvice” transaction-manager=”transactionManager”>
<tx:attributes>
<tx:method name=”get*” read-only=”true” />
</tx:attributes>
</tx:advice>

<context:property-placeholder location=”classpath:redis.properties” ignore-unresolvable=”true”/>
<!–redis配置  –>
<bean id=”poolConfig” class=”redis.clients.jedis.JedisPoolConfig”>
<property name=”maxIdle” value=”${redis.maxIdle}” />  <!–最大空闲时间  –>
        <property name=”maxTotal” value=”${redis.maxActive}” />  <!–连接池的最大数据库连接数  –>
        <property name=”maxWaitMillis” value=”${redis.maxWait}” />  <!–最大建立连接等待时间。如果超过此时间将接到异常  –>
        <property name=”testOnBorrow” value=”${redis.testOnBorrow}” /><!–是否检测池里连接的可用性  –>
</bean>
<bean id=”connectionFactory” class=”org.springframework.data.redis.connection.jedis.JedisConnectionFactory”
 p:host-name=”${redis.host}” p:port=”${redis.port}”  p:pool-config-ref=”poolConfig”>
</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=”hashKeySerializer”>  
       <bean class=”org.springframework.data.redis.serializer.StringRedisSerializer” />  
   </property>    
</bean>
<!–cache配置  –>
<!–用来缓存和去除数据  –>
<bean id=”redisUtil” class=”com.ddp.cache.RedisUtil”>
<property name=”redisTemplate” ref=”redisTemplate”></property>
</bean>
<!–一个切面,在查询数据时用于判断是否存在缓存,若存在,则从缓存里取数据;若不存在,则从数据库里取出数据并将数据放入缓存  –>
<bean id=”methodCacheInterceptor” class=”com.ddp.aop.MethodCacheInterceptor”>
<!– <property name=”redisUtil” ref=”redisUtil”></property> –>
</bean>
</beans>

6.不要问我上面jdbc的配置为啥不放在jdbc.properties里面,试过很多次了,要报错,据说是因为MapperScannerConfigurer的原因,不废话,redis.properties配置如下:

redis.key.prefix=500

redis.host=localhost
redis.port=6379
redis.maxIdle=300
redis.maxActive=600 
redis.maxWait=1000
redis.testOnBorrow=true
redis.timeout=1000

做完上面的步骤就该没什么了,测试的话,可自己写一个测试类,如下:

package com.ddp.service.spring;

import static org.junit.Assert.*;

import java.lang.reflect.InvocationTargetException;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.ddp.cache.RedisUtil;
import com.ddp.mybatis.po.Student;

public class StudentServiceTest {
public static ApplicationContext ct;
public static StudentService studentService;
static{
ct = new ClassPathXmlApplicationContext(“spring-mybatis.xml”);
studentService = (StudentService) ct.getBean(“studentService”);
}
@Test
public void testGetStudent() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//RedisUtil.clearAllCache();
Student student = studentService.getStudent(1);
System.out.println(“username=”+student.getUserName());
System.out.println(“password=”+student.getPassword());
}

}

至此,自己模仿声明式事物管理做的一个声明式缓存管理就差不多,要是本人会写xml的标签的话,那就更完美了,那时便和声明式事务管理的配置没什么差别了

    原文作者:AOP
    原文地址: https://blog.csdn.net/DDPWY/article/details/73350337
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞