天天看點

Spring AOP技術--動态代理使用spring的ProxyFactory來建立代理對象:

概述:

Spring AOP是一種面向切面的程式設計方式,使用這種方式,我們可以在處理業務邏輯代碼的前後織入某些公共的處理,比如:事務管理,在業務邏輯處理前後添加性能監視代碼等,最常用到AOP的地方就是事務處理。spring的AOP使用動态代理技術在代碼運作期間織入前置或者後置通知(advice)代碼。spring使用到了java的動态代理和cglib的動态代理來實作AOP的功能。

關于java的動态代理可以參看一個簡單執行個體:點選打開連結 關于cglib的動态代理可以參看這個簡單示例:點選打開連結

使用spring的ProxyFactory來建立代理對象:

前置通知、後置通知

下面通過一個示例使用ProxyFactory來建立代理對象,在業務處理的方法前後使用前置通知(MethodBeforeAdvice)和後置通知(AfterReturningAdvice)來圍繞業務處理方法添加額外的處理。示例的結構如下:

Spring AOP技術--動态代理使用spring的ProxyFactory來建立代理對象:

其中StudentServiceImpl類為業務操作類,MyMethodBeforAdvice和MyAfterReturningAdvice類分别是前置通知和後置通知類,最後一個是測試類。 StudentServiceImpl類代碼如下:

package cn.qing.spring.aop;
/**
 * 業務處理類
 * @author ding
 *
 */
public class StudentServiceImpl {

	public String findStudent(String stuName) {
		System.out.println("execute findStudent method....");
		return "hello "+stuName;
	}

	public int findStudentNum() {
		System.out.println("findStudentNum method ...");
		return 20;
	}

}
           

MyMethodBeforAdvice類的代碼:

package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class MyMethodBeforAdvice implements MethodBeforeAdvice {

	/**
	 * method		目标執行個體的方法
	 * args			目标方法的參數
	 * obj			目标執行個體
	 */
	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		System.out.println("通路的目标對象:"+obj.getClass());
		System.out.println("通路的方法名稱:"+method.getName());
		if(args!=null && args.length>0)	
		{
			for(Object o : args)
				System.out.println("方法的參數:"+o.toString());
		}
		System.out.println("這個是spring的前置通知,可以在方法執行前執行這裡的代碼....");
		System.out.println("**************************************");
	}

}
           

MyAfterReturningAdvice類代碼:

package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class MyAfterReturningAdvice implements AfterReturningAdvice {

	/**
	 * returnObj	目标執行個體方法傳回的結果
	 * method		目标類的方法
	 * args			目标執行個體的方法入參
	 * obj			目标執行個體
	 */
	public void afterReturning(Object returnObj, Method method, Object[] args,
			Object obj) throws Throwable {
		System.out.println("****************************************");
		System.out.println("通路的目标對象為:"+obj.getClass());
		
		System.out.println("通路的方法為:"+method.getName());
		
		if(returnObj!=null)
			System.out.println("目标執行個體方法傳回的結果為:"+returnObj.toString());
		
		System.out.println("可以在這裡進行一些後置處理...");
	}

}
           

測試類:

package cn.qing.spring.aop;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class TestAdvice {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		StudentServiceImpl ssi = new StudentServiceImpl();
		//執行個體化自定義前置通知
		BeforeAdvice beforeAdvice = new MyMethodBeforAdvice();
		//執行個體化自定義後置通知
		AfterAdvice afterAdvice = new MyAfterReturningAdvice();
		//使用spring的ProxyFactory來建立代理對象
		ProxyFactory proxyFactory = new ProxyFactory();
		//設定目标對象
		proxyFactory.setTarget(ssi);
		//為代理目标添加前置通知
		proxyFactory.addAdvice(beforeAdvice);
		//為代理目标添加後置通知
		proxyFactory.addAdvice(afterAdvice);
		
		StudentServiceImpl impl = (StudentServiceImpl)proxyFactory.getProxy();
		
		impl.findStudent("xiaoming");
		//列印出從ProxyFactory中擷取的執行個體的class,可以看到傳回的是cglib建立的代理類
		System.out.println("impl class:"+impl.getClass());
	}

}
           

