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>