Spring中AOP基于Annotation的零配置(1)

来源:互联网 发布:破解二维码软件 编辑:程序博客网 时间:2024/05/19 11:37



一、AOP需要程序员参与的三个部分:

1、定义普通业务组件

2、定义切入点,一个切入点可能横切多个业务组件

3、定义曾强处理,增强处理就是AOP框架为普通业务组件织入的处理动作

一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理,而AOP代理方法大致有如下公式:

代理对象的方法 = 增强处理 + 被代理对象的方法

建议使用AspectJ方式来定义切入点和增强处理,在这种公式下,Spring依然有如下两种选择来定义切入点和增强处理

①基于Annotation的“零配置”方式:使用@Aspect、@Pointcut等Annotation来标注切入点和增强处理

②基于XML配置文件的管理方式:使用SPring配置文件来定义切入点和增强处理

二、Spring依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要AspectJ的织入器支持;而AspectJ在采用编译时增强,所以AspectJ需要使用自己的编译器来编译Java文件,还需要织入器。

为启动Spring对@AspectJ切面配置的支持,并保证Spring容器的目标Bean被一个或多个切面自动增强,必须Spring配置文件中配置如下片段:

bean.xml

<?xml version="1.0" encoding="UTF-8"?>

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="html"><!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 指定自动搜索Bean组件、自动搜索切面类 --><context:component-scan base-package="tju.chc.app.service.impl,tju.chc.app.service"><context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/></context:component-scan><!-- 启动!AspectJ支持 --><aop:aspectj-autoproxy/><bean id="korea" class="tju.chc.app.service.impl.Korea" /></beans>

TxApect增强处理

