在非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);
}
到这还差最后一步就是给需要加强的方法做注解了
加上如**解之后就会对该方法进行业务增强。
这样就实现了不入侵业务代码了。