Spring有兩大核心,IOC和AOP。IOC在java web項目中無時無刻不在使用(上篇博客已經寫了IOC基礎的使用)。然而AOP用的比較少,但是這並不以爲這不使用,在一些系統中,經常需要在一個服務流程中插入一些與業務邏輯無關的系統服務邏輯(最常見的就是記錄日誌,權限檢查等),如果把所有這些與業務邏輯無關的服務與業務邏輯編織在一起,就會使業務邏輯對象的負擔加重,因爲它不但要具有業務邏輯的功能,還帶有例如記錄日誌等其他功能,這樣就容易產生對象的職責混淆。
AOP的基本概念:
《Spring參考手冊》中定義了以下幾個AOP的重要概念:
•切面(Aspect):官方的抽象定義爲“一個關注點的模塊化,這個關注點可能會橫切多個對象”,在本例中,“切面”就是類TestAspect所關注的具體行爲
•連接點(Joinpoint):程序執行過程中的某一行爲,
•通知(Advice):“切面”對於某個“連接點”所產生的動作,.其中,一個“切面”可以包含多個“Advice”.
•切入點(Pointcut):匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯。
•目標對象(Target Object):被一個或者多個切面所通知的對象。
• AOP代理(AOP Proxy)在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。默認情況下,TargetObject實現了接口時,則採用JDK動態代理.
通知(Advice)類型
•前置通知(Before advice):在某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。
•後通知(After advice):當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>裏面使用<aop:after>元素進行聲明。
•返回後通知(After returnadvice):在某連接點正常完成後執行的通知,不包括拋出異常的情況。
•環繞通知(Around advice):包圍一個連接點的通知,類似Web中Servlet規範中的Filter的doFilter方法。可以在方法的調用前後完成自定義的行爲,也可以選擇不執行。ApplicationContext中在<aop:aspect>裏面使用<aop:around>元素進行聲明。
•出異常後通知(Afterthrowing advice):在方法拋出異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裏面使用<aop:after-throwing>元素進行聲明。
切入點表達式
•常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:
Java代碼
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
difiers-pattern:方法的操作權限
t-type-pattern:返回值
claring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:參數名
throws-pattern:異常
其中,除ret-type-pattern和name-pattern之外,其他都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值爲任意類型;方法名任意;參數不作限制的所有方法。
•通知參數
可以通過args來綁定參數,這樣就可以在通知(Advice)中訪問具體參數了。例如,<aop:aspect>配置如下
Java代碼
<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<aop:pointcut id="businessService"
expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
<aop:after pointcut-ref="businessService" method="doAfter"/>
</aop:aspect>
</aop:config>
TestAspect的doAfter方法中就可以訪問msg參數,但這樣以來AService中的barA()和BServiceImpl中的barB()就不再是連接點,因爲execution(*com.spring.service.*.*(String,..))只配置第一個參數爲String類型的方法。其中,doAfter方法定義如下:
Java代碼
public void doAfter(JoinPoint jp,String msg)
• 訪問當前的連接點
任何通知(Advice)方法可以將第一個參數定義爲 org.aspectj.lang.JoinPoint 類型。JoinPoint 接口提供了一系列有用的方法,比如 getArgs() (返回方法參數)、getThis()(返回代理對象)、getTarget() (返回目標)、getSignature() (返回正在被通知的方法相關信息)和 toString() 。
AOP 的使用
首先,我們需要在一個對一個用戶的增刪改查前或後增加橫切方法:
package com.bjpowernode.spring;
publicclass UserManagerImpl implements UserManager {
@Override
publicvoid addUser(Stringusername, String password) {
System.out.println("---------UserManagerImpl.addUser()-----------------");
}
@Override
publicvoid delUser(int userId) {
System.out.println("---------UserManagerImpl.delUser()-----------------");
}
@Override
public String findUserById(int userId) {
System.out.println("---------findUserById.addUser()-----------------");
return"張三";
}
@Override
publicvoid modifyUser(int userId, String usrename, String password) {
System.out.println("---------findUserById.modifyUser()-----------------");
}
}
調用該方法:
publicstaticvoid main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserManager userManager = (UserManager)factory.getBean("userManager");
userManager.addUser("張三", "123");
}
現在代碼已經寫好,要求我們在不修改代碼的情況下加入該方法。這裏有兩種方法實現。
第一種,使用註釋的的方法:
加入SecurityHandler類:
package com.bjpowernode.spring;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SecurityHandler {
/**
* 定義Pointcut,Pointcut的名稱爲addAddMethod(),此方法沒有返回值和參數
* 該方法就是一個標識,不進行調用
*/
@Pointcut("execution(*add*(..))")
privatevoid addAddMethod(){};
/**
* 定義Advice,表示我們的Advice應用到哪些Pointcut訂閱的Joinpoint上
*/
@Before("addAddMethod()")
//@After("addAddMethod()")
privatevoid checkSecurity() {
System.out.println("-------checkSecurity-------");
}
}
配置文件:
<!-- 啓用AspectJ對Annotation的支持 -->
<aop:aspectj-autoproxy/>
<bean id="userManager" class="com.bjpowernode.spring.UserManagerImpl"/>
<bean id="securityHandler" class="com.bjpowernode.spring.SecurityHandler"/>
</beans>
第二種方法,把需要的配置寫在xml文件中:
<aop:config>
<aop:aspect id="securityAspect" ref="securityHandler">
<!--
以add開頭的方法
<aop:pointcut id="addAddMethod" expression="execution(* add*(..))"/>
-->
<!--
com.bjpowernode.spring包下所有的類所有的方法
<aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.*(..))"/>
-->
<aop:pointcut id="addAddMethod" expression="execution(* com.bjpowernode.spring.*.add*(..)) || execution(* com.bjpowernode.spring.*.del*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="addAddMethod"/>
</aop:aspect>
</aop:config>
checkSecurity類爲:
在很多情況下,我們需要獲取該方法的一些參數,進行分析,比如添加用戶是我們這裏要或許添加用戶的用戶名作爲記錄,那麼AOP是怎麼實現獲取參數的呢?
這裏我們寫一個循環即可:
for (int i=0;i<joinPoint.getArgs().length; i++) {
System.out.println(joinPoint.getArgs()[i]);
}
運行結果爲:
這樣,就在我們需要添加方法的前面或後面加入需要的方法。這裏和filter有着異曲同工之處,但是我感覺比起filter更加方便。
到此,spring的兩個核心技術都介紹完了,比較基礎,我相信以後做項目的時候一定會有更深刻的理解的。