spring心得8--AOP各種通知案例講解.doc

y_keven發表於2013-05-07

    上一篇部落格中已經簡單介紹了,這裡通過案例詳細說明一下aop中各種通知的用法。

    先列出後面介紹的JDK動態代理通知(主要區別於cglib代理而言,下面會具體介紹)、靜態切入點、正則切入點等都依賴使用的抽象主題(一個介面,可以是多個)、是、真實主題(改介面的實現類)

   介面:SayService.java

package www.csdn.spring.proxy.advice;

public interface SayService {

	public void say(String content);
	public void sayHi();
	public void sayHello();
	public void byebye();
}


實現類:SayServiceImpl.java

package www.csdn.spring.proxy.advice;

public interface SayService {

	public void say(String content);
	public void sayHi();
	public void sayHello();
	public void byebye();
}
   實現類:SayServiceImpl.java
package www.csdn.spring.proxy.advice;

public class SayServiceImpl implements SayService{

	@Override
	public void say(String content) {
		System.out.println("say:"+content);
		//int i=1/0;
	}

	@Override
	public void sayHi() {
		System.out.println("===sayHi()方法執行了===");
	}

	@Override
	public void sayHello() {
		System.out.println("===sayHello()方法執行了===");
	}

	@Override
	public void byebye() {
		System.out.println("===byebye()方法執行了===");
	}

}


 

1.各種通知的使用,JDK動態代理

   前置通知、後置通知、環繞通知、異常通知、引入通知的案例分析,下面註釋都有詳細解釋,這裡不再贅述。

   spring-advice.xml  spring配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 建立前置通知 -->
  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
  <!-- 建立後置通知 -->
  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
  <!-- 建立環繞通知 -->
  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
  <!-- 建立異常通知 -->
  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
  <!-- 引入通知 --> 
  <bean id="auditableAdvice" class="www.csdn.spring.proxy.advice.AuditableImpl"/>
  

  <!-- 真實主題   目標物件 -->
  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
  
  
  <!-- 配置代理操作 -->
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
   
    <!--抽象主題   實現介面 -->
    <property name="proxyInterfaces">
      <array>
        <value>www.csdn.spring.proxy.advice.SayService</value>
        <value>www.csdn.spring.proxy.advice.Auditable</value>
      </array>
    </property>
    
    <!-- 目標物件 -->
    <property name="target">
      <ref bean="sayServiceImpl"/>
    </property>
    
    <!-- 織入的通知的名稱 -->
    <property name="interceptorNames">
      <array>
        <value>beforeAdvice</value>
        <value>afterAdvice</value> 
        <value>aroundAdvice</value> 
        <value>throwAdvice</value> 
        <value>auditableAdvice</value> 
      </array>
    </property>
  </bean>
</beans>


 

下面是這幾個通知具體反映到的類,最關鍵的是這個幾個通知代理類所要繼承的介面,不可寫錯,更不可不寫,否則會出錯,這些錯誤將在後期本人一篇spring錯誤總結的部落格中涉及到,敬請期待。

   前者通知類:BeforeAdvice.java

package www.csdn.spring.proxy.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method method, Object[] args, Object target)
			throws Throwable {

		System.out.println("在目標方法執行之前執行........................");

		// 執行完之後執行exit方法,退出程式
		//System.exit(0);

		// 暴露的引數的含義
		System.out.println("目標方法:" + method.getName());
		if (args != null && args.length > 0) {
			for (Object arg : args) {
				System.out.println("傳遞的引數值:" + arg);
			}
		}
		System.out.println("目標物件:" + target.getClass());
	}

}

後置通知類:AfterAdvice.java
package www.csdn.spring.proxy.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method,
			Object[] args, Object target) throws Throwable {
		System.out.println("在目標方法執行之後執行..............................");
	}

}

環繞通知類:AroundAdvice.java
package www.csdn.spring.proxy.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AroundAdvice implements MethodInterceptor {

	public void beforeMethod() {
		System.out.println("around---------------------目標方法之前執行");
	}

	public void afterMethod() {
		System.out.println("around---------------------目標方法之後執行");
	}

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {

		Object returnValue = null;

		beforeMethod();

		returnValue = invocation.proceed();

		afterMethod();

		return returnValue;
	}
}

