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 实现原理也是动态代理
Spring 详解(一)------- AOP前序的更多相关文章
- Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现
前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...
- Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建
上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...
- Spring详解(五)------AOP
这章我们接着讲 Spring 的核心概念---AOP,这也是 Spring 框架中最为核心的一个概念. PS:本篇博客源码下载链接:http://pan.baidu.com/s/1skZjg7r 密码 ...
- Spring详解篇之 AOP面向切面编程
一.概述 Aop(aspect oriented programming面向切面编程),是spring框架的另一个特征.AOP包括切面.连接点.通知(advice).切入点(pointCut) . 1 ...
- Spring详解(一)------概述
本系列教程我们将对 Spring 进行详解的介绍,相信你在看完后一定能够有所收获. 1.什么是 Spring ? Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开 ...
- Spring详解
https://gitee.com/xiaomosheng888老师的码云 1.核心容器:核心容器提供 Spring 框架的基本功能(Spring Core).核心容器的主要组件是 BeanFacto ...
- Spring详解------概述
1.什么是 Spring ? Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2E ...
- spring详解(1)
1. 什么是spring? Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发 ...
- Spring详解(五)------面向切面编程
.AOP 什么? AOP(Aspect Oriented Programming),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...
随机推荐
- Bootstrap历练实例:基本按钮群组
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 相机 感光度iso,焦距,光圈,ccd 和 噪点, 景深关系表格
表格 矩阵 感官度iso: 越低曝光速度越慢,所谓慢工出细活,成像质量会好,如果形成的话. 但是因为慢,所以要更多的光量,才能画完. 就需要更慢的快门 (但是太慢手抖的话就糊掉,或者动的物体形成轨迹. ...
- Alert and Action sheets and Timer and Animation
- 1008: ASCII码
题目描述 相信大家一定都知道大名鼎鼎的ASCII码,这次给你的任务是输入数字(表示ASCII码),输出相对应的字符信息. 输入 第一行为一个整数T(1<=T<=1000).接下来包括T个正 ...
- TB平台搭建之二
主要想记录关于debug问题: 一般我会1.定位问题所在位置比如使能信号错误.地址读写错误.数据流pipeline错误.... 2.首先看问题的源头(对应信号)是否还正确,比如出现XX要查看她的第一级 ...
- CM3中数据传输对齐/非对齐方式
在CM3中,非对齐的数据传输只发生在常规的数据传送指令中,如LDR.LDRH.LDRSH.其他指令则不支持,包括: 1.多个数据的加载.存储(LDM/STM). 2.堆栈操作PUSH.POP. 3.互 ...
- 我的Python分析成长之路10
matplot数据可视化基础 制作提供信息的可视化(有时称作绘图)是数据分析中最重要任务之一. 1.图片(画布)与子图 plt.figure :创建一张空白的图片,可以指定图片的大小.像素. figu ...
- JAVA复制
复制 直接赋值 直接赋值.在 Java 中, A a1 = a2,我们需要理解的是这实际上复制的是引用,也就是说 a1 和 a2 指向的是同一个对象.因此,当 a1 变化的时候, a2 里面的成员变量 ...
- tomcat 下catalina.out 日志乱码问题处理
问题: 项目部署到Linux服务器之后,控制台 catalina.out 文件输出的中文为乱码: 解决办法: 方法一:修改tomcat下的模板编码 bin/catalina.sh 文件添加如下配置:J ...
- luogu2483 【模板】k短路([SDOI2010]魔法猪学院)
模板题 #include <iostream> #include <cstring> #include <cstdio> #include <queue> ...