采用redis作为独立缓存服务器(三)_spring Cache后传之aop优化

请先阅读上一篇!!!

aop的知识请参考我的博文【http://blog.csdn.net/wx5040257/article/details/78878645】

上一篇提到,spring cache要精确控制缓存,比方说删除一个用户数据不至于清空所有的用户缓存,可以办到,但是代码不优雅,需要项目组程序员要有较高的素养,

那么切面编程(aop)就可以解决这一困境!!!

废话不多少了,上代码!

实体类

====================UserEntity===========================

package com.wx.entitys;

import java.io.Serializable;

public class UserEntity implements Serializable {
	private static final long serialVersionUID = 1034088741557780953L;
	private Integer userId;
	private String userName;
	private String passWord;
	private String email;
	
	
	public UserEntity() {
	}
	public UserEntity(String userName, String passWord) {
		super();
		this.userName = userName;
		this.passWord = passWord;
	}
	public Integer getUserId() {
		return userId;
	}
	public void setUserId(Integer userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassWord() {
		return passWord;
	}
	public void setPassWord(String passWord) {
		this.passWord = passWord;
	}
	
	public void setEmail(String email) {
		this.email = email;
	}
	
	public String getEmail() {
		return email;
	}
	
	//给对象打个标志,方便比对删除等操作
	public String toString() {
		return this.userId+"@"+this.userName;
	}
}

工具类

package com.wx.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
//存取对象工具类
public class CacheSeriUtil {
	public static byte[] toByteArray(Object obj) {
		byte[] bytes = null;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(obj);
			oos.flush();
			bytes = bos.toByteArray();
			oos.close();
			bos.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return bytes;
	}

	public static Object toObject(byte[] bytes) {
		Object obj = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			obj = ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		return obj;
	}
}

spring cache实现类

========================SpringRedisCache=========================

package com.wx.utils;


import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

public class SpringRedisCache implements Cache {

	private JedisConnectionFactory jedisConnectionFactory;
	private String name;

	
	public void setJedisConnectionFactory(
			JedisConnectionFactory jedisConnectionFactory) {
		this.jedisConnectionFactory = jedisConnectionFactory;
	}
	
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public Object getNativeCache() {
		return null;
	}

	@Override
	public ValueWrapper get(Object key) {
		System.out.println("get key from cache************:"+key.toString());
		final String keyf = key.toString();
		RedisConnection conn=null;
		Object object = null;
		try {
			conn=jedisConnectionFactory.getConnection();
			byte[] bkey = keyf.getBytes();
			byte[] value = conn.get(bkey);
			if (value == null) {
				System.out.println("缓存中木有,要查数据库了");
				object= null;
			}else{
				object=CacheSeriUtil.toObject(value);
				System.out.println("恭喜,从缓存中找到了:"+object.toString());
			}
		} catch (Exception e) {
			object=null;
			e.printStackTrace();
		}finally{
			if(conn!=null){
				conn.close();
			}
		}
		return (object!=null) ? new SimpleValueWrapper(object) : null;	
	}

	/**
	 * 添加缓存的操作
	 */
	public void put(Object key, Object value) {
		String srcKey = key.toString();
		final long liveTime = 86400;    //默认缓存一天
		RedisConnection conn=null;
		try {
			conn=jedisConnectionFactory.getConnection();
			byte[] keyb = srcKey.getBytes();
			byte[] valueb = CacheSeriUtil.toByteArray(value);
			conn.set(keyb, valueb);
			if (liveTime > 0) {
				conn.expire(keyb, liveTime);
			}
			System.out.println("***键:"+srcKey+"*********添加进了缓存");
		} catch (Exception e1) {
			e1.printStackTrace();
		}finally{
			if(conn!=null){
				conn.close();
			}
		}
		
	}
	

	/**
	 *由切面编程完成
	 */
	public void evict(Object key) {
		
	}
	