異常通知類:ThrowsAdvice.java
package www.csdn.spring.proxy.advice;

import java.lang.reflect.Method;

import org.springframework.aop.ThrowsAdvice;

public class ThrowAdvice implements ThrowsAdvice{
	
	public void afterThrowing(Method m,Object[] os,Object
			target,Throwable throwable){
		System.out.println("出現異常了:"+throwable.getLocalizedMessage());
	}

}


 

引用通知需要注意是,因為這裡spring使用的是jdk動態代理,所以使用引用通知的時候一定先建立一個引用通知的抽象介面,再建立一個引用通知類。

   抽象主題  Auditable.java

package www.csdn.spring.proxy.advice;

import java.util.Date;

public interface Auditable {
	
	public void setDate(Date date);
	public Date getDate();

}
  引用通知類 AuditableService.java
package www.csdn.spring.proxy.advice;

import java.util.Date;

import org.springframework.aop.support.DelegatingIntroductionInterceptor;

public class AuditableImpl extends DelegatingIntroductionInterceptor implements Auditable {

	private Date date;
	@Override
	public void setDate(Date date) {
		this.date = date;
	}

	@Override
	public Date getDate() {
		return date;
	}

}

測試類  AdviceTest.java
package www.csdn.spring.proxy.advice;

import java.util.Date;

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

public class AdviceTest {

	@Test
	public void testAdvice() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"classpath:spring-advice.xml");

		// 不使用代理的時候
		// context.getBean("sayServiceImpl",SayServiceImpl.class).say("嗨!楊凱!");

		// 使用代理的時候
		SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
		sayService.say("嗨!楊凱!");
		
		//引入通知測試
		Auditable auditable = (Auditable) sayService;
		auditable.setDate(new Date());
		System.out.println(auditable.getDate());

	}
}


 

2.各種通知的使用,cglib代理

   spring中的JDK動態代理和cglib代理區別就是:1)在配置檔案中的區別,即在配置檔案中抽象主鍵,介面物件配置不同,具體不同詳見下面紅色部分;主要通過property屬性的proxyTargetClass值設定。2)即JDK動態代理和cglib代理的不同,前者基於介面代理,後者基於類代理,所以這裡就有了真實主題的不同,即實現類的不同,cglib的真實主題沒有實現任何介面。

spring-advices.xml  spring配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 建立前置通知 -->
  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
  <!-- 建立後置通知 -->
  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
  <!-- 建立環繞通知 -->
  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
  <!-- 建立異常通知 -->
  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
  

  <!-- 真實主題   目標物件 -->
  <bean id="sayServiceImpls" class="www.csdn.spring.proxy.advice.SayServiceImpls"/>
  
  
  <!-- 配置代理操作 -->
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
   
    <!--抽象主題   實現介面 -->
    <!-- 寫法一:直接使用代理類,不再去指明是哪個代理類 ;這個時候value的值不管是true或false都其作用-->
    <property name="proxyTargetClass" value="true"/>      
    
    <!-- 指明代理類,這時候value必須是true的時候才起作用 ;
    使用false會報錯:Bean named 'proxyFactoryBean' must be of type [www.csdn.spring.advice.SayServiceImpls], 
    but was actually of type [$Proxy4]
    <property name="proxyInterfaces">
      <array>
        <value>www.csdn.spring.proxy.advice.SayService</value>
      </array>
    </property>
    <property name="proxyTargetClass">
      <value>true</value>
    </property> 
   -->
    
    <!-- 目標物件 -->
    <property name="target">
      <ref bean="sayServiceImpls"/>
    </property>
    
    <!-- 織入的通知的名稱 -->
    <property name="interceptorNames">
      <array>
        <value>beforeAdvice</value>
        <value>afterAdvice</value> 
        <value>aroundAdvice</value> 
        <value>throwAdvice</value> 
      </array>
    </property>
  </bean>
</beans>


真實主題:SayServiceImpls.java

package www.csdn.spring.proxy.advice;

public class SayServiceImpls{

	public void say(String content) {
		System.out.println("say:"+content);
	}

}
測試類  AdviceTests.java
package www.csdn.spring.proxy.advice;

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

