Spring AOP 的实现机制
Spring AOP 的设计哲学也是简单而强大的。 它不打算将所有的 AOP 需求全部囊括在内,而是要以有限的 20% 的 AOP
支持,在满足 80% 的 AOP 需求。 如果觉得 Spring AOP 无法满足你所需要的那 80% 之外的需求,那么可以求助于
AspectJ 来完成, Spring AOP 对 AspectJ 提供了很好的集成。
Spring AOP 属于第二代 AOP, 采用动态代理机制和字节码生成技术实现
。与最初的 AspectJ 采用编译器将横切逻辑织入
目标对象不同,动态代理机制和字节码生成都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象
中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象。
1. 动态代理机制
关于代理模式:
在这个场景中,Client 想要请求具体的 SubjectImpl 实例,但是 Client 无法直接请求真正要访问的资源 SubjectImpl
而是必须通过 ISubject 资源的访问代理类 SubjectProxy 进行。
package prx.aop.proxy;
public interface ISubject {
public void request();
}
package prx.aop.proxy;
public class SubjectImpl implements ISubject {
public void request() {
System.out.println("this is the subjectImpl request logic");
}
}
package prx.aop.proxy;
public class SubjectProxy implements ISubject {
private ISubject subject;
public SubjectProxy() {
this.subject = new SubjectImpl();
}
public void request() {
subject.request();
}
}
package prx.aop.proxy;
public class Client {
public static void main(String[] args) {
ISubject proxyObject = new SubjectProxy();
//Client 想访问 SubjectImpl,但是找它不到,就去找 它的代理 SubjectProxy, 要 这个代理去通知 SubjectImpl
proxyObject.request();
}
}
当 Client 通过 request() 请求服务的时候, SubjectProxy 将请求转发给了 SubjectImpl 。 从这个角度上说,
SubjectProxy 反而有多此一举之嫌了。 不过, SubjectProxy 的作用不只局限于请求的转发, 更多时候是对请求添加
更多访问限制。
假设现在要增加系统需求,对 SubjectImpl 增加一个限制条件,只有在 上午 9 点 到 下午 6 点之间才能求情数据。
这是我们只需要修改 代理对象 就可以了。
package prx.aop.proxy;
import org.joda.time.TimeOfDay;
public class SubjectProxy implements ISubject {
private ISubject subject;
public SubjectProxy() {
this.subject = new SubjectImpl();
}
public void request() {
TimeOfDay startTime = new TimeOfDay(9, 0, 0);
TimeOfDay endTime = new TimeOfDay(17, 59, 59);
TimeOfDay currentTime = new TimeOfDay();
if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
return;
}
subject.request();
}
}
代理对象 SubjectProxy 就像是 SubjectImpl 的影子, 只不过这个影子通常拥有更过的功能。 如果 SubjectImpl 是
系统中的 Joinpoint 所在的对象, 即目标对象,那么就可以为这个目标对象创建一个代理对象,然后将横切逻辑添加到
这个代理对象中。 当系统使用这个代理对象运行的时候,原有逻辑的实现和横切逻辑就完全融合到一个系统中了。
Spring AOP 本质上就是采用这种代理机制实现的
, 但是,还有点差别。
这是因为, 系统中可不一定就 ISubject 的实现类有 request() 方法, IRequestable 接口以及相应实现类可能也有
request() 方法, 它们也是我们需要横切的关注点。
package prx.aop.proxy;
public interface IRequestable {
public void request();
}
package prx.aop.proxy;
public class RequestableImpl implements IRequestable {
public void request() {
System.out.println(" request processed in RequestableImpl ");
}
}
为了能够为 IRequestable 相应实现类也织入以上的横切逻辑, 我们又得提供对应的代理对象
package prx.aop.proxy;
import org.joda.time.TimeOfDay;
public class RequestableProxy implements IRequestable {
private IRequestable targetObject;
public RequestableProxy(IRequestable targetObject) {
this.targetObject = targetObject;
}
public void request() {
TimeOfDay startTime = new TimeOfDay(9, 0, 0);
TimeOfDay endTime = new TimeOfDay(17, 59, 59);
TimeOfDay currentTime = new TimeOfDay();
if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
return;
}
targetObject.request();
}
}
并且将该代理对象而不是目标对象绑定到系统中
IRequestable targetObject = new RequestableImpl();
IRequestable proxy = new RequestableProxy(targetObject);
proxy.request();
于是问题出现。 虽然 Joinpoint 相同 (request() 方法的执行 ), 但是对应的目标对象类型是不一样的。 针对不一样的
目标对象类型,需要为其单独实现一个代理对象。 而实际上,这些代理对象所有添加的横切逻辑是一样的。 当系统中存在
成百上千的符合 Pointcut 匹配条件的目标对象时,我们就要为这成百上千的目标对象创建成百上千的代理对象。
这种为对应的目标对象创建静态代理的方法,原理上是可行的,但具体应用上存在问题,所以要寻找其他方法,以避免以上
问题。
动态代理
JDK 1.3 后引入了一种称之为动态代理 (Dynamic Proxy)的机制。使用该机制,我们可以为指定的接口在系统运行期间
动态的生成代理对象, 从而帮助我们走出最初使用静态代理实现 AOP 的窘境。
动态代理机制的实现主要由 java.lang.reflect.Proxy 类 和 java.lang.reflect.InvocationHandler 接口组成。
使用动态代理实现前面的 “request时间控制” 功能。
package prx.aop.proxy;
public interface ISubject {
public void request();
}
package prx.aop.proxy;
public class SubjectImpl implements ISubject {
public void request() {
System.out.println("this is the subjectImpl request logic");
}
}
package prx.aop.proxy;
public interface IRequestable {
public void request();
}
package prx.aop.proxy;
public class RequestableImpl implements IRequestable {
public void request() {
System.out.println(" request processed in RequestableImpl ");
}
}
package prx.aop.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.joda.time.TimeOfDay;
public class RequestInvocationHandler implements InvocationHandler{
private Object target;
public RequestInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("request")) {
TimeOfDay startTime = new TimeOfDay(9, 0, 0);
TimeOfDay endTime = new TimeOfDay(17, 59, 59);
TimeOfDay currentTime = new TimeOfDay();
if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
System.out.println("service is not avaliable now.");
return null;
} else {
System.out.println("service is avaliable now");
}
return method.invoke(target, args);
}
return null;
}
}
package prx.aop.proxy;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
ISubject subject = (ISubject) Proxy.newProxyInstance(
Client.class.getClassLoader(),
new Class[]{ISubject.class},
new RequestInvocationHandler(new SubjectImpl()));
subject.request();
IRequestable requestable = (IRequestable) Proxy.newProxyInstance(
Client.class.getClassLoader(),
new Class[]{IRequestable.class},
new RequestInvocationHandler(new RequestableImpl()));
requestable.request();
}
}
即使还有更多的目标对象,只要它依然织入的横切逻辑相同,用 RequestInvocationHandler 一个类并通过 Proxy 为
它们生成相应的动态代理实例就可以满足要求。 当 Proxy 动态生成的代理对象上相应的接口方法被调用时,对应的
InvocationHandler 就会拦截相应的方法调用,并进行相应处理。
InvocationHandler 就是我们事先横切逻辑的地方,它是横切逻辑的载体,作用跟 Advice 是一样的。
动态代理虽好,但是不能满足所有的需求。因为动态代理机制只能对实现了相应 Interface 的类使用,如果某个类没有
实现任何的 Interface,就无法使用动态代理机制为其生成动态代理对象。 虽然面向接口编程应该是提倡的做法,但
不排除其他的编程实践。 对于没有实现任何 Interface 的目标对象,我们需要寻找其他方式为其动态的生成代理对象。
2. 动态字节码生成
使用动态字节码生成技术扩展对象行为的原理是:对目标对象进行集成扩展,为其生成相应的子类,而子类可以通过覆写来
扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式
相同的效果了。
使用继承的方式来扩展对象定义,也不能像静态代理模式那样,为每个不同类型的目标对象都创建相应的扩展子类。
所以,需要借组与 CGLIB 这样的动态字节码生成库,在系统运行期间动态的为目标对象生成相应的扩展子类。
package prx.aop.proxy;
public class Requestable {
public void request() {
System.out.println("requestable without implementint any interface");
}
}
package prx.aop.proxy;
import java.lang.reflect.Method;
import org.joda.time.TimeOfDay;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class RequestCallback implements MethodInterceptor {
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if(method.getName().equals("request")) {
TimeOfDay startTime = new TimeOfDay(9, 0, 0);
TimeOfDay endTime = new TimeOfDay(17, 59, 59);
TimeOfDay currentTime = new TimeOfDay();
if(! (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) ) {
System.out.println("service is not avaliable now.");
return null;
} else {
System.out.println("service is avaliable now");
}
return proxy.invokeSuper(object, args);
}
return null;
}
}
package prx.aop.proxy;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Requestable.class);
enhancer.setCallback(new RequestCallback());
Requestable proxy = (Requestable) enhancer.create();
proxy.request();
}
}
RequestCallback 实现了对 request() 方法请求进行访问控制的横切逻辑。 然后通过 CGLIB 的 Enhancer 为
目标对象动态地成成一个子类,并将 RequestCallback 中的横切逻辑附加到该子类中。
通过为 enhancer 指定需要生成的子类对应的父类,以及 Callback 实现, enhancer 最终生成了需要的代理对象实例。
使用 CGLIB 对类进行扩展的唯一限制就是 无法对 final 方法进行覆写。
以上两种技术(即:动态代理 与 动态字节码生成) 就是 Spring AOP 所使用的核心技术。 也就是 Spring AOP 的
Weaving And Weaver 的实现原理了。
- 大小: 26.4 KB
- 大小: 84.2 KB
- 大小: 26.9 KB
分享到:
相关推荐
Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...
AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析...
**Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的...
《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...
Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和抽象化的方法来处理系统中的交叉关注点,如日志、事务管理、安全性等。本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际...
在本文中,我们将从实现的角度来认识 SpringAOP 框架,从外部接口、内部实现、组成部分、执行过程四个方面来介绍 Spring AOP 框架的结构分析。 最后,本文的目标是从实现的角度来认识 SpringAOP 框架,观察的角度是...
**Spring AOP 简介** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要模块,它扩展了传统的面向对象编程,允许开发者定义“横切关注点”(cross-cutting concerns),如日志、事务...
在IT领域,Spring框架是一个广泛使用的Java应用框架,它提供了许多功能,包括依赖注入、面向切面编程(AOP)等。"spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨...
在本文中,我们将深入探讨Spring AOP的运用,并结合源码分析其工作原理。 首先,了解AOP的基本概念: 1. 切面(Aspect):切面是关注点的模块化,这些关注点通常是跨越多个对象的横切关注点,例如事务管理、日志...
Spring AOP,全称Aspect-Oriented Programming,是Spring框架中的一个重要组成部分,它引入了面向切面编程的概念,使得开发者可以将关注点分离,更好地实现业务逻辑与系统服务的解耦。在这个经典例子中,我们将深入...
通过分析这个测试案例,你可以了解如何在实际项目中实现AOP。例如,它可能包含了一个带有切面逻辑的切面类,使用了`@Before`注解的方法会在目标方法执行前运行,而使用`@After`注解的方法则会在目标方法执行后运行。...
本篇文章将深入探讨如何使用Spring AOP实现性能监控器,并通过源码分析来理解其工作原理。 首先,我们要了解AOP的核心概念——切面(Aspect)、通知(Advice)、连接点(Join Point)、切入点(Pointcut)和织入...
**Spring AOP 使用实例** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要组成部分,它提供了一种在不修改原有代码的情况下,通过代理方式添加额外功能的技术。这种技术使得我们...
在提供的压缩包文件"springAOP"中,可能包含了以下内容: - **切面类(Aspect Class)**:包含切点和通知的Java类,可能使用了`@Aspect`注解。 - **目标类(Target Class)**:被AOP代理的对象,通常包含业务逻辑。...
Spring AOP(面向切面编程)是Spring框架的一个重要组成部分,它允许开发者将横切关注点与业务逻辑分离,实现业务逻辑的模块化。AOP核心组件包括几个关键概念,如切面(Aspect)、通知(Advice)、连接点(Joinpoint...
4. spring-context.jar:扩展了spring-beans,提供了上下文支持,包括事件发布、国际化、资源加载等功能,也是AOP的基础。 5. aspectjrt.jar:AspectJ运行时库,用于在运行时处理切面。 6. aspectjweaver.jar:...
**Spring AOP 实现详解** 在Java开发中,Spring框架以其强大的功能和灵活性深受开发者喜爱。其中,AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的一个重要特性,它允许开发者将关注点从核心业务...
Spring AOP 源码分析笔记 Spring AOP(Aspect-Oriented Programming)是一种编程范式,它允许开发者 modularize cross-cutting concerns,即将横切关注点模块化。AOP 使得开发者可以将一些公共的功能模块化,以便在...
4. **AOP代理的创建** Spring AOP在启动时会根据配置创建代理对象。如果目标类实现了接口,将使用JDK动态代理;否则,使用CGLIB代理。代理对象在调用目标方法时会插入通知逻辑。 5. **代理对象的工作原理** 当...