在spring框架越来越风靡的时候,大家是否考虑过为什么spring如此受欢迎。spring的核心功能:IOC + AOP +事务管理;那么今天小编就带大家一起来深入了解spring的AOP:
✪ 为什么需要AOP
在传统的OOP编程中以对象为核心,整个软件系统由一系列相互依赖的对象组成,而这些对象将被抽象成一个个类,并允许使用类的继承来管理类与类之间一般到特殊的关系。随着软件规模的扩大,专业化的分工越来越细致,以及OOP应用实践的不断增多,慢慢的出现了一些OOP无法解决的问题,如下图:
如图一和图二所示,当系统的规模越来越大,而图一中的红色框内的代码需要需要修改时,就需要对当前系统中所有涉及到该代码块的方法进行修改??那么这种情况就没有解决方法吗??
当然有,就是spring提供的AOP ,AOP专门用于处理系统中分布于各个模块(或不同的方法)中交叉的关注点的问题,常常通过AOP来处理一些具有横切性性质的系统级服务,如事务管理,安全检查,缓存,对象池管理等。
✪ 使用AspectJ实现AOP
AspectJ 是最早的,功能比较强大的aop实现之一,从spring2.0开始,spring aop 就已经引入了对AspectJ的支持,并允许直接使用AspectJ进行AOP编程:
1.下载并安装Aspect:http://www.eclipse.org/aspect/downloads.php
2.配置环境变量
PS:AspectJ是绿色软件,所以只需要解压,但是需要配置环境变量;具体的步骤这里不再赘述;
3.AspectJ实现AOP实例
首先编写两个简单的java类,这两个类用于模拟系统中的业务组件:
第二个组件 World.java<span style="font-size:18px;">package testAOP; public class Hello { //定义一个简单的方法,模拟应用中的业务逻辑方法 public void foo(){ System.out.println("执行Hello组件的foo()方法"); } //定义一个addUser的方法,模拟应用程序中添加用户的方法 public int addUser(String name,String pass){ System.out.println("执行Hello组件的addUser()方法" + name); return 20; } } </span>
定义一个AspectJTest.java 并编写一个main方法:<span style="font-size:18px;">package testAOP; public class World { //定义一个简单的方法,模拟应用程序中的业务逻辑方法 public void bar(){ System.out.println("执行World组件的bar()方法"); } }</span>
<span style="font-size:18px;">package testAOP; public class AspectJTest { public static void main(String[] args) { Hello hello = new Hello(); hello.foo(); hello.addUser("孙悟空", "123456"); World world = new World(); world.bar(); } }</span>
执行上述方法之后,可以打印出如下结果:
但是,如果用户提出需求:在执行所有的方法时必须检查该用户的权限,我们开发怎么办??拒绝用户的需求?可是用户是上帝!所以我们必须拥抱需求,满足客户的需求,但是满足需求的同时,又必须考虑程序的代码复用和扩展维护,所以我们坚决不能使用图一中的方式。因此我们需要使用Aspect对AOP的支持,所以需要添加一个特殊的java类:AuthAspect.java<span style="font-size:18px;">执行Hello组件的foo()方法 执行Hello组件的addUser()方法孙悟空 执行World组件的bar()方法</span>
由于该类的特殊性,我们必须使用ajc.bat命令来编译上面的程序,得出以下的运行结果:<span style="font-size:18px;">package testAOP; public <strong>aspect </strong>AuthAspect { //指定在执行testAOP包中任意类的任意方法之前执行下面的代码块 //第一个星号表示返回值类型不限,第二个星号表示类名不限 //第三个星号表示方法名不限,圆括号中 代表任意个数,类型不限的形参 before():execution(* testAOP.*.*(..)){ System.out.println("模拟进行权限检查"); } } </span>
所以使用AspectJ 的AOP之后,我们根本不用对hello和world的两个组件类进行修改,而且可以完美的满足用户的需求;因此,我们也可以使用这种方法满足用户对增加记录日志的需求:<span style="font-size:18px;">模拟进行权限检查 执行Hello组件的foo()方法 模拟进行权限检查 执行Hello组件的addUser()方法孙悟空 模拟进行权限检查 执行World组件的bar()方法</span>
编译该类,可以发现以下的执行结果:<span style="font-size:18px;">package testAOP; public aspect LogAspect { //定义一个PointCut,名为logPointCut //该PointCut代表了后面给出的切入点的表达式,这样就可以复用该切入点的表达式 pointCut logPointcut():execution(* testAOP.*.*.(..)); after():logPointcut() { System.out.println("模拟记录日志"); } }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span></span>
当上述两种需求都实现之后,如果我们需要控制方法执行之前开启事务,方法执行完毕之后关闭事务要怎么实现呢?同样只要定义TxAspect.java即可:<span style="font-size:18px;">模拟进行权限检查 执行Hello组件的foo()方法 模拟记录日志 模拟进行权限检查 执行Hello组件的addUser()方法孙悟空 模拟记录日志 模拟进行权限检查 执行World组件的bar()方法 模拟记录日志</span>
<span style="font-size:18px;">package testAOP; public aspect TxAspect { Object around:call(* testAOP.*.*(..)){ System.out.println("模拟开启日志"); //回调原先的方法 Object rvt = proceed(); System.out.println("模拟结束日志"); return rvt; } }</span>
Object rvt = proceed(); 指定proceed()代表回调原来的目标方法,这样位于proceed()之前的代码会被添加在目标方法之前,位于之后的会被添加在目标 方法之后;
当我们编译上面所有的java类,会发现系统中的两个组件中添加了很多新的内容:检查权限,日志记录和事务开启关闭,但我们却丝毫不用对两个组件类进行修改,而这就是Aspect的强大之处,所以Aspect通常被称为编译时增强的AOP框架;
所以,AOP的实现可以分为两类:
* 静态AOP实现:在编译阶段对程序进行修改,即实现对目标类的增强,生成静态的AOP代理类;以AspectJ为代表;
* 动态AOP实现:AOP框架在运行阶段动态生成AOP代理(在内存中使用jdk动态代理或cglib动态生成AOP代理类),以实现对目标对象的增强,以Spring AOP为代表;
由于篇幅原因,关于Spring AOP的介绍先到这里,欢迎大家继续关注spring的AOP实现!