专栏首页海向Spring 详解(一)------- AOP前序

Spring 详解(一)------- AOP前序

目录


1. AOP 简介

​ AOP(Aspect Oriented Programming),通常称为面向切面编程。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

2. 示例需求

想要为写好的 ArithmeticCalculator 添加日志 ,即每次运算前后添加 采用以下方法太过繁琐,修改内容需要每个跟着都修改,可维护性差

public interface ArithmeticCalculator {
    int add(int i, int j);
    int sub(int i, int j);

    int mul(int i, int j);
    int div(int i, int j);
}
public class MyArithmeticCalculatorImp implements ArithmeticCalculator {
    public int add(int i, int j) {
        System.out.println("The method add begins with["+i+","+j+"]");
        int result = i + j;
        System.out.println("The method add ends with["+result+"]");
        return result;

    }

    public int sub(int i, int j) {
        System.out.println("The method sub begins with["+i+","+j+"]");
        int result = i - j;
        System.out.println("The method sub ends with["+result+"]");
        return result;
    }

    public int mul(int i, int j) {
        System.out.println("The method mul begins with["+i+","+j+"]");
        int result = i * j;
        System.out.println("The method mul ends with["+result+"]");
        return result;
    }

    public int div(int i, int j) {
        System.out.println("The method div begins with["+i+","+j+"]");
        int result = i / j;
        System.out.println("The method div ends with["+result+"]");
        return result;
    }
}

结果

The method add begins with[1,2]
The method add ends with[3]
-->3
The method mul begins with[5,2]
The method mul ends with[10]
-->10

问题: 代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀,每个方法在处理核心逻辑的同时还必须兼顾替他多个关注点。

代码分散:以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码,如果日志需求发生变化,必须修改所有模块。

3. 解决方法一:使用静态代理

创建干净的实现类

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
    @Override
    public int add(int i, int j) {
        return i + j;
    }
    @Override
    public int sub(int i, int j) {
        return i - j;
    }
    @Override
    public int mul(int i, int j) {
        return i * j;
    }
    @Override
    public int div(int i, int j) {
        return i / j;
    }
}

创建日志类 MyLogger

/**
 * 创建日志类
 */
public class MyLogger {
    /**
     * 入参日志
     * @param a
     * @param b
     */
    public void showParam(int a, int b) {
        System.out.println("The method add begins with["+a+","+b+"]");
    }

    /**
     * 运算结果日志
     * @param result
     */
    public void showResult(int result) {
        System.out.println("The method add ends with["+3+"]");
    }
}

创建静态代理类

/**
 * 代理类
 */
public class ProxyLogger implements ArithmeticCalculator {

    //目标类
    private ArithmeticCalculator target;

    //日志类
    private MyLogger logger;

