Spring源码分析—-AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

一、基本知识

1.1.AOP基本概念:

Aspect-Oriented Programming,面向方面编程的简称,Aspect是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern),从关注点中分离出横切关注点是面向方面程序设计的核心所在。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过方面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。

1.2.AOP和OOP的区别:

面向方面编程AOP和面向对象编程OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。

OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

1.3. AOP常用的技术以及实现:

常用的AOP技术有:

(1).AspectJ:源代码和字节码级别的方面编织器,用户需要使用不同于Java的新语言。

(2).AspectWerkz:AOP框架,使用字节码动态编织器和XML配置。

(3).JBoss-AOP:基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上。

AOP中使用的一些实现技术有:、

(1).BCEL:Byte-Code Engineering Library,Java字节码操作类库。

(2).Javassist:Java字节码操作类库,JBoss的一个子项目。

1.4.面向方面编程(AOP)的常用术语:

(1).切面Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些切入点Pointcut 以及对切入点进行相应的操作的通知Advice。

(2).连接点Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它连接点jointpoint。

(3).切入点Pointcut:表示一组连接点jointpoint,这些连接点或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的操作处理通知Advice将要发生的地方。

(4).通知Advice:Advice 定义了在切入点pointcut 里面定义的程序点具体要做的操作和处理,它通过 before、after 和 around 来区别是在每个切入点之前、之后还是代替执行的代码。

(5).目标对象Target:代理的目标对象,即切面要被应用到的目标对象。

(6).织入Weave:指将切面应用到目标对象,并导致代理对象创建的过程。

二、Advice通知:

Advice通知是AOP联盟定义的一个接口,定义当拦截到连接点做相应的处理操作,为切面增强提供织入接口。在spring AOP中,通知主要描述Spring AOP围绕方法调用而注入切面的行为,Spring AOP的通知扩展了AOP联盟的通知接口,提供了前置通知BeforeAdvice、后置通知AfterReturningAdvice、最终通知AfterAdvice和例外通知ThrowsAdvice等。
(1).
以一个方法为例,讲解
Spring
中通知的类型:

[java]
view plain
copy

  1. /**
  2. * proxy: 代理对象。 一般不使用该对象
  3. * method: 正在被调用的方法
  4. * args: 调用方法传入的参数
  5. */
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args)
  8. throws Throwable { // 环绕通知 @Around
  9. String methodName = method.getName();
  10. //打印日志
  11. System.out.println(“[before] The method “ + methodName + ” begins with “ + Arrays.asList(args));
  12. //调用目标方法
  13. Object result = null;
  14. try {
  15. //前置通知 @Before
  16. result = method.invoke(target, args);
  17. //返回通知 @AfterReturning, 可以访问到方法的返回值
  18. } catch (NullPointerException e) {
  19. e.printStackTrace();
  20. //异常通知 @AfterThrowing, 可以访问到方法出现的异常
  21. }
  22. //后置通知 @After. 因为方法可以能会出异常, 所以访问不到方法的返回值
  23. //打印日志
  24. System.out.println(“[after] The method ends with “ + result);
  25. return result;
  26. }

20191102100847\_1.png

[java]
view plain
copy

  1. public**interface** Advice {
  2. }
  3. public**interface BeforeAdvice extends** Advice {
  4. }
  5. // 前置增强接口,使用这个前置接口需要实现一个回调函数
  6. public**interface MethodBeforeAdvice extends** BeforeAdvice {
  7. /**
  8. * 作为回调函数,该方法的实现在Advice中被配置到目标方法后,会调用目标方法时被回调。
  9. * @param method Method对象,是目标方法的反射对象
  10. * @param args 对象数组,包含目标方法的输入参数
  11. * @param target
  12. * @throws Throwable
  13. */
  14. void before(Method method, Object[] args, Object target) throws Throwable;
  15. }
  16. public**interface AfterAdvice extends** Advice {
  17. }
  18. public**interface AfterReturningAdvice extends** AfterAdvice {
  19. // 该方法也是一个回调方法
  20. void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
  21. }
  22. public**interface ThrowsAdvice extends** AfterAdvice {
  23. }

三、Pointcut切点

Pointcut切入点决定通知Advice应该作用于哪个连接点,即通过Pointcut切入点来定义目标对象中需要使用AOP增强的方法集合,这些集合的选取可以按照一定的规则来完成。

Pointcut通常意味着标识方法,这些需要增强的方法可以被某个正则表达式进行标识,或者根据指定方法名进行匹配等。下面是Pointcut设计:

20191102100847\_2.png

(1).Pointcut切入点源码:

