天天看點

Spring2.0 AOP 随筆

Spring的AOP其實使用的是一種代理機制,可以算是代理或是裝飾模式的應用。下面是一些常用的術語:

Aspect:通常翻譯成切面,是一個橫跨多個核心邏輯的功能,比如說散落在系統中多個角落的一塊代碼。

Joinpoint:通常翻譯成連接配接點,是說程式中什麼地方插入一個Aspect。

Pointcut:切入點,一組Joinpoint的集合,比如一個Aspect需要在每個以query開頭的方法時執行,可以用正則(.*query.*)來表示這一組Joinpoint。

Advice:對Joinpoint的增強。

另外還有Introduct,Weaving,Interceptor,Target Object,Proxy。

常用的Advice:

1.MethodBeforeAdvice

2.AfterReturningAdvice

3.ThrowsAdvice

4.MethodInterceptor

1. 如果對UserServiceImpl增加advice增強,例如:

<?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"

>

    <!-- 配置UserServiceImpl -->

    <bean id="userServiceTarget" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

    <!-- 定義MethodBeforeAdvice -->

    <bean id="loginMethodBeforeAdvice" class="example.chapter4.LoginMethodBeforeAdvice" />

    <!-- 定義AfterReturningAdvice -->

    <bean id="loginAfterReturningAdvice" class="example.chapter4.LoginAfterReturningAdvice" />

    <!-- 定義ThrowsAdvice -->

    <bean id="loginThrowsAdvice" class="example.chapter4.LoginThrowsAdvice" />

    <!-- 定義MethodInterceptor -->

    <bean id="loginAroundAdvice" class="example.chapter4.LoginAroundAdvice" />

    <!-- 配置AOP -->

    <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">

            <list>

                <value>loginMethodBeforeAdvice</value>

                <value>loginAfterReturningAdvice</value>

                <value>loginThrowsAdvice</value>

            </list>

        </property>

        <property name="target" ref="userServiceTarget" />

    </bean>

</beans>

2.但是,有個缺點,這樣使用的話,會對UserServiceImpl中每個方法都加上這幾種advice,這個時候就需要Advisor。

<?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"

>

    <!-- 配置UserServiceImpl -->

    <bean id="userServiceTarget" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

    <!-- 使用NameMatchMethodPointcutAdvisor來定義loginBeforeAdvisor -->

    <bean id="loginBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

        <!--  指定切入方法為login -->

        <property name="mappedName" value="login" />

        <!-- 指定Advice -->

        <property name="advice">

            <bean class="example.chapter4.LoginMethodBeforeAdvice" />

        </property>

    </bean>

    <!-- 使用RegexpMethodPointcutAdvisor來定義createAfterAdvisor -->

    <bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

        <!-- 指定切入方法為.*create.*,攔截所有以create開頭的方法 -->

        <property name="pattern" value=".*create.*" />

        <!-- 指定Advice -->

        <property name="advice">

            <bean class="example.chapter4.CreateAfterReturningAdvice" />

        </property>

    </bean>

    <!-- 配置AOP -->

    <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">

            <list>

                <value>loginBeforeAdvisor</value>

                <value>createAfterAdvisor</value>

            </list>

        </property>

        <property name="target" ref="userServiceTarget" />

    </bean>

</beans>

這樣一來,可以分别對不同的方法進行分别增強。

3.不過,還是有個問題,使用者此時可以繞過AOP代理直接擷取原始bean,那麼,就需要自動代理來彌補這個問題。

<?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"

>

    <!-- 配置UserServiceImpl -->

    <bean id="userService" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

    <bean id="loginBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

        <!-- 限定切入點僅作用于UserService的login()方法 -->

        <property name="pattern" value=".*UserService/.login" />

        <property name="advice">

            <bean class="example.chapter4.LoginMethodBeforeAdvice" />

        </property>

    </bean>

    <bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

        <!-- 限定切入點僅作用于UserService的create()方法 -->

        <property name="pattern" value=".*UserService/.create" />

        <property name="advice">

            <bean class="example.chapter4.CreateAfterReturningAdvice" />

        </property>

    </bean>

    <!-- 配置BeanNameAutoProxyCreator -->

<!--

    <bean id="beanNameAutoProxy"

        class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

        <property name="beanNames" value="userService" />

        <property name="interceptorNames">

            <list>

                <value>loginBeforeAdvisor</value>

                <value>createAfterAdvisor</value>

            </list>

        </property>

    </bean>

-->

    <!-- 配置DefaultAdvisorAutoProxyCreator -->

   <bean id="defaultAutoProxy"

        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

</beans>

經過這樣配置後,可以将advisor應用的某個接口的某些方法上。

BeanNameAutoProxyCreator 與DefaultAdvisorAutoProxyCreator的不同,是後者可以自動查找advisor(不查找advice)和普通bean,然後根據advisor計算他們能夠适用于哪些bean,凡是能被适用的bean都将被自動建立AOP代理。

對于.*UserService/.create的形式,如果不指定接口,spring會去找其他符合條件的類中的create方法,那麼會提示CGLIB沒有找到。因為AOP代理對接口适用JDK動态代理,而對類級别使用CGLIB代理。

4.還有一中Introduction的使用,它是一種攔截器,我感覺是裝飾模式的一種應用

比如說有一個類User,在編譯器類A并未靜态實作其他接口,而想動态增強其行為,可以建立一個接口Mutable

配置代碼如下:

<?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"

>

    <!-- 具有Mutable接口的代理Bean -->

    <bean id="user" scope="prototype"

        class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="target" ref="target" />

        <property name="proxyTargetClass" value="true" />

        <property name="proxyInterfaces">

            <list>

                <value>example.chapter4.Mutable</value>

            </list>

        </property>

        <property name="interceptorNames">

            <list>

                <value>introductionAdvisor</value>

            </list>

        </property>

    </bean>

    <bean id="introductionAdvisor" scope="prototype"

        class="org.springframework.aop.support.DefaultIntroductionAdvisor">

        <constructor-arg>

            <bean class="example.chapter4.MutableInterceptor" />

        </constructor-arg>

    </bean>

    <!-- 原始Bean -->

    <bean id="target" class="example.chapter4.User" scope="prototype">

        <property name="username" value="default-name" />

        <property name="password" value="PaSsWoRd" />

        <property name="email" value="[email protected]" />

    </bean>

</beans>

繼續閱讀