    public ProxyLogger(ArithmeticCalculator target, MyLogger logger) {
        this.target = target;
        this.logger = logger;
    }
    @Override
    public int add(int i, int j) {
        logger.showParam(i, j);
        int result =  target.add(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        logger.showParam(i, j);
        int result =  target.sub(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        logger.showParam(i, j);
        int result =  target.mul(i,j);
        logger.showResult(result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        logger.showParam(i, j);
        int result =  target.div(i,j);
        logger.showResult(result);
        return result;
    }
}

结果测试

public class Main {
    public static void main(String[] args) {
        ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
        MyLogger logger = new MyLogger();
        ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
        System.out.println(proxy.add(1, 9));
        System.out.println(proxy.mul(3, 3));

    }
}
/**
The method add begins with[1,9]
The method add ends with[3]
10
The method add begins with[3,3]
The method add ends with[3]
9
*/

总结

这是一个很基础的静态代理,业务类 ArithmeticCalculatorImpl 只需要关注业务逻辑本身,保证了业务的重用性,这也是代理类的优点,没什么好说的。我们主要说说这样写的缺点:

  • 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  • 如果接口增加一个方法,比如 ArithmeticCalculatorImpl 增加归零 changeZero()方法,则除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

4. 解决方法二:使用动态代理

我们去掉 ProxyLogger. 类,增加一个 ObjectInterceptor.java 类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ObjectInterceptor implements InvocationHandler {

    //目标类
    private Object target;

    //切面类(这里指 日志类)
    private MyLogger logger;

    public ObjectInterceptor(Object target, MyLogger logger) {
        this.target = target;
        this.logger = logger;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //参数日志
        logger.showParam((int)args[0], (int)args[1]);
        System.out.println(args[0]+"--"+args[1]);
        int result = (int)method.invoke(target, args);
        //结果日志
        logger.showResult(result);
        return result;
    }
}

测试

public class Main {
    public static void main(String[] args) {
        MyLogger logger = new MyLogger();
        ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
        System.out.println(proxy.add(1, 9));
        System.out.println(proxy.mul(3, 3));*/

        Object object = new ArithmeticCalculatorImpl();
        MyLogger logger = new MyLogger();
        ObjectInterceptor objectInterceptor = new ObjectInterceptor(object, logger);
        /**
         * 三个参数的含义:
         * 1、目标类的类加载器
         * 2、目标类所有实现的接口
         * 3、拦截器
        */
        ArithmeticCalculator calculator = (ArithmeticCalculator) Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(), objectInterceptor);

        System.out.println(calculator.add(1, 5));
         /*
            The method add begins with[1,5]
            1--5
            The method add ends with[3]
            6
         */

 那么使用动态代理来完成这个需求就很好了,后期在 ArithmeticCalculator 中增加业务方法,都不用更改代码就能自动给我们生成代理对象。而且将 ArithmeticCalculator 换成别的类也是可以的。也就是做到了代理对象能够代理多个目标类,多个目标方法。

 注意:我们这里使用的是 JDK 动态代理,要求是必须要实现接口。与之对应的另外一种动态代理实现模式 Cglib,则不需要,我们这里就不讲解 cglib 的实现方式了。

不管是哪种方式实现动态代理。本章的主角:AOP 实现原理也是动态代理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring 详解(二)------- AOP关键概念以及两种实现方式

    当我们为系统做参数验证,登录权限验证或者日志操作等,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法,这样的话模...

    海向
  • Lambda表达式详解

    Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结...

    海向
  • 抽象工厂模式

    ?原文地址为https://www.cnblogs.com/haixiang/p/12055272.html,转载请注明出处!

    海向
  • 2843 拯救炜哥

    2843 拯救炜哥 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 有一天,炜...

    attack
  • LeetCode第166场周赛题解

    这是LeetCode的第166场周赛的题解,不出意外的又爆炸了,前三题只做了20分钟,第4题因为题意读错了耽误了40分钟,到1小时15分钟左右才写完。排名直接1...

    BBuf
  • 2017.10.27涩会题大乱斗部分题解

    A  P3741 honoka的键盘 和昨天的T1一样, 枚举改哪一个 1 #include<cstdio> 2 #include<cstring> 3 ...

    attack
  • 探索JAVA并发 - 如何减少锁的竞争

    所谓可伸缩性,即当增加计算资源(如CPU、内存、带宽等)时,程序的吞吐量或处理能力会相应增加。这个时候,我们当然希望增加的效果越明显越好,不过如果锁竞争太严重,...

    acupt
  • Idea 使用 Junit4 进行单元测试

    Idea 默认使用 arquillian junit4 作为测试框架,我们将其更改为 Junit4。

    希希里之海
  • 1215 迷宫

    1215 迷宫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 在N*N的迷宫...

    attack
  • C#访问非托管DLL

    前段时间写了一个读卡器程序,使用到了一个DLL。在.NET 中调用DLL还是相当简单的。我也是转别人的代码,自己的不写。下面就是代码: C#中要使用动态链接库...

    用户1105954

扫码关注云+社区

领取腾讯云代金券