[java]
view plain
copy

  1. public**interface** Pointcut {
  2. //获取类过滤器
  3. ClassFilter getClassFilter();
  4. //获取匹配切入点的方法
  5. MethodMatcher getMethodMatcher();
  6. //总匹配的标准切入点实例
  7. Pointcut TRUE = TruePointcut.INSTANCE;
  8. }

    在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好Advice通知。

    2).在Pointcut的类继承关系中,以正则表达式切点JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。该类完成通过正则表达式对方法名进行匹配的功能。

[java]
view plain
copy

  1. public**class JdkRegexpMethodPointcut extends** AbstractRegexpMethodPointcut {
  2. //要编译的正则表达式模式
  3. private Pattern[] compiledPatterns = new Pattern[0];
  4. //编译时要排除的正则表达式模式
  5. private Pattern[] compiledExclusionPatterns = new Pattern[0];
  6. //将给定的模式字符串数组初始化为编译的正则表达式模式
  7. protected**void initPatternRepresentation(String[] patterns) throws** PatternSyntaxException {
  8. this.compiledPatterns = compilePatterns(patterns);
  9. }
  10. //将给定的模式字符串数组初始化为编译时要排除的正则表达式模式
  11. protected**void initExcludedPatternRepresentation(String[] excludedPatterns) throws** PatternSyntaxException {
  12. this.compiledExclusionPatterns = compilePatterns(excludedPatterns);
  13. }
  14. //使用正则表达式匹配给定的名称
  15. protected**boolean matches(String pattern, int** patternIndex) {
  16. Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
  17. return matcher.matches();
  18. }
  19. //使用要排除的正则表达式匹配给定的名称
  20. protected**boolean matchesExclusion(String candidate, int** patternIndex) {
  21. Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);
  22. return matcher.matches();
  23. }
  24. //将给定的字符串数组编译为正则表达的模式
  25. private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {
  26. Pattern[] destination = new Pattern[source.length];
  27. for (int i = 0; i < source.length; i++) {
  28. destination[i] = Pattern.compile(source[i]);
  29. }
  30. return destination;
  31. }
  32. }

    从上面的源码分析中,我们可以看到,最简单的使用正则表达式匹配的Pointcut切入点基本功能就是根据正则表达式判断方法名等是否匹配。

四、Advisor通知器:

当完成对目标对象方法的增强行为操作(Advice)和切入点Point的设计开发之后,需要一个对象将目标对象、增强行为和切入点三者结合起来,通知器Advisor就是一个实现这个功能的对象,即通过Advisor通知器,可以定义那些目标对象的那些方法在什么地方使用这些增加的行为。

(1).Advisor通知器:

Advisor通知器的源码如下:

[java]
view plain
copy

  1. public**interface** Advisor {
  2. //获取切面的通知Advice
  3. Advice getAdvice();
  4. //判断这个通知是否和某个特定的实例对象相关
  5. boolean isPerInstance();
  6. }

(2). Advisor通知器的实现类DefaultPointcutAdvisor:

查看Advisor通知器的继承体系,发现Advisor的实现类很多,我们以最常用的DefaultPointcutAdvisor为例,分析通知器的工作原理。

a. DefaultPointcutAdvisor源码如下

[java]
view plain
copy

  1. public**class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements** Serializable {
  2. //默认切入点 — – – ——>
  3. //Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;
  4. private Pointcut pointcut = Pointcut.TRUE;
  5. //无参构造方法,创建一个空的通知器
  6. public DefaultPointcutAdvisor() {
  7. }
  8. //创建一个匹配所有方法的通知器
  9. public DefaultPointcutAdvisor(Advice advice) {
  10. this(Pointcut.TRUE, advice);
  11. }
  12. //创建一个指定切入点和通知的通知器
  13. public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
  14. this.pointcut = pointcut;
  15. setAdvice(advice);
  16. }
  17. //为通知设置切入点
  18. public**void** setPointcut(Pointcut pointcut) {
  19. this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
  20. }
  21. //获取切入点
  22. public Pointcut getPointcut() {
  23. return**this**.pointcut;
  24. }
  25. public String toString() {
  26. return getClass().getName() + “: pointcut [“ + getPointcut() + “]; advice [“ + getAdvice() + “]”;
  27. }
  28. }

b.TruePointcut:

TruePointcut作用通知器默认的切入点,其主要功能是配置默认的类过滤器和方法匹配器,即定义Spring AOP对于哪些类的哪些方法其作用,源码如下:

