- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章aop的实现原理_动力节点Java学院整理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
面向方面编程(Aspect Oriented Programming,简称AOP)是一种声明式编程(Declarative Programming)。声明式编程是和命令式编程(Imperative Programming)相对的概念。我们平时使用的编程语言,比如C++、Java、Ruby、Python等,都属命令式编程。命令式编程的意思是,程序员需要一步步写清楚程序需要如何做什么(How to do What)。声明式编程的意思是,程序员不需要一步步告诉程序如何做,只需要告诉程序在哪些地方做什么(Where to do What)。比起命令式编程来,声明式编程是在一个更高的层次上编程。声明式编程语言是更高级的语言。声明式编程通常处理一些总结性、总览性的工作,不适合做顺序相关的细节相关的底层工作.
如果说命令式编程是拼杀在第一线的基层工作人员,声明式编程就是总设计师、规则制定者。声明式编程语言的概念,和领域专用语言(Domain Specific Language,简称DSL)的概念有相通之处。DSL主要是指一些对应专门领域的高层编程语言,和通用编程语言的概念相对。DSL对应的专门领域(Domain)一般比较狭窄,或者对应于某个行业,或者对应于某一类具体应用程序,比如数据库等.
最常见的DSL就是关系数据库的结构化数据查询语言SQL。同时,SQL也是一门声明式语言。SQL只需要告诉数据库,处理符合一定条件的数据,而不需要自己一步步判断每一条数据是否符合条件。SQL的形式一般是 select … where …,update … where …,delete … where …。当然,这样一来,很多基层工作,SQL做不了。因此,大部分数据库都提供了另外的命令式编程语言,用来编写存储过程等,以便处理一些更加细节的工作。 常见的DSL还有规则引擎(Rule Engine)语言、工作流(Workflow)语言等。规则引擎和工作流同时带有命令式编程和声明式 。
编程的特点。规则引擎允许用户按照优先级定义一系列条件组合,并定义对满足条件的数据的处理过程。工作流也大致类似。工作流把最基本的条件判断和循环语句的常见组合,定义为更加高级复杂的常用程序流程逻辑块。用户可以用这些高级流程块组合更加复杂的流程块,从而定义更加复杂的流程跳转条件。用户也可以定义当程序运行上下文满足一定条件的时候,应该做什么样的处理工作。规则引擎和工作流的语言形式有可能是XML格式,也有可能是Ruby、Python、JavaScript等脚本格式。我个人比较倾向于脚本格式,因为XML适合表达结构化数据,而不擅长表达逻辑流程。当然,XML格式的好处也是显而易见的。解析器可以很容易分析XML文件的结构,XML定义的条件或者程序流程都可以很方便地作为数据来处理.
介绍了声明式编程和DSL之后,我们来看本章题目表达的内容——AOP。AOP是声明式编程,AOP语言也可以看作是DSL。AOP语言对应的专门领域(Domain)就是程序结构的方方面面(Aspect),比如程序的类、方法、成员变量等结构,以及针对这些程序结构的通用工作处理,比如日志管理、权限管理、事务管理等.
AOP处理的工作内容一般都是这样的一些总结性工作:“我想让所有的数据库类都自动进行数据库映射”、“我想打印出所有业务类的工作流程日志”、“我想给所有关键业务方法都加上事务管理功能”、“我想给所有敏感数据处理方法都加上安全管理授权机制”等等。 。
下面我们介绍AOP的实现原理和使用方法.
AOP实现原理 。
AOP的实现原理可以看作是Proxy/Decorator设计模式的泛化。我们先来看Proxy模式的简单例子.
1
2
3
4
5
6
7
8
9
10
|
Proxy {
innerObject; // 真正的对象
f1() {
// 做一些额外的事情
innerObject.f1(); // 调用真正的对象的对应方法
// 做一些额外的事情
}
}
|
在Python、Ruby等动态类型语言中,只要实现了f1()方法的类,都可以被Proxy包装。在Java等静态类型语言中,则要求Proxy和被包装对象实现相同的接口。动态语言实现Proxy模式要比静态语言容易得多,动态语言实现AOP也要比静态语言容易得多。假设我们用Proxy包装了10个类,我们通过调用Proxy的f1()方法来调用这10个类的f1()方法,这样,所有的f1()调用都会执行同样的一段“额外的工作”,从而实现了“所有被Proxy包装的类,都执行一段同样的额外工作”的任务。这段“额外的工作”可能是进行日志记录,权限检查,事务管理等常见工作.
Proxy模式是可以叠加的。我们可以定义多种完成特定方面任务(Aspect),比如,我们可以定义LogProxy、SecurityProxy、TransactionProxy,分别进行日志管理、权限管理、事务管理.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
LogProxy {
f1(){
// 记录方法进入信息
innerObject.f1();// 调用真正的对象的对应方法
// 记录方法退出信息
}
}
SecurityProxy {
f1(){
// 进行权限验证
innerObject.f1();// 调用真正的对象的对应方法
}
}
TransactonProxy {
f1(){
Open Transaction
innerObject.f1();// 调用真正的对象的对应方法
Close Transaction
}
}
|
根据AOP的惯用叫法,上述的这些Proxy也叫做Advice。这些Proxy(or Advice)可以按照一定的内外顺序套起来,最外面的Proxy会最先执行。包装f1()方法,也叫做截获(Intercept)f1()方法。Proxy/Advice有时候也叫做Interceptor.
看到这里,读者可能会产生两个问题.
问题一:上述代码采用的Proxy模式只是面向对象的特性,怎么会扯上一个新概念“面向方面(AOP)”呢?
问题二:Proxy模式虽然避免了重复“额外工作”代码的问题,但是,每个相关类都要被Proxy包装,这个工作也是很烦人。AOP Proxy如何能在应用程序中大规模使用呢?
下面我们来解答着两个问题.
对于问题一,我们来看一个复杂一点的例子。假设被包装对象有f1()和f2()两个方法都要被包装.
1
2
3
4
|
RealObject{
f1() {…}
f2() {…}
}
|
这个时候,我们应该如何做?难道让Proxy也定义f1()和f2()两个方法?就象下面代码这样?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Proxy {
innerObject; // 真正的对象
f1() {
// 做一些额外的事情
innerObject.f1(); // 调用真正的对象的对应方法
// 做一些额外的事情
}
f2() {
// 做一些额外的事情
innerObject.f2(); // 调用真正的对象的对应方法
// 做一些额外的事情
}
}
|
这样做有几个不利之处。一是会造成代码重复,Proxy的f1()和f2()里面的“做一些额外的事情”代码重复。二是难以扩展,被包装对象可能有多个不同的方法,不同的被包装对象需要被包装的方法也可能不同。现在的问题就变成,“Proxy如何才能包装截获任何类的任何方法?” 。
答案呼之欲出。对,就是Reflection。Java、Python、Ruby都支持Reflection,都支持Method(方法)对象。那么我们就利用Method Reflection编写一个能够解惑任何类的任何方法的Proxy/Advice/Interceptor.
1
2
3
4
5
6
7
8
9
10
|
MethodInterceptor{
around( method ){
// 做些额外的工作
method.invoke(…); // 调用真正的对象方法
// 做些额外的工作
}
}
|
上述的MethodInterceptor就可以分别包装和截获f1()和f2()两个方法.
这里的method参数就是方法对象,在Java、Ruby等面向对象语言中,需要用Reflection获取方法对象。这个方法对象就相当于函数式编程的函数对象。在函数式编程中,函数对象属于“一等公民”,函数对象的获取不需要经过Reflection机制。所以,函数式编程对AOP的支持,比面向对象编程更好。由此我们看到,AOP对应的问题领域确实超出了OOP的力所能及的范围。OOP只能处理同一个类体系内的同一个方法签名的截获和包装工作,一旦涉及到一个类的多个不同方法,或者多个不同类体系的不同方法,OOP就黔驴技穷,无能为力了.
使用Method Reflection的方式截获任何方法对象,是AOP的常用实现手段之一。另一个常见手段就是自动代码生成了。这也回答了前面提出的问题二——如何在应用系统中大规模使用AOP.
Proxy Pattern + Method Reflection + 自动代码生成这样一个三元组合,就是AOP的基本实现原理。Proxy Pattern 和 Method Reflection,前面已经做了阐述,下面我们来讲解自动代码生成.
首先,AOP需要定义一种Aspect描述的DSL。Aspect DSL主要用来描述这样的内容:“用TransactionProxy包装截获business目录下的所有类的公共业务方法”、“ 用SecurityProxy包装截获所有Login/Logout开头的类的所有公共方法”、“用LogProxy包装截获所有文件的所有方法”等等。Aspect DSL的形式有多种多样。有的是一种类似Java的语法,比如AspectJ;有的是XML格式或者各种脚本语言,比如,Spring AOP等.
有了Aspect DSL,AOP处理程序就可以生成代码了。AOP生成代码有三种可能方式:
(1)静态编译时期,源代码生成。为每个符合条件的类方法产生对应的Proxy对象。AspectJ以前就是这种方式.
(2)静态编译时期,处理编译后的字节码。Java、Python之类的虚拟机语言都有一种中间代码(Java的中间代码叫做字节码),AOP处理程序可以分析字节码,并直接产生字节码形式的Proxy。这种方式也叫做静态字节码增强。AspectJ也支持这种方式。Java有一些开源项目,比如 ASM、Cglib等,可以分析并生成Java字节码。这些开源项目不仅可以静态分析增强字节码,还可以在程序运行期动态分析增强字节码。很多AOP项目,比如Spring AOP,都采用ASM/Cglib处理字节码.
(3)动态运行时期,即时处理装载到虚拟机内部的类结构字节码。这也叫做动态增强。比如,Spring AOP。如前所述,Spring AOP使用ASM/Cglib之类的处理字节码的开源项目。Java运行库本身也提供了类似于ASM/Cglib的简单的动态处理字节码的API,叫做 Dynamic Proxy.
以上就是AOP的实现原理:Proxy Pattern + Method Reflection + Aspect DSL + 自动代码生成.
总体来说,实现AOP的便利程度,函数式编程语言 > 动态类型语言 > 静态类型语言。当然,这个不等式并不是绝对的。有些动态类型语言提供了丰富强大的语法特性,实现AOP的便利程度,可能要超过函数式编程语言.
最后此篇关于aop的实现原理_动力节点Java学院整理的文章就讲到这里了,如果你想了解更多关于aop的实现原理_动力节点Java学院整理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
过去几天我一直试图解决这个问题,但我做不到。我正在尝试生成 _ _ _ 形式的随机数。 _ _ _ _ 小数点前 3 位,然后是 4 位小数。 非常感谢任何帮助。谢谢, 院长 最佳答案 您发布的代码有
我的方法有问题。我需要从主类调用的方法的输出打印我: 需要这个输出:_ _ _ _ _ 我知道我可以将 System 的静态方法放入循环中,但这不是我想要的解决方案。我需要它来打印主类中方法的输出。
我正在学习 Scala,有一个非常基本的问题。考虑以下两个使用占位符语法的表达式 - // Syntax A val fnA = (_: Int, _: Int) => _ / _ // Synta
我正在使用图书馆 URLEmbeddedView 它在其库中定义了以下代码: func addConstraints(with view: UIView, center: CGPoint, multi
我一直在许多受人尊敬的文档中看到这个相当令人尴尬的事情:_|_ 或 (_|_) 找不到它的定义(Google 不能很好地处理符号)。那到底是什么呢? 最佳答案 来自 here :- Bottom Th
,_,( ){ ,_,| ,_,&};,_, 不知道是什么意思... 看起来像一个 bash 命令,但它可能是 s bash shell 指令或其他东西如果有人可以帮助理解这一点,我们将不胜感激。当我
所以我正在尝试构建一个函数,它接受一个元组列表并找到具有最大第二个元素的元组。但是我遇到了模式匹配错误。 这是我的代码。 resultTuple :: [((Int,Int),Int)] ->
我在 try Flow 编辑器中重现了我的情况,可以访问 here . 以下是链接发生问题时的代码: /* @flow */ type PayloadType = 1 | 2 | 3; type Tr
我在plfa读到这样一段代码。 import Relation.Binary.PropositionalEquality as Eq open Eq using (_≡_; refl; cong; s
这个问题在这里已经有了答案: Swift 3.0: compiler error when calling global func min(T,T) in Array or Dictionary e
是否有理由使用一个而不是另一个?似乎 _.some 和 _.map 更易于使用或适用于更多情况(根据我非常有限的经验),但从阅读它来看,它们听起来好像应该做同样的事情。我敢肯定还有其他这样的例子,我很
在 Xcode 7 Beta 中开始使用 Swift 2 后,出现错误 cannot invoke。是什么导致了这个问题? 我试图通过以下两个问题找出我的问题,但我仍然收到错误:Question 1
所以我玩了一会儿,试图写一些关于存在和变化的东西,我遇到了这段有趣的代码。 final case class Box[+T](val value: T) { def >>=[U](f: T =>
Here is the screenshot for the error. 遵循本教程 https://developers.google.com/places/ios-api/start 在本教程中
我正在为许多标准的 Underscore.js 函数重写底层代码,以提高我的 JavaScript 技能,但我有点受困于 _.every/ _.全部。似乎在库本身中,_.every/_.all 函数仅
我在 shell 脚本中多次看到他们在 if 比较中使用 "_",如下所示: if [ "_$str" = "_" ]; then ....; fi 上面的代码通过比较 if [ "_$str"= "
我正在尝试快速过滤字典: var data: [String: String] = [:] data = data.filter { $0.1 == "Test" } 上面的过滤器代码在 Swift
我在 Entity Framework 核心映射方面遇到了问题。我收到此异常“不支持从‘付款’到‘购买。付款’的关系,因为拥有的实体类型‘购买’不能位于非所有权关系的主要方面。”在调试此功能的测试时。
我正在尝试模拟groovy.sql.Sql调用(查询,params [],闭包)类。 下面是我正在尝试在DatabaseService类文件中的方法。 public void getUsers(Lis
在阅读 dart 代码时,我经常看到一些仅使用下划线 _ 参数调用的函数。这让我困扰了一段时间,由于 flutter 改进了它的分析消息,我有了一些线索......但我觉得我并没有真正理解这个概念:-
我是一名优秀的程序员,十分优秀!