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=& ...
- ios UITableViewCell重用问题
在写sina 微博界面的过程中使用到了cell,那么就是在cell上添加一些控件,但是由于每条微博的内容都是不同的,所以在显示的过程中,出现了内容重叠的问题,其实就是UITableViewCell重用 ...
- ios lazying load
初步写一些自己对于lazyload的看法吧.这篇文章主要针对普通view,对于image相关的的懒加载,准备过几天研究一下在写. 懒加载,又称为延迟加载.通常用法,你有一个UITextField类型的 ...
- javascript设计模式(张容铭)学习笔记 - 照猫画虎-模板方法模式
模板方法模式(Template Method):父类中定义一组操作算法骨架,而降一些实现步骤延迟到子类中,使得子类可以不改变父类的算法结构的同时可重新定义算法中某些实现步骤. 项目经理体验了各个页面的 ...
- 博弈论入门 Bash 、Nim 、Wythoff's Game结论及c++代码实现
SG函数先不说,给自己总结下三大博弈.和二进制及黄金分割联系密切,数学真奇妙,如果不用考试就更好了. 1.Bash Game:n个物品,最少取1个,最多取m个,先取完者胜. 给对手留下(m+1)的倍数 ...
- minGw64编译Qt时遇到too many sections问题
minGw64编译Qt时遇到too many sections问题: 修改\Src\qtbase\mkspecs\win32-g++\qmake.conf中 QMAKE_CFLAGS ...
- 使用Spring Cloud需要了解一些概念
Spring Cloud是一个基于Spring Boot实现的微服务架构开发工具,它为基于JVM的微服务开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集 ...
- 【树形背包】bzoj4033: [HAOI2015]树上染色
仔细思考后会发现和51nod1677 treecnt有异曲同工之妙 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 ...
- Windows 10 Mac 为Vs Code配置C/C++环境
2019-06-10 更新: 加上Mac版本的Vscode配置文件 0.前言 实现效果:右键一键编译运行C/C++文件 Vs code的代码效果很好看,也很轻量,所以想为Vs Code配置C/C++环 ...
- **没有规则可以创建“XXX”需要的目标“XXX”问题的解决方案
一.现象 我将之前Redhat9.0编译好的uboot,转到ubuntu12.04环境.在ubuntu环境下对 uboot重新编译提示错误.编译过程如下: root@hailin-virtual-ma ...