因为公司人手原因,最近在为项目搭建架构,在异常,缓存,日志,方面都打算用Aop来做,在原来的项目中对在对异常,日志方面可能都是Log log=Logfactory.getLog();这样既麻烦,又紧耦合在一起。所以打算用Aop试试。下面是对一些缓存对象的Aop处理。主要是根据方法签名来做key值。
定义一个注解
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodCache { int second() default 0; }
定义一个业务处理类
public class Sev { @MethodCache(second=3) public Map getSort(int type,int parentid){ System.out.println("no cache----"); Map m =new HashMap(); return m; } @MethodCache(second=3) public void getSort(){ System.out.println("no cache----"); } }
定义一个Aop
import java.io.Serializable; import java.lang.reflect.Method; import java.util.Date; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class MethodCacheAspectJ { private Cache cache; /** * 设置缓存名 */ public void setCache(Cache cache) { this.cache = cache; } @Pointcut("@annotation(com.zhang.shine.cache.MethodCache)") public void methodCachePointcut(){ } @Around("methodCachePointcut()") public Object methodCacheHold(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("aop start "); String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Object result = null; String cacheKey = getCacheKey(targetName, methodName, arguments); System.out.println("key--"+cacheKey); Element element = cache.get(cacheKey); if (element == null) { try{ result = joinPoint.proceed(); }catch(Exception e){ } if(result!=null){ try{ element = new Element(cacheKey, (Serializable) result); Class targetClass = Class.forName(targetName); Method[] method = targetClass.getMethods(); int second = 0; for(Method m:method){ if (m.getName().equals(methodName)) { Class[] tmpCs = m.getParameterTypes(); if(tmpCs.length==arguments.length){ MethodCache methodCache = m.getAnnotation(MethodCache.class); second = methodCache.second(); break; } } } if(second>0){ // annotation没有设second值则使用ehcache.xml中自定义值 element.setTimeToIdle(second); element.setTimeToLive(second); } cache.put(element); }catch(Exception e){ } } } System.out.println("aop end "); return element.getValue(); } private String getCacheKey(String targetName, String methodName, Object[] arguments) { StringBuffer sb = new StringBuffer(); sb.append(targetName).append(".").append(methodName); if ((arguments != null) && (arguments.length != 0)) { for (int i = 0; i < arguments.length; i++) { if (arguments[i] instanceof Date) { sb.append(".").append( DateUtil.datetoString((Date) arguments[i])); } else { sb.append(".").append(arguments[i]); } } } return sb.toString(); } }
Spring配置文件
<bean id = "methodCacheAspectJ" class="com.zhang.shine.cache.MethodCacheAspectJ" > <property name="cache"> <!-- <ref local="methodCache" /> --> <ref bean="methodCache"/> </property> </bean> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation"> <value>classpath:ehcache.xml</value> </property> </bean> <!-- 定义ehCache的工厂,并设置所使用的Cache name --> <bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager"> <ref local="cacheManager" /> </property> <property name="cacheName"> <value>DEFAULT_CACHE</value> </property> </bean> <bean id="sev" class="com.zhang.shine.cache.Sev"></bean>
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="/home/workspace/gzshine/trunk/ehcache"/> <defaultCache maxElementsInMemory="50000" eternal="false" overflowToDisk="false" timeToIdleSeconds="7200" timeToLiveSeconds="7200" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <cache name="DEFAULT_CACHE" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="true" /> </ehcache>
测试类
package test; import net.sf.cglib.core.DebuggingClassWriter; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zhang.shine.cache.CallImpl; import com.zhang.shine.cache.MyImpl; import com.zhang.shine.cache.Sev; import sun.misc.*; /** * 1.产生代理类$Proxy0类 执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 将产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体; 2. 将代理类$Proxy0类加载到JVM中 这时候是根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中 3. 创建代理类$Proxy0类的对象 调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象 参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数 这个参数就是我们自己实现的InvocationHandler对象,我们知道InvocationHandler对象中组合加入了代理类代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现; 4. 生成代理类的class byte 动态代理生成的都是二进制class字节码 * @author zhang_zengmin * */ public class TestAOP { public static void main(String[] args) throws Exception{ //cglib 代理对象class文件输出目录 如果是jdk动态代理就不输出 /** * cglib头部信息 public class Sev$$EnhancerByCGLIB$$bb4c2585 extends Sev implements SpringProxy, Advised, Factory */ System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "c://class" ); ApplicationContext ap =new ClassPathXmlApplicationContext("ApplicationContent.xml"); Sev s = (Sev)ap.getBean("sev"); //Sev s = new Sev(); s.getSort(1, 2); //Thread.sleep(3010); s.getSort(1, 3); // s.getSort(); System.out.println(s.getClass()); MyImpl my=(MyImpl)ap.getBean("my"); my.pao(); //动态代理获取字节码 头部信息 //public final class $Proxy0 extends Proxy implements Manager { /*byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);*/ /** * 接口默认Jdk代理 */ // ICall call = (ICall)ap.getBean("call"); // call.cll(); // Thread.sleep(60000); /* Enhancer enhancer = new Enhancer();//通过类Enhancer创建代理对象 enhancer.setSuperclass(Sev.class);//传入创建代理对象的类 ClassReader cr = new ClassReader(enhancer.getClass().getName()); byte[] a = cr.b; File f =new File("c://Sev.class"); FileOutputStream fout = new FileOutputStream(f); fout.write(a); fout.flush(); fout.close();*/ } }