博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
手把手教你用Java实现AOP
阅读量:2118 次
发布时间:2019-04-30

本文共 4131 字,大约阅读时间需要 13 分钟。

介绍

众所周知,AOP(面向切面编程)是Spring框架的特色功能之一。通过设置横切关注点(cross cutting concerns),AOP提供了极高的扩展性。那AOP在Spring中是怎样运作的呢?当你只能使用core java,却需要AOP技术时,这个问题的解答变得极为关键。不仅如此,在高级技术岗位的面试中,此类问题也常作为考题出现。这不,我的朋友最近参加了一个面试,就被问到了这样一个棘手的问题——如何在不使用Spring及相关库,只用core Java的条件下实现AOP。因此,我将在本文中提供一份大纲,帮助大家了解如何只用core Java实现一个AOP(当然啦,这种AOP在功能上有一定的局限性)。注意,本文不是一篇有关Spring AOP与Java AOP的对比研究,而是有关在core Java中借助固有的实现AOP的教程。

想必读者已经知道AOP是什么,也知道在Spring框架中如何使用它,因此本文只着眼于如何在不用Spring的前提下实现AOP。首先,我们得知道,Spring是借助了JDK proxy和CGlib两种技术实现AOP的。JDK dynamic proxy提供了一种灵活的方式来hook一个方法并执行指定的操作,但执行操作时得有一个限制条件:必须先提供一个相关的接口以及该接口的实现类。实践出真知,让我们透过一个案例来理解这句吧!现在有一个计算器程序,用于完成一些数学运算。让我们来考虑下除法功能,此时的问题是:如果core framework 已经具备了一份实现除法的代码,我们能否在代码执行时劫持(highjack)它并执行额外的校验呢?答案是肯定的,我将用下面提供的代码片段来证明这点。首先来看基础接口的代码:

1
2
3
public
interface
Calculator {
    
public
int
calculate(
int
a ,
int
b);
}

该接口实现类的代码如下:

1
2
3
4
5
6
public
class
CalculatorImpl
implements
Calculator {
    
@Override
    
public
int
calculate(
int
a,
int
b) {
        
return
a/b;
    
}
}

假设我们既不能修该上面的代码,也不能对核心库进行任何改动,怎样才能完美地实现校验功能呢?不如试下JDK dynamic proxy的功能吧。

1
2
3
4
5
6
7
8
9
10
11
12
public
class
SomeHandler
implements
InvocationHandler {
 
// Code omitted for simplicity…..
 
    
@Override
    
public
Object invoke(Object proxy, Method method, Object[] params)
throws
Throwable {
// Your complex business validation and logic
        
Object result = method.invoke(targetObject ,params);
        
return
result;
    
}
 
}

让我们通过测试类来看看由JDK dynamic proxy实现的校验功能的效果如何。

1
2
3
4
5
6
7
public
static
void
main(String[] args) {
        
CalculatorImpl calcImpl =
new
CalculatorImpl();
        
Calculator proxied = (Calculator)ProxyFactory.getProxy (Calculator.
class
, calcImpl,
                
new
SomeHandler(calcImpl));
        
int
result = proxied.calculate(
20
,
10
);
        
System.out.println(
"FInal Result :::"
+ result);
    
}

从结果可以看出,简单地实现功能强大的InvocationHandler接口,我们便能得到一个hooking implementation。按照JDK文档的描述,InvocationHandler接口是借助一个代理实例(proxy instance)来处理一个方法调用的。

现在我们已经知道,InvocationHandler的invoke()方法能够帮助我们解决问题。那么再来解决一个新问题——怎样才能在方法执行的前后执行操作呢?说的更具体一些,我们能通过添加多个aop(before、after、around)来hook一个方法吗(译注:原文为add multiple aops,但我认为Handler是充当Aspect的角色)?答案同样是肯定的。按照以下的步骤建立一个精简的代码模板便能满足这样的需求:

  1. 创建一个抽象类,用于将aop应用于目标对象上。
  2. 创建名为BeforeHandler 和 AfterHandler的两个aop。前者在方法执行之前工作,而后者则在方法执行结束后工作。
  3. 创建一个代理类,使所有的aop handler和目标对象只需作为参数传入,就能创建一个hook。
  4. 加入你自己的业务逻辑或者横切关注点。
  5. 最后,通过传入相关的参数创建代理对象(proxy object)。

技术实现概要

