Spring中AOP简介与切面编程的使用
Spring中AOP简介与使用
什么是AOP?
Aspect Oriented Programming(AOP),多译作 “面向切面编程”,也就是说,对一段程序,从侧面插入,进行操做。即通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
为什么要用AOP?
日志记录,性能统计,安全控制,事务处理,异常处理等等。例如日志记录,在程序运行的某些节点上添加记录执行操作状态的一些代码,获取执行情况。而通过切面编程,我们将这些插入的内容分离出来,将它们独立到业务逻辑的方法之外,进而使这些行为的时候不影响业务逻辑的执行。
如何使用AOP?
下面我们以一个简单计算题目的例子用日志记录的方法演示一下面向切面编程。
(同时我们使用到Junit4来测试程序)
环境: jdk1.8
新建Dynamic Web Project
Jar包: (包的版本不定,可以根据个人开发需求添加,下面基本为必须包)
-com.springsource.net.sf.cglib-2.2.0.jar
-com.springsource.org.aopalliance-1.0.0.jar
-com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
-commons-logging-1.1.3.jar
-spring-aop-4.0.0.RELEASE.jar
-spring-aspects-4.0.0.RELEASE.jar
-spring-beans-4.0.0.RELEASE.jar
-spring-context-4.0.0.RELEASE.jar
-spring-core-4.0.0.RELEASE.jar
-spring-expression-4.0.0.RELEASE.jar
以下分两种方式来说明
一、注解的方式
开发步骤:
1、创建方法类
这个类中写了主要的业务操作步骤,选取了加法和出发作为案例(除法较为典型)
1 package da.wei.aop; 2 import org.springframework.stereotype.Component; 3 4 @Component //普通类的注解配置spring.xml的配置,加入IOC容器 5 public class MathTestImpl { 6 public Integer add(int i,int j){ 7 System.out.println("执行add,参数i为: "+i+" ,参数j为: "+j+" ,结果为: "+(i+j)); 8 return (i+j); 9 } 10 11 public Integer div(int i,int j){ 12 System.out.println("执行div,参数i为: "+i+" ,参数j为: "+j+" ,结果为: "+(i/j)); 13 return (i/j); 14 } 15 }
该方法中需要强调一点规范问题,在使用函数返回值类型或者类的属性设置时,使用包装类型(Integer)来代替基本类型(int)可以避免不少错误。
2、创建多个方法的切面类
在这个切面类中,我们将每个通知类型的内容都单独列出来了,每一层都罗列其执行的效果。
其中有以下几个知识点
1)、切点注释@PointCut 通过配置切点方法可以将切点统一使用,减少了代码量,提高开发效率。
2)、切入点表达式
execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )
此处用到的时详细的有制定切唯一的方法,开发中我们不可能只有一个方法,因此可以用’*’通配符的方式表示,同时,方法返回值类型、权限修饰符、参数列表、包名、也可能不唯一,因此,可以优化为通配格式:
execution(* *.*(..)) 第一个’*’表示了权限修饰符以及方法返回值类型‘
3)、Before 中JoinPoint参数
Before 中JoinPoint参数可以获取切入点处方法的几乎全部信息,其中的方法名以及参数列表是常用信息
4)、AfterReturning 中的returning参数
AfterReturning 中的returning参数是用来接收切入方法的返回值的,其参数名需要其修饰方法体的参数名相同,同时由于参数类型不定,需要设为Object类型的
5)、Throwing 中的throwing参数
Throwing 中的throwing参数,用来获取异常信息值;其中throwing= “参数名a” 需要与方法中的参数相等对应。
1 package da.wei.aop; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.Signature; 8 import org.aspectj.lang.annotation.After; 9 import org.aspectj.lang.annotation.AfterReturning; 10 import org.aspectj.lang.annotation.AfterThrowing; 11 import org.aspectj.lang.annotation.Aspect; 12 import org.aspectj.lang.annotation.Before; 13 import org.aspectj.lang.annotation.Pointcut; 14 import org.springframework.core.annotation.Order; 15 import org.springframework.stereotype.Component; 16 /* 17 * @Component 普通类的注解配置spring.xml的配置,加入IOC容器 18 * @Aspect 标识出此类为切面方法类 19 * Order 是为了指定在多个切面类的情况下,其执行的顺序; 20 * value值为int类型的,值越小越先执行 21 */ 22 @Component 23 @Aspect 24 @Order(value=100) 25 public class MathAspectMutil { 26 //设置切点位置 此处设置的是add(int,int)方法 27 @Pointcut(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 28 public void getPointCut(){} 29 30 //注解,设置此方法是‘前置通知’ 31 @Before(value="getPointCut()") 32 public void before(JoinPoint point){ 33 System.out.println("[MathAspectMutil]【Before日志】"); 34 //获取插入点的参数 35 Object[] args = point.getArgs(); 36 //获取方法签名 37 Signature signature = point.getSignature(); 38 //获取方法名 39 String name = signature.getName(); 40 //将参数数组传为list 41 List<Object> asList = Arrays.asList(args); 42 System.out.println("方法签名 : "+signature); 43 System.out.println("方法名为 : "+name+"方法体参数列表 : "+asList); 44 } 45 46 /* 47 * 注解,设置此处是‘后置通知’ 48 * 此处可以看出其value值与上面before方法的不一样 49 * 实则是一样的,只是通过PointCut将其统一封装使用而已,效果相同 50 */ 51 @After(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 52 public void after(){ 53 System.out.println("[MathAspectMutil]【After日志】"); 54 } 55 56 //注解,设置此方法是‘返回通知’ 57 @AfterReturning(value="getPointCut()",returning="result") 58 public void afterReturning(JoinPoint point,Object result){ 59 System.out.println("[MathAspectMutil]【AfterReturning日志】"); 60 //获取插入点的参数 61 Object[] args = point.getArgs(); 62 //获取方法签名 63 Signature signature = point.getSignature(); 64 //获取方法名 65 String name = signature.getName(); 66 //将参数数组传为list 67 List<Object> asList = Arrays.asList(args); 68 System.out.println("方法签名 : "+signature); 69 System.out.println("方法名为 : "+name+"方法体参数列表 : "+asList+" ,执行结果为: "+result); 70 } 71 72 //注解,设置此方法是‘异常通知’ 73 @AfterThrowing(value="getPointCut()",throwing="ex") 74 public void afterThrowing(Throwable ex){ 75 System.out.println("[MathAspectMutil]【AfterThrowing日志】"); 76 System.out.println("错误信息为 : "+ex.getMessage()); 77 } 78 }
3、创建Around方法的切面类
Around方法其就是对上面四种通知的合并,在环绕通知中上面的四种通知都有体现到
通过对下面代码的分析就可以基本了解其运行结果了
1 package da.wei.aop; 2 import java.util.Arrays; 3 import java.util.List; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.Signature; 7 import org.aspectj.lang.annotation.Around; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Pointcut; 10 import org.springframework.core.annotation.Order; 11 import org.springframework.stereotype.Component; 12 13 @Component 14 @Aspect 15 @Order(value=5) 16 public class MathAspectAround { 17 //设置切点位置 此处设置的是add(int,int)方法 18 @Pointcut(value="execution(public Integer da.wei.aop.MathTestImpl.add(int, int) )") 19 public void getPointCut(){} 20 21 @Around(value="getPointCut()") 22 public Object around(ProceedingJoinPoint pJoinPoint){ 23 //获取插入点的参数 24 Object[] args = pJoinPoint.getArgs(); 25 //获取方法签名 26 Signature signature = pJoinPoint.getSignature(); 27 //获取方法名 28 String name = signature.getName(); 29 //将参数数组传为list 30 List<Object> asList = Arrays.asList(args); 31 Object result = null; 32 try { 33 try{ 34 System.out.println("[MathAspectAround]【Before】..."); 35 System.out.println("方法签名 : "+signature); 36 System.out.println("方法名为 : "+name+"方法体参数列表 : "+asList); 37 //获得结果 38 result = pJoinPoint.proceed(args); 39 }finally { 40 System.out.println("[MathAspectAround]【After】...."); 41 } 42 System.out.println("[MathAspectAround]【AfteReturning】..结果为"+result+"...."); 43 } catch (Throwable e) { 44 // TODO Auto-generated catch block 45 System.out.println("[MathAspectAround]【Throwing】..原因为 "+e.getMessage()); 46 } 47 return result; 48 } 49 }
4、配置spring.xml
我这里的spring配置文件名为applicationContext.xml
1 <!-- 扫描包,创建Bean对象 --> 2 <context:component-scan base-package="da.wei.aop"></context:component-scan> 3 <!-- 启用 切面编程注解 --> 4 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
5、Junit中代码编写
此处讲解AOP的使用至于IOC的使用就不再赘述。
1 @Test 2 public void test() { 3 //获取applicationContext配置信息,主要用于获得IOC容器中的类对象 4 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 5 //获取bean对象 6 MathTestImpl bean = (MathTestImpl) applicationContext.getBean("mathTestImpl"); 7 /* 8 * 调用方法 9 * 由于在配置里只编写了针对add的切入点这里只执行add方法 10 * 在之后的xml配置的方法中执行div方法 11 */ 12 bean.add(1,2); 13 //bean.div(10, 2); 14 }
6、执行结果
从执行结果中我们能看到已经正确执行了,同时我们也要注意到两种方式的执行顺序。
由于我们设置MathAspectMutil的Order为100 比MathAspectAround的5大因此MathAspectAround先执行,当其执行完before之后释放的方法又被MathAspectMutil获取,当MathAspectMutil执行完全部之后MathAspectAround再执行其他的方法,类似于拦截器的运行顺序。
二、spring.xml配置的方式
spring.xml配置的方式其类的建立与上面相同,只是需要去除所有的注解,使用简单的方法,简单的类
1、其中的applicationContext.xml配置如下
1 <bean id="mathTestImpl" class="da.wei.aop.MathTestImpl"></bean> 2 <bean id="mathAspectMutil" class="da.wei.aop.MathAspectMutil"></bean> 3 <bean id="mathAspectAround" class="da.wei.aop.MathAspectAround"></bean> 4 <aop:config > 5 <!-- 第一个 好多方法的切面类 --> 6 <aop:pointcut expression="execution(* da.wei.aop.MathTestImpl.*(..))" id="myPointCut"/> 7 <aop:aspect ref="mathAspectMutil" order="10"> 8 <aop:before method="before" pointcut-ref="myPointCut" /> 9 <aop:after method="after" pointcut-ref="myPointCut"/> 10 <aop:after-returning method="afterReturning" pointcut-ref="myPointCut" returning="result"/> 11 <aop:after-throwing method="afterThrowing" pointcut-ref="myPointCut" throwing="ex"/> 12 </aop:aspect> 13 <!-- Around --> 14 <aop:aspect ref="mathAspectAround" order="5"> 15 <aop:around method="around" pointcut-ref="myPointCut" /> 16 </aop:aspect> 17 </aop:config>
2、Junit代码如下
1 @Test 2 public void test() { 3 //获取applicationContext配置信息,主要用于获得IOC容器中的类对象 4 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); 5 //获取bean对象 6 MathTestImpl bean = (MathTestImpl) applicationContext.getBean("mathTestImpl"); 7 bean.div(10, 2); 8 System.out.println(); 9 bean.div(10, 0); //此处我们测试了异常情况 10 }
3、执行结果
从结果总我们可以看出当执行异常出现的时候,会执行性Throwing而不执行AfterReturning,这个也可以在Around的代码中可以看出。
以上是我最近学习AOP切面编程的一点总结,内容多为代码,总结不足,不过开发与思路过程在注释中有体现,希望能给大家一些帮助。
同时欢迎过路大神指点批评。