	@Override
	public void clear() {
		System.out.println("clear key");
		RedisConnection conn=null;
		try {
			conn=jedisConnectionFactory.getConnection();
			conn.flushDb();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(conn!=null){
				conn.close();
			}
		}
	}
}

redis配置文件

===================redis.properties==============================

#*****************jedis连接参数设置*********************
#redis服务器ip
redis.ip=169.254.130.122
#redis服务器端口号
redis.port=6379
#redis访问密码
redis.passWord=test123
#与服务器建立连接的超时时间
redis.timeout=3000
#************************jedis池参数设置*******************
#jedis的最大活跃连接数
jedis.pool.maxActive=50
#jedis最大空闲连接数
jedis.pool.maxIdle=10
#jedis最小空闲连接数
jedis.pool.minIdle=5
#jedis池没有连接对象返回时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。
#如果超过等待时间,则直接抛出JedisConnectionException
jedis.pool.maxWait=1500
#从池中获取连接的时候,是否进行有效检查
jedis.pool.testOnBorrow=true
#归还连接的时候,是否进行有效检查
jedis.pool.testOnReturn=false

spring核心配置文件

=================================applicationContext.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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	 http://www.springframework.org/schema/beans/spring-beans-3.0.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.0.xsd
	 http://www.springframework.org/schema/context
	 http://www.springframework.org/schema/context/spring-context-3.0.xsd
	 http://www.springframework.org/schema/cache
     http://www.springframework.org/schema/cache/spring-cache.xsd">
	<!-- 扫描包含注解类所在的包 -->
	<context:component-scan base-package="com.wx.dao.*"></context:component-scan>
	<context:component-scan base-package="com.wx.biz"></context:component-scan>
	
