到目前为止,关于spring中面向切向编程的部分已经有了三种方法,第一种是使用jdk动态代理,第二种是使用cglib手动代理,第三种是使用声明式工厂Bean。目前比较流行的是使用AspectJ开发,使用这种开发方式有两种不同的开发方向,其中一个是使用基于XML的声明式AspectJ,这种方式是指,通过在XMl文件中进行配置,来定义切面、切入点以及声明通知,所有的切面和通知都必须定义在<aop:config>中,有点类似于使用声明式工厂Bean。话不多说,直接上代码:
切面类:
package cn.itcast.aspectj.xml; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /* *在切面类中封装了我们需要使用到的advice */ public class MyAspect{ //前置通知 public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知,目标:"); System.out.println(jointPoint.getTarget()+",方法名称:"); System.out.println("jointPoint.getSignature().getName()"); } //后置通知 public void myAfterReturning(JoinPoint joinPoint){ System.out.println("后置名称,方法名称:"+joinPoint.getSignature().getName()); } //环绕通知 //ProceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法 //*1.必选返回Object类型值 //*2.必须接受一个参数,类型为ProceedingJoinPoint //*3.必须throws Throwable public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ //开始 System.out.println("环绕开始"); //执行当前目标方法 Object obj=proceedingJoinPoint.proceed(); //结束 system.out.println("环绕结束"); return obj; }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--1.目标类--> <bean id="userDao" class="cn.itcast.dao.userDaoImpl"></bean> <!--2.切面--> <bean id="myAspect" class="cn.itcast.aspectj.xml.MyAspect"></bean> <!--3.aop编程--> <aop:config> <aop:aspect ref="myAspect"> <!--3.1配置切入点,通知最后增强哪些方法--> <aop:pointcut expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/> <使切入点和关联通知(advice)相关联> <!--#1:使前置通知和切入点相互关联--> <aop:before method="myBefore" pointcut-ref="myPointCut"/> <!--#2:使后置通知和切入点相关联,后置通知在切入点中的方法返回后执行--> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/> <!--#3:环绕通知--> <aop:around method="myAround" pointcut-ref="myPointCut"/> <!--抛出异常,如果当前程序有异常,就可以接受当前方法产生的异常,换言之,如果当前程序没有发生异常,这个方法就不会执行--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"> <!--最终通知,无论发现任何事情,都将执行--> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config> </beans>
//异常通知 public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("异常通知,出错了"+e.getMessage());}//最终通知public void myAfter(){ System.out.println("最终通知"); }}
由上面代码可见,这个类中封装了各种各样的advice,其执行顺序是前置通知->环绕通知(环绕开始->执行目标方法->最终通知->环绕结束)->后置通知,值得注意的是,这里的代码中的前置通知和后置通知以及环绕通知所传入的参数都是通过一个实例,换句话讲,它们都在与同一个对象“通信”(参考《java编程思想》),所以这三个方法其实处理的是同一块内存。通过上面的叙述,有一点是可以明确的,那就是所有通知所环绕的“中心”是我们的目标类中的方法,那么我们就必须明确这个目标类是什么,在哪里,所以我们需要在相关文件中进行配置,这个“相关文件”常被命名为applicationContext.xml(请忽略文件头信息,主要的逻辑体现在下面的代码中):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--1、配置目标类--> <bean id="userDao" class="cn.itcast.dao.UserDaoImpl"></bean> <!--2、配置切面--> <bean id="myAspect" class="cn.itcast.aspectj.xml.MyAspect"></bean> <!--3、aop编程,注意要使用到上面定义的目标类和切面--> <aop:config> <aop:aspect ref="myAspect"> <!--3.1:配置切入点,通知最后增强哪些方法--> <aop:point-cut expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/> <!--3.2:使通知advice和切入点相关联--> <!--3.2#1:使前置通知和切入点相关联,执行完前置通知后再执行切入点中的方法--> <aop:before method="myBefore" pointcut-ref="myPointCut"/> <--3.3#2:使后置通知和切入点相关联。执行完切入点中的方法,在切入点中的方法返回后,执行后置通知--> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/> <--3.3#3:环绕通知--> <aop:around method="myAround" pointcut-ref="myPointCut"/> <--3.3#4:抛出通知,如果程序发生异常,就会捕获程序中的异常,换句话讲,如果程序没有发生异常,那么这个捕获异常的方法就不会执行--> <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> <!--最终通知,无论程序发生任何事情,都将执行最终通知,最终通知执行在切面中的方法执行之后--> <aop:after method="myAfter" pointcut-ref="myPointCut"> </aop:aspect> </aop:config> </beans>
下面我们将增强过的目标类取出,打印出来,代码如下:package cn.itcast.aspectj.xml; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.itcast.dao.UserDao; public class TestXml{ @Test public void demo01(){ String xmlPath="cn/itcast/aspectj/xml/applicationContext.xml"; ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath); //从Spring容器获得 UserDao userDao=(UserDao)applicationContext.getBean("userDao"); //执行方法 userDao.save(); } }
执行Junit测试后的最终结果如下所示: