一起学Spring之注解和Schema方式实现AOP
概述
在上一篇,我们了解了通过实现接口和xml配置的方式来实现aop,在实现注解方式aop之前,先了解一下aspectj。aspectj是一个面向切面的框架,它扩展了java语言,定义了aop语法,能够在编译时实现代码的注入。spring通过集成apsectj实现了以注解方式定义通知类,大大减少了配置文件的工作量。本文主要讲解通过注解方式和schema方式实现aop编程,仅供学习分享使用,如有不足之处,还请指正。
实现aop的三种方式:
- 通过实现相应接口,并配置xml进行实现,可以参考上一篇。
- 通过注解方式实现。
- 通过schema的方式实现。
通过注解方式实现aop
@aspect 放在类的上面,表示这个类在spring容器中是一个切点注入类。
@component("logannotation") 表示此类是一个bean,在spring ioc容器里面进行注入。
步骤如下:
1. 定义一个类,并在类上面增加@aspect注解,表名此类为切面通知类。
如下所示:
1 package com.hex.second; 2 3 import org.aspectj.lang.joinpoint; 4 import org.aspectj.lang.proceedingjoinpoint; 5 import org.aspectj.lang.annotation.afterreturning; 6 import org.aspectj.lang.annotation.afterthrowing; 7 import org.aspectj.lang.annotation.around; 8 import org.aspectj.lang.annotation.aspect; 9 import org.aspectj.lang.annotation.before; 10 import org.springframework.stereotype.component; 11 12 /** 13 * @aspect:声明类为一个通知类 14 * @component("logannotation"):通过注解方法生成一个bean,但是需要配置注解的支持 15 * 通过注解的方式声明通知 16 * @author administrator 17 * 18 */ 19 @component("logannotation") 20 @aspect 21 public class logaspectannotation { 22 23 }
2. 前置通知函数
@before("execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 表示前置通知,用两个参数,默认为value值,指示切入点,告诉spring在哪些地方进行注入。
joinpoint 可以获取切入点的所有内容,包括目标对象,函数名称,参数,返回值等等。如下所示:
1 /** 2 * 前置通知,@before的参数为目标通知类的表达式 3 * joinpoint 用来获取目标函数的参数及对象等信息 4 */ 5 @before("execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 6 public void mybefore(joinpoint jp){ 7 system.out.println("我是注解方式的前置通知"); 8 system.out.println("method="+jp.getsignature().getname()+",args数量="+jp.getargs().length+",target="+jp.gettarget()); 9 }
3. 后置通知
@afterreturning(pointcut = "execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))",returning="returningvalue") 表示后置通知,其中value和poingcut都表示切入点,功能一样。returning表示定义目标函数的返回值。如下所示:
1 /** 2 * 功能:后置通知 3 * 注解形式实现aop通知时,参数不能随便写,否则和目标函数对应不上,会报错 4 * @param jp :切入点目标对象 5 * @param returningvalue 返回值 6 */ 7 @afterreturning(pointcut = "execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))",returning="returningvalue") 8 public void myafterreturning(joinpoint jp,object returningvalue){ 9 system.out.println("我是注解方式的后置通知"); 10 system.out.println("返回值是:"+returningvalue); 11 }
4. 异常通知
@afterthrowing(pointcut="execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))",throwing="e") 表示异常通知,其中trowing表示将抛出异常绑定到参数中。当切入函数抛出异常时将会触发,如下所示:
1 /** 2 * 异常通知 3 */ 4 @afterthrowing(pointcut="execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))",throwing="e") 5 public void myafterthrow(joinpoint jp,throwable e){ 6 system.out.println("我是注解方式的异常通知"); 7 }
5. 环绕通知
@around(value="execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 环绕通知功能最全面,可以实现其他几种,其中参数使用proceedingjoinpoint,是joinpoint的子类。
1 /** 2 * 环绕通知 3 * @param jp 才用的是joinpoint的子类 4 */ 5 @around(value="execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 6 public void myaround(proceedingjoinpoint jp){ 7 object obj = null; 8 try { 9 10 // 前置通知 11 system.out.println("注解环绕实现前置通知。。。"); 12 system.out.println("环绕通知:target="+jp.gettarget()+",method="+jp.getsignature().getname()+",args="+jp.getargs().length); 13 // 控制目标方法的执行 obj表示目标方法的返回值,表示执行addstudent(student)方法 14 //此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知 15 obj = jp.proceed(); 16 // 后置通知 17 system.out.println("注解环绕实现后置通知。。。"); 18 } catch (throwable e) { 19 // 异常通知 20 system.out.println("注解环绕实现异常通知。。。"); 21 }finally{ 22 //最终通知 23 system.out.println("注解环绕实现最终通知。。。"); 24 } 25 }
6. 最终通知
@after("execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 表示最终通知,不管是否抛出异常,都会得到执行,类似于finally。如下所示:
1 /** 2 * 最终通知,@after的参数为目标通知类的表达式 3 * joinpoint 用来获取目标函数的参数及对象等信息 4 */ 5 @after("execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))") 6 public void myafter(joinpoint jp){ 7 system.out.println("我是注解方式最终通知"); 8 system.out.println("method="+jp.getsignature().getname()+",args数量="+jp.getargs().length+",target="+jp.gettarget()); 9 }
7. 除了上述注解之外,还需要在sping容器中,配置对注解的支持和aop的自动扫描。
如下所示:
1 <?xml version="1.0" encoding="utf-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xsi:schemalocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/aop 10 http://www.springframework.org/schema/aop/spring-aop.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context.xsd"> 13 14 <!-- 服务类 --> 15 <bean id="studentservice" class="com.hex.second.studentserviceimpl"> 16 17 </bean> 18 <!-- 将addstudent和通知进行关联 --> 19 <!-- 配置对注解方式aop的支持 --> 20 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 21 <!-- 配置对注解的扫描 --> 22 <context:component-scan base-package="com.hex.second"></context:component-scan> 23 </beans>
通过schema方式实现aop
通过schema方式实现步骤如下:
1. 定义一个普通的类,分别实现各种功能的通知,参数和注解方式的一致。
如下所示:
1 package com.hex.second; 2 3 import org.aspectj.lang.joinpoint; 4 import org.aspectj.lang.proceedingjoinpoint; 5 6 /** 7 * 通过schema配置的方式实现通知 8 * @author administrator 9 * 10 */ 11 public class logaspectschema { 12 /** 13 * 前置通知,@before的参数为目标通知类的表达式 14 * joinpoint 用来获取目标函数的参数及对象等信息 15 */ 16 public void mybefore(joinpoint jp){ 17 system.out.println("我是schema方式的前置通知"); 18 system.out.println("method="+jp.getsignature().getname()+",args数量="+jp.getargs().length+",target="+jp.gettarget()); 19 } 20 21 /** 22 * 功能:后置通知 23 * schema形式实现aop通知时,参数不能随便写,否则和目标函数对应不上,会报错 24 * @param jp :切入点目标对象 25 * @param returningvalue 返回值 26 */ 27 public void myafterreturning(joinpoint jp,object returningvalue){ 28 system.out.println("我是schema方式的后置通知"); 29 system.out.println("返回值是:"+returningvalue); 30 } 31 32 /** 33 * 异常通知 34 */ 35 public void myafterthrow(joinpoint jp ,throwable ex){ 36 system.out.println("我是schema方式的异常通知"); 37 system.out.println("ex:"+ex.getmessage()); 38 } 39 40 /** 41 * 环绕通知 42 * @param jp 才用的是joinpoint的子类 43 */ 44 public void myaround(proceedingjoinpoint jp){ 45 object obj = null; 46 try { 47 48 // 前置通知 49 system.out.println("schema环绕实现前置通知。。。"); 50 system.out.println("schema环绕通知:target="+jp.gettarget()+",method="+jp.getthis()+",args="+jp.getargs().length); 51 // 控制目标方法的执行 obj表示目标方法的返回值,表示执行addstudent(student)方法 52 //此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知 53 obj = jp.proceed(); 54 // 后置通知 55 system.out.println("schema环绕实现后置通知。。。"); 56 } catch (throwable e) { 57 // 异常通知 58 system.out.println("schema环绕实现异常通知。。。"); 59 }finally{ 60 //最终通知 61 system.out.println("schema环绕实现最终通知。。。"); 62 } 63 } 64 65 /** 66 * 最终通知 67 * @param jp 68 */ 69 public void myafter(joinpoint jp){ 70 system.out.println("我是schema方式的最终通知"); 71 } 72 }
2. 在spring容器中配置
首先将通知类注入到spring ioc容器中,然后配置<aop:config>将业务类和切面类关联起来。如下所示:
1 <?xml version="1.0" encoding="utf-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xsi:schemalocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/aop 10 http://www.springframework.org/schema/aop/spring-aop.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context.xsd"> 13 14 <!-- 服务类 --> 15 <bean id="studentservice" class="com.hex.second.studentserviceimpl"> 16 17 </bean> 18 <bean id="logschema" class="com.hex.second.logaspectschema"></bean> 19 <aop:config> 20 <!-- 配置切入点 --> 21 <aop:pointcut expression="execution(public void com.hex.second.studentserviceimpl.deletestudent(int)) or execution(public void com.hex.second.studentserviceimpl.addstudent(com.hex.second.student))" id="pc"/> 22 <aop:aspect ref="logschema"> 23 <!-- 通过schema实现的通知 --> 24 <aop:before method="mybefore" pointcut-ref="pc"/> 25 <aop:after-returning method="myafterreturning" pointcut-ref="pc" returning="returningvalue"/> 26 <aop:after-throwing method="myafterthrow" pointcut-ref="pc" throwing="ex" /> 27 <aop:around method="myaround" pointcut-ref="pc"/> 28 <aop:after method="myafter" pointcut-ref="pc"/> 29 </aop:aspect> 30 </aop:config> 31 </beans>
备注
假如你不够快乐
也不要把眉头深锁
人生本来短暂
为什么 还要栽培苦涩
打开尘封的门窗
让阳光雨露洒遍每个角落
走向生命的原野
让风儿熨平前额
博大可以稀释忧愁
深色能够覆盖浅色