Spring AOP基础

来源:互联网 发布:阿里国际站数据分析 编辑:程序博客网 时间:2024/06/04 01:04

【一】为什么需要 AOP?

            <<interface>>              Calculator· add(in a:double,in b:double):double· sub(in a:double,in b:double):double· mul(in a:double,in b:double):double· div(in a:double,in b:double):double            CalculatorImpl· add(in a:double,in b:double):double· sub(in a:double,in b:double):double· mul(in a:double,in b:double):double· div(in a:double,in b:double):double

需求1-日志:在程序执行期间追踪正在发生的活动
需求2-验证:希望计算器只能处理正数的预算

代码:

public interface Calculator {    double add(double a,double b);    double sub(double a,double b);    double mul(double a,double b);    double div(double a,double b);}public class CalculatorLoggingImpl implements Calculator {    @Override    public double add(double a, double b) {        System.out.println("The Method add begins with[" + a + "," + b + "]");        double result = a + b;        System.out.println("The Method add End with " + result);        return result;    }    @Override    public double sub(double a, double b) {        System.out.println("The Method sub begins with[" + a + "," + b + "]");        double result = a - b;        System.out.println("The Method sub End with " + result);        return result;    }    @Override    public double mul(double a, double b) {        System.out.println("The Method mul begins with[" + a + "," + b + "]");        double result = a * b;        System.out.println("The Method mul End with " + result);        return result;    }    @Override    public double div(double a, double b) {        System.out.println("The Method div begins with[" + a + "," + b + "]");        double result = a / b;        System.out.println("The Method div End with " + result);        return result;    }}public class Main {    public static void main(String[] args) {        Calculator calculator = new CalculatorImpl();        calculator.add(2, 2);        calculator.div(2, 2);        calculator.mul(2, 2);        calculator.sub(2, 2);    }}

结果:

The Method add begins with[2.0,2.0]The Method add End with 4.0The Method div begins with[2.0,2.0]The Method div End with 1.0The Method mul begins with[2.0,2.0]The Method mul End with 4.0The Method sub begins with[2.0,2.0]The Method sub End with 0.0

可见,普通方法实现有大量冗余代码,代码分散,不利于维护,且巨麻烦。

改变思路,通过动态代理解决以上问题。
原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.
这里写图片描述

代码:

public interface Calculator {    double add(double a,double b);    double sub(double a,double b);    double mul(double a,double b);    double div(double a,double b);}public class CalculatorImpl implements Calculator {    @Override    public double add(double a, double b) {        double result = a + b;        return result;    }    @Override    public double sub(double a, double b) {        double result = a - b;        return result;    }    @Override    public double mul(double a, double b) {        double result = a * b;        return result;    }    @Override    public double div(double a, double b) {        double result = a / b;        return result;    }}public class CalculatorLoggingProxy {    //被代理对象    private Calculator target;    public CalculatorLoggingProxy(Calculator calculator) {        this.target = calculator;    }    //返回日志代理    public Calculator getLoggingProxy() {        Calculator proxy = null;        //代理对象由哪一个类加载器负责加载        ClassLoader loader = target.getClass().getClassLoader();        //代理对象的类型, 即其中有哪些方法        Class[] interfaces = new Class[]{Calculator.class};        //当调用代理对象其中的方法时, 该执行的代码        InvocationHandler h = new InvocationHandler() {            /**             * proxy: 正在返回的代理对象. 一般情况下, 在 invoke 方法中不使用该对象.             * method: 正在被调用的方法.             * args: 调用方法时, 传入的参数             */            @Override            public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {                String methodName = method.getName();                //日志                System.out.println("--> spring The Method " + methodName + " begins with" + Arrays.asList(args));                //执行方法                Object result = method.invoke(target, args);                System.out.println("--> spring The Method " + methodName + "end with " + result);                return result;            }        };        proxy =  (Calculator) Proxy.newProxyInstance(loader, interfaces, h);        return proxy;    }}    public static void main(String[] args) {        /*Calculator calculator = new CalculatorLoggingImpl();*/        Calculator target = new CalculatorImpl();        Calculator proxy = new CalculatorLoggingProxy(target).getLoggingProxy();        double add = proxy.add(2, 2);        System.out.println("--> " + add);        double div = proxy.div(2, 2);        System.out.println("--> " + div);        double mul = proxy.mul(2, 2);        System.out.println("--> " + mul);        double sub = proxy.sub(2, 2);        System.out.println("--> " + sub);    }}

测试结果:

--> spring The Method add begins with[2.0, 2.0]--> spring The Method addend with 4.0--> 4.0--> spring The Method div begins with[2.0, 2.0]--> spring The Method divend with 1.0--> 1.0--> spring The Method mul begins with[2.0, 2.0]--> spring The Method mulend with 4.0--> 4.0--> spring The Method sub begins with[2.0, 2.0]--> spring The Method subend with 0.0--> 0.0

使用动态代理就解决了前面代码的不可行性,但是开发过程中还是不建议使用,原因如下:
一、实现过程还是比较麻烦的
二、一般程序员使用动态代理可能有问题

所以再考虑还有没有更简单的方式解决需求? 现在就该用到 AOP 了。

AOP 简介:
【1】 AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
【2】 AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
【3】 在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
【4】 AOP 的好处:
- 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
- 业务模块更简洁, 只包含核心业务代码.

现在再用图来区别 第一次 需求实现和 AOP 实现的不同之处
这里写图片描述

关于 AOP 的术语(生涩, 自行理解)

切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象通知(Advice):  切面必须要完成的工作目标(Target): 被通知的对象代理(Proxy): 向目标对象应用通知之后创建的对象连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 Calculator#add() 方法执行前的连接点,执行点为 Calculator#add(); 方位为该方法执行前的位置切点(pointcut):每个类都拥有多个连接点:例如 Calculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
原创粉丝点击