本文只适合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对象,然后直接调用即可。
遗留的问题:
源码,问题一已解决