在非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);
    }

到这还差最后一步就是给需要加强的方法做注解了
在非spring环境下实现类似AOP的效果
加上如**解之后就会对该方法进行业务增强。
这样就实现了不入侵业务代码了。