Spring3系列9- Spring AOP——Advice


Spring3系列9- Spring AOP——Advice

 

  Spring AOP即Aspect-oriented programming,面向切面編程,是作為面向對象編程的一種補充,專門用於處理系統中分布於各個模塊(不同方法)中的交叉關注點的問題。簡單地說,就是一個攔截器(interceptor)攔截一些處理過程。例如,當一個method被執行,Spring AOP能夠劫持正在運行的method,在method執行前或者后加入一些額外的功能。

 

在Spring AOP中,支持4中類型的通知(Advice)

Before advice      ——method執行前通知

After returning advice ——method返回一個結果后通知

After throwing advice – method拋出異常后通知

Around advice – 環繞通知,結合了以上三種

 

下邊這個例子解釋Spring AOP怎樣工作。

首先一個簡單的不使用AOP的例子。

先創建一個簡單的Service,為了稍后演示,這個類中加了幾個簡單的打印method。

CustomerService.java如下:

package com.lei.demo.aop.advice;

public class CustomerService {

    private String name;
    private String url;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
 
    public void printName() {
        System.out.println("Customer name : " + this.name);
    }
 
    public void printURL() {
        System.out.println("Customer website : " + this.url);
    }
 
    public void printThrowException() {
        throw new IllegalArgumentException();
    }

}

 

Xml配置文件Apring-AOP-Advice.xml如下:

<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-2.5.xsd">
 
    <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">
        <property name="name" value="LeiOOLei" />
        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />
    </bean>
 
</beans>

 

 運行以下代碼App.java:

package com.lei.demo.aop.advice;

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

public class App {

    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                new String[] { "Spring-AOP-Advice.xml" });
 
        CustomerService cust = (CustomerService) appContext.getBean("customerService");
 
        System.out.println("*************************");
        cust.printName();
        System.out.println("*************************");
        cust.printURL();
        System.out.println("*************************");
        try {
            cust.printThrowException();
        } catch (Exception e) {
 
        }
 
    }

}

 

運行結果:

*************************

Customer name : LeiOOLei

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

 

1.      Before Advice

創建一個實現了接口MethodBeforeAdvice的class,method運行前,將運行下邊的代碼

HijackBeforeMethod.java如下:

package com.lei.demo.aop.advice;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class HijackBeforeMethod implements MethodBeforeAdvice {

    public void before(Method arg0, Object[] args, Object target)
            throws Throwable {
        System.out.println("HijackBeforeMethod : Before method hijacked!");
        
    }

}

 

 在配置文件中加入新的bean配置HijackBeforeMethod,然后創建一個新的代理(proxy),命名為customerServiceProxy

“target”定義你想劫持哪個bean;

“interceptorNames”定義你想用哪個class(advice)劫持target。

Apring-AOP-Advice.xml如下:

<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-2.5.xsd">
 
    <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">
        <property name="name" value="LeiOOLei" />
        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />
    </bean>
    
    <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />
 
    <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="customerService" />
        <property name="interceptorNames">
            <list>
                <value>hijackBeforeMethodBean</value>
            </list>
        </property>
    </bean>
 
</beans>

 

注意:

用Spring proxy之前,必須添加CGLIB2類庫,,以下是pom.xml依賴

  <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>

 

運行如下代碼,注意代理

App.java如下

package com.lei.demo.aop.advice;

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

public class App {

    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                new String[] { "Spring-AOP-Advice.xml" });
 
        CustomerService cust = (CustomerService) appContext.getBean("customerServiceProxy");
 
        System.out.println("使用Spring AOP 如下");
        System.out.println("*************************");
        cust.printName();
        System.out.println("*************************");
        cust.printURL();
        System.out.println("*************************");
        
        try {
            cust.printThrowException();
        } catch (Exception e) {
 
        }
 
    }

}

 

輸出結果:

使用Spring AOP 如下

*************************

HijackBeforeMethod : Before method hijacked!

Customer name : LeiOOLei

*************************

HijackBeforeMethod : Before method hijacked!

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

HijackBeforeMethod : Before method hijacked!

 

每一個customerService的method運行前,都將先執行HijackBeforeMethod的before方法。

 

2.      After Returning Advice

創建一個實現了接口AfterReturningAdvice的class,method運行后,直到返回結果后,才運行下邊的代碼,如果沒有返回結果,將不運行切入的代碼。

HijackAfterMethod.java如下:

package com.lei.demo.aop.advice;

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class HijackAfterMethod implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] args,
            Object target) throws Throwable {
        System.out.println("HijackAfterMethod : After method hijacked!");

    }

}

 

修改bean配置文件,加入hijackAfterMethodBean配置,Apring-AOP-Advice.xml如下:

<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-2.5.xsd">
 
    <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">
        <property name="name" value="LeiOOLei" />
        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />
    </bean>
    
    <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />
    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />
 
    <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="customerService" />
        <property name="interceptorNames">
            <list>
                <value>hijackAfterMethodBean</value>
            </list>
        </property>
    </bean>
 
