`

征服Spring AOP—— Schema

阅读更多
自从开始使用Spring,就接触到AOP,但一直未能深入,沉淀一段时间后,开始全面整理!
这里针对一个接口中各个方法做为切面,通过Hello接口实现的例子来诠释Spring AOP 2.0 的特性。


相关内容:
征服Spring AOP—— Schema
征服Spring AOP—— @AspectJ


定义一个接口Hello,定义可触发BeforeAdvice、AfterAdvice、AroundAdvice、ThrowsAdvice、Introduction的方法。
Hello接口
public interface Hello {

	/**
	 * 前置增强
	 */
	void sayHelloBefore();

	/**
	 * 后置增强
	 */
	void sayHelloAfter();

	/**
	 * 后置返回增强
	 * 
	 * @return 
	 */
	String sayHelloAfterReturning();

	/**
	 * 环绕增强
	 */
	void sayHelloAround();

	/**
	 * 介入增强
	 */
	void sayHelloIntroduction();

	/**
	 * 异常抛出增强
	 */
	void sayHelloThrows();
}

针对Hello接口做具体实现,注意sayHelloThrows方法实现中,刻意抛出异常,用于触发ThrowsAdvice。
SayHello类,对Hello做具体实现。
public class SayHello implements Hello {
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloBefore()
	 */
	public void sayHelloBefore() {
		System.out.println("Say Hello Before!");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloAfter()
	 */
	public void sayHelloAfter() {
		System.out.println("Say Hello After!");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloAfterRunning()
	 */
	@Override
	public String sayHelloAfterReturning() {
		System.out.println("Say Hello After Returning!");
		// 返回值
		return "Hello";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloAround()
	 */
	public void sayHelloAround() {
		System.out.println("Say Hello Around!");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloAfterThrowing()
	 */
	@Override
	public void sayHelloAfterThrowing() {
		System.out.println("Say Hello Throws!");
		// 强制抛出异常,触发AfterThrowingAdvice
		throw new RuntimeException("Hello Excetion");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.zlex.aop.Hello#sayHelloIntroduction()
	 */
	public void sayHelloIntroduction() {
		System.out.println("Say Hello Introduction!");
	}

}


先来个测试代码:
public class AllTest {
	private ApplicationContext app;
	private Hello hello;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void before() throws Exception {
		app = new ClassPathXmlApplicationContext("applicationContext.xml");
		hello = (Hello) app.getBean("hello");
	}

	@Test
	public void testBefore() {
		System.out.println("=====Before Advice Begin=====");
		hello.sayHelloBefore();
		System.out.println("=====Before Advice End=====");
	}

	@Test
	public void testAfter() {
		System.out.println("=====After Advice Begin=====");
		hello.sayHelloAfter();
		System.out.println("=====After Advice End=====");
	}

	@Test
	public void testAfterReturning() {
		System.out.println("=====After Returning Advice Begin=====");
		String value = hello.sayHelloAfterReturning();
		// AfterReturning获得返回值,但不修改值内容!
		assertEquals("Hello", value);
		System.out.println("=====After Returning Advice End=====");
	}

	@Test
	public void testAround() {
		System.out.println("=====Around Advice Begin=====");
		hello.sayHelloAround();
		System.out.println("=====Around Advice End=====");
	}

	@Test
	public void testAfterThrowing() {
		System.out.println("=====After Throwing Advice Begin=====");
		try {
			hello.sayHelloAfterThrowing();
		} catch (Exception e) {
			assertNotNull(e);
		}
		System.out.println("=====After Throwing Advice End=====");
	}

	@Test
	public final void testIntroduction() {
		System.out.println("=====Introduction Begin=====");
		// 由于对Hello接口进行了引入,使得实现了Hello接口的类可以具备Ok接口的功能
		hello.sayHelloIntroduction();
		((Ok) hello).sayOk();
		System.out.println("=====Introduction End=====");
	}

}


Spring配置:
	<bean
		id="hello"
		class="org.zlex.aop.SayHello" />
	<bean
		id="advice"
		class="org.zlex.aop.Advice" />


这里的org.zlex.aop.Advice用于各个Advice的具体实现,以下是该类中各个方法的诠释。

对上述SayHello类的方法做具体Advice实现:
BeforeAdvice
	/**
	 * Before
	 * 
	 * @param joinPoint
	 */
	public void before(JoinPoint joinPoint) {
		System.out.println("Before: " + joinPoint.getSignature().getName());
	}


Spring配置:
	<!-- before -->
	<aop:config>
		<aop:pointcut
			id="beforePoint"
			expression="execution(* org.zlex.aop.Hello.sayHelloBefore(..))" />
		<aop:aspect
			id="beforeAspect"
			ref="advice"
		>
			<aop:before
				method="before"
				pointcut-ref="beforePoint" />
		</aop:aspect>
	</aop:config>


控制台输出:
引用

=====Before Advice Begin=====
Before: sayHelloBefore
Say Hello Before!
=====Before Advice End=====

BeforeAdvice在sayHelloBefore方法执行前调用Before类的invoke方法。

AfterAdvice
	public void after(JoinPoint joinPoint) {
		System.out.println("After: " + joinPoint.getSignature().getName());
	}

Spring配置:
	<!-- after -->
	<aop:config>
		<aop:pointcut
			id="afterPoint"
			expression="execution(* org.zlex.aop.Hello.sayHelloAfter(..))" />
		<aop:aspect
			id="afterAspect"
			ref="advice"
		>
			<aop:after
				method="after"
				pointcut-ref="afterPoint" />
		</aop:aspect>
	</aop:config>


控制台输出:
引用

=====After Advice Begin=====
Say Hello After!
After: sayHelloAfter
=====After Advice End=====

BeforeAdvice和AfterAdvice在实现上没有差异,其差别只是触发时机而已。

AfterAdvice只是在目标方法执行后触发,但无法获得目标方法的返回值,对于这点可以通过AfterReturningAdvice增强实现。

AfterReturningAdvice
	public void afterReturning(JoinPoint joinPoint, String retVal) {
		// 返回值参数名称(retVal)必须与XML配置文件中的'returning="retVal"'保持一致
		System.out.println("After: " + joinPoint.getSignature().getName());
		System.out.println("Return Value: " + retVal);
	}

Spring配置:
	<!-- afterReturning -->
	<aop:config>
		<aop:pointcut
			id="afterReturningPoint"
			expression="execution(* org.zlex.aop.Hello.sayHelloAfterReturning(..))" />
		<aop:aspect
			id="afterAspect"
			ref="advice"
		>
			<aop:after-returning
				method="after"
				pointcut-ref="afterReturningPoint"
				returning="retVal"
			/>
		</aop:aspect>
	</aop:config>


控制台输出:
引用

=====After Returning Advice Begin=====
Say Hello After Returning!
After: sayHelloAfterReturning
Return Value: Hello
=====After Returning Advice End=====


AroundAdvice
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("Around: " + joinPoint.getSignature().getName());
		System.out.println("Before");
		Object obj = joinPoint.proceed();
		System.out.println("End");
		return obj;
	}


Spring配置:
	<!-- around -->
	<aop:config>
		<aop:pointcut
			id="aroundPoint"
			expression="execution(* org.zlex.aop.Hello.sayHelloAround(..))" />
		<aop:aspect
			id="aroundAspect"
			ref="advice"
		>
			<aop:around
				method="around"
				pointcut-ref="aroundPoint" />
		</aop:aspect>
	</aop:config>


控制台输出:
引用

=====Around Advice Begin=====
Around: sayHelloAround
Before
Say Hello Around!
End
=====Around Advice End=====

AroundAdvice是BeforeAdvice和AfterAdvice的综合体。可以,在方法触发前、后分别进行操作。

如果方法执行过程中产生异常,就需要ThrowsAdvice。
AfterThrowingAdvice
	public void afterThrowing(JoinPoint joinPoint, Exception e) {
		// 异常参数名称(e)必须与XML配置文件中的'throwing="e"'保持一致
		System.out.println("AfterThrowing: "
				+ joinPoint.getSignature().getName());
		System.out.println("Exception Message: " + e.getMessage());
	}


Spring配置:
	<!-- afterThrowing -->
	<aop:config>
		<aop:pointcut
			id="afterThrowingPoint"
			expression="execution(* org.zlex.aop.Hello.sayHelloAfterThrowing(..))" />
		<aop:aspect
			id="afterThrowingAspect"
			ref="advice"
		>
			<aop:after-throwing
				method="afterThrowing"
				pointcut-ref="afterThrowingPoint"
				throwing="e"
			/>
		</aop:aspect>
	</aop:config>

控制台输出:
引用

=====After Throwing Advice Begin=====
Say Hello Throws!
AfterThrowing: sayHelloAfterThrowing
Exception Message: Hello Exception
=====After Throwing Advice End=====


AfterThrowingAdvice是Spring事务处理的核心触发环节。当事务提交产生异常时,将直接触发AfterThrowingAdvice,产生数据库回滚等动作。

除了上述常规增强实现外,还可以通过IntroductionInterceptor构建一个原本不存在的实现。
Introduction
	public Object introduction(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out
				.println("Introduction: " + joinPoint.getSignature().getName());
		return joinPoint.proceed();
	}


Introduction 可以对一个类的代码在不做任何修改(非入侵式),而使得该类拥有另一套方法,或者说具备另一套本领。
现在,我们就让这个实现了Hello接口的SayHello类拥有Ok接口的实现。
Ok接口,使得Hello接口的所有实现类都绑定这个接口:
public interface Ok {
	void sayOk();
}

IntroductionOk用于实现Ok接口,使得Hello接口实现类都具有sayOk的本领:
public class IntroductionOk implements Ok {
 
	@Override
	public void sayOk() {
		System.out.println("Ok!");
	}
}


Spring配置:
	<!-- introduction -->
	<!-- .*+是用于包下的,不是用于接口 +定义目标为该接口的所有实现类 -->
	<bean
		id="ok"
		class="org.zlex.aop.IntroductionOk" />
	<aop:config>
		<aop:aspect ref="advice">
			<aop:pointcut
				id="introductionPoint"
				expression="execution(* org.zlex.aop.Hello.sayHelloIntroduction(..))" />
			<aop:declare-parents
				implement-interface="org.zlex.aop.Ok"
				types-matching="org.zlex.aop.Hello+"
				delegate-ref="ok" />
			<aop:around
				method="introduction"
				pointcut-ref="introductionPoint" />
		</aop:aspect>
	</aop:config>



控制台输出:
引用

=====Introduction Advice Begin=====
Introduction: sayHelloIntroduction
Say Hello Introduction!
Ok!
=====Introduction Advice End=====

同样是Hello接口,在执行了sayHelloIntroduction方法时被拦截,同时输出say Hello introduction!,此时还可以执行Ok的方法输出Ok!。显然,Hello的实现类多了Ok接口的本领。

关于表达式符号:
引用
.*+定义目标为包下的所有类
+定义目标为该接口的所有实现类




Spring Beans 结构图如下:



代码详见附件!



相关内容:
征服Spring AOP—— Schema
征服Spring AOP—— @AspectJ

  • 大小: 224.6 KB
6
0
分享到:
评论
1 楼 1314520ln 2009-05-12  
写的很清晰,多谢分享~

相关推荐

Global site tag (gtag.js) - Google Analytics