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中存在数据,则不需要再获取数据库链接。

    源码,问题一已解决
     
     
     
     
  • 相关阅读:
    BF算法和KMP算法
    Python课程笔记 (五)
    0268. Missing Number (E)
    0009. Palindrome Number (E)
    0008. String to Integer (atoi) (M)
    0213. House Robber II (M)
    0198. House Robber (E)
    0187. Repeated DNA Sequences (M)
    0007. Reverse Integer (E)
    0006. ZigZag Conversion (M)
  • 原文地址:https://www.cnblogs.com/JustAlloc/p/4110789.html
Copyright © 2011-2022 走看看