首页 > 代码库 > 第二十九天 月出惊山鸟 —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.jarcommons-logging-1.1.1.jar两个jar包,外加一个log4j.jar还要下载aspectj-1.7.0.jar,解压后有四个JAR:aspectjrt.jar,aspectjtools.jar,aspectjweaver.jarorg.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)其余代码和配置不变,运行结果相同。