引子
在过去的几年里,
AOP(
面向方面编程
)
已成为
Java
领域的热门话题,对
JAVA
开发人员来说,现在已经能够找到许多关于
AOP
的文章、讨论和实现了。
AOP
通常被称为实现横切关注点的工具,这意味着你可以使用
AOP
来将独立的逻辑片段模块化,也就是我们熟知的关注点,并将这些关注点应用于应用程序的多个地方。
AOP
和
OOP
并不相互抵触,它们是可以相辅相成的两个设计模型,
Spring
AOP
是实现
AOP
的一种技术,而
Spring AOP
也是
Spring
中一些子框架或子功能所依赖的核心。
在本文中,首先会讲解两种截然不同的
AOP
类型,静态的和动态的。在静态
AOP
中
,
如
AspectJ
的
AOP
,横切逻辑会在编译时应用到你的代码上,若非修改代码并重新编译的话你是不能改变它的。而使用动态
AOP
时,如
Spring
的
AOP
,横切逻辑是在运行时被动态加入的,这允许你无需重新编译代码就能修改横切的使用。这两种
AOP
相互补充,将它们结合使用将会在我们的应用中形成强有力的组合。
Spring
项目中有很多能集各种设计模式、编码技巧为一体的编码艺术,在灵活应用
Spring
的同时,若能把
Spring
项目里面的精华、设计思想、编码技巧等吸纳过来,这对于程序员来说将会是一件非常有意义的事。
从代理机制初探
AOP
我们暂且把
AOP
放到一边,先从一个简单例子来看一个议题,这个例子当中包含日志
(Logging)
动作,程序中常需要为某些动作或事件记下记录,以便在事后检查程序运作过程,或是作为出错时的信息。
来看一个最简单的例子,当你需要在执行某些方法时留下日志信息,可能会如下编写:
package inside.aop;
import java.util.logging.Level;
import java.util.logging.Logger;
public class HelloSpeaker {
private Logger logger=Logger.getLogger(this.getClass().getName());
public void hello(String name){
//方法执行开始时留下记录
logger.log(Level.INFO,"hello method start..............");
//程序主要功能
System.out.println("hello"+name);
//程序执行完毕时留下记录
logger.log(Level.INFO,"hello method end..............");
}
}
在
HelloSpeaker
类中,当执行
hello()
时,你希望方法在开始执行和执行完毕时都能留下记录,最简单的作法就是如以上的程序设计,在方法执行的前后加上日志动作,然而日志的这几行程序代码横切入
(Cross-cutting)HelloSpeaker
类中,对于
HelloSpeaker
类来说,日志的这几个动作并不属于
HelloSpeaker
业务逻辑,这无疑是
HelloSpeaker
增加了额外的职责
(
违反了面向对象设计的类的单一职责原则
)
。
可以使用代理
(Proxy)
机制来解决这个问题,在这里讨论两种代理方法:静态代理
(Static Proxy)
与动态代理
(Dynamic Proxy)
。
静态代理
在静态代理的实现中,代理对象与被代理对象必须实现同一个接口,在代理对象中可以实现日志等相关服务,并在需要的时候在调用被代理的对象,如此,被代理对象当中就可以仅保留与业务相关的职责。
重新设计
HelloSpeaker
类,首先定义一个
IHello
接口:
package inside.aop;
public interface IHello {
public void hello(String name);
}
然后让实现业务逻辑的
HelloSpeaker
类实现
IHello
接口,例如:
package inside.aop;
public class HelloSpeaker implements IHello{
public void hello(String name){
//程序主要业务逻辑
System.out.println("hello"+name);
}
}
可以看到,在
HelloSpeaker
类中现在没有任何日志的程序插入其中,日志服务的实现将被放置代理之中,代理对象同样也要实现
IHello
接口,例如:
package inside.aop;
import java.util.logging.Level;
import java.util.logging.Logger;
public class HelloProxy implements IHello {
private Logger logger=Logger.getLogger(this.getClass().getName());
private IHello helloObject;
public HelloProxy(IHello helloObject){
this.helloObject=helloObject;
}
public void hello(String name) {
//日志服务
logger.log(Level.INFO,"hello method start..............");
//执行业务逻辑
helloObject.hello(name);
//日志服务
logger.log(Level.INFO,"hello method end..............");
}
}
在
HelloProxy
类的
hello()
方法中,要真正实现业务逻辑前后可以安排日志服务,下面我们编写一个测试程序来看看如何使用代理对象。
package inside.aop;
public class StaticProxyTest {
public static void main(String[] args) {
IHello proxy=new HelloProxy(new HelloSpeaker());
proxy.hello("aop");
}
}
程序中调用执行的是代理对象,构造代理对象时必须给它一个被代理对象,记得在操作取回代理对象时,必须转换操作接口为
IHello
接口,下面是实际执行的结果。
2010-9-29 19:10:04 inside.aop.HelloProxy hello
信息: hello method start..............
hello,aop
2010-9-29 19:10:04 inside.aop.HelloProxy hello
信息: hello method end..............
这是静态代理的基本范例,然而正如你看到的,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每种方法进行代理,静态代理在程序规模较大时就无法胜任了,根据这个设计思想在
JDK1.3
之后就加入了动态代理的功能。在这里介绍静态代理的目的,是为了了解代理的基本原理。
动态代理
在
JDK1.3
之后加入了可协助开发动态代理功能的
API
,从此不必为特定的对象和方法编写特定的代理对象。使用代理对象,可以使用一个处理者
(Handler)
服务于各个对象。首先,一个处理者的类设计必须实现
java.lang.reflect.InvocationHandler
接口,下面用实例来进行说明,设计一个
LogHandler
类:
package inside.aop;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LogHandler implements InvocationHandler {
private Logger logger=Logger.getLogger(this.getClass().getName());
private Object target;
public Object bind(Object target){
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retValue=null;
logger.log(Level.INFO,"method start..........");
retValue=method.invoke(target, args);
logger.log(Level.INFO,"method end..........");
return retValue;
}
}
主要的概念是使用
Proxy
.newProxyInstance()
静态方法建立一个代理对象,建立代理对象时必须告知要代理的接口,之后就可以操作所建立的代理对象。在每次操作时会执行
InvocationHandler
的
invoke()
方法,
invoke()
方法会传入被代理对象的方法名称与执行参数,实际上要执行的方法会交由
method.invoke()
。如果对上面代码不清楚的地方,请读者自行查阅相关的
JDK
动态代理的只是。
要实现动态代理,同样必须定义所要代理的接口,此处使用之前定义的
IHello
接口,以及
HelloSpeaker
类。测试程序如下所示:
package inside.aop;
public class DynamicProxyTest {
public static void main(String[] args) {
LogHandler logHandler=new LogHandler();
IHello proxy=(IHello) logHandler.bind(new HelloSpeaker());
proxy.hello("AOP");
}
}
来看一下执行结果,如下所示:
2010-9-29 19:42:36 inside.aop.LogHandler invoke
信息: method start..........
hello,AOP
2010-9-29 19:42:36 inside.aop.LogHandler invoke
信息: method end..........
LogHandler
不再服务于特定对象接口,而
HelloSpeaker
也不用插入任何有关日志的动作,它不用意识到日志动作的存在。
现在回到
AOP
的议题上,那么我们之前的例子和
AOP
有什么关系?
HelloSpeaker
本身的职责是显示文字,却要插入日志动作,这使得
HelloSpeaker
的职责加重,用
AOP
的术语来说,日志的程序代码横切
(Cross-cutting)
入
HelloSpeaker
的程序执行流程中,日志这样的动作在
AOP
中称之为横切关注点
(Cross-cutting
concern)
。
使用代理对象将日志等于业务逻辑无关的动作或任务提取出来,设计成一个服务对象,这样的对象称之为切面
(Aspect)
。
AOP
中的
Aspect
所指的像日志等这类的动作或服务,将这些动作
(Cross-cutting
concerns)
设计为通用、不介入特定业务对象的一个职责清楚地
Aspect
对象,这就是所谓的
Aspect-oriented programming
,缩写名称即为
AOP
。
从上面的几个例子中可以看出,在好的设计之下,
Aspect
可以独立于应用程序之外,在必要的时候,可以介入应用程序之中提供服务,在不需要相关服务的时候,又可以将这些
Aspect
直接从应用程序中脱离出来,而应用程序本身不需要修改任何一行程序代码。
AOP
术语
Cross-cutting concern(
横切关注点
)
在上面的例子中,日志的动作原先被横切
(Cross -cutting)
入至
HelloSpeaker
本身所负责的业务流程中,另外类似于日志这类的动作,如安全
(Security)
检查、事务
(Transaction)
等系统层面的服务
(Service)
,在一些应用程序之中常被见到安插到各个对象的处理流程之中,这些动作在
AOP
的术语称为
Cross-cutting
concerns
。
Aspect(切面)
将散落在各个业务逻辑之中的
Cross-cutting concerns
收集起来,设计成各个独立可重用的对象,这些对象称为
Aspect
。例如,在我们的例子中,将日志的动作设计为一个
LogHandler
类,
LogHandler
类在
AOP
的术语就是
Aspect
的一个具体事例。
在
AOP
中着重于
Aspect
的辨认,使之从业务流程中独立出来。在需要该服务的时候,织入
(weave)
至应用程序之上;在不需要服务的时候,也可以马上从应用程序中剥离,且应用程序中的可重用组件不用做任何修改。
另一方面,对于应用程序中的可重用组件来说,按照
AOP
的设计方式,它不用知道提供服务的对象是否存在,具体地说,与服务相关的
API
不会出现在可重用的应用程序组件之上,因而可提高这些组件的可重用性,你可以把这些组件应用至其他的应用程序之中,不会因为加入了某些服务而与目前的应用程序框架发生耦合。
在
Spring AOP
中,一个方面是由一个实现
Advisor(
通知者
)
接口的类来表示。
Spring
提供了一些使用方便的
Advisor
接口的接口类,这样不用在自己的程序中创建各种各样不同的
Advisor
实例。
Advice(增强或通知)
Advice
不管怎么翻译成建议、通知或者增强,都不能直接反映其内容。笔者认为通知稍微能够体现出
Advice
的本质。
Aspect
当中
Cross-cutting concerns
的具体实现称之为
Advice
。以日志的动作而言,
Advice
中会包含日志程序代码是如何实现。
Advice
中包含了
Cross-cutting concerns
的行为或所要提供的服务。
换一种说法,通知
(Advice)
是指在定义好的切入点处,所要执行的程序代码。
JoinPoint(连接点)
Advice
在应用程序执行时加入业务流程的点或时机称之为
Joinpoint
,具体来说,就是
Advice
在应用程序中被执行的时机。
Spring
只支持方法的
Joinpoint
,执行时机可能是某个方法被执行之前或之后
(
或两者都有
)
,或是方法中某个异常发生的时候。
Spring AOP
中最明显的简化之一就是它只支持一种类型的连接点:方法调用。我们可以用它来完成大多数用到
AOP
的日常编程任务。
Pointcut(切入点)
Pointcut
定义了感兴趣的
Joinpoint
,当调用的方法符合
Pointcut
表示式时,将
Advice
织入至应用程序上提供服务。切入点指一个或多个连接点,可以理解成一个点的集合。切入点的描述比较具体,而且一般会跟连接点上下文环境结合。
Target(目标对象)
一个
Advice
被应用的对象或目标对象,在基于拦截器机制实现的
AOP
框架中,位于拦截器链上最末端的对象实例。一般情况下,拦截器末端都包含一个目标对象,通常也是实际业务对象。
Introduction(引入)
对于一个现存的类,
Introduction
可以为其增加行为,且不用修改该类的程序,具体来说,可以为某个已编写或编译完的类,在执行时期动态地加入一些方法或行为,而不用修改或新增任何一行程序代码。
在
Spring
中,引入被认为是一种特殊的通知。
Interceptor(拦截器)
拦截器是用来实现对连接点进行拦截,从而在连接点或后加入自定义的切面模块功能。在大多数
JAVA
的框架实现中,都是使用拦截器来实现字段访问及方法调用的拦截
(Interception)
。所以作用于同一个连接点的多个拦截器组成的一个拦截器链
(Interceptor
chain)
,链接上的每个拦截器通常会调用下一个拦截器。
Spring
AOP
就是采用拦截器来实现。
Proxy(代理)
在之前的静态代理和动态代理中,已经使用了实际的程序范例介绍过的代理机制,
Spring
的
AOP
主要是通过动态代理来完成的,可用于代理任何的接口。另一方面,
Spring
也可以使用
CGLIB
代理,可以代理类。
Weave(织入)
Advice
被应用至对象之上的过程成为织入
(Weave)
,在
AOP
中织入的方式有几个时间点:编译时期
(Compile time)
、类加载时期
(Classload
time)
、执行时期
(Runtime).
分享到:
相关推荐
Spring5 框架 ---- AOP ---- 代码 Spring5 框架 ---- AOP ---- 代码 Spring5 框架 ---- AOP ---- 代码 Spring5 框架 ---- AOP ---- 代码 Spring5 框架 ---- AOP ---- 代码 Spring5 框架 ---- AOP ---- 代码 Spring5 ...
赠送jar包:spring-aop-5.3.10.jar; 赠送原API文档:spring-aop-5.3.10-javadoc.jar; 赠送源代码:spring-aop-5.3.10-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.10.pom; 包含翻译后的API文档:spring...
spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...
开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE...
赠送jar包:spring-aop-5.3.12.jar; 赠送原API文档:spring-aop-5.3.12-javadoc.jar; 赠送源代码:spring-aop-5.3.12-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.12.pom; 包含翻译后的API文档:spring...
赠送jar包:spring-aop-5.3.15.jar; 赠送原API文档:spring-aop-5.3.15-javadoc.jar; 赠送源代码:spring-aop-5.3.15-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.15.pom; 包含翻译后的API文档:spring...
赠送jar包:spring-aop-5.3.15.jar; 赠送原API文档:spring-aop-5.3.15-javadoc.jar; 赠送源代码:spring-aop-5.3.15-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.15.pom; 包含翻译后的API文档:spring...
赠送jar包:spring-aop-5.3.7.jar; 赠送原API文档:spring-aop-5.3.7-javadoc.jar; 赠送源代码:spring-aop-5.3.7-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.7.pom; 包含翻译后的API文档:spring-aop...
本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际应用。 1. **核心概念** - **切面(Aspect)**:切面是关注点的模块化,包含业务逻辑之外的横切关注点,如日志、事务管理。 - **连接点(Join Point...
赠送jar包:spring-aop-5.2.0.RELEASE.jar; 赠送原API文档:spring-aop-5.2.0.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.2.0.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.2.0.RELEASE.pom;...
赠送jar包:spring-aop-5.3.7.jar; 赠送原API文档:spring-aop-5.3.7-javadoc.jar; 赠送源代码:spring-aop-5.3.7-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.7.pom; 包含翻译后的API文档:spring-aop...
赠送jar包:spring-aop-5.3.10.jar;赠送原API文档:spring-aop-5.3.10-javadoc.jar;赠送源代码:spring-aop-5.3.10-sources.jar;赠送Maven依赖信息文件:spring-aop-5.3.10.pom;包含翻译后的API文档:spring-aop...
spring-aop-5.3.22.jar Spring AOP provides an Alliance-compliant aspect-oriented programming implementation allowing you to define method interceptors and pointcuts to cleanly decouple code that ...
Spring AOP 源码分析笔记 Spring AOP(Aspect-Oriented Programming)是一种编程范式,它允许开发者 modularize cross-cutting concerns,即将横切关注点模块化。AOP 使得开发者可以将一些公共的功能模块化,以便在...
aopalliance-1.0.jar,org.springframework.aop-3.0.0.RELEASE.jar,org.springframework.jdbc-3.0.0.RELEASEorg.springframework.beans-3.0.0.RELEASE.jar等
赠送jar包:spring-aop-5.0.8.RELEASE.jar; 赠送原API文档:spring-aop-5.0.8.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.8.RELEASE.pom;...
"spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨Spring AOP、相关jar文件以及它们在实际开发中的作用。 首先,我们来看一下提供的文件: 1. aopalliance.jar:这是一...
org.springframework.aop-sources-3.0.5.release.jar
赠送jar包:spring-aop-5.0.10.RELEASE.jar; 赠送原API文档:spring-aop-5.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.10.RELEASE....
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种强大的方式来实现横切关注点,如日志、事务管理、性能监控等,而无需侵入业务代码。下面将详细介绍Spring AOP的注解方式和XML配置方式。 ### ...