1. AOP 简介

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

2. 示例需求

想要为写好的 ArithmeticCalculator 添加日志 ,即每次运算前后添加

采用以下方法太过繁琐,修改内容需要每个跟着都修改,可维护性差

  1. public interface ArithmeticCalculator {
  2. int add(int i, int j);
  3. int sub(int i, int j);
  4. int mul(int i, int j);
  5. int div(int i, int j);
  6. }
  1. public class MyArithmeticCalculatorImp implements ArithmeticCalculator {
  2. public int add(int i, int j) {
  3. System.out.println("The method add begins with["+i+","+j+"]");
  4. int result = i + j;
  5. System.out.println("The method add ends with["+result+"]");
  6. return result;
  7. }
  8. public int sub(int i, int j) {
  9. System.out.println("The method sub begins with["+i+","+j+"]");
  10. int result = i - j;
  11. System.out.println("The method sub ends with["+result+"]");
  12. return result;
  13. }
  14. public int mul(int i, int j) {
  15. System.out.println("The method mul begins with["+i+","+j+"]");
  16. int result = i * j;
  17. System.out.println("The method mul ends with["+result+"]");
  18. return result;
  19. }
  20. public int div(int i, int j) {
  21. System.out.println("The method div begins with["+i+","+j+"]");
  22. int result = i / j;
  23. System.out.println("The method div ends with["+result+"]");
  24. return result;
  25. }
  26. }

结果

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

问题:

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

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

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

创建干净的实现类

  1. public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
  2. @Override
  3. public int add(int i, int j) {
  4. return i + j;
  5. }
  6. @Override
  7. public int sub(int i, int j) {
  8. return i - j;
  9. }
  10. @Override
  11. public int mul(int i, int j) {
  12. return i * j;
  13. }
  14. @Override
  15. public int div(int i, int j) {
  16. return i / j;
  17. }
  18. }

创建日志类 MyLogger

  1. /**
  2. * 创建日志类
  3. */
  4. public class MyLogger {
  5. /**
  6. * 入参日志
  7. * @param a
  8. * @param b
  9. */
  10. public void showParam(int a, int b) {
  11. System.out.println("The method add begins with["+a+","+b+"]");
  12. }
  13. /**
  14. * 运算结果日志
  15. * @param result
  16. */
  17. public void showResult(int result) {
  18. System.out.println("The method add ends with["+3+"]");
  19. }
  20. }

创建静态代理类

  1. /**
  2. * 代理类
  3. */
  4. public class ProxyLogger implements ArithmeticCalculator {
  5. //目标类
  6. private ArithmeticCalculator target;
  7. //日志类
  8. private MyLogger logger;
  9. public ProxyLogger(ArithmeticCalculator target, MyLogger logger) {
  10. this.target = target;
  11. this.logger = logger;
  12. }
  13. @Override
  14. public int add(int i, int j) {
  15. logger.showParam(i, j);
  16. int result = target.add(i,j);
  17. logger.showResult(result);
  18. return result;
  19. }
  20. @Override
  21. public int sub(int i, int j) {
  22. logger.showParam(i, j);
  23. int result = target.sub(i,j);
  24. logger.showResult(result);
  25. return result;
  26. }
  27. @Override
  28. public int mul(int i, int j) {
  29. logger.showParam(i, j);
  30. int result = target.mul(i,j);
  31. logger.showResult(result);
  32. return result;
  33. }
  34. @Override
  35. public int div(int i, int j) {
  36. logger.showParam(i, j);
  37. int result = target.div(i,j);
  38. logger.showResult(result);
  39. return result;
  40. }
  41. }

结果测试

  1. public class Main {
  2. public static void main(String[] args) {
  3. ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
  4. MyLogger logger = new MyLogger();
  5. ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
  6. System.out.println(proxy.add(1, 9));
  7. System.out.println(proxy.mul(3, 3));
  8. }
  9. }
  10. /**
  11. The method add begins with[1,9]
  12. The method add ends with[3]
  13. 10
  14. The method add begins with[3,3]
  15. The method add ends with[3]
  16. 9
  17. */

总结

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

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

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

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

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. public class ObjectInterceptor implements InvocationHandler {
  4. //目标类
  5. private Object target;
  6. //切面类(这里指 日志类)
  7. private MyLogger logger;
  8. public ObjectInterceptor(Object target, MyLogger logger) {
  9. this.target = target;
  10. this.logger = logger;
  11. }
  12. @Override
  13. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  14. //参数日志
  15. logger.showParam((int)args[0], (int)args[1]);
  16. System.out.println(args[0]+"--"+args[1]);
  17. int result = (int)method.invoke(target, args);
  18. //结果日志
  19. logger.showResult(result);
  20. return result;
  21. }
  22. }

