在非spring環境下實現類似AOP的效果

最近正在做一個薪酬覈算系統,本來已經做好了,但又有需求說XXX,XXX。。。工資項在發錢的時候不能給15號不在公司的人發,當時一想,十幾二十個工資項,我要是每一個都加上這樣一個邏輯那我豈不得累死?
當時的項目架構是一個父類下面有N個子類,N個子類對應着N個工資項,各個子類的實例都是通過工廠模式獲取的。
後來仔細意向,發現這個需求如果用Spring的AOP編程不就可以完美的解決了。我只需要給需要加上“不能給15號不在公司的人發錢”這個邏輯的方法加一個@Aspect註解就可以了,但是可惜了。當前項目並不是一個spring項目,那我應該怎麼辦呢?
這是後我又想到了一種方法,那就是動態代理,其實spring的AOP底層實現也是基於動態代理的,使用動態代理還有一點好處就是不需要引入任何的外部jar包,因爲這是jdk本身就提供的功能。但是卻有一個缺陷就是jdk的動態代理只能基於接口來實現,所以我選擇了cglib ,這是一個可以基於非接口實現類來實現動態代理的jar包,正好符合我的要求。
OK。選好開發模式就要進行開發了,首先我們要創建一個動態代理類來實現cglib的動態代理接口,該接口有一個需要實現的方法
public class CalByDateProxy implements MethodInterceptor { public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy){return null}}
這個方法就是我們的攔截方法了。
然後我們再編寫一個綁定代理的方法,該方法需要實現的功能就是把我們的實際業務對象傳入該方法,然後生成的代理會用這個對象的類型作爲父類並實例化返回該對象。代碼如下:

   //此處會調用有參構造方法
   public Object getInstance(Object target,Class[] args,Object[] params ) {  
       Enhancer enhancer = new Enhancer(); //創建業務加強器,用來創建動態代理類
       enhancer.setSuperclass(target.getClass());  //爲代理類指定一個父類
       //設置回調:對於代理類上所有方法的調用,都會調用CallBack,而Callback則需要實現intercept()方法進行攔
       enhancer.setCallback(this); 
      // 創建動態代理類對象並返回  (使用有參構造方法)
      return enhancer.create(args,params); 
   }

如果Class[] args,Object[] params這兩個參數不傳的話代理對象會默認使用業務對象的無參構造方法
在下面需要實現的方法中我們對doCalculate這個方法進行了攔截,並且獲取了這個方法上面的註解,該註解是一個自定義的註解,拿到這個註解中的參數之後,會執行下面的語句,如果滿足條件則調用業務方法,反之則不調用

    public Object intercept(Object object, Method method, Object[] objects,
                            MethodProxy methodProxy) {
        
        if("doCalculate".equals(methodProxy.getSignature().getName())){  
            try {
                //判斷是否有定義的註解,如果有就判斷是否該發工資
                if(method.isAnnotationPresent(NotCalNotInGS.class)){
                    //得到要調用的方法的註解
                    NotCalNotInGS not = method.getAnnotation(NotCalNotInGS.class);
                    String date = Util.getStartDate().substring(0, 8)+"15";
                    int index = Integer.parseInt(not.paramIndex());
                    //判斷15號當天的所屬公司
                    String comp = DBUtil.getEmployeeComp(date, String.valueOf(objects[index] ) );
                    Util.outPut("有註解,執行代理!   所屬公司===>"+comp);
                    //判斷是否屬於股司
                    if("8000".equals(comp)){ 
                        //調用方法
                     return methodProxy.invokeSuper(object, objects);
                    }
                    return "S";
                    
                }else{
                    return methodProxy.invokeSuper(object, objects);
                }
               
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        try {
            return methodProxy.invokeSuper(object, objects);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

到這裏,代理類就完成了,接下來就是如何增強我們的業務對象了,先前說過,我們的項目採用的是工廠模式獲取的對象,所以我們只需要這工廠中加強一下對象就好,如下,在工廠生產完對象之後我們會進行一個加強裝飾,然後返回給工廠

    /**
     *裝飾對象
     * @param tra
     * @param authId
     * @param authUser
     * @param curThreadCount
     * @param lock
     * @param taskList
     * @param curListIndex
     * @param paramArray
     */
    private static Object doDecorate(SlrItemCalExecutorAbstract target,
                                   String authId, String authUser,
                                   int curThreadCount, Object lock,
                                   List<SlrApiTask> taskList, int curListIndex,
                                   String[] paramArray) {
        //創建代理對象                        
        CalByDateProxy proxy = new CalByDateProxy();
        //定義參數類型
        Class[] args = new Class[]{String.class,String.class,int.class,Object.class,List.class,int.class,String[].class};
        //定義參數
        Object[] params = new Object[]{authId,authUser,curThreadCount,lock,taskList,curListIndex,paramArray};
        //獲取加強後的業務對象
        return proxy.getInstance(target, args, params);
    }

到這還差最後一步就是給需要加強的方法做註解了
在這裏插入圖片描述
加上如下註解之後就會對該方法進行業務增強。
這樣就實現了不入侵業務代碼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章