译注:此处是核心代码片段,如果想运行该实例,需进入下方提供的链接下载完整代码

创建一个handler的抽象类:

1
2
3
4
5
6
7
8
public
abstract
class
AbstractHandler
implements
InvocationHandler {
 
    
private
Object targetObject;
 
    
public
void
setTargetObject(Object targetObject) {
        
this
.targetObject = targetObject;
    
}
}

创建名为BeforeHandlerAfterHandler的两个易扩展的handler抽象类:

1
2
3
4
5
6
7
public
abstract
class
BeforeHandler
extends
AbstractHandler {
    
public
abstract
void
handleBefore(Object proxy, Method method, Object[] args);
    
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
        
handleBefore(proxy, method, args);
        
return
method.invoke(getTargetObject(), args);
    
}
}
1
2
3
4
5
6
7
8
public
abstract
class
AfterHandler
extends
AbstractHandler {
    
public
abstract
void
handleAfter(Object proxy, Method method, Object[] args);
    
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
        
Object result = method.invoke(getTargetObject(), args);
        
handleAfter(proxy, method, args);
        
return
result;
    
}
}

创建Proxy的工厂类:

1
2
3
4
5
6
7
8
9
10
11
public
class
ProxyFactory {
 
    
public
static
Object getProxy(Object targetObject,
            
List handlers) {
            
//Code to get the proxy
            
return
proxyObject;
        
}
else
{
            
return
targetObject;
        
}
    
}
}

以下为测试代码:

1
2
3
4
5
6
7
8
9
CalculatorImpl calcImpl =
new
CalculatorImpl();
BeforeHandler before =
new
BeforeHandlerImpl();
AfterHandler after =
new
AfterHandlerImpl();
List<AbstractHandler> handlers =
new
ArrayList<AbstractHandler>();
handlers.add(before);
handlers.add(after);
Calculator proxy = (Calculator) ProxyFactory.getProxy(calcImpl,
                
handlers);
int
result = proxy.calculate(
20
,
10
);

配置

以上的代码片段简明扼要地解释了AOP在结构上的实现(structural implementation)。当然,如果能通过实际的测试将其运用到现实中去,那就再好不过了。读者可在下面的链接中获取完整的工程文件,并在Java编辑器中配置它们,最后通过其中的测试类来检验效果。

总结

希望这篇简短的有关AOP文章能够帮助到大家。需说明的是,本文只实现了before和after两种aop,而另外两种,即“Around”和“Throw”,则希望读者自行完成。

译注:关于AOP技术的内容,推荐大家看下这篇

原文链接: 
 翻译: 

译文链接: 

本文由  -  翻译自 

转载地址:http://zmgef.baihongyu.com/

你可能感兴趣的文章
Leetcode C++《热题 Hot 100-46》739.每日温度
查看>>
Leetcode C++《热题 Hot 100-47》236.二叉树的最近公共祖先
查看>>
Leetcode C++《热题 Hot 100-48》406.根据身高重建队列
查看>>
《kubernetes权威指南·第四版》第二章:kubernetes安装配置指南
查看>>
Leetcode C++《热题 Hot 100-49》399.除法求值
查看>>
Leetcode C++《热题 Hot 100-51》152. 乘积最大子序列
查看>>
[Kick Start 2020] Round A 1.Allocation
查看>>
Leetcode C++ 《第181场周赛-1》 5364. 按既定顺序创建目标数组
查看>>
Leetcode C++ 《第181场周赛-2》 1390. 四因数
查看>>
阿里云《云原生》公开课笔记 第一章 云原生启蒙
查看>>
阿里云《云原生》公开课笔记 第二章 容器基本概念
查看>>
阿里云《云原生》公开课笔记 第三章 kubernetes核心概念
查看>>
阿里云《云原生》公开课笔记 第四章 理解Pod和容器设计模式
查看>>
阿里云《云原生》公开课笔记 第五章 应用编排与管理
查看>>
阿里云《云原生》公开课笔记 第六章 应用编排与管理:Deployment
查看>>
阿里云《云原生》公开课笔记 第七章 应用编排与管理:Job和DaemonSet
查看>>
阿里云《云原生》公开课笔记 第八章 应用配置管理
查看>>
阿里云《云原生》公开课笔记 第九章 应用存储和持久化数据卷:核心知识
查看>>
linux系统 阿里云源
查看>>
国内外helm源记录
查看>>