`

Spring AOP 剖析(5)

阅读更多

在动态代理 和 CGLIB 的支持下, Spring AOP 框架的实现经过了两代。

 

从 Spring AOP 框架第一次发布,到 Spring 2.0 发布之前的 AOP 实现,是 Spring 第一代 AOP 实现。

 

Spring 2.0 发布后的 AOP 实现是第二代。

 

但是,Spring AOP 的底层实现机制一直没有变,唯一改变的,是各种 AOP 概念实现的表现形式以及 Spring AOP  的使用


方式。

 

 

Spring AOP 的底层实现机制

 

 

1. Spring AOP 中的 Joinpoint

 

Spring AOP 中,仅支持方法级别的 Joinpoint ,更确切的说,只支持方法执行 (Method Execution )类型的 Joinpoint

 

虽然 Spring AOP 仅提供方法拦截,但是实际的开发过程中,这已经可以满足 80% 的开发需求了。Spring AOP 之所以

 

如此,主要有以下几个原因。

 

a.  Spring AOP 要提供一个简单而强大的 AOP 框架,并不想因大而全使得框架本身过于臃肿。能够仅付出 20% 的

 

努力,就能够得到 80% 的回报。否则,事倍功半,并不是想看到的结果。

 

b.  对于类中属性 (Field )级别的 Joinpoint ,如果提供这个级别的拦截,那么就破坏了面向对象的封装,而且,完全

 

可以通过 setter 和 getter 方法的拦截达到同样的目的。

 

c.   如果应用需求非常特殊,完全超出了 Spring AOP 提供的那 80% 的需求支持,可以求助于现有其他 AOP 实现产品,

 

如 AspectJ。 目前看来, AspectJ 是 Java 平台对 AOP 支持最完善的产品,同时,Spring AOP 也提供了对 Aspect

 

的支持。

 

 

 

2.  Spring AOP 中的 Pointcut

 

Spring 中以接口 org.springframework.aop.Pointcut 作为其 AOP 框架中的所有 Pointcut 的最顶层抽象。

 

package org.springframework.aop;

public interface Pointcut {

	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();

	Pointcut TRUE = TruePointcut.INSTANCE;
}

 

ClassFilter  和  MethodMatcher 分别用于匹配被执行织入操作的对象以及相应的方法。

 

 

a.  ClassFilter

 

ClassFilter 接口的作用是对 Joinpoint 所处的对象进行 Class 级别的类型匹配。

 

package org.springframework.aop;

public interface ClassFilter {

	boolean matches(Class<?> clazz);

	ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

 

当织入的目标对象的 Class 类型与 Pointcut 所规定的类型相符时,matchers 方法将会返回 true,否则返回 false。

 

即意味着不会对这个类型的目标对象进行织入操作。比如,如果仅希望对系统中的 Foo 类型的对象执行织入,则可以

 

package prx.aop.proxy;

import org.springframework.aop.ClassFilter;

public class FooClassFilter implements ClassFilter{

	public boolean matches(Class<?> clazz) {
		return Foo.class.equals(clazz);
	}

}

 

如果类型对所捕捉的 Joinpoint 无所谓,那么 Pointcut 中使用的 ClassFilter 可以直接使用

 

ClassFilter TRUE = TrueClassFilter.INSTANCE 。

 

当 Pointcut 中返回的 ClassFilter 类型为该类型实例时,Pointcut 的匹配将会针对系统中所有类的实例。

 


b.  MethodMatcher

 

MethodMatcher 接口的作用是描述 Pointcut 中 Method Execution 的 Joinpoint 的集合。即对象中的方法是否匹配

 

该 Pointcut 的而需要拦截。也就是 Spring 主要支持的 方法级别的拦截 的依据。

 

package org.springframework.aop;

import java.lang.reflect.Method;


public interface MethodMatcher {

	boolean matches(Method method, Class<?> targetClass);

	boolean isRuntime();

	boolean matches(Method method, Class<?> targetClass, Object[] args);

	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}
 

MethodMatcher 通过重载,定义了两个 matches 方法,而这两个方法的分界线就是 isRuntime 方法。

 

两个 mathcers 方法的区别在于:

 

在进行方法拦截的时候,可以选择忽略方法执行时的传入参数,也可以每次都检查方法执行时的传入参数。

 

 

比如:现在要对 登录方法 login(String username, String passwod) 进行拦截

 

1. 只想在 login 方法之前插入计数功能,那么 login 方法的参数对于 Joinpoint 捕捉就是可以忽略的。

 

2. 在用户登录的时候对某个用户做单独处理(拒绝登录 或 给予特殊权限),那么方法的参数在匹配 Joinpoint 时必须要考虑到

 

(1).  StaticMethodMatcher

 

前一种情况下, isRuntime 返回 false , 表示不会考虑具体 Joinpoint 的方法参数, 这种类型的 MethodMatcher

 

称之为 StaticMethodMatcher。因为不用每次都检查参数,那么对于同样的类型的方法匹配结果,就可以在框架内部

 

缓存以提高性能。isRuntime 方法返回 false 表明当前的 MethodMatcher 为 StaticMethodMatcher 的时候, 只有

 

boolean matches(Method method, Class<?> targetClass);

 方法将被执行, 它的匹配结果将会成为其所属的 Pointcut 主要依据。

 

package org.springframework.aop.support;

import java.lang.reflect.Method;

import org.springframework.aop.MethodMatcher;

public abstract class StaticMethodMatcher implements MethodMatcher {

	public final boolean isRuntime() {
		return false;
	}

	public final boolean matches(Method method, Class<?> targetClass, Object[] args) {
		// should never be invoked because isRuntime() returns false
		throw new UnsupportedOperationException("Illegal MethodMatcher usage");
	}

}
 

(2). DynamicMethodMatcher

 

当 isRuntime 方法返回 true 时, 表明 MethodMatcher 将会每次都对方法调用的参数进行匹配检查,这种类型的

 

MethodMatcher 称之为 DynamicMethodMatcher。 因为每次都要对方法参数进行检查,无法对匹配结果进行缓存,

 

所以,匹配效率相对 StatisMethodMatcher 来说要差。

 

大部分情况下, StaticMethodMatcher 已经够用了,最好避免使用 DynamicMethodMatcher 类型。

 

如果一个 MethodMatcher 为 DynamicMethodMatcher , 那么只有 isRuntime 返回 true, 而且

 

matchers(Method method, Class targetClass) 也返回 true 的时候, 三个参数的 matchers 方法将被执行,进行

 

进一步检查匹配条件。否则不会执行 三个参数的 matchers 方法,直接返回 false 了。

 

package org.springframework.aop.support;

import java.lang.reflect.Method;

import org.springframework.aop.MethodMatcher;

public abstract class DynamicMethodMatcher implements MethodMatcher {

	public final boolean isRuntime() {
		return true;
	}

	/**
	 * Can override to add preconditions for dynamic matching. This implementation
	 * always returns true.
	 */
	public boolean matches(Method method, Class<?> targetClass) {
		return true;
	}

}
 

在 MethodMatcher 类型的基础上, Pointcut 可以分为两类, 即 StaticMethodMatcherPointcut 和 DynamicMethodMatcherPointcut。

 

因为 StaticMethodMatcherPointcut 具有明显的性能优势, 所以, Spring 为其提供了更多的支持。

 

Spring 中 Pointcut  局部类结构图

 


 

部分资料说明  AbstractRegexpMethodPointcut 还有个 子类  Perl5RegexpMethodPointcut,但是我在

 

Spring 3.0.5 包中没有看到此类。

 

从上图中看出 Spring 提供了集中常见的 Pointcut 实现 (浅红色的类图 )。


1.  NameMatchMethodPointcut

 

最简单的 Pointcut 实现,根据自身指定的一组方法名称与 Joinpoint 处的方法的名称进行匹配,支持“*”通配符实现简单

 

的模糊匹配。

 

NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();

pointcut.setMappedName("methodName");

pointcut.setMappedNames(new String[]{"methodName1", "methodName2"});

pointcut.setMappedNames(new String[]{"method*", "*Name", "method*Num"});

 

但是, NameMatchMethodPointcut 无法对重载的方法名进行匹配, 因为它仅对方法名匹配,不考虑参数信息。

 


2.  JdkRegexpMethodPointcut

 

StaticMethodMatcherPointcut 中正则表达式的分支实现,基于 JDK1.4 之后引入的 JDK 标准正则表达式。

 

JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();

pointcut.setPattern(".*method.*");

pointcut.setPatterns(new String[]{".*method.*", ".*name.*"});

 

注意:使用正则表达式来匹配对应的 Joinpoint 所处的方法时, 正则表达式的匹配模式必须以匹配整个方法签名的形式


指定,而不能像 NameMatchMethodPointcut 那样仅给出匹配的方法名称。

 

 

package prx.aop.proxy;

public class Foo {
	public void doSomething() {
		
	}
}

 

如果使用正则表达式  .*doS.* 则会匹配 Foo 的 doSomething  方法, 即完整签名:

 

prx.aop.proxy.Foo.doSomething  。 但是如果 Pointcut 使用 doS.* 作为匹配的正则表达式模式,就无法捕捉到

 

Foo 的 doSomething 方法的执行。

 


3.  AnnotationMatchingPointcut

 

根据目标对象中是否存在指定类型的注解来匹配 Joinpoint , 只能使用在 JDK5 或更高版本中, 因为注解是 JDK5

 

发布后才有的。

 

AnnotationMatchingPointcut 根据目标对象中是否存在指定类型的注解来匹配 Joinpoint ,要使用该类型的 Pointcut

 

首先需要声明相应的注解。

 

例如:

 

package prx.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassLevelAnnotation {

}

 

package prx.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodLevelAnnotation {

}

 

注解定义中的 @Target 指定注解可以标注的类型。 ClassLevelAnnotation用于类层次,MethodLevelAnnotation

 

用于方法层次。

 

不同的 AnnotationMatchingPointcut 的定义方式会产生不同的匹配行为。

 

//仅指定类级别的注解, 标注了 ClassLevelAnnotation 注解的类中的所有方法执行的时候,将全部匹配。
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class);

//还可以使用静态方法创建 pointcut 实例
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);


//仅指定方法级别的注解,标注了 MethodLeavelAnnotaion 注解的方法(忽略类匹配)都将匹配
AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MethodLevelAnnotation.class);


//同时限定类级别和方法级别的注解,只有标注了 ClassLevelAnnotation 的类中 同时标注了 MethodLevelAnnotation 的方法才会匹配
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class, MethodLevelAnnotation.class);

 

 讲了这么多,比较空泛了, 来个简单的实际例子看下: (注解的定义延用上面的代码)

 

package prx.aop.annotation;

@ClassLevelAnnotation
public class TargetObject {

	@MethodLevelAnnotation
	public void method1() {
		System.out.println("target : method1");
	}
	
	public void method2() {
		System.out.println("target : method2");
	}
}
package prx.aop.annotation;

import java.lang.reflect.Method;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;

public class Client {

	public static void main(String[] args) {	
		//pointcut 定义, 匹配方式可以按上面的说明修改,  这里是注解类的所有方法都匹配
		AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);
		
		// advice 定义, 根据前面的介绍知道 这个是 横切逻辑的定义, 这里是 方法执行前插入横切逻辑
		BeforeAdvice advice = new MethodBeforeAdvice() {
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println(target.getClass().getSimpleName() + ":" + method.getName() + " - before logic ");
			}	
		};
		
		// Spring 中的 Aspect , pointcut 和 advice 的封装类
		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
		advisor.setPointcut(pointcut);
		advisor.setAdvice(advice);
		
		// Spring 基本织入器 weaving 和 weaver
		ProxyFactory weaver = new ProxyFactory();
		weaver.setTarget(new TargetObject());	//指定代理目标对象
		weaver.addAdvisor(advisor);				//指定 Aspect
		
		Object proxyObject = weaver.getProxy();	//生成代理对象 (这里没接口, Spring 使用 CGLIB 创建子类)
		
		((TargetObject) proxyObject).method1();
		((TargetObject) proxyObject).method2();
	}
}
 

 


4.   ComposablePointcut

 

ComposablePointcut 是 Spring AOP 提供的可以进行 Pointcut 逻辑运算的 Pointcut 实现, 它可以进行 Pointcut

 

之间的 “并” 以及 “交” 运算。

 

ComposablePointcut pointcut1 = new ComposablePointcut(classFilter1, methodMatcher1);
ComposablePointcut pointcut2 = new ComposablePointcut(classFilter2, methodMatcher2);

//求并集
ComposablePointcut unitedPointcut 		= pointcut1.union(pointcut2);
//求交集
ComposablePointcut intersectionPointcut	= pointcut1.intersection(unitedPointcut);

assertEquals(pointcut1, intersectionPointcut);

 

同时, Spring AOP 还提供了 工具类: org.springframework.aop.support.Pointcuts

 

Pointcut pointcut1 = ...;
Pointcut pointcut2 = ...;

//求并集
Pointcut unitedPointcut = Pointcuts.union(pointcut1, pointcut2);
//求交集
Pointcut intersectionPointcut = Pointcuts.intersection(pointcut1, unitedPointcut);

assertEquals(pointcut1, intersectionPointcut);
 


5.   ControlFlowPointcut

 

非常有个性的 Pointcut 类型, 不是很常用。 指定只有当 Joinpoint 指定的某个方法 在 某个特定的 类中被调用时,才

 

对其进行拦截。而一般情况是,Joinpoint  指定的方法,无论被谁调用,都会被拦截。

 

package prx.aop.controlflow;

public class TargetObject {

	public void method1() {
		System.out.println("TargetObject : method1");
	}
}

 

package prx.aop.controlflow;

public class TargetCaller {
	
	private TargetObject target;
	
	public void callMethod() {
		target.method1();
	}
	
	public void setTarget(TargetObject target) {
		this.target = target;
	}
}

 

package prx.aop.controlflow;

import java.lang.reflect.Method;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class Client {

	public static void main(String[] args) {	
		//该 Pointcut 表示 Joinpoint 指定的方法 只有在 TargetCaller 类中被调用,才能拦截织入横切逻辑
		ControlFlowPointcut pointcut = new ControlFlowPointcut(TargetCaller.class);
		
		// advice 定义, 根据前面的介绍知道 这个是 横切逻辑的定义, 这里是 方法执行前插入横切逻辑
		BeforeAdvice advice = new MethodBeforeAdvice() {
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println(target.getClass().getSimpleName() + ":" + method.getName() + " - before logic ");
			}	
		};
		
		// Spring 中的 Aspect , pointcut 和 advice 的封装类
		DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
		advisor.setPointcut(pointcut);
		advisor.setAdvice(advice);
		
		// Spring 基本织入器 weaving 和 weaver
		ProxyFactory weaver = new ProxyFactory();
		weaver.setTarget(new TargetObject());	//指定代理目标对象
		weaver.addAdvisor(advisor);				//指定 Aspect
		
		Object proxyObject = weaver.getProxy();	//生成代理对象 (这里没接口, Spring 使用 CGLIB 创建子类)
	
		//直接调用 method1 不会触发横切逻辑执行
		((TargetObject) proxyObject).method1();

		System.out.println("-----------------");
		
		//advice的逻辑在这里才能被触发执行
		//因为 TargetCaller 的 callMethod() 将调用 method1
		TargetCaller caller = new TargetCaller();
		caller.setTarget((TargetObject)proxyObject);
		caller.callMethod();

	}
}

 

如果在 ControlFlowPointcut 的构造方法中单独指定 Class 类型的参数,如上面的例子,那么 ControlFlowPointcut

 

将尝试匹配指定的 Class 中声明的所有方法,跟目标对象的 Joinpoint 处的方法流程组合。 所以,如果是想要做到

 

“只有 TargetCaller 类的 callMethod 方法调用 TargetObject.method1() 才拦截,而 TargetCaller 的其他方法

 

全都忽略” 的话,可以在构造时,传入第二个参数

 

ControlFlowPointcut pointcut = new ControlFlowPointcut(TargetCaller.class, "callMethod");

 

因为 ControlFlowPointcut 类型的 Pointcut 需要在运行期间检查程序的调用栈,而且每次方法调用都需要检查,所以


性能比较差,应该尽量避免使用。

 

 

太长了。。。下回待续。O(∩_∩)O~

  • 大小: 28.4 KB
分享到:
评论

相关推荐

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    AOP流程源码分析-SpringAOP中定义的类图

    AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析-SpringAOP中定义的类图AOP流程源码分析...

    Spring AOP实现机制

    **Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的情况下,通过“切面”来插入额外的业务逻辑,如日志、事务管理等。AOP的引入极大地提高了代码的...

    五、Spring源码分析——Spring Aop

    《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...

    spring aop 学习笔记

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和抽象化的方法来处理系统中的交叉关注点,如日志、事务管理、安全性等。本学习笔记将深入探讨Spring AOP的核心概念、工作原理以及实际...

    Spring AOP框架实现的结构分析

    在本文中,我们将从实现的角度来认识 SpringAOP 框架,从外部接口、内部实现、组成部分、执行过程四个方面来介绍 Spring AOP 框架的结构分析。 最后,本文的目标是从实现的角度来认识 SpringAOP 框架,观察的角度是...

    Spring AOP简单demo

    **Spring AOP 简介** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要模块,它扩展了传统的面向对象编程,允许开发者定义“横切关注点”(cross-cutting concerns),如日志、事务...

    spring-aop-jar

    在IT领域,Spring框架是一个广泛使用的Java应用框架,它提供了许多功能,包括依赖注入、面向切面编程(AOP)等。"spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨...

    spring AOP的运用

    在本文中,我们将深入探讨Spring AOP的运用,并结合源码分析其工作原理。 首先,了解AOP的基本概念: 1. 切面(Aspect):切面是关注点的模块化,这些关注点通常是跨越多个对象的横切关注点,例如事务管理、日志...

    spring aop 经典例子(原创)

    Spring AOP,全称Aspect-Oriented Programming,是Spring框架中的一个重要组成部分,它引入了面向切面编程的概念,使得开发者可以将关注点分离,更好地实现业务逻辑与系统服务的解耦。在这个经典例子中,我们将深入...

    JavaEE Spring AOP使用

    通过分析这个测试案例,你可以了解如何在实际项目中实现AOP。例如,它可能包含了一个带有切面逻辑的切面类,使用了`@Before`注解的方法会在目标方法执行前运行,而使用`@After`注解的方法则会在目标方法执行后运行。...

    Spring aop 性能监控器

    本篇文章将深入探讨如何使用Spring AOP实现性能监控器,并通过源码分析来理解其工作原理。 首先,我们要了解AOP的核心概念——切面(Aspect)、通知(Advice)、连接点(Join Point)、切入点(Pointcut)和织入...

    Spring Aop使用实例

    **Spring AOP 使用实例** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的一个重要组成部分,它提供了一种在不修改原有代码的情况下,通过代理方式添加额外功能的技术。这种技术使得我们...

    spring aop 附带测试实例

    在提供的压缩包文件"springAOP"中,可能包含了以下内容: - **切面类(Aspect Class)**:包含切点和通知的Java类,可能使用了`@Aspect`注解。 - **目标类(Target Class)**:被AOP代理的对象,通常包含业务逻辑。...

    springAOP核心组件分析.pdf

    Spring AOP(面向切面编程)是Spring框架的一个重要组成部分,它允许开发者将横切关注点与业务逻辑分离,实现业务逻辑的模块化。AOP核心组件包括几个关键概念,如切面(Aspect)、通知(Advice)、连接点(Joinpoint...

    Spring AOP需要jar包.rar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理、性能监控等。在Java应用中,AOP通过代理模式实现,使得我们可以将横切关注...

    Spring AOP实现 项目源码 Myeclipse 直接导入可用

    **Spring AOP 实现详解** 在Java开发中,Spring框架以其强大的功能和灵活性深受开发者喜爱。其中,AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的一个重要特性,它允许开发者将关注点从核心业务...

    spring源码--AOP流程--笔记.docx

    Spring AOP 源码分析笔记 Spring AOP(Aspect-Oriented Programming)是一种编程范式,它允许开发者 modularize cross-cutting concerns,即将横切关注点模块化。AOP 使得开发者可以将一些公共的功能模块化,以便在...

    Spring AOP的底层实现技术

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理、性能监控等。AOP的核心概念包括切面(Aspect)、通知(Advice)、连接点...

Global site tag (gtag.js) - Google Analytics