首页 > 代码库 > 第二十九天 月出惊山鸟 —Spring的AOP_AspectJ @annotation
第二十九天 月出惊山鸟 —Spring的AOP_AspectJ @annotation
6月14日,阴转雨。“四面垂杨十里荷,向云何处最花多, 画楼南畔夕阳和。天气乍凉人寂寞, 光阴须得酒消磨,且来花里听笙歌。”
面向切面的框架AspectJ邂逅Spring,不仅造就一种简洁,更带来更多的选择空间。
上篇的切面与代理配置方式,麻烦且繁琐。好在Spring 与 AspectJ 进行了集成,从接口和配置的泥潭爬出,善莫大焉。
下面把上一篇Spring的AOP 的例子改写一下,达到同样的效果。
1-3步骤不变。
4、定义一个 Aspect 切面类BallHandler.java
package edu.eurasia.aop; import org.apache.log4j.Logger; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.AfterReturning; @Aspect public class BallHandler { static Logger logger = Logger.getLogger(TestBall.class); @Pointcut("execution(* edu.eurasia.aop.WatchBallImpl.watchfoot*(..))") public void watchfootball(){}; @Pointcut("execution(* edu.eurasia.aop.WatchBallImpl.watchbasket*(..))") public void watchbasketball(){}; @Before(value=http://www.mamicode.com/"watchfootball()&& args(city,..)") >类上面标注的 @Aspect 注解,这表明该类是一个 Aspect(其实就是 Advisor)-切面类。该类无需实现任何的接口,只需定义一个方法(方法叫什么名字都无所谓)。
@Pointcut使用这个方法可以将edu.eurasia.aop.WatchBallImpl.watchfoot*(..)方法声明为poincut即切入点。作用,在watchfoot*(..)方法被调用的时候执行watchfootball()方法。其中execution是匹配方法执行的切入点,也就是spring最常用的切入点定义方式。
@Before(value=http://www.mamicode.com/"watchfootball()&& args(city,..)):@Before声明为在切入点方法执行之前执行,而后面没有直接声明切入点,而是value=http://www.mamicode.com/"watchfootball()",是因为如果@afterReturning等都有所改动的时候都必须全部改动,所以统一用Pointcut的watchfootball代替,这样子有改动的时候仅需改动一个地方。其它@AfterReturning类同。&& args(city,..)可以获取切入点方法watchfoot*(..)的参数。
下面分析一下这个切点表达式:
execution(* edu.eurasia.aop.WatchBallImpl.watchfoot*(..))
execution():表示拦截方法,括号中可定义需要匹配的规则。
第一个“*”:表示方法的返回值是任意的。
第二个“*”:表示匹配该类中所有的方法。
(..):表示方法的参数是任意的。
5、编写spring的配置文件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-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd" > <!-- 定义Aspect --> <bean id="ballHandler" class="edu.eurasia.aop.BallHandler" /> <!-- 定义Common --> <bean id="watchBall" class="edu.eurasia.aop.WatchBallImpl" /> <!-- 启动AspectJ支持 --> <aop:aspectj-autoproxy /> </beans>6、编写测试类TestBall.java
package edu.eurasia.aop; import org.apache.log4j.Logger; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestBall { static Logger logger = Logger.getLogger(TestBall.class); @Test public void testball() { logger.debug("test begin "); ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml"); WatchBall watchball = (WatchBall) ctx.getBean("watchBall"); watchball.watchfootball("里约热内卢"); logger.debug("----------------------------"); watchball.watchbasketball("德克萨斯州"); } }7、运行结果和环境配置
运行结果如下:
DEBUG [main] (BallHandler.java:21) - 看球前,先去 里约热内卢 咖啡馆喝杯巴西咖啡 ...DEBUG [main] (WatchBallImpl.java:11) - 去 里约热内卢 看2014巴西世界杯
DEBUG [main] (TestBall.java:25) - ----------------------------
DEBUG [main] (WatchBallImpl.java:16) - 去德克萨斯州 看2014NBA总决赛
DEBUG [main] (BallHandler.java:26) - 看完球去 德克萨斯州 麦当劳吃汉堡包!
本例使用spring 2.5.6,除了找出spring.jar,commons-logging-1.1.1.jar两个jar包,外加一个log4j.jar。还要下载aspectj-1.7.0.jar,解压后有四个JAR:aspectjrt.jar,aspectjtools.jar,aspectjweaver.jar和org.aspectj.matcher.jar。还需要下载一个aopalliance-1.0.jar。
目录结构如下:
8、基于配置的AspectJ
除了使用 @Aspect 注解来定义切面类以外,Spring AOP 也提供了基于配置的方式来定义切面类。
(1) 配置文件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-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 定义Aspect --> <bean id="ballHandler" class="edu.eurasia.aop.BallHandler" /> <!-- 定义Common --> <bean id="watchBall" class="edu.eurasia.aop.WatchBallImpl" /> <aop:config> <aop:aspect ref="ballHandler"> <aop:before method="watchBefore" arg-names="city" pointcut="execution(* edu.eurasia.aop.WatchBallImpl.watchfoot*(String,..)) and args(city,..)" /> <aop:after method="watchAfter" arg-names="city" pointcut="execution(* edu.eurasia.aop.WatchBallImpl.watchbasket*(String,..)) and args(city,..)" /> </aop:aspect> </aop:config> </beans>
(String,..)声明切入点至少含有一个String类型的参数,显然可以匹配WatchBallImpl中的watchfoot*(String city,..);args(n,..)声明给watchfoot*(String city,..)的第一个参数起了个别名“n”传递给Advice,如果<aop:before...>中arg-names不是“n”,将抛出异常。
(2) BallHandler.java 代码变化不大:
package edu.eurasia.aop; import org.apache.log4j.Logger; public class BallHandler { static Logger logger = Logger.getLogger(TestBall.class); public void watchBefore(String city){ logger.debug(" 看球前,先去 " + city + " 咖啡馆喝杯巴西咖啡 ..."); } public void watchAfter(String city){ logger.debug("看完球去 " + city + " 麦当劳吃汉堡包!"); } }(3)其余代码和配置不变,运行结果相同。