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),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...
随机推荐
- 响应式Web设计- Viewport
什么是Viewport? viewport是用户网页的可视区域, 翻译为中文可以叫做"视区". 设置Viewport 一个常用的针对移动网页优化过的页面的Viewport meta ...
- ios之键盘的自定义
一.键盘通知 当文本View(如UITextField,UITextView,UIWebView内的输入框)进入编辑模式成为first responder时,系统会自动显示键盘.成为firstresp ...
- Codeforces Round #510 #B
http://codeforces.com/contest/1042/problem/B 题意: 给出n种饮料,每种饮料还有一种或多种维生素(A或B或C),某人想集齐三种维生素,问最少需要花费多少? ...
- (52)zabbix_sender提交item数据
zabbix_sender是什么?有什么作用 zabbix获取key值有超时时间,如果自定义的key脚本一般需要执行很长时间,这根本没法去做监控,那怎么办呢?使用zabbix监控类型zabbix tr ...
- Python赋值运算及流程控制
1. 内置函数 1> len:统计元素长度 str1 = 'wonderful' print(len(str1)) result: li = [,,] print(len(li)) result ...
- Pycharm快捷键及Python常用转义符
不管是windows.xshell或者pycharm,学会使用快捷键都会使学习工作达到事半功倍的效果.这篇博客收集了部分常用的pycharm快捷键,分享给大家,希望对大家有用. 1. 常用快捷键 Py ...
- JS获取单选框checked的value方法
; var obj = document.getElementsByTagName("input"); document.getElementById('gender').oncl ...
- 【php】 php 的注释和结束符号之间的关系
Closing PHP tags are recognised within single-line comments: <?php // Code will end here ?> ...
- python-数字类型内置方法
数字类型内置方法 为什么要有数据类型? 数据是用来表示状态的,不同的状态就应该用不同的数据类型去表示 整型(int) 用途:年龄.号码.银行卡号等 定义:可以使用int()方法将纯数字的字符串转换为十 ...
- Python9-模块2-序列化-day20
序列化 什么叫序列化——将原本的字典.列表等内容转换成一个字符串的过程就叫做序列化. 序列就是字符串 序列化的目的1.以某种存储形式使自定义对象持久化:2.将对象从一个地方传递到另一个地方.3.使程序 ...