public aspect TxAspect {//指定执行Hello.sayHello()方法时执行下面代码块void around():call(void Hello.sayHello()){System.out.println("开始事务");proceed();System.out.println("事物结束");}}


BeforeAdvice增强处理
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">定义Before增强处理</span>
package tju.chc.aspectJTest;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeAdviceTest {//匹配  包下所有的类的所有方法的执行  作为切入点@Before("execution(* tju.chc.app.service.impl.*.*(..))")public void authority(){System.out.println("before 切入");}}

定义AfterReturning增强处理

package tju.chc.aspectJTest;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class AfterReturningAdviceTest {//匹配tju.chc.app.service.impl包下所有类的所有方法的执行作为切入点@AfterReturning(returning="rvt",pointcut="execution(* tju.chc.app.service.impl.*.*(..))")public void log(Object rvt){System.out.println("获取目标方法返回值:" + rvt);System.out.println("模拟记录日志功能。。。");}}

输出

开始事务Hello AspectJ!事物结束before 切入获取目标方法返回值:adfHello , SPring AOP模拟记录日志功能。。。adfHello , SPring AOPbefore 切入Korea eat 泡菜获取目标方法返回值:null模拟记录日志功能。。。

4AfterThrowing增强处理

package tju.chc.aspectJTest;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class AfterThrowingAdviceTest {//匹配  包下所有类的所有方法的执行作为切入点@AfterThrowing(throwing ="ex",pointcut="execution(* tju.chc.app.service.impl.*.*(..))")public void doRecoveryActions(Throwable ex){System.out.println("目标方法中抛出的异常:"+ ex);System.out.println("模拟抛出异常增强处理");}}
Korea类增加的两个方法
//定义一个sayHi方法public String sayHi(String name){try{System.out.println("start running");new FileInputStream("a.txt");}catch(Exception ex){System.out.println("目标异常处理" + ex.getMessage());}return name + ", Hi,Spring AOP";}public void divide(){int a = 5 / 0;System.out.println("divide compelete");}

输出

----------------------sayHi
before 切入
start running
目标异常处理a.txt (系统找不到指定的文件。)
获取目标方法返回值:mashang6, Hi,Spring AOP
模拟记录日志功能。。。
----------------------divide
before 切入
目标方法中抛出的异常:java.lang.ArithmeticException: / by zero
模拟抛出异常增强处理
Exception in thread "main" java.lang.ArithmeticException: / by zero
at tju.chc.app.service.impl.Korea.divide(Korea.java:36)
at tju.chc.aspectJTest.Hello.main(Hello.java:25)

5、After增强处理

与AfterReturning增强处理优点类似,但也有区别:

①AfterReturning增强处理只有在目标方法成功完成后才会被织入

②After增强处理不管目标方法如何结束(包括成功完成和遇到异常终止两种情况),它都会被织入

因此After增强处理必须准备处理正常返回和异常返回两种情况,这种增强处理通常用于释放资源。

package tju.chc.aspectJTest;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class AfterAdviceTest {//匹配包下所有类的所有方法的执行作为切入点@After("execution(* tju.chc.app.service.impl.*.*(..))")public void release(){System.out.println("模拟方法结束后的释放资源。。。");}}


输出

目标方法中抛出的异常:java.lang.ArithmeticException: / by zero模拟抛出异常增强处理模拟方法结束后的释放资源。。。Exception in thread "main" java.lang.ArithmeticException: / by zeroat tju.chc.app.service.impl.Korea.divide(Korea.java:36)at tju.chc.aspectJTest.Hello.main(Hello.java:25)

6.Around增强处理

Around 增强处理既可以在执行目标方法之前织入增强动作,也可以执行目标方法之后织入增强动作

Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止方法的执行

可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值

(通常需要在线程安全的环境下使用,因此如果普通的Before增强处理和AfterReturning 增强处理就能解觉得问题,就没必要使用Around)

当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体内,调用ProceedingJoinPoint的proceed()方法才会执行目标方法(这就是Around可以完全控制目标方法执行时机。如何执行的关键;如果程序没有调用ProceedingJoinPoint的proceed()方法,则目标方法不会执行)

调用ProceedingJoinPoint的proceed()方法时,还可以传入一个Object【】对象,该数组中的值将被出入目标方法作为执行方法的实参。(用于改变目标方法的的参数)

切面定义如下

package tju.chc.aspectJTest;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class AroundAdviceTest {//匹配包下的所有类的所有方法的执行作为切入点@Around("execution(* tju.chc.app.service.impl.*.*(..))")public Object processTx(ProceedingJoinPoint pjp) throws Throwable{System.out.println("z执行目标方法之前,模拟开始事务");//执行目标方法,并保存目标方法执行后的返回值Object ret = pjp.proceed(new String[]{"被改变的参数"});System.out.println("z执行目标方法之后,模拟结束事务。。。");//该变目标方法的返回值return ret + " new added content"; }}

输出

before 切入z执行目标方法之前,模拟开始事务获取目标方法返回值:被改变的参数Hello , SPring AOP模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。被改变的参数Hello , SPring AOPnew added contentbefore 切入z执行目标方法之前,模拟开始事务Korea eat 被改变的参数获取目标方法返回值:null模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。----------------------sayHibefore 切入z执行目标方法之前,模拟开始事务start running目标异常处理a.txt (系统找不到指定的文件。)获取目标方法返回值:被改变的参数, Hi,Spring AOP模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。----------------------dividebefore 切入z执行目标方法之前,模拟开始事务目标方法中抛出的异常:java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint模拟抛出异常增强处理模拟方法结束后的释放资源。。。Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPointat tju.chc.app.service.impl.Korea$AjcClosure9.run(Korea.java:1)at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:221)at tju.chc.aspectJTest.AroundAdviceTest.processTx(AroundAdviceTest.java:16)at tju.chc.app.service.impl.Korea.divide(Korea.java:35)at tju.chc.aspectJTest.Hello.main(Hello.java:25)


7、访问目标函数的参数

在定义增强处理方法时将第一个参数定义为JoinPoint类型,当该类型增强处理方法被调用时,该JoinPoint参数就代表了织入曾强处理的连接点。JoinPoint中几个常用的方法

Object[]  getArgs():返回执行目标方法时的参数

Signature getSignature()  放回被增强的方法的相关信息

Object getTarget() :返回被织入增强处理的目标对象

Object getThis()  : 返回AOP框架为目标对象生成的代理对象

@Aspectpublic class FourAdviceTest {// 匹配包下的所有类的所有方法的执行作为切入点@Around("execution(* tju.chc.app.service.impl.*.*(..))")public Object processTx(ProceedingJoinPoint pjp) throws Throwable {System.out.println("Around增强:z执行目标方法之前,模拟开始事务");//访问执行目标方法的参数Object[] args = pjp.getArgs();//当执行目标方法的参数存在且第一个参数是字符串参数if(args != null && args.length > 0 && args[0].getClass() == String.class){//改变第一个目标方法的第一个参数args[0] = "被改变参数4";}// 执行目标方法,并保存目标方法执行后的返回值Object ret = pjp.proceed(args);System.out.println("Around增强:z执行目标方法之后,模拟结束事务。。。");// 该变目标方法的返回值return ret + " new added content";}//Before增强处理 匹配  包下所有的类的所有方法的执行  作为切入点@Before("execution(* tju.chc.app.service.impl.*.*(..))")public void authority(JoinPoint jp){System.out.println("Before 增强处理:模拟执行权限检查!");//返回被织入增强处理目标方法System.out.println("Before增强:被织入增强处理目标方法为:" + jp.getSignature().getName());//访问执行目标方法的参数System.out.println("Before增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));//访问被增强处理的目标对象System.out.println("Before增强:被织入增强处理的目标对象为:" + jp.getTarget());}// After 增强处理执行 匹配 包下所有类的所有方法的执行作为切入点@After("execution(* tju.chc.app.service.impl.*.*(..))")public void release(JoinPoint jp) {System.out.println("After 增强处理:模拟方法结束后的释放资源。。。");//返回被织入增强处理目标方法System.out.println("After 增强:被织入增强处理目标方法为:" + jp.getSignature().getName());//访问执行目标方法的参数System.out.println("After 增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));//访问被增强处理的目标对象System.out.println("Atfer 增强:被织入增强处理的目标对象为:" + jp.getTarget());}//定义AfterReturning增强处理执行  匹配tju.chc.app.service.impl包下所有类的所有方法的执行作为切入点@AfterReturning(returning="rvt",pointcut="execution(* tju.chc.app.service.impl.*.*(..))")public void log(JoinPoint jp, Object rvt){System.out.println("AfterReturning 增强处理:模拟记录日志功能。。。");//返回被织入增强处理目标方法System.out.println("AfterReturning 增强:被织入增强处理目标方法为:" + jp.getSignature().getName());//访问执行目标方法的参数System.out.println("AfterReturning 增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));//访问被增强处理的目标对象System.out.println("AfterReturning 增强:被织入增强处理的目标对象为:" + jp.getTarget());}}


输出:

before 切入z执行目标方法之前,模拟开始事务Around增强:z执行目标方法之前,模拟开始事务Before 增强处理:模拟执行权限检查!Before增强:被织入增强处理目标方法为:sayHelloBefore增强:目标方法的参数为:[adf]Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549Around增强:z执行目标方法之后,模拟结束事务。。。After 增强处理:模拟方法结束后的释放资源。。。After 增强:被织入增强处理目标方法为:sayHelloAfter 增强:目标方法的参数为:[adf]Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549AfterReturning 增强处理:模拟记录日志功能。。。AfterReturning 增强:被织入增强处理目标方法为:sayHelloAfterReturning 增强:目标方法的参数为:[adf]AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549获取目标方法返回值:被改变参数4Hello , SPring AOP new added content模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。被改变参数4Hello , SPring AOP new added content new added contentbefore 切入z执行目标方法之前,模拟开始事务Around增强:z执行目标方法之前,模拟开始事务Before 增强处理:模拟执行权限检查!Before增强:被织入增强处理目标方法为:eatBefore增强:目标方法的参数为:[泡菜]Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549Korea eat 被改变参数4Around增强:z执行目标方法之后,模拟结束事务。。。After 增强处理:模拟方法结束后的释放资源。。。After 增强:被织入增强处理目标方法为:eatAfter 增强:目标方法的参数为:[泡菜]Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549AfterReturning 增强处理:模拟记录日志功能。。。AfterReturning 增强:被织入增强处理目标方法为:eatAfterReturning 增强:目标方法的参数为:[泡菜]AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549获取目标方法返回值:null模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。----------------------sayHibefore 切入z执行目标方法之前,模拟开始事务Around增强:z执行目标方法之前,模拟开始事务Before 增强处理:模拟执行权限检查!Before增强:被织入增强处理目标方法为:sayHiBefore增强:目标方法的参数为:[mashang6]Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549start running目标异常处理a.txt (系统找不到指定的文件。)Around增强:z执行目标方法之后,模拟结束事务。。。After 增强处理:模拟方法结束后的释放资源。。。After 增强:被织入增强处理目标方法为:sayHiAfter 增强:目标方法的参数为:[mashang6]Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549AfterReturning 增强处理:模拟记录日志功能。。。AfterReturning 增强:被织入增强处理目标方法为:sayHiAfterReturning 增强:目标方法的参数为:[mashang6]AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549获取目标方法返回值:被改变参数4, Hi,Spring AOP new added content模拟记录日志功能。。。z执行目标方法之后,模拟结束事务。。。模拟方法结束后的释放资源。。。----------------------dividebefore 切入z执行目标方法之前,模拟开始事务模拟方法结束后的释放资源。。。Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPointat tju.chc.app.service.impl.Korea$AjcClosure19.run(Korea.java:1)at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:221)at tju.chc.aspectJTest.AroundAdviceTest.processTx(AroundAdviceTest.java:15)at tju.chc.app.service.impl.Korea.divide(Korea.java:35)at tju.chc.aspectJTest.Hello.main(Hello.java:25)



0 0
原创粉丝点击