AOP+Redis实现缓存

今天早上用AOP+redis实现缓存,记录一下

配置文件:spring配置文件:记得开启注解

<beans xmlns="http://www.springframework.org/schema/beans"
	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"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	
	<!--1.开启包扫描  -->
	<context:component-scan base-package="com.jt"/>

	<!--2.管理数据源,导入properties  -->
	<context:property-placeholder location="classpath:/properties/*.properties"/>
	
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <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>
	
	<!--3.添加事务控制  注解形式 配置简单,使用繁琐  
	xml形式  配置繁琐  使用简单 -->
	
	<!--3.1开启注解标签  -->
	<tx:annotation-driven/>
	
	<!--3.2配置文件形式
		1.定义事务管理器 负责事务开关/配置数据源
		2.AOP 面向切面编程  通知 + 切入点 ~~切面
			1.环绕通知:只有环绕能够控制目标方法执行.
			2.前置通知/后置通知/异常通知/最终通知
			剩余的通知 一般用于记录程序的执行情况.记录日志log
		3.切入点表达式写法:
			within(包名.类名)   粒度:粗的   按类匹配
			execution(返回值类型 包名.类名.方法名(参数列表))
			粒度:细的   按照方法参数匹配.
	  -->
	
	<!--3.2.1定义事务管理器  -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<!--3.2.2定义通知  
		propagation="REQUIRED" 必须添加事务 该配置为默认值
		propagation="SUPPORTS" 表示事务支持,如果线程操作前有事务,则合并为一个事务
							      如果操作前没事务,则以非事务方式运行.
		read-only="true"  表示只读
	-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*"    propagation="REQUIRED"/>
			<tx:method name="update*"  propagation="REQUIRED"/>
			<tx:method name="del*" 	   propagation="REQUIRED"/>
			<tx:method name="find*"    propagation="SUPPORTS" read-only="true"/>
			<tx:method name="*"        propagation="SUPPORTS" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	
	<!--3.2.3形式切面  -->
	<aop:config>
		<aop:pointcut expression="execution(* com.jt.*.service..*.*(..))" id="pc"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
	</aop:config>	
	
	<!-- 开启aop注解 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

Redis配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.0.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd" >


	 <!-- 1.创建一个ObjectMapper对象 -->
	 <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"></bean>
	   <!--2.创建Jedis对象  -->
	 <bean id="jedis" class="redis.clients.jedis.Jedis">
		<constructor-arg name="host" value="${redis.host}"/>
		<constructor-arg name="port" value="${redis.port}"/>
	</bean> 


	 <!-- 实现哨兵的配置 -->
	 <!-- <bean id="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool">
		<constructor-arg name="masterName" value="${redis.masterName}"/>
		<constructor-arg name="sentinels">
			<set>
				<value>${redis.node1}</value>
			</set>
		</constructor-arg>
	</bean> -->
	
	 <!-- 2.redis分片 -->
	<!--  <bean id="shardedJedis" class="redis.clients.jedis.ShardedJedis">
	 	<constructor-arg name="shards">
	 		<list>
	 			<ref bean="shardInfo1"/>
	 			<ref bean="shardInfo2"/>
	 			<ref bean="shardInfo3"/>
	 		</list>
	 	</constructor-arg>
	 </bean>
	 
     <bean id="shardInfo1" class="redis.clients.jedis.JedisShardInfo">
     	<constructor-arg name="host" value="${redis.host}"></constructor-arg>
     	<constructor-arg name="port" value="${redis.port.a}"></constructor-arg>
     </bean> 
     <bean id="shardInfo2" class="redis.clients.jedis.JedisShardInfo">
     	<constructor-arg name="host" value="${redis.host}"></constructor-arg>
     	<constructor-arg name="port" value="${redis.port.b}"></constructor-arg>
     </bean> 
     <bean id="shardInfo3" class="redis.clients.jedis.JedisShardInfo">
     	<constructor-arg name="host" value="${redis.host}"></constructor-arg>
     	<constructor-arg name="port" value="${redis.port.c}"></constructor-arg>
     </bean>  -->
</beans>

redis.properties配置文件:

redis.host=192.168.169.130   对应我搭建redis的虚拟机号
redis.port=6379

具体代码:

