AOP(面向方面编程)可以说是OOP(面向对象编程)的补充和完善。本文介绍了Spring的aop实现原理,让感兴趣的朋友一起学习。
什么是AOP
AOP(面向方面编程)可以说是OOP(面向对象编程)的补充和完善。OOP引入了封装、继承和多态的概念来建立一个对象层次结构来模拟一组公共行为。当我们需要为分散的对象引入公共行为时,OOP是无能为力的。也就是说,OOP允许你从上到下定义关系,但不适合从左到右定义关系。比如日志功能。日志通常水平分布在所有对象层次结构中,与它们所分布到的对象的核心功能无关。其他类型的代码也是如此,比如安全性、异常处理和透明持久性。这种分散在各处的无关代码叫做横切代码。在OOP设计中,导致大量的代码重复,不利于各个模块的重用。
简介
代理模式,前段时间写的一个java设计模式,最近在看Spring Aop的时候感觉应该和代理模式有密切关系,所以决定了解一下Spring Aop的实现原理。
说到AOP,就不得不说OOP,它引入封装、继承、多态等概念建立一个对象层次来模拟一组公共行为。但是,如果我们需要为某些对象引入公共部分,OOP会引入大量重复代码。比如:日志功能。
AOP技术使用一种叫做“横切”的技术来解剖封装对象的内部,将那些影响多个类的公共行为封装成一个可复用的模块,这样可以减少系统的重复代码,降低模块之间的耦合度,有利于以后的可操作性和可维护性。AOP将软件系统分为两部分:核心关注点和横切关注点。主要业务流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,它们经常出现在核心关注点的很多地方,而且所有的地方基本上都是相似的。例如授权认证、日志记录和事务处理。
实现原理
在前面学习agent模式的时候,我了解到agent模式可以分为动态agent和静态agent。现在,我们将首先基于代理模式实现我们自己的AOP框架,然后研究Spring的AOP的实现原理。
首先,实现静态代理。静态代理的关键是实现代理对象和目标对象的公共接口,代理对象持有目标对象的引用。
公共接口代码:
公共接口IHello {
/**
*商业方法
* @param str
*/
void sayHello(字符串str);
}
类别代码:
公共类Hello实现IHello{
@覆盖
public void sayHello(String str) {
system . out . println( hello str);
}
}
类代码,我们给它添加日志功能,在方法启动前后执行特定的方法。是不是和AOP特别像?
公共类ProxyHello实现IHello{
二等兵IHello你好;
公共代理Hello(IHello hello) {
super();
this.hello=hello
}
@覆盖
public void sayHello(String str) {
logger . start();//添加特定的方法
hello . say hello(str);
logger . end();
}
}
日志类代码:
公共类记录器{
公共静态void start(){
System.out.println(new Date()说你好开始.);
}
公共静态void end(){
system . out . println(new Date() say hello end );
}
}
测试代码:
公共类测试{
公共静态void main(String[] args) {
IHello Hello=new proxy Hello(new Hello());//如果我们需要日志功能,请使用代理类
//IHello Hello=new Hello();//如果我们不需要log函数,就使用目标类
Hello.sayHello(明天);
}
}
这样我们实现了最简单的AOP,但是会有一个问题:如果我们有很多像Hello这样的类,是不是也要写很多像HelloProxy这样的类?其实也是一件很麻烦的事情。jdk1.3之后,jdk为我们提供了一个API Java . lang . reflect . invocation handler的类,允许我们在JVM调用一些方法的时候,动态地为它们做一些事情。让我们实现动态代理。
动态代理实现主要是实现InvocationHandler的方法,将目标对象注入代理对象,利用反射机制执行目标对象。
接口的实现与静态代理相同,代理类代码为:
公共类DynaProxyHello实现InvocationHandler{
私有对象目标;//目标对象
/**
*通过反射实例化目标对象
* @param对象
* @返回
*/
公共对象绑定(对象对象){
this.target=object
返回proxy . newproxyinstance(this . target . getclass()。getClassLoader()、this.target.getClass()。getInterfaces(),this);
}
@覆盖
公共对象调用(对象代理、方法方法、对象[]参数)
可投掷的
对象结果=null
logger . start();//添加其他方法
//通过反射机制运行目标对象的方法
result=method . invoke(this . target,args);
logger . end();
返回结果;
}
}
测试类代码:
公共类DynaTest {
公共静态void main(String[] args) {
IHello hello=(IHello)新DynaProxyHello()。bind(new Hello());//如果我们需要日志功能,请使用代理类
//IHello Hello=new Hello();//如果我们不需要log函数,就使用目标类
Hello.sayHello(明天);
}
}
看了上面的代码,对比Spring AOP可能会有问题。log类只能在方法之前和之后打印,但是AOP在条件满足的情况下应该是可以执行的。那么,有没有可能将DynaPoxyHello对象与日志操作对象(Logger)解耦呢?
请看下面的代码实现,它将把DynaPoxyHello对象从日志操作对象(Logger)中分离出来:
如果我们想在代理对象的方法之前或之后添加日志操作码(或其他操作码),那么我们可以抽象一个接口。这个接口只有两个方法:一个是代理对象执行方法之前执行的方法,我们称之为start,第二个是代理对象执行方法之后执行的方法,我们称之为end。
Logger的接口:
公共接口ILogger {
void start(方法Method);
void end(方法方法);
}
记录器的接口实现:
公共类DLogger实现ILogger{
@覆盖
公共void开始(方法方法){
system . out . println(new Date()method . getname() say hello start . );
}
@覆盖
公共void end(方法method) {
system . out . println(new Date()method . getname() say hello end );
}
}
动态代理类:
公共类DynaProxyHello实现InvocationHandler{
//调用对象
私有对象代理;
//目标对象
私有对象目标;
公共对象绑定(对象目标、对象代理){
this.target=target
this.proxy=proxy
返回proxy . newproxyinstance(this . target . getclass()。getClassLoader()、this.target.getClass()。getInterfaces(),this);
}
@覆盖
公共对象调用(对象代理、方法方法、对象[]参数)
可投掷的
对象结果=null
//Reflect以获取运算符的示例
class clazz=this . proxy . getclass();
//反射以获取运算符的开始方法
method start=clazz . getdeclaredmethod( start ,new Class[]{ method . Class });
//反射执行start方法
start.invoke(this.proxy,new Object[]{ this . proxy . getclass()});
//执行要处理的对象的原始方法
方法.调用(this.target,args);
//反射以获取运算符的结束方法
method end=clazz . getdeclaredmethod( end ,new Class[]{ method . Class });
//反射执行end方法
end.invoke(this.proxy,新对象[]{ method });
返回结果;
}
}
测试代码:
公共类DynaTest {
公共静态void main(String[] args) {
IHello hello=(IHello)新DynaProxyHello()。bind(new Hello()、new DLogger());//如果我们需要日志功能,请使用代理类
//IHello Hello=new Hello();//如果我们不需要log函数,就使用目标类
Hello.sayHello(明天);
}
}
通过上面的例子可以发现,通过动态代理和启动技术,AOP的功能已经基本实现。如果只需要在方法执行前打印日志,可以不实现end()方法,这样就可以控制打印的时间。如果要让指定的方法打印日志,我们只需要在invoke()方法中添加一个对方法名称的判断,方法名称就可以写入xml文件中,这样就可以实现与配置文件的解耦,从而实现了一个简单的spring aop框架。
以上是边肖介绍的Spring的aop实现原理。希望对你有帮助!