zoukankan      html  css  js  c++  java
  • 利用cglib实现aop,简化缓存的使用

    本文只适合java菜鸟看,大大请多指教。

    关于AOP:

    百度百科:面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

     

    关于AOP的几个概念

    百度百科:

    Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
    Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
    Pointcut:表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
    Advice:Advice 定义了在 pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
     
     
    常用的AOP:
    1)利用java动态代理,实现AOP
      优点:比较底层,灵活方便
      缺点:被代理的方法要实现接口,比较麻烦。
    2)Spring自带AOP功能
      优点:
      缺点:
    3)使用cglib
      优点:封装好,无需使用接口即可代理所有方法,易上手。
      缺点:
     
     
    为何使用cglib实现AOP:

    由于项目中使用到Redis缓存,经常引起一些纠结:在一个接口要获取数据时,先调用缓存,当缓存中不存在数据时,再查询数据库;而在接口要插入数据时,则先插入数据库,插入成功后,再讲数据存入缓存……

    因此思考后,决定使用AOP解决该问题,使得代码稍优美些。
     
    1)自定义注解:
    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只需要改变类名即可,不再全部粘贴
    

      

     
    2)先创建dao类:
    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对象,然后直接调用即可。  

    遗留的问题:

    1) 在Main方法的调用处,dao和cacher依然存在需要解耦的问题。(感觉可以使用配置文件)
    2)是否考虑将dao的实例化步骤放入DaoCglibHanlder类处,对应的getInstance()方法只传Class对象或者类名。因为dao对象实例化的时候需传入Connection对象,若执行query时,在cacher中存在数据,则不需要再获取数据库链接。

    源码,问题一已解决
     
     
     
     
  • 相关阅读:
    用nodejs删除mongodb中ObjectId类型数据
    关于easyui模拟win2012桌面的一个例子系列
    div里常用的class命名
    XMLHttpRequest对象中readyState与status的几种常见状态
    我们经常注册用的页面是怎么实现的
    html与xhtml区别
    mysql重置密码
    服务器80端口映射到8080端口
    服务器端增加tomcat使用内存
    更新服务器ssh登录端口
  • 原文地址:https://www.cnblogs.com/JustAlloc/p/4110789.html
Copyright © 2011-2022 走看看