测试

  1. public class Main {
  2. public static void main(String[] args) {
  3. MyLogger logger = new MyLogger();
  4. ProxyLogger proxy = new ProxyLogger(arithmeticCalculator, logger);
  5. System.out.println(proxy.add(1, 9));
  6. System.out.println(proxy.mul(3, 3));*/
  7. Object object = new ArithmeticCalculatorImpl();
  8. MyLogger logger = new MyLogger();
  9. ObjectInterceptor objectInterceptor = new ObjectInterceptor(object, logger);
  10. /**
  11. * 三个参数的含义:
  12. * 1、目标类的类加载器
  13. * 2、目标类所有实现的接口
  14. * 3、拦截器
  15. */
  16. ArithmeticCalculator calculator = (ArithmeticCalculator) Proxy.newProxyInstance(object.getClass().getClassLoader(),
  17. object.getClass().getInterfaces(), objectInterceptor);
  18. System.out.println(calculator.add(1, 5));
  19. /*
  20. The method add begins with[1,5]
  21. 1--5
  22. The method add ends with[3]
  23. 6
  24. */

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

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

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

Spring 详解(一)------- AOP前序的更多相关文章

  1. Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现

    前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...

  2. Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建

    上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...

  3. Spring详解(五)------AOP

    这章我们接着讲 Spring 的核心概念---AOP,这也是 Spring 框架中最为核心的一个概念. PS:本篇博客源码下载链接:http://pan.baidu.com/s/1skZjg7r 密码 ...

  4. Spring详解篇之 AOP面向切面编程

    一.概述 Aop(aspect oriented programming面向切面编程),是spring框架的另一个特征.AOP包括切面.连接点.通知(advice).切入点(pointCut) . 1 ...

  5. Spring详解(一)------概述

    本系列教程我们将对 Spring 进行详解的介绍,相信你在看完后一定能够有所收获. 1.什么是 Spring ? Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开 ...

  6. Spring详解

    https://gitee.com/xiaomosheng888老师的码云 1.核心容器:核心容器提供 Spring 框架的基本功能(Spring Core).核心容器的主要组件是 BeanFacto ...

  7. Spring详解------概述

    1.什么是 Spring ? Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2E ...

  8. spring详解(1)

    1.  什么是spring? Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发 ...

  9. Spring详解(五)------面向切面编程

    .AOP 什么? AOP(Aspect Oriented Programming),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...

随机推荐

  1. UIScreen, UIWindow

    模仿书上或网上的例子,每次最开始就是 在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: ...

  2. postcss.config.js配置文件的配置方法

    module.exports = { plugins: { 'autoprefixer': {}, } }

  3. 【OS_Linux】三大文本处理工具之grep命令

    grep(global search regular expression(RE) and print out the line,整行搜索并打印匹配成功的行 语法:grep  [选项]   搜索词  ...

  4. break、continue、exit、return的区别和对比

    break.continue.exit.return的区别和对比 一:说明 break.continue在条件循环语句及循环语句(for.while.if等)中用于控制程序的走向:而exit则用于种植 ...

  5. RN安卓原生模块

    https://facebook.github.io/react-native/docs/native-modules-android.html RN实际就是依附在原生平台上,把各种各样的RN组件展示 ...

  6. memory bist lib

    model NVIC_REG6T_1024x32(resetb,margin,clk,en,we,addr,d,q) ( bist_definition( clock clk high; chip_e ...

  7. python中魔法方法(持续更新)

    1.对于一个自定义的类,如果实现了 __call__ 方法,那么该类的实例对象的行为就是一个函数,是一个可以被调用(callable)的对象.例如: class Add: def __init__(s ...

  8. Centos7 安装python3详细教程,解决升级后不兼容问题

    一.确认当前python版本 [root@centos Python-3.6.1]# python Python 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC ...

  9. wordpress无法登录的解决方法

    使用wordpress建站的朋友可能会遇到wordpress管理密码,有时甚至是正确的密码,但是多次尝试输入都无法登录,并且输入用户名和输入电子邮件都无法获取密码,遇到这种情况怎么办,本文教你如何处理 ...

  10. php 上传文件名出现乱码

    想必很多朋友在进行utf8编码的php开发上传功能的时候,都会遇到这样的一个问题,就是上传中文文件名的文件时,文件名会变成乱码,其实我们可以用iconv函数对文件名进行重新编码就解决问题了 可能会有不 ...