[Spring实战系列] - No.3 Spring Aop编程与AspectJ

2024-04-12 14:48

本文主要是介绍[Spring实战系列] - No.3 Spring Aop编程与AspectJ,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在本篇文章,我们讲解什么叫AOP以及AOP在Spring中应用的方法。那么什么叫AOP呢?AOP为Aspect Oriented
Programming的缩写,意为:面向切面编程。听到这个名词,你可能会非常的困惑,什么叫面向切面编程呢?我们来回
顾一下在《Spring实战》中的一个例子。Knight(骑士)类和Minstrel(吟唱诗人)类之间有一个关系是,骑士每次经历一次
战斗,就会有一个吟唱诗人给他作诗歌颂。那么我们的骑士类和诗人之间是什么关系呢?需要每个骑士类内部包含一个
诗人,然后其实调用写诗的功能么?事实上,骑士并不知道诗人为他写诗,同时,诗人也不属于任何骑士,诗人为所有
骑士写诗。那么我们该怎么实现这个功能呢?这里我们就应用到了AOP编程,即面向切面编程。
想象一下,如果有一个诗人,以某种方式“包裹”了骑士(或者说覆盖在了骑士外边),在骑士每次进行战斗后(或前),

就会得知骑士战斗的消息,这样是不是就符合我们的需求了?这里,那个诗人就是我们需要的切面。

那么回到我们的AOP编程上,我们来分析一下AOP的架构以及其中的术语:

1.通知(Advice): AOP中,切面的功能被称为通知。通知定义了切面的功能以及何时被使用,它应该在方法调用 之前?之后?或者抛出异常时?
2.连接点(Joinpoint):程序可能有很多很多时机可以应用通知,这些时机被称为是“连接点”。连接点是在程序执
行过程中能够插入切面的一个点。
3.切入点(Pointcut):切入点的定义匹配通知要织入一个或多个连接点。切入点定义了“何地”应用通知。比如一个
程序会可能有很多函数,我们只在某个函数应用时,才应用通知。这就是切入点。
4.切面(Aspect):切面是切入点和通知的结合,他们共同定义了切面的功能,以及何时何地被应用。
5.引入(Introduction):引入允许我们向现有的类添加新方法或属性,从而可以在无需修改现有类的情况下,让它
们具有新的行为和状态。
6.目标(Target):被通知的对象
7.代理(Proxy):”代理“是向目标对象应用通知以后被创建的对象。可以和织入(Weaving)配合理解。
8.织入(Weaving):”织入“是把切面应用到目标对象来创建新的代理对象的过程。
下面我们来看如何创建一个经典的Spring切面:
还记得我们之前的例子么?在这里我们增加一个观众来对表演进行反馈。同时,我们应用上面所说的模式,把观
众做成一个切面。

首先创建观众类:

public class Audience {public Audience(){}public void takeSeats(){System.out.println("I'm taking seat");}public void turnOffPhone(){System.out.println("I'm turing off my phone");}public void applaud(){System.out.println("Claping...PA PA PA PA PA PA PA PA");}public void demandRefund(){System.out.println("Boo! I want my money back!");}
}

然后,我们创建一个观众服务:

public class AudienceAdvice  implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice{private Audience audience;public AudienceAdvice(){}public void before(Method method,Object[]args,Object target) throws Throwable{audience.takeSeats();audience.turnOffPhone();}public void afterReturning(Object returnValue,Method method,Object[]args,Object target) throws Throwable{audience.applaud();}public void afterThrowing(Throwable throwable){audience.demandRefund();}public void setAudience(Audience audience){this.audience = audience;}
}

那么在哪里使用这些通知呢?我们来定义切点和通知者:

<bean id="performancePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"><property name="pattern" value=".*perform"></property>
</bean>
<bean id="audienceAdvice" class="AudienceAdvice"><property name="audience" ref="audience"></property>
</bean>
<bean id="audienceAdvisor" class="org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"><property name="advice" ref="audienceAdvice"></property><property name="pointcut" ref="performancePointcut"></property>
</bean>

当然我们也可以使用AspectJ的切点

        <bean id="audienceAdvisor"class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"><property name="advice" ref="audienceAdvice"></property><property name="expression" value="execution(* *.perform(..))"></property></bean>

我们已经把切点,通知者都定义好了,接下来就要创建代理了:

        <bean id="sonnet29" class="Sonnet29"></bean><bean id="dukeTarget"class="PoeticJuggler"autowire="constructor"><constructor-arg ref="sonnet29"></constructor-arg></bean>
        <bean id="duke"class="org.springframework.aop.framework.ProxyFactoryBean"><property name="target" ref="dukeTarget"></property><property name="interceptorNames" value="audienceAdvisor"></property><property name="proxyInterfaces" value="Performer"></property></bean>-->

这样一来,代理也实现了。到此,一个切面就完成了。你有没有觉得这样非常的复杂呢?是的!上面的过程虽然
创建步骤很清晰,但是太冗长了。我们该怎么更好的使用切面呢?答案很简单。我们使用自动代理的@AspecJ切面。

一切回到原点。删掉你刚刚在intellij中敲的代码,回到我们的audience。按照下面,把audience注解为切面:

/*** Created by YanMing on 2017/2/21.*/
import org.aspectj.lang.annotation.*;@Aspect
public class Audience {public Audience(){}@Pointcut("execution(* *.perform(..))")public void performance(){}@Before("performance()")public void takeSeats(){System.out.println("I'm taking seat");}@Before("performance()")public void turnOffPhone(){System.out.println("I'm turing off my phone");}@AfterReturning("performance()")public void applaud(){System.out.println("Claping...PA PA PA PA PA PA PA PA");}@AfterThrowing("performance()")public void demandRefund(){System.out.println("Boo! I want my money back!");}
}

我们来解释一下上面的代码,按照切面-切点-通知的顺序来。首先切面就是我们的Audience类啦,因为我们使用
了@Aspect注解,将该类注解为一个切面。接下来切点就是我们Pointcut那两行。我们将凡是有执行*.perform()的地方
都定义为切点,并且以performance()函数进行标记,我们将会在这里应用通知。接下来,通知就是下面的四种函数。同
时,我们用注解标记了函数,声明了通知的何时使用。

这样就好了么?你是不是发现,我们并没有实现代理?对的。我们使用自动代理,只需要在aop.xml中这样写道:

<aop:aspectj-autoproxy/>

那么简短!快来试试我们的代码吧!像我们前面那样写单元测试。运行结果如下:


这种注解的方式要求,我们必须对源码进行操作。如果我们无法操作通知(audience)的具体代码是无法实现的。那
如果凑巧我们无法接触到源码,只知道其中的函数该怎么办呢?那么我们可以在aop.xml中,定义纯粹的POJO切面

在你的aop.xml中这么写,可以取代你以上的所有工作:

<aop:config><aop:aspect ref="audience"><aop:pointcut id="performance"expression="execution(* Performer.perform(..))" /><aop:before pointcut-ref="performance" method="takeSeats" /><aop:before pointcut-ref="performance" method="turnOffPhone" /><aop:after-returning pointcut-ref="performance"method="applaud" /><aop:after-throwing pointcut-ref="performance"method="demandRefund" /></aop:aspect></aop:config>

到此,本篇文章想要介绍的关于面向切面编程的部分讲解完了。下一篇文章我们将对Spring JDBC展开研究

本文Github源代码下载





这篇关于[Spring实战系列] - No.3 Spring Aop编程与AspectJ的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/897439

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input