最近了解了下注解的实现原理,下面是缓存注解的一个例子。不详细介绍了,贴上代码。
aop依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
spring配置
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.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:property-placeholder location="classpath:/conf.properties"/>
<context:component-scan base-package="com.test"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<import resource="classpath:app-redis.xml"/>
</beans>
redis配置(注:修改下配置参数)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxIdle" value="${redis.maxIdle}" />
</bean>
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="defaultSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
</beans>
缓存注解定义
package com.test;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Cache {
/**
* 缓存时间
* 单位:MINUTES
* @return
*/
public int value() default 5;
}
aop切面
package com.test;
import com.alibaba.dubbo.common.json.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class CacheAdvice {
@Resource
private RedisTemplate<String,String> redisTemplate;
@Around("execution(public * com.test.Hello.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
Object proceed;
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Cache annotation = method.getAnnotation(Cache.class);
if(annotation == null){
return pjp.proceed();
}
Class<?> aClass = AopProxyUtils.ultimateTargetClass(pjp.getThis());
Object[] args = pjp.getArgs();
String value = get(code(aClass.getName(), method.getName(), args));
if(value == null){
proceed = pjp.proceed();
set(code(aClass.getName(), method.getName(), args),JSON.json(proceed),annotation.value());
}else {
proceed = JSON.parse(value,method.getGenericReturnType().getClass());
}
return proceed;
} catch (Throwable throwable) {
throw throwable;
}
}
private String code(String className,String methodName,Object[] args){
StringBuilder builder = new StringBuilder(className+"."+methodName+".");
for (Object a : args){
builder.append(a.hashCode()+".");
}
return builder.toString();
}
private String get(String key){
String value;
try {
value = redisTemplate.opsForValue().get(key);
}catch (Exception e){
return null;
}
return value;
}
private void set(String key,String value, long time){
try {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.MINUTES);
}catch (Exception e){
return;
}
}
}
接口定义
package com.test;
public interface HelloIface {
public String get(String id);
}
接口实现
package com.test;
import org.springframework.stereotype.Service;
@Service
public class Hello implements HelloIface {
@Cache(3)
public String get(String id){
return "hello_world";
}
}
测试用例
import com.test.HelloIface;
import org.junit.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.UUID;
public class Test {
private ApplicationContext context;
private HelloIface iface;
@Before
public void before(){
context = new ClassPathXmlApplicationContext("classpath:app.xml");
iface = context.getBean(HelloIface.class);
}
@org.junit.Test
public void test(){
String id = iface.get("12345");
System.out.println(id);
}
}