1 自定义注解

package com.jt.manage.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 * @author Administrator
 *
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheAnno {

	String key();		//定义key值
	int index();		//定义参数位置  用户传多参时用
	Class targetClass(); //定义目标类型
	CACHE_TYPE cacheType() default CACHE_TYPE.FIND;
	
	/**
	 * 查找和更新缓存的处理方法不一样,更新逻辑为先删除再同步数据到缓存
	 * @author Administrator
	 *
	 */
	enum CACHE_TYPE{
		FIND,		//定义查找
		UPDATE		//定义更新
	}
	
}

2.编写切面:

package com.jt.manage.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.jt.common.util.ObjectMapperUtil;
import com.jt.manage.anno.CacheAnno;
import com.jt.manage.anno.CacheAnno.CACHE_TYPE;

import redis.clients.jedis.Jedis;

/**
 * 定义cache缓存的aop切面  记得开启注解标签
 * @author Administrator
 *
 */
@Component	//交给spring容器管理
@Aspect		//标识切面
public class CacheAspect {

	@Autowired
	private Jedis jedis; //注入redis
	
	//定义只拦截类型为CacheAnno类型的注解
	@Around("@annotation(cacheAnn)")
	public Object around(ProceedingJoinPoint joinPoint,CacheAnno cacheAnn) {
		return getObject(joinPoint,cacheAnn);
	}

	public Object getObject(ProceedingJoinPoint joinPoint, CacheAnno cacheAnn) {
		//获取注解中需要的四个参数
		CACHE_TYPE cacheType = cacheAnn.cacheType();
		String 	key = cacheAnn.key();
		int index = cacheAnn.index();
		Class<?> targetClass = cacheAnn.targetClass();
		
		//根据位置获取目标方法的参数   getArgs()返回Object[]
		Long id =  (Long) joinPoint.getArgs()[index];
		
		//拼接参数 ITEM_CAT_0
		String redisKey = key + id;
		Object object = null;
		switch (cacheType) {
		case FIND:		//表示查询缓存
			object = findObject(joinPoint,redisKey,targetClass);
			break;
		case UPDATE:	//表示更新缓存
			object = updateObject(joinPoint,redisKey);
			break;
		}
		return object;
	}

	private Object findObject(ProceedingJoinPoint joinPoint, String key, Class<?> targetClass) {
		
		//检查缓存中是否有数据
		String result = jedis.get(key);
		Object object = null;
		try {
			if(StringUtils.isEmpty(result)) {
				//表示缓存中没有数据,则查询数据库
				//执行目标方法查询数据库
				object = joinPoint.proceed();
				//将查出的数据转成json串放入缓存(调用工具类)
				String json = ObjectMapperUtil.toJSON(object);
				jedis.set(key, json);
				System.out.println("AOP查询真实数据库!!");
			}else {
				//表示缓存中有数据,查询缓存
				object = ObjectMapperUtil.toObject(result, targetClass);
				System.out.println("AOP查询缓存!!");
			}
		} catch (Throwable e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		return object;
	}

	private Object updateObject(ProceedingJoinPoint joinPoint, String redisKey) {
		//更新缓存,删除即可
		Object object = null;
		try {
			jedis.del(redisKey);
			object = joinPoint.proceed();
			System.out.println("AOP缓存删除");
		} catch (Throwable e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		return object;
	}
}

3.在目标方法上添加注解

@Override
	@CacheAnno(cacheType=CACHE_TYPE.FIND,index=0,key="ITEM_CAT_",targetClass=ArrayList.class)
	public List<EasyUITree> findItemCatList(Long parentId) {
		List<EasyUITree> treeList = new ArrayList<EasyUITree>();
		
		List<ItemCat> itemCatList   = findItenCatByParentId(parentId);
		for (ItemCat itemCat : itemCatList ) {
			EasyUITree easyUITree = new EasyUITree();
			easyUITree.setId(itemCat.getId());
			easyUITree.setText(itemCat.getName());
			//一二级使用closed  三级使用open
	        String state = itemCat.getIsParent()?"closed":"open";
	        easyUITree.setState(state);
	        treeList .add(easyUITree);
		}
		System.out.println("我是目标方法查询数据库");
		return treeList;
	}

测试

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值