Spring中的AOP(七)(实际上应该是一)——AspectJ的基本使用

2024-04-27 11:58

本文主要是介绍Spring中的AOP(七)(实际上应该是一)——AspectJ的基本使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充,当前已经成为一种比较成熟的编程思想,其实AOP问世的时间并不长,甚至在国内的翻译还不太统一(另有人翻译为“面向方面编程”)。AOP和OOP(Object Orient Programming,面向对象编程)互为补充,OOP将程序分解成各个层次的对象,而AOP则将程序运行过程分解成各个切面。可以这样理解:OOP是从静态角度考虑程序结构,而AOP则从动态角度考虑程序运行过程。


为什么需要AOP

    在传统OOP变成立,以对象为核心,整个软件系统由系列相互依赖的对象组成,而这些对象被抽象成一个一个的类,并允许使用类继承来管理类与类之间从一般到特殊的关系。随着软件规模的增大,应用的逐渐升级,慢慢出现了一些OOP很难解决的问题。

    我们可以通过分析、抽象出一系列具有一定属性与行为的对象,并通过这些对象之间的协作来形成一个完整的软件功能。由于对象可以继承,因此我们可以把具有相同功能或相同特性的属性抽象到一个层次分明的类结构体系中。随着软件规模的不断扩大,专业化分工越来越系列,以及OOP应用实践的不断增多,随之也暴露出一些OOP无法很好解决的问题。

    假设系统中有3段完全相似的代码,这些代码通常会采用“复制”、“粘贴”方式来完成,通过这种复制和粘贴完成的代码在后期将很难维护:想想一下,如果有一天,这些被复制和粘贴的代码需要修改,那么,是不是会修改这3处呢?如果这段代码被复制和粘贴了100遍呢,1000遍呢,如何维护?大多数人会想到将这段代码抽取出来,作为一个公共的方法,在需要使用这段代码的地方,调用这个方法即可。这样如果这段代码需要修改,只需要修改这个公共的方法即可。但实际的情况是:即使将公共的部分抽取出来了,每个地方还是需要去显式调用这个方法,这能够解决大部分问题。但是对于一些更加特殊的情况:应用需要将公共的部分与调用的地方彻底分离,那又应该如何解决呢?

    因为软件系统需求变更时很频繁的事情,假设系统前期设计方法1、2、3时只实现了核心业务,一段时间之后,我们需要对这些方法都进行事务控制;又过了一段时间,客户提出这些方法需要进行合法的用户验证,只有合法的用户才能调用这些方法;又过了一段时间,客户又提出这些方法需要增加日志记录;又过了一段时间……面对这种问题,我们应该怎么办呢?是不是每次先定义一个新的方法,然后再去修改方法1、2、3增加调用新的方法的代码块呢?这样做的工作量也不小啊!

    我们希望有一种特殊的方法:我们只要定义该方法,无需在方法1、2、3中显式调用它,系统会“自动”调用该方法。

    注意:上面的自动被加上了引号,是因为在编程过程中,没有所谓的自动的事情,在程序的世界里,任何事情都是由代码驱动的。这里的自动是指,无需开发者关心,由系统来驱动。

    上面的想法听起来很神奇,甚至有些不切实际,但其实是完全可以实现的,实现这个需求的技术就是AOP。AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,例如事务管理、安全检查、缓存、对象池等等,AOP已经成为一种非常常用的方案。