</beans>

 

現在再運行App.java后輸出如下:

使用Spring AOP 如下

*************************

Customer name : LeiOOLei

HijackAfterMethod : After method hijacked!

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

HijackAfterMethod : After method hijacked!

*************************

 

可以看到輸出結果,每一個customerService的method運行返回結果后,都將再執行HijackAfterMethodafterReturning方法。但是執行到cust.printThrowException()后,直接拋出異常,方法沒有正常執行完畢(或者說沒有返回結果),所以不運行切入的afterReturning方法。

 

3.      Afetr Throwing Advice

創建一個實現了ThrowsAdvice接口的class,劫持IllegalArgumentException異常,目標method運行時,拋出IllegalArgumentException異常后,運行切入的方法。

HijackThrowException.java如下:

package com.lei.demo.aop.advice;

import org.springframework.aop.ThrowsAdvice;

import sun.awt.SunToolkit.IllegalThreadException;

public class HijackThrowException implements ThrowsAdvice {

    public void afterThrowing(IllegalArgumentException e) throws Throwable {
        System.out.println("HijackThrowException : Throw exception hijacked!");
    }

}

 

修改bean配置文件,加入了hijackThrowExceptionBean,Apring-AOP-Advice.xml如下:

<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-2.5.xsd">
 
    <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">
        <property name="name" value="LeiOOLei" />
        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />
    </bean>
    
    <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />
    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />
    <bean id="hijackThrowExceptionBean" class="com.lei.demo.aop.advice.HijackThrowException" />
 
    <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="customerService" />
        <property name="interceptorNames">
            <list>
                <value>hijackThrowExceptionBean</value>
            </list>
        </property>
    </bean>
 
</beans>

 

運行結果如下:

使用Spring AOP 如下

*************************

Customer name : LeiOOLei

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

HijackThrowException : Throw exception hijacked!

 

當運行CustomerService中的printThrowException方法時,認為的拋出IllegalArgumentException異常,被HijackThrowException截獲,運行其中的afterThrowing方法。注意,如果拋出異常不是IllegalArgumentException,則不能被截獲。

 

4.      Around Advice

結合了以上3種形式的Advice,創建一個實現了接口MethodInterceptor的class,你必須通過methodInvocation.proceed()來調用原來的方法,即通過調用methodInvocation.proceed()來調用CustomerService中的每一個方法,當然也可以不調用原方法。

HijackAroundMethod.java如下:

package com.lei.demo.aop.advice;

import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class HijackAroundMethod implements MethodInterceptor {

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("Method name : "
                + methodInvocation.getMethod().getName());
        System.out.println("Method arguments : "
                + Arrays.toString(methodInvocation.getArguments()));
 
        // 相當於  MethodBeforeAdvice
        System.out.println("HijackAroundMethod : Before method hijacked!");
 
        try {
            // 調用原方法,即調用CustomerService中的方法
            Object result = methodInvocation.proceed();
 
            // 相當於 AfterReturningAdvice
            System.out.println("HijackAroundMethod : After method hijacked!");
 
            return result;
 
        } catch (IllegalArgumentException e) {
            // 相當於 ThrowsAdvice
            System.out.println("HijackAroundMethod : Throw exception hijacked!");
            throw e;
        }
    }

}

 

修改bean配置文件,加入了hijackAroundMethodBean,Apring-AOP-Advice.xml如下:

<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-2.5.xsd">
 
    <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">
        <property name="name" value="LeiOOLei" />
        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />
    </bean>
    
    <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />
    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />
    <bean id="hijackThrowExceptionBean" class="com.lei.demo.aop.advice.HijackThrowException" />
    <bean id="hijackAroundMethodBean" class="com.lei.demo.aop.advice.HijackAroundMethod" />
 
    <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="customerService" />
        <property name="interceptorNames">
            <list>
                <value>hijackAroundMethodBean</value>
            </list>
        </property>
    </bean>
 
</beans>

 

執行App.java,輸出結果:

使用Spring AOP 如下

*************************

Method name : printName

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer name : LeiOOLei

HijackAroundMethod : After method hijacked!

*************************

Method name : printURL

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer website : http://www.cnblogs.com/leiOOlei/

HijackAroundMethod : After method hijacked!

*************************

Method name : printThrowException

Method arguments : []

HijackAroundMethod : Before method hijacked!

HijackAroundMethod : Throw exception hijacked!

 

CustomerService中每一個方法的調用,都會執行HijackAroundMethod中的invoke方法,可以看到整個切入點將目標around。

 

大多數的Spring開發者只用Around Advice,因為它能夠實現所有類型的Advice。在實際的項目開發中,我們還是要盡量選擇適合的Advice。

       在以上的例子中,CustomerService中的所有方法都被自動攔截,但是大多數情況下,我們不需要攔截一個class中的所有方法,而是攔截符合條件的方法。這時,我們就需要用到Pointcut and Advice,即切入點和通知,以后的章節中會逐漸介紹。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM