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),通常称为面向切面编程.它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的 ...
随机推荐
- lru缓存测试类
package demo.mytest; import java.io.Serializable;import java.util.LinkedHashMap;import java.util.con ...
- Spring根据XML配置文件注入对象类型属性
这里有dao.service和Servlet三个地方 通过配过文件xml生成对象,并注入对象类型的属性,降低耦合 dao文件代码: package com.swift; public class Da ...
- VUE2中axios的使用方法
一,安装 npm install axios 二,在http.js中引入 import axios from 'axios'; 三,定义http request 拦截器,添加数据请求公用信息 axio ...
- 初涉最小表示法&&bzoj1398: Vijos1382寻找主人 Necklace
把最小表示法的坑填了 Description 给定两个项链的表示,判断他们是否可能是一条项链. Input 输入文件只有两行,每行一个由0至9组成的字符串,描述一个项链的表示(保证项链的长度是相等的) ...
- PHP操作MySQL事务实例
PHP与MYSQL事务处理 一般来说,事务都应该具备ACID特征.所谓ACID是Atomic(原子性),Consistent(一致性),Isolated(隔离性),Durable(持续性)四个词的首字 ...
- python插件,pycharm基本用法,markdown文本编写,jupyter notebook的基本操作汇总
5.14自我总结 一.python插件插件相关技巧汇总 安装在cmd上运行 #比如安装 安装:wxpy模块(支持 Python 3.4-3.+ 以及 2.7 版本):pip3 install wxpy ...
- 又面试了Python爬虫工程师,碰到这么几道面试题,Python面试题No9
第1题:动态加载又对及时性要求很高怎么处理? 如何知道一个网站是动态加载的数据? 用火狐或者谷歌浏览器 打开你网页,右键查看页面源代码,ctrl +F 查询输入内容,源代码里面并没有这个值,说明是动态 ...
- (转)5个Xcode开发调试技巧
1.Enable NSZombie Objects(开启僵尸对象) Enable NSZombie Objects可能是整个Xcode开发环境中最有用的调试技巧.这个技巧非常非常容易追踪到重复释放的问 ...
- debian 中的jdk
1 openjdk 2 debian安装java的脚本 3 下载bin自己编译
- Android自动化测试Uiautomator--UiSelector接口简介
UiSelector对象可以理解为一种条件对象,描述的是一种条件,可以配合UiObject使用得到某个符合条件的控件对象. 所有的方法都是public的,且都返回UiSelector类的对象. 文本方 ...