輸出結果:

通路的目标對象:class cn.qing.spring.aop.StudentServiceImpl
通路的方法名稱:findStudent
方法的參數:xiaoming
這個是spring的前置通知,可以在方法執行前執行這裡的代碼....
**************************************
execute findStudent method....
****************************************
通路的目标對象為:class cn.qing.spring.aop.StudentServiceImpl
通路的方法為:findStudent
目标執行個體方法傳回的結果為:hello xiaoming
可以在這裡進行一些後置處理...
impl class:class cn.qing.spring.aop.StudentServiceImpl$$EnhancerByCGLIB$$d304c998
           

從輸出結果可以看出,在調用StudentServiceImpl類的findStudent("xiaoming")方法前後都輸出了我們在前置通知和後置通知中添加的代碼。需要注意的是,在spring 3.x之後的版本都是将spring按各個子產品分開打的包,是以在使用aop時需要再導入一個aop的标準包aopalliance-1.0.jar,隻有加入這個包才能正常使用。在spring 2.x的版本中所有的jar包都是打在了spring.jar中,是以不存在這個問題。

在上面的TestAdvice類中是通過顯示聲明ProxyFactory類,然後通過代碼添加前置後置通知,最後再擷取代理對象,這種通過代碼的操作可以在spring的配置檔案中通過配置bean來替換,下面是在spring的配置檔案中進行的配置:

<span style="white-space:pre">	</span><!-- 配置自定義的前置通知Bean -->
	<bean id="beforAdvice" class="cn.qing.spring.aop.MyMethodBeforAdvice"/>
	<!-- 配置自定義的後置通知Bean -->
	<bean id="afterAdvice" class="cn.qing.spring.aop.MyAfterReturningAdvice"/>
	<!-- 配置業務處理實體Bean -->
	<bean id="serviceImpl" class="cn.qing.spring.aop.StudentServiceImpl"/>
	<!-- 配置代理對象 -->
	<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 配置代理的目标 -->
		<property name="target" ref="serviceImpl"/>
		<property name="interceptorNames" value="beforAdvice,afterAdvice"/>
	</bean>
           

然後得到代理bean,并執行對應的方法:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentServiceImpl impl = (StudentServiceImpl)context.getBean("serviceProxy");
impl.findStudent("xiaoming");
System.out.println("impl name:"+impl.getClass());
           

最終的輸出結果和上面通過代碼的輸出是一樣。

環繞通知:

環繞通知使用的是AOP聯盟所定義的MethodInterceptor接口來實作的,這個接口其實和cglib中的MethodInterceptor很類似,都是可以在業務方法前後添加相應的處理,是以稱之為環繞通知,它實作的效果其實和同時使用前置通知和後置通知沒太大差別。使用的具體代碼如下:

package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 自定義環繞通知
 * @author ding
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		//得到目标對象
		Object obj = invocation.getThis();		
		//擷取目标對象方法
		Method method = invocation.getMethod();
		//擷取參數清單資訊
		Object[] args = invocation.getArguments();
		
		System.out.println("通路的目标對象:"+obj.getClass());
		System.out.println("通路的方法名稱:"+method.getName());
		if(args!=null && args.length>0)	
		{
			for(Object o : args)
				System.out.println("方法的參數:"+o.toString());
		}
		System.out.println("這個是spring的前置通知,可以在方法執行前執行這裡的代碼....");
		System.out.println("**************************************");
		
		//通過反射調用目标方法
		<strong>Object object = invocation.proceed();</strong>
		
		System.out.println("****************************************");
		System.out.println("通路的目标對象為:"+obj.getClass());
		
		System.out.println("通路的方法為:"+method.getName());
		System.out.println("可以在這裡進行一些後置處理...");
		return object;
	}

}
           

這個通知在使用時和前面的一樣,可以使用ProxyFactory.addAdvice()方法直接添加通知,也可以在spring 配置檔案中配置使用,在擷取代理對象以後通路某個具體方法時也會在前後輸出上面的列印資訊。

繼續閱讀