public class AdviceTests {

	@Test
	public void testAdvice() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"classpath:spring-advices.xml");

		// 不使用代理的時候
		// context.getBean("sayServiceImpls",SayServiceImpls.class).say("嗨!你好!");

		// 使用代理的時候
		context.getBean("proxyFactoryBean", SayServiceImpls.class).say("嗨!你好!");

	}
}


 

3.靜態切入點

   為什麼會有靜態切入點,上一篇看部落格已經介紹過,為了使你編寫的通知,即代理在指定切入點起作用,也就是所謂的是aop的通知有實用價值。這裡通過案例詳細分析靜態切入點。

spring-staticAdvisor.xml  spring配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 建立前置通知 -->
  <bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
  <!-- 建立後置通知 -->
  <bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
  <!-- 建立環繞通知 -->
  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
  <!-- 建立異常通知 -->
  <bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
  
  
  <!-- 靜態切入點 -->
  <bean id="nameMatchMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <!-- 織入通知,比如環繞通知 -->
    <property name="advice">
      <ref bean="aroundAdvice"/>
    </property>
    <!-- 指明切入點 -->
    <property name="mappedName">
      <value>say</value>
    </property>
  </bean>
  

  <!-- 真實主題   目標物件 -->
  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
  
  
  <!-- 配置代理操作 -->
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
   
    <!--抽象主題   實現介面 -->
    <property name="proxyInterfaces">
      <array>
        <value>www.csdn.spring.proxy.advice.SayService</value>
      </array>
    </property>
    
    <!-- 目標物件 -->
    <property name="target">
      <ref bean="sayServiceImpl"/>
    </property>
    
    <!-- 織入的通知的名稱 -->
    <property name="interceptorNames">
      <array>
        <value>nameMatchMethodPointcutAdvisor</value>
      </array>
    </property>
  </bean>
</beans>


 

測試類  AdvisorTest.java
package www.csdn.spring.proxy.advice;

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

public class AdvisorTest {

	@Test
	public void testAdvice() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"classpath:spring-s*.xml");
		// 使用代理的時候
		SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
		sayService.say("嗨!楊凱,你好!");
		sayService.sayHi();
		sayService.sayHello();
		sayService.byebye();

	}
}


 

4.正則切入點

   閒話少說,與靜態切入點不同的就是正則切入點匹配正規表示式,重點程式碼見下面紅色部分.

spring-regAdvisor.xml  spring配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

  <!-- 建立環繞通知 -->
  <bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
   
  
  <!-- 靜態切入點 -->
  <bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <!-- 織入通知,比如環繞通知 -->
    <property name="advice">
      <ref bean="aroundAdvice"/>
    </property>
    <!-- 指明切入點 -->
    <property name="patterns">
    <!-- .是萬用字元的意思;第一個.*代表匹配任何包名和類名 ;第二個.*代表匹配以say開頭的任意方法-->
      <array>
      <value>.*sayH.</value>
      <value>.*bye.*</value>
      <value>www.*\.SayService\.sayHell.</value>
      </array>
    </property>
  </bean>
  

  <!-- 真實主題   目標物件 -->
  <bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
  
  
  <!-- 配置代理操作 -->
  <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
   
    <!--抽象主題   實現介面 -->
    <property name="proxyInterfaces">
      <array>
        <value>www.csdn.spring.proxy.advice.SayService</value>
      </array>
    </property>
    
    <!-- 目標物件 -->
    <property name="target">
      <ref bean="sayServiceImpl"/>
    </property>
    
    <!-- 織入的通知的名稱 -->
    <property name="interceptorNames">
      <array>
        <value>regexpMethodPointcutAdvisor</value>
      </array>
    </property>
  </bean>
</beans>


 

測試類  AdvisorTest.java

package www.csdn.spring.proxy.advice;

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

public class AdvisorTest {

	@Test
	public void testAdvice() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"classpath:spring-r*.xml");
		// 使用代理的時候
		SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
		sayService.say("嗨!楊凱,你好!");
		sayService.sayHi();
		sayService.sayHello();
		sayService.byebye();

	}
}


 

相關文章