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 实现原理也是动态代理

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. Tarjan求强联通分量+缩点

    提到Tarjan算法就不得不提一提Tarjan这位老人家 Robert Tarjan,计算机科学家,以LCA.强连通分量等算法闻名.他拥有丰富的商业工作经验,1985年开始任教于普林斯顿大学.Tarj ...

  2. shell脚本,awk数组之如何处理多个文件。

    [root@localhost | > file [root@localhost - | > file1 [root@localhost awk]# cat file [root@loca ...

  3. url地址数据参数转化JSON对象(js三种方法实现)

    当我们用get方法提交表单时,在url上会显示出请求的参数组成的字符串,例如:http://localhost:3000/index.html?phone=12345678901&pwd=12 ...

  4. [POJ] 3362 Telephone Lines

    Telephone Lines Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7978 Accepted: 2885 Descr ...

  5. RN在设备上运行

    https://facebook.github.io/react-native/docs/running-on-device.html 在发布之前,最好是在真实的设备上测试一下应用.如果是通过crea ...

  6. asyn_fifo

    //Module Name:afifo_ctrl //Description:parameterized afifo module afifo_ctrl( clk_push, rst_push_n, ...

  7. DRF filter

    filter 配置 fiter定义 自定义filter继承BaseFilterBackend,必须重写filter_queryset,返回值为过滤后的queryset filter在GenericAP ...

  8. 修改JVM的参数、Jstat、Jstack、gclog

    ---恢复内容开始--- 1. jetty 修改JVM的参数 deploy/bin/env.sh 在上面的环境变量脚本中进行修改:如果分配给JVM的内存是4g 这个里面的JAVA_OPTS 的配置项就 ...

  9. 【07】像使用命令行一样使用 GitHub URL

    [07] 像使用命令行一样使用 GitHub URL 既然说到了 URL,那么久接着聊一下.使用 UI 浏览 GitHub 很 方面也很好,不过很多时候最快的方式是使用 URL 来浏览.举个例子,如果 ...

  10. luogu2219 [HAOI2007]修筑绿化带

    和「理想的正方形」比较相似,需要先掌握那道题. 花坛外头每一边必须套上绿化带 #include <iostream> #include <cstdio> using names ...