spring5——Aop的实现原理(动态代理)
spring框架的核心之一AOP,面向切面编程是一种编程思想。我对于面向切面编程的理解是:可以让我们动态的控制程序的执行流程及执行结果。spring框架对AOP的实现是为了使业务逻辑之间实现分离,分离主业务逻辑及次要业务逻辑,进而降低系统间的耦合度。
spring框架对于这种编程思想的实现基于两种动态代理模式,分别是 JDK动态代理 及 CGLIB的动态代理,这两种动态代理的区别是 JDK动态代理需要目标对象实现接口,而 CGLIB的动态代理则不需要。下面我们通过一个实例来实现动态代理,进而帮助我们理解面向切面编程。
需求:实现一个除法运算器,要求计算出两个整数相除的商,其中除数为0时提示用户"除数不能为零"并返回-1,被除数为0零时直接返回0。
我们来看一下一般的做法。
public int division(int a,int b){ if(b == 0){ System.out.println("除数不能为0"); return -1; }else if(a == 0){ return 0; }else{ return a/b; } }
我们来分析一下需求,“计算出两个整数相除的商”是我们的主业务逻辑,而“除数为0时提示用户"除数不能为零"并返回-1,被除数为0零时直接返回0”是我们的次要业务逻辑,以上的做法无疑是吧,主业务逻辑和次要业务逻辑偶合到了一块。这里只是做了一个除法,可当你的主业务逻辑本身就就比价复杂的,再这样做的话就会增加代码的偶合度及可读性。
下面我们使用 JDK的 动态代理来实现以上的需求,虽然以下的做法看起来像是杀鸡用牛刀,但我们这里只是为了说明问题。
JDK的动态代理要使用到一个类 Proxy 用于创建动态代理的对象,一个接口 InvocationHandler用于监听代理对象的行为,其实动态代理的本质就是对代理对象行为的监听。
首先我们需要提供一个主业务逻辑的接口,这 JDK动态代理实现的先决条件。
public interface IDivisionService { int division(int a ,int b); }
下面是实现类(实现类中只有主页业务逻辑)
public class DivisionServiceImpl implements IDivisionService { @Override public int division(int a, int b) { return a/b; } }
下面我们来看一下动态代理是怎么分离主业务逻辑和次要业务逻辑的。
首先准备一个创建代理对象的工厂类
public class BeanFactory { public static Object getAgencyObj(final Object target){ Object agencyObj = null; agencyObj = Proxy.newProxyInstance( //获取目标对象类的加载器 target.getClass().getClassLoader(), //获取对象接口的Class对象数组 target.getClass().getInterfaces(), //一个内部类,用于创建监听对象 new InvocationHandler() { /** * 通过反射机制获得实现类中方法的实例 method 及方法的参数 args 这取决于代理对象 agencyObj调用了那个方法。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { int a = (int)args[0]; int b = (int)args[1]; //用于处理次要业务逻辑 if(b == 0){ System.out.println("除数不能为0"); return -1; } //用于处理次要业务逻辑 if(a == 0){ return 0; } //执行主业务逻辑 Object result = method.invoke(target, args); return result; } }); return agencyObj; } }
最后测试类:
public class MyTest { public static void main(String[] args) { //创建目标类 IDivisionService target = new DivisionServiceImpl(); //创建代理对象 IDivisionService agencyObj = (IDivisionService)BeanFactory.getAgencyObj(target); int consult = agencyObj.division(0, 2); System.out.println("两数相除商为--->"+consult); } }
在这里我们看到,除法计算器的实现类DivisionServiceImpl 只是来做除法这一主业务逻辑,而 “除数为0时提示用户"除数不能为零"并返回-1,被除数为0零时直接返回0”这一次要业务逻辑交由JDK的动态代理来实现。
最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。