Spring3系列9- Spring AOP——Advice

Spring3系列9- Spring AOP——Advice

 

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

 

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

Before advice      ——method執行前通知spring

After returning advice ——method返回一個結果後通知編程

After throwing advice – method拋出異常後通知app

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

 

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

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

先建立一個簡單的Service,爲了稍後演示,這個類中加了幾個簡單的打印method。代理

CustomerService.java以下:code

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,即切入點和通知,之後的章節中會逐漸介紹。

相關文章
相關標籤/搜索