[java]
view plain
copy

  1. class TruePointcut implements Pointcut, Serializable {
  2. //INSTANCE是TruePointcut类的一个常量单件,即整个应用中只有这个一个,
  3. //不会创建第二个实例对象,确保该实例对象的唯一性,单例模型
  4. public**static**final TruePointcut INSTANCE = new TruePointcut();
  5. //单态模式构造方法
  6. private TruePointcut() {
  7. }
  8. //获取切入点的类过滤器
  9. public ClassFilter getClassFilter() {
  10. return ClassFilter.TRUE;
  11. }
  12. //获取切入点的方法匹配器
  13. public MethodMatcher getMethodMatcher() {
  14. return MethodMatcher.TRUE; // ————>
  15. }
  16. //获取单态模式对象的方法
  17. private Object readResolve() {
  18. return INSTANCE;
  19. }
  20. public String toString() {
  21. return“Pointcut.TRUE”;
  22. }
  23. }
  24. //———————————————
  25. public**interface** MethodMatcher {
  26. boolean matches(Method method, Class<?> targetClass);
  27. boolean isRuntime();
  28. boolean matches(Method method, Class<?> targetClass, Object… args);
  29. MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
  30. }

    从TruePointcut的源码我们看到,切入点使用TrueClassFilter作为类过滤器,匹配任意的类; 使用TrueMethodMatcher作为方法匹配器,匹配任意的方法。下面我们继续分析TrueClassFilter类过滤器和TrueMethodMatcher方法匹配器。

c.TrueClassFilter:

TrueClassFilter作为默认切入点的默认类过滤器,主要告诉切入点对哪些类进行增强,源码如下

[java]
view plain
copy

  1. class TrueClassFilter implements ClassFilter, Serializable {
  2. //单态模式
  3. public**static**final TrueClassFilter INSTANCE = new TrueClassFilter();
  4. //单态模式的构造方法
  5. private TrueClassFilter() {
  6. }
  7. //切入点过滤匹配类的方法,默认对所有的类都增强
  8. public**boolean** matches(Class clazz) {
  9. return**true**;
  10. }
  11. //获取单态模式对象的方法
  12. private Object readResolve() {
  13. return INSTANCE;
  14. }
  15. public String toString() {
  16. return“ClassFilter.TRUE”;
  17. }
  18. }

d.TrueMethodMatcher:

TrueMethodMatcher作为默认切入点的默认方法匹配器,主要告诉切入点对哪些方法进行增强,源码如下:

[java]
view plain
copy

  1. class TrueMethodMatcher implements MethodMatcher, Serializable {
  2. //单态模式
  3. public**static**final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
  4. //单态模式构造方法
  5. private TrueMethodMatcher() {
  6. }
  7. //不支持运行时调用
  8. public**boolean** isRuntime() {
  9. return**false**;
  10. }
  11. //切入点匹配方法时调用,默认匹配所有的方法
  12. public**boolean** matches(Method method, Class targetClass) {
  13. return**true**;
  14. }
  15. //运行时调用将抛出异常
  16. public**boolean** matches(Method method, Class targetClass, Object[] args) {
  17. throw**new** UnsupportedOperationException();
  18. }
  19. //获取单态模式对象的方法
  20. private Object readResolve() {
  21. return INSTANCE;
  22. }
  23. public String toString() {
  24. return“MethodMatcher.TRUE”;
  25. }
  26. }

    从上面方法匹配器的源码,我们可以看出,切入点对方法进行匹配时不支持运行时的匹配,如果在运行时进行匹配将抛出异常。

六、AOP的设计与实现

在Spring AOP实现中,使用的核心技术是动态代理,而这种动态代理实现上是JDK的一个特性。通过JDK的动态代理特性,可以为任意Java对象创建代理对象,对于具体使用来说,这个特性是通过Java Reflection API来完成的。

Proxy模式:
http://blog.csdn.net/ochangwen/article/details/51475807

1.Spring AOP的设计分析

AOP模块是Spring的核心模块,虽然Java社区里AspectJ是最完整的AOP框架,但Spring AOP也提供了另外一种实现,这种实现并不是AspectJ的竞争者,相反,Spring AOP还将AspectJ集成了进来,为IoC容器和Spring应用开发提供了一个一致性的AOP解决方案。

Spring AOP的核心技术是动态代理
。以动态代理技术为基础设计出了一系列AOP的横切实现,比如前置通知,返回通知,异常通知等。同是,Spring AOP还提供了一系列的Pointcut来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关的Pointcut方法来实现切入需要。

为了让AOP起作用,需要完成一系列过程,如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器CGLIB来完成。然后,还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现的。通过一系列Adapter的设计,可以把AOP的横切面设计和Proxy模式有机地结合起来,从而实现在AOP中定义好的各种织入方式。具体实现后面分析。

2.Spring AOP的应用场景

Spring AOP为IoC的使用提供了更多的便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的,如事务处理。从这两个角度就已经看到Spring AOP的核心地位了。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析—-AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