使用AspectJ实现AOP

    AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其他很多AOP框架都借鉴或采纳其中的一些思想。由于spring 3.0的AOP与AspectJ进行了很好的集成,因此掌握AspectJ是学习Spring AOP的基础。

    AspectJ是Java语言的一个AOP实现,其主要包括两个部分:第一个部分定义了如何表达、定义AOP编程中的语法规范,通过这个规范,我们可以方便的用AOP来解决Java语言中存在的交叉关注点的问题;另一个部分是工具部分,包括编译器、调试工具等。

    AspectJ是最早、功能比较强大的AOP实现之一,对嵌套AOP机制都有较好的实现,很多其他语言的AOP实现,也借鉴或采纳了AspectJ中的很多设计。在Java领域,AspectJ中的很多语法结构基本上已成为AOP领域的标准。

    从Spring 2.0开始,Spring AOP已经引入了对AspectJ的支持,并允许直接使用AspectJ进行AOP编程,而Spring自身的AOP API也努力与AspectJ保持一致。因此,学习SpringAOP就必然需要从AspectJ开始,因为它是Java领域最流行的AOP解决方案,我们甚至可以直接使用AspectJ进行AOP编程。

  • AspectJ的下载和安装(安装AspectJ之前,请确保系统已经安装了JDK)。

    1. 下载AspectJ的最新稳定版: http://www.eclipse.org/aspectj/downloads.php#stable_release 下载下来后是一个jar包。

    2. 打开命令行,cd到该jar包所在的文件夹,运行java -jar aspectj-1.7.4.jar命令,打开AspectJ的安装界面。第一个界面是欢迎界面,直接next。

    3. 第二个界面中,选择jre的安装路径,next。

    4. 第三个界面中,选择AspectJ的安装路径,next。因为安装过程的实质是解压一个压缩包,并不需要太多的依赖于系统,因此路径可以任意选择,这里我选择和Java安装在一起。

    5. 安装完成后,按照界面提示,需要配置classpath和PATH,这里不做介绍。

     至此,AspectJ安装完成。

    AspectJ提供了编译、运行AspectJ的一些工具命令,这些命令放在AspectJ的bin目录下,而lib路径下的aspectjrt.jar则是AspectJ的运行时环境,所以我们需要分别添加这两个环境变量。

  • AspectJ的使用入门

    成功安装AspectJ后,在其安装目录下有如下结构:

    bin:该路径下存放了AspectJ的常用命令,其中ajc最为常用,其作用类似于javac,用于对普通Java类进行编译时增强

    docs:该路径下存放了AspectJ的使用说明、参考手册和API等文档

    lib:该路径下的4个jar文件是AspectJ运行的核心类库

    license和readme文件

    这里要提到的是,一些文档、AspectJ的入门书籍,一谈到使用AspectJ,就认为必须使用Eclipse工具,似乎离开了该工具就不能使用AspectJ了。实际上,虽然AspectJ是Eclipse基金组织的开源项目,而且提供了Eclipse的AJDT(AspectJ Development Tools)插件来开发AspectJ应用,但AspectJ绝对无需依赖于Eclipse工具。

    AspectJ的用法很简单,就像我们使用JDK编译、运行Java程序一样。下面通过一个简单的程序来示范AspectJ的用法:

?
1
2
3
4
5
6
7
8
9
public  class  HelloWorld {
     public  void  sayHello(){
         System.out.println( "Hello AspectJ!" );
     }
     public  static  void  main(String args[]) {
         HelloWorld h =  new  HelloWorld();
         h.sayHello();
     }
}

    毫无疑问,结果将输出"Hello AspectJ!"字符串。假设现在客户需要在执行sayHello方法前启动事务,当该方法结束时关闭事务,在传统编程模式下,我们必须手动修改sayHello方法——如果改为使用AspectJ,则可以无需修改上面的sayHello方法。下面我们定义一个特殊的“类”:

?
1
2
3
4
5
6
7
public  aspect TxAspect {
     void  around():call( void  sayHello()) {
         System.out.println( "Transaction Begin" );
         proceed();
         System.out.println( "Transaction End" );
     }
}

    可能有人已经发现,上面的类文件中不是使用class、interface或者enum来定义Java类,而是使用aspect——难道Java语言又增加关键字了?No!上面的TxAspect根本不是一个Java类,所以aspect也不是Java支持的关键字,它只是AspectJ才认识的关键字。

    上面"void around"中的内容也不是方法,它只是指定当程序执行HelloWorld对象的sayHello方法时,执行这个代码块,其中proceed表示调用原来的sayHello方法。正如前面提到的,Java无法识别TxAspect.java文件中的内容所以我们需要使用ajc.exe来执行编译:

