本文只适合java菜鸟看,大大请多指教。
关于AOP:
百度百科:面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
关于AOP的几个概念
百度百科:
由于项目中使用到Redis缓存,经常引起一些纠结:在一个接口要获取数据时,先调用缓存,当缓存中不存在数据时,再查询数据库;而在接口要插入数据时,则先插入数据库,插入成功后,再讲数据存入缓存……
package com.tim.cacherAop.annotiation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DaoInsert {
}
//同时还有其他三种注解:DaoQuery,DaoUpdate,DaoDelete只需要改变类名即可,不再全部粘贴
package com.tim.cacherAop.dao; import com.tim.cacherAop.annotiation.DaoDelete; import com.tim.cacherAop.annotiation.DaoInsert; import com.tim.cacherAop.annotiation.DaoQuery; import com.tim.cacherAop.annotiation.DaoUpdate; /** * 创建Dao类 模拟数据库的操作 * @author Tim * */ public class TestDao {//implements DataInterface { public static final int NORMAL_INSERT = 1; public TestDao() { } //模拟插入操作 @DaoInsert public int doInsert(String name){ System.out.println("dao do insert :" + name); return 1; } //模拟更新操作 @DaoUpdate public int doUpdate(String name,String id){ System.out.println("dao do doUpdate name :" + name + " and id :" + id); return 1; } //模拟查询操作 @DaoQuery public String doQuery(String id){ System.out.println("dao do doQuery id :" + id); return id; } //模拟删除操作 @DaoDelete public Object doDelete(String id){ System.out.println("dao do doDelete id :" + id); return 1; } }
其中几个public方法模拟最基本的SQL操作,同时用到第一步定义的@DaoInsert,@DaoQuery,@DaoUpdate,@DaoDelete四个注解,稍后解释。
3)然后创建Cacher类
package com.tim.cacherAop.cacher; /** * cacher类 模拟针对缓存进行操作 适用于Redis等缓存 * @author Tim * */ public class TestCacher {//implements DataInterface { public static final int NORMAL_INSERT = 1; public TestCacher() { // TODO Auto-generated constructor stub } public int doInsert(String name){ System.out.println("cacher do insert :" + name); return 1; } public int doUpdate(String name,String id){ System.out.println("cacher do doUpdate name :" + name + " and id :" + id); return 1; } public String doQuery(String id){ System.out.println("cacher do doQuery id :" + id); return id; } public Object doDelete(String id){ System.out.println("cacher do doDelete id :" + id); return 1; } }
稍仔细观察下即可发现,cacher类和dao类中方法名和方法参数均相同,这样做是为了解耦(更准确的说是方便调用),在第四步即可发现。
4)创建Hanlder类
package com.tim.cacherAop.Hanlder; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import com.tim.cacherAop.annotiation.DaoDelete; import com.tim.cacherAop.annotiation.DaoInsert; import com.tim.cacherAop.annotiation.DaoQuery; import com.tim.cacherAop.annotiation.DaoUpdate; import com.tim.cacherAop.cacher.TestCacher; import com.tim.cacherAop.dao.TestDao; /** * 代理类 继承 net.sf.cglib.proxy.MethodInterceptor * @author Tim * */ public class DaoCglibHanlder implements MethodInterceptor { public DaoCglibHanlder() { // TODO Auto-generated constructor stub } private TestCacher cacher; private TestDao dao; public static Object getInstance(TestCacher cacher, TestDao dao) { DaoCglibHanlder hanlder = new DaoCglibHanlder(); hanlder.cacher = cacher; hanlder.dao = dao; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(dao.getClass()); // 回调方法 enhancer.setCallback(hanlder); // 创建代理对象 return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object result = null; try { //遍历注解 Annotation []annArray = method.getDeclaredAnnotations(); //遍历注解 //doCacherFirst = -1 表示 无注解; //doCacherFirst = 0 表示 查询; //doCacherFirst = 1 表示 增 |改| 删; int doCacherFirst = -1; for (Annotation an : annArray) { Class<?> annClass = an.annotationType(); if(annClass == DaoQuery.class){ doCacherFirst = 0; break; }else if(annClass == DaoInsert.class || annClass == DaoUpdate.class || annClass == DaoDelete.class){ doCacherFirst = 1; } } //获取cacher中对应的方法 String methodName = method.getName(); Class<?> [] paramTypes = method.getParameterTypes(); Method cacheMethod = cacher.getClass().getMethod(methodName, paramTypes); //cacher中有同名同参数的方法 if(cacheMethod != null){ //doCacherFirst == 0 先调用query方法 if(doCacherFirst == 0){ result = cacheMethod.invoke(cacher, args); } //不为null时候 说明cacher中有数据,不用调用dao if(result == null){ method.invoke(dao, args); } //增 |改| 删 在处理完数据库后处理缓存 if(doCacherFirst == 1){ result = cacheMethod.invoke(cacher, args); } }else { result = method.invoke(dao, args); } } catch (Exception e) { result = null; e.printStackTrace(); } return result; } }
该类中有两个变量:testCacher和testDao。只要在getInstance()方法中将cacher对象和dao对象传入即可。
在调用dao的任意方法时,若该方法存在@DaoInsert,@DaoQuery,@DaoUpdate,@DaoDelete四个自定义注解中的任意一个,即会去testCacher对象中寻找同名同参数的方法(也就是说,cacher和dao之间方法的解耦方式,是两方法同名并且同参数。同时dao的方法记得加上注解)。同时根据注解,判断调用cacher的时机(详细见代码中注释)。
5)创建测试类
package com.tim.cacherAop.run; import com.tim.cacherAop.Hanlder.DaoCglibHanlder; import com.tim.cacherAop.cacher.TestCacher; import com.tim.cacherAop.dao.TestDao; public class GOGOGO { public GOGOGO() { // TODO Auto-generated constructor stub } public static void main(String[] args) { TestDao dao = new TestDao(); TestCacher cacher = new TestCacher(); TestDao d = (TestDao) DaoCglibHanlder.getInstance(cacher, dao); //插入操作 System.out.println(d.doInsert("hihi")); System.out.println("/////////////////////////////"); //更新操作 System.out.println(d.doUpdate("双蛋龙", "001")); System.out.println("/////////////////////////////"); //查询操作 System.out.println(d.doQuery("001")); System.out.println("/////////////////////////////"); //删除操作 System.out.println(d.doDelete("001")); System.out.println("/////////////////////////////"); /** 输出结果 dao do insert :hihi cacher do insert :hihi 1 ///////////////////////////// dao do doUpdate name :双蛋龙 and id :001 cacher do doUpdate name :双蛋龙 and id :001 1 ///////////////////////////// cacher do doQuery id :001 001 ///////////////////////////// dao do doDelete id :001 cacher do doDelete id :001 1 ///////////////////////////// */ } }
在测试时,通过DaoCglibHanlder的getInstance()方法,获取dao对象,然后直接调用即可。
遗留的问题:
源码,问题一已解决