Spring基本使用(AOP簡單使用,基於代理的方式)

0.0
  • 這是一個基於代理的 spring AOP 簡單示例
  • 這種通過配置來達到修改程序邏輯的方式,必須被小心/留意/謹慎使用,因爲這對原始 bean 的代碼來說是透明的,這很可能給調試代碼帶來不必要的阻礙。所以在 切點位置 最好/必須 添加必要的註釋 說明/強調 AOP 這種影響代碼邏輯的特殊情況。
  • A pointcut picks out certain join points and values at those points. A piece of advice is code that is executed when a join point is reached. — from : Introduction to AspectJ - Chapter 1. Getting Started with AspectJ
1. spring 基於代理的 AOP 示例
  1. 首先定義一個接口(該接口用於客戶端編程,基於面向接口編程的原則。並非一定必須。)
package com.willhonor.test.useaop;

public interface IAPersonA {
	/** 將在該方法 *執行之前* 切入 */
	void saySucceedA();
	/** 將在該方法 *成功返回之後* 切入 */
	void saySucceedB();
	/** 該方法僅僅*拋出異常* */
	void sayFailed();
}
  1. 上面接口 IAPersonA 的具體實現類
package com.willhonor.test.useaop;

public class APersonA implements IAPersonA {
	/** 將在該方法 *執行之前* 切入 */
	public void saySucceedA() {
		System.out.println("A say hello, this world.");
	}
	/** 將在該方法 *成功返回之後* 切入 */
	public void saySucceedB() {
		System.out.println("B say hello, this world.");
	}
	/** 該方法僅僅*拋出異常* */
	public void sayFailed() {
		throw new RuntimeException("unknown exception");
	}
}
  1. 下面連續定義 3 個通知,它們分別用於上述接口 IAPersonA 中的 3 個方法(即 saySucceedA / saySucceedB / sayFailed ,它們的通知方式分別爲: 方法執行之前 / 方法成功返回之後 / 方法拋出異常)
package com.willhonor.test.useaop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;
/** 將應用於接口 IAPersonA 中 saySucceedA 方法 *執行之前**/
public class MMthodBeforeAdvice implements MethodBeforeAdvice {
	private int methodInvokeCount = 0;
	public void before(Method method, Object[] args, Object target) throws Throwable {
		this.methodInvokeCount++;
		System.out.println(String.format("methodBeforeAdvice: 方法:[%s]執行之前通知", new Object[] {method.getName()}));
	}
	
	public int getMethodInvokeCount() {
		return methodInvokeCount;
	}
}
package com.willhonor.test.useaop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
/** 將應用於接口 IAPersonA 中 saySucceedB 方法 *成功返回之後**/
public class MAfterMethodReturningAdvice implements AfterReturningAdvice {
	private int methodSucceedReturningCount = 0;
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		this.methodSucceedReturningCount++;
		System.out.println(String.format("afterReturningAdvice: 方法[%s]成功返回", new Object[] {method.getName()}));
	}

	public int getMethodSucceedReturningCount() {
		return methodSucceedReturningCount;
	}
}
package com.willhonor.test.useaop;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;
/** 將應用於接口 IAPersonA 中 sayFailed 方法 *拋出異常*的情況*/
public class MThrowsAdvice implements ThrowsAdvice {
	private int methodInvokeException = 0;
	public void afterThrowing(Method method, Object[] args, Object obj, Throwable ex) {
		this.methodInvokeException++;
		System.out.println(String.format("throwsAdvice: 方法:[%s]拋出異常:" + ex, new Object[] {method.getName()}));
	}
	
	public int getMethodInvokeException() {
		return methodInvokeException;
	}
}
  1. spring 配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans SYSTEM "spring-beans.dtd">
<beans>
<!-- 	persona :這是一個 未被代理 的原始的 bean -->
	<bean id="persona" 
		class="com.willhonor.test.useaop.APersonA">
	</bean>
	
<!-- 	定義多個通知 -->
	<bean id="afteradvice" class="com.willhonor.test.useaop.MAfterMethodReturningAdvice">
		<description>爲了切入:方法成功返回 returning 之後</description>
	</bean>
	<bean id="beforeadvice" class="com.willhonor.test.useaop.MMthodBeforeAdvice">
		<description>爲了切入:方法被調用 invoke 之前</description>
	</bean>
	<bean id="throwsadvice" class="com.willhonor.test.useaop.MThrowsAdvice">
		<description>爲了切入:方法拋出異常 throws ex 之後</description>
	</bean>
	