	<bean id="config" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:configs/druidConfig.properties</value>
				<value>classpath:configs/redis.properties</value>
			</list>
		</property>
	</bean>
	<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${driverClassName}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
		<property name="filters" value="${filters}" />
		<property name="initialSize" value="${initialSize}" />
		<property name="maxActive" value="${maxActive}" />
		<property name="minIdle" value="${minIdle}" />
		<property name="maxWait" value="${maxWait}" />
		<property name="validationQuery" value="${validationQuery}" />
		<property name="testWhileIdle" value="${testWhileIdle}" />
		<property name="testOnBorrow" value="${testOnBorrow}" />
		<property name="testOnReturn" value="${testOnReturn}" />
		<property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}" />
		<property name="removeAbandoned" value="${removeAbandoned}" />
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
		<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
		<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
	</bean>
	<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="druidDataSource"></property>
		<property name="configLocation">
			<value>classpath:configs/mybatis-config.xml</value>
		</property>
		<property name="mapperLocations">
			<list>
				<value>classpath:configs/mappers/*.xml</value>
			</list>
		</property>
	</bean>
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" ref="sessionFactory"></constructor-arg>
	</bean>
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="druidDataSource"></property>
	</bean>
	<tx:advice id="txAdvise" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="search*" read-only="true"/>
			<tx:method name="query*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="add*" propagation="REQUIRED"/>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="do*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="del*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut expression="execution(* com.wx.biz.*.*(..))" id="myCut"/>
		<aop:advisor advice-ref="txAdvise" pointcut-ref="myCut"/>
	</aop:config>
	
	<!-- 配置redis 单机版 -->
    <!-- redis数据源 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
        <!-- 最小空闲数 -->
        <property name="minIdle" value="${jedis.pool.minIdle}"></property>
        <!-- 最大空闲数 -->
        <property name="maxIdle" value="${jedis.pool.maxIdle}" />  
        <!-- 最大活跃数 -->
        <property name="maxTotal" value="${jedis.pool.maxActive}" />  
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="${jedis.pool.maxWait}" />  
        <!-- 返回连接时,检测连接是否成功 -->
        <property name="testOnBorrow" value="${jedis.pool.testOnBorrow}" />  
    </bean>
    
    <!-- Spring-redis连接池管理工厂 -->
    <bean id="jedisConnectionFactory" scope="singleton" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <!-- IP地址 -->
        <property name="hostName" value="${redis.ip}" />
        <!-- 端口号 -->
        <property name="port" value="${redis.port}" />
        <!-- 密码 -->
        <property name="password" value="${redis.passWord}"/>
        <!-- 超时时间 -->
        <property name="timeout" value="${redis.timeout}" />
        <property name="poolConfig" ref="poolConfig" />
    </bean> 
    <!-- redis缓存配置结束 -->
    
    <!-- spring自己的缓存管理器,这里定义了一个缓存位置名称 ,既注解中的value --> 
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="com.wx.utils.SpringRedisCache">
                    <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
                    <property name="name" value="redis_cache"/>
                </bean>
            </set>
        </property>
    </bean>
    <!-- 开启Spring缓存 -->
    <cache:annotation-driven cache-manager="cacheManager"/>
    
    <!-- 采用切面编程的方式对缓存精确控制 -->
    <!-- 启用对于@AspectJ注解的支持,表明是增强处理的代码 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	<bean id="cacheAdvise" class="com.wx.cache.aops.FlushCacheAdvise">
		<property name="jedisConnectionFactory" ref="jedisConnectionFactory"></property>
	</bean>
</beans>

统一处理缓存的aop类

====================FlushCacheAdvise.java=================

package com.wx.cache.aops;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

import com.wx.utils.CacheSeriUtil;

@Aspect
public class FlushCacheAdvise {

	private JedisConnectionFactory jedisConnectionFactory;

	public void setJedisConnectionFactory(
			JedisConnectionFactory jedisConnectionFactory) {
		this.jedisConnectionFactory = jedisConnectionFactory;
	}

	/**
	 * 删除操作,如果只是根据id删除一个对象,则把其它缓存里面的该对象也删除
	 * 如果是删除很多对象(例如是根据性别删),则把该类下所有缓存全部清除
	 * @param key 形式"[className].[id]"
	 */
	@AfterReturning(pointcut = "execution(* com.wx.biz.*.del*(..))", 
			    returning = "returnValue")
	public void afterDelete(JoinPoint jp, Object returnValue) throws Throwable {
		String keyPrefix = jp.getTarget().getClass().getName();
		String methName = jp.getSignature().getName();
		final long liveTime = 86400; // 默认缓存一天
		RedisConnection conn = null;
		String toDel = "";
		try {
			conn = jedisConnectionFactory.getConnection();
			// 如果只是删除了一个对象或者更新了一个对象,则删除所有list缓存中的该对象
			if (methName.equals("deleteById")) {
				toDel = keyPrefix + "." + jp.getArgs()[0];
				conn.del(toDel.getBytes());
				System.out.println("*****aop****del_key:" + toDel+ "==============");
				// 其它list缓存中删除该对象
				toDel = keyPrefix + ".list.*";
				Set<byte[]> keys = conn.keys(toDel.getBytes());
				Iterator<byte[]> it = keys.iterator();
				while (it.hasNext()) {
					byte[] onelistKey = it.next();
					byte[] oneListValue = conn.get(onelistKey);
					List<Object> theList = (List<Object>) CacheSeriUtil.toObject(oneListValue);
					for (Object theObj : theList) {
						if (theObj.toString().startsWith(jp.getArgs()[0] + "@")) {
							// 找到了,list里面的对象被去掉了
							theList.remove(theObj);
							break;
						}
					}
					// 重新放回缓存中
					oneListValue = CacheSeriUtil.toByteArray(theList);
					conn.set(onelistKey, oneListValue);
					if (liveTime > 0) {
						conn.expire(onelistKey, liveTime);
					}
					System.out.println("**aop**del**" + new String(onelistKey)+ "****list缓存中的数据更新了*********");
				}
			} else { // 如果删除了很多对象,则目标类下所有的缓存全部删除
				toDel = keyPrefix + "*";
				Set<byte[]> keys = conn.keys(toDel.getBytes());
				Iterator<byte[]> it = keys.iterator();
				while (it.hasNext()) {
					byte[] onekey = it.next();
					conn.del(onekey);
					System.out.println("**aop**del key:" + new String(onekey));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
	}

	/**
	 * 添加操作,如果是save方法,则添加该对象的缓存的同时,把它添加到其它list缓存中去
	 * @param key 形式"[className].[id]"
	 */
	@AfterReturning(pointcut = "execution(* com.wx.biz.*.save*(..))||execution(* com.wx.biz.*.add*(..))",
			    returning = "returnValue")
	public void afterAdd(JoinPoint jp, Object returnValue) throws Throwable {
		String targetName = jp.getTarget().getClass().getName();
		final long liveTime = 86400; // 默认缓存一天
		RedisConnection conn = null;
		try {
			conn = jedisConnectionFactory.getConnection();
			String objStr = returnValue.toString();
			String theId = objStr.substring(0, objStr.indexOf('@'));// table.primarykey的形式
			String keyf = targetName + "." + theId;
			System.out.println("****aop add key:" + keyf + "===value:" + objStr);
			byte[] keyb = keyf.getBytes();
			byte[] valueb = CacheSeriUtil.toByteArray(returnValue);
			conn.set(keyb, valueb);
			if (liveTime > 0) {
				conn.expire(keyb, liveTime);
			}
			// 往其它list缓存里添加数据
			String toAdd = targetName + ".list.*";
			Set<byte[]> keys = conn.keys(toAdd.getBytes());
			Iterator<byte[]> it = keys.iterator();
			while (it.hasNext()) {
				byte[] onelistKey = it.next();
				byte[] oneListValue = conn.get(onelistKey);
				List<Object> theList = (List<Object>)CacheSeriUtil.toObject(oneListValue);
				theList.add(returnValue);
				// 重新放回缓存中
				oneListValue = CacheSeriUtil.toByteArray(theList);
				conn.set(onelistKey, oneListValue);
				if (liveTime > 0) {
					conn.expire(onelistKey, liveTime);
				}
				System.out.println("***aop add后***" + new String(onelistKey)
						+ "****list缓存中的数据更新了*********");
			}

		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
	}
	
	/**
	 * 更新操作,先更新一个对象的缓存,再更新其它list中的缓存
	 * @param key 形式"[className].[id]"
	 */
	@AfterReturning(pointcut = "execution(* com.wx.biz.*.modify*(..))||execution(* com.wx.biz.*.update*(..))",
			   returning = "returnValue")
	public void afterModify(JoinPoint jp, Object returnValue) throws Throwable {
		String targetName = jp.getTarget().getClass().getName();
		String paramStr=jp.getArgs()[0].toString();
		final long liveTime = 86400; // 默认缓存一天
		RedisConnection conn = null;
		try {
			conn = jedisConnectionFactory.getConnection();
			//先更新缓存中的对应的key值
			String optKey = targetName + "." + paramStr.substring(0, paramStr.indexOf('@'));
			byte[] keyb = optKey.getBytes();
			byte[] valueb = CacheSeriUtil.toByteArray(returnValue);
			conn.set(keyb, valueb);
			if (liveTime > 0) {
				conn.expire(keyb, liveTime);
			}
			System.out.println("*****aop_update****update_key:" + optKey+ "==============");
			
			// 再更新其它list缓存中的数据
			String toUpdate = targetName + ".list.*";
			Set<byte[]> keys = conn.keys(toUpdate.getBytes());
			Iterator<byte[]> it = keys.iterator();
			while (it.hasNext()) {
				byte[] onelistKey = it.next();
				byte[] oneListValue = conn.get(onelistKey);
				List<Object> theList = (List<Object>) CacheSeriUtil.toObject(oneListValue);
				for (Object theObj : theList) {
					if (theObj.toString().startsWith(paramStr.substring(0, paramStr.indexOf('@')+1))) {
						// 找到了,list里面先清除旧数据,再添加新的对象
						theList.remove(theObj);
						theList.add(returnValue);
						break;
					}
				}
				// 重新放回缓存中
				oneListValue = CacheSeriUtil.toByteArray(theList);
				conn.set(onelistKey, oneListValue);
				if (liveTime > 0) {
					conn.expire(onelistKey, liveTime);
				}
				System.out.println("**aop**update**" + new String(onelistKey)
						+ "****list缓存中的数据更新了*********");
			}

		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
	}
}

应用缓存的类

===================UserBiz========================

package com.wx.biz;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Service;

import com.wx.dao.user.IUserDao;
import com.wx.entitys.UserEntity;

@Service("userBiz")
@EnableCaching
public class UserBiz {
	@Autowired
	private IUserDao userDao;
	
	public boolean isLogin(UserEntity user){
		return userDao.isLogin(user);
	}
	
	
	public UserEntity save(UserEntity user){
		return userDao.add(user);
	}
	
	@Cacheable(value="redis_cache",key="#root.targetClass.getName()+'.'+#userId")
	public UserEntity getById(String userId){
		return userDao.getById(userId);
	}
	
	@Cacheable(value="redis_cache",key="#root.targetClass.getName()+'.'+#userName")
	public boolean isUserExist(String userName){
		return userDao.isUserExist(userName);
	}
	
	@Cacheable(value="redis_cache", key="#root.targetClass.getName()+'.list.'+#root.methodName")
	public List<UserEntity> queryMany(UserEntity user){
		return userDao.queryMany(user);
	}
	
	public Integer deleteById(String id) {
		return userDao.deleteById(id);
	}
	
	public UserEntity modify(UserEntity user) {
		return userDao.modify(user);
	}
}
现在来看运行效果

第一进管理界面

先从缓存里找,没找到,从数据库查,然后放入缓存

然后多次刷新当前页码,都是从缓存取数据

再来看添加

添加进数据库的同时更新键【com.wx.biz.UserBiz.list.queryMany】,而不是清空缓存

接下来看修改

修改数据库里面的数据的同时,删除键【com.wx.biz.UserBiz.list.queryMany】中对应的旧数据,再放入修改后的新数据

最后看删除

删除数据库数据的同时,删除键【com.wx.biz.UserBiz.list.queryMany】中的数据

读者们,aop方式是不是很强大,其实你也可以完全不用spring cache,全部改为aop方式!!!!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值