?
1
ajc HelloWorld.java TxAspect.java

    我们可以把ajc命令理解为javac命令,都用于编译Java程序,区别是ajc命令可以识别AspectJ的语法。从这个角度看,我们可以将ajc命令当成一个增强版的javac命令。

    运行该HelloWorld类依然无需任何改变:

?
1
java HelloWorld

    其结果将是:

    从上面的运行结果来看,我们可以完全不对HelloWorld.java文件做修改,也不用对执行HelloWorld的命令做修改,就可以实现上文中的实现事务管理的需求。上面的“Transaction Begin”和“Transaction End”仅仅是模拟事务的事件,实际开发中,用代码替换掉这段输出即可实现事务管理。

    如果客户又提出了为方法增加日志的需求,那也很简单,我们可以再定义一个LogAspect类,如下:

?
1
2
3
4
5
6
7
8
9
//同样使用aspect作为“关键字”
public  aspect LogAspect {
     //定义一个名为logPointcut的PointCut,对应于HelloWorld对象的sayHello方法
     pointcut logPointcut():execution( void  HelloWorld.sayHello());
     //在logPointcut之后指定下面的代码
     after():logPointcut() {
         System.out.println( "Log Recoding" );
     }
}

    上面的代码中定义了一个PointCut——logPointcut,等同于执行HelloWorld对象的sayHello方法,并指定在logPointcut之后执行简单的代码块,也就是说,在sayHello方法结束之后执行输出语句。使用如下命令编译这几个java文件:

?
1
ajc *.java

    再次运行HelloWorld类,将输出以下结果:

    由此可见,通过使用AspectJ提供的AOP支持,我们可以为sayHello方法不断增加新功能。

    实际上,AspectJ允许同时为多个方法添加新功能,只要我们定义Pointcut时指定匹配更多的方法即可,下面是一个代码片段:

?
1
pointcut xxxPointcut:execution( void  H*.say*());

    上面的程序中的xxxPointcut将可以匹配所有以H开头的类,以say开头的方法名和返回值为void类型的所有方法。AspectJ甚至允许下面的形式:

?
1
pointcut xxxPointcut:execution(* H*.say*());

    如果装有Java反编译工具,可以将HelloWorld.class进行反编译,我们将发现该HelloWorld.class文件不是由HelloWorld.java文件编译得到的,HelloWorld.class里面增加了很多新的内容——这表明AspectJ在编译时已经增强了HelloWorld.class的功能,因此AspectJ通常被称为编译时增强的AOP框架。

    拓展:与AspectJ相对的还有另外一种AOP框架,它们不需要在编译的时候对目标类进行增强,而是运行时生成目标类的代理类,该代理类要么实现了目标类实现的相同接口,要么是目标类的子类。总之,代理类都对目标类进行了增强处理,前者是JDK动态代理的处理策略,后者是CGLIB代理的处理策略。Spring AOP以创建动态代理的方式来生成代理类,底层既可使用JDK动态代理,也可以采用CGLIB代理。一般来说,编译时增强的AOP框架在性能上更有优势——因为运行时动态增强的AOP框架需要每次运行时都进行动态增强。

这篇关于Spring中的AOP(七)(实际上应该是一)——AspectJ的基本使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

详细分析Springmvc中的@ModelAttribute基本知识(附Demo)

目录 前言1. 注解用法1.1 方法参数1.2 方法1.3 类 2. 注解场景2.1 表单参数2.2 AJAX请求2.3 文件上传 3. 实战4. 总结 前言 将请求参数绑定到模型对象上,或者在请求处理之前添加模型属性 可以在方法参数、方法或者类上使用 一般适用这几种场景: 表单处理:通过 @ModelAttribute 将表单数据绑定到模型对象上预处理逻辑:在请求处理之前

eclipse运行springboot项目,找不到主类

解决办法尝试了很多种,下载sts压缩包行不通。最后解决办法如图: help--->Eclipse Marketplace--->Popular--->找到Spring Tools 3---->Installed。