<!-- 	定義切點 -->
	<bean id="pointcut:saySucceedA" class="org.springframework.aop.support.NameMatchMethodPointcut">
		<description>切點爲 saySucceedA 方法</description>
		<property name="mappedNames">
			<list>
				<value>saySucceedA</value>
			</list>
		</property>
	</bean>
	<bean id="pointcut:saySucceedB" class="org.springframework.aop.support.NameMatchMethodPointcut">
		<description>切點爲 saySucceedB 方法</description>
		<property name="mappedName">
			<value>saySucceedB</value>
		</property>
	</bean>
	<bean id="pointcut:sayFailed" class="org.springframework.aop.support.NameMatchMethodPointcut">
		<description>切點爲 sayFailed 方法</description>
		<property name="mappedName">
			<value>sayFailed</value>
		</property>
	</bean>
	
	<!-- 	定義多個通知器(組合一個 *通知* 和 一個 *切點*) -->
	<!-- 	通知器(advisor)的目的是:爲了讓 *通知* 和 *切點* 都可以單獨地重用 -->
	<bean name="advisor:before"
		class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<!-- 在切點即方法 saySucceedA 執行之前,進行通知即應用beforeadvice -->
		<constructor-arg index="0">			
			<ref local="pointcut:saySucceedA"/>
		</constructor-arg>
		<constructor-arg index="1">			
			<ref local="beforeadvice"/>
		</constructor-arg>
	</bean>
	<bean name="advisor:after"
		class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<!-- 在切點即方法 saySucceedB 成功返回之後,進行通知即應用afteradvice -->
		<constructor-arg index="0">			
			<ref local="pointcut:saySucceedB"/>
		</constructor-arg>
		<constructor-arg index="1">			
			<ref local="afteradvice"/>
		</constructor-arg>
	</bean>
	<bean name="advisor:throws"
		class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<!-- 在切點即方法 sayFailed 拋出異常時,進行通知即應用throwsadvice-->
		<constructor-arg index="0">			
			<ref local="pointcut:sayFailed"/>
		</constructor-arg>
		<constructor-arg index="1">			
			<ref local="throwsadvice"/>
		</constructor-arg>
	</bean>
	
<!-- 	personb :被代理的 bean(基於代理的 AOP 方式) -->
	<bean id="personb" parent="proxytemplate">
		<property name="target">
			<bean class="com.willhonor.test.useaop.APersonA">
			</bean>
		</property>
		<property name="interfaces">
			<list>
				<value>com.willhonor.test.useaop.IAPersonA</value>
			</list>
		</property>
	</bean>
<!-- 	template(模板) -->
	<bean id="proxytemplate" abstract="true"
		class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="interceptorNames">
			<list>
				<value>advisor:before</value>
				<value>advisor:after</value>
				<value>advisor:throws</value>
			</list>
		</property>
	</bean>
</beans>
  1. 測試代碼
package com.willhonor.test.useaop;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test_use_aop_1 {
	@Test
	public void test_use_aop_1() throws Exception {
		String pathA = "com/willhonor/test/configs/application.m.xml";
		String[] path = new String[] {pathA};
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
		// 獲取未被代理的 原始的實例
		IAPersonA persona = (IAPersonA) context.getBean("persona");
		persona.saySucceedA();
		System.out.println("--");
		persona.saySucceedB();
		System.out.println("--");
		try{persona.sayFailed();}catch(RuntimeException e) {System.out.println(e.getMessage());}
		System.out.println("--");
		System.out.println("\n--------------------------------\n");
		// 獲取被代理的實例
		IAPersonA personb = (IAPersonA) context.getBean("personb");
		personb.saySucceedA();
		System.out.println("--");
		personb.saySucceedB();
		System.out.println("--");
		try{personb.sayFailed();}catch(RuntimeException e) {System.out.println(e.getMessage());}
		System.out.println("--");
		// close context
		context.close();
	}
}
  1. 測試執行結果爲(通過原始 bean 實例 和 被代理的 bean 實例之間執行結果的對比,可見,通過代理正確切入了指定方法的執行前/後以及方法執行異常的情況。基於代理的 AOP 已經可以滿足很多一般性的需求了。)(在實際應用中,如果需要對原始的 bean 進行切入,可以僅僅通過修改 spring 的配置文件,即 使用本文介紹的基於代理的 AOP 實現即可達到目的,而客戶端代碼對此卻毫不知情,即無需修改客戶端代碼。)(這種通過配置來達到修改程序邏輯的方式,必須被小心/留意/謹慎使用,因爲這對原始 bean 的代碼來說是透明的,這很可能給調試代碼帶來不必要的阻礙。所以在 切點 位置最好/必須添加必要的註釋說明/強調 AOP 這種影響代碼邏輯的特殊情況。)
...
# 省略 spring 日誌打印
...
A say hello, this world.
--
B say hello, this world.
--
unknown exception
--

--------------------------------

methodBeforeAdvice: 方法:[saySucceedA]執行之前通知
A say hello, this world.
--
B say hello, this world.
afterReturningAdvice: 方法[saySucceedB]成功返回
--
throwsAdvice: 方法:[sayFailed]拋出異常:java.lang.RuntimeException: unknown exception
unknown exception
--
...
# 省略 spring 日誌打印
...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章