Spring 學習筆記一 IOC AOP
Spring的核心架構圖
什麼是IoC?
IoC Inversion of Control (控制反轉),注意它是⼀個技術思想,不是⼀個技術實現
描述的事情:Java開發領域物件的建立,管理的問題
例項化Bean的三種方式?
方式一: 使用無參構造。在預設情況下,它會通過反射調⽤⽆參建構函式來建立物件。如果類中沒有⽆參建構函式,將建立失敗。
<!--配置service物件-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl"> </bean>
⽅式⼆:使⽤靜態⽅法建立
<!--使⽤靜態⽅法建立物件的配置⽅式--> <bean id="userService" class="com.lagou.factory.BeanFactory" factory-method="getTransferService"></bean>
⽅式三:使⽤例項化⽅法建立
此種⽅式和上⾯靜態⽅法建立其實類似,區別是⽤於獲取物件的⽅法不再是static修飾的了,⽽是類中的⼀ 個普通⽅法。此種⽅式⽐靜態⽅法建立的使⽤⼏率要⾼⼀些。 在早期開發的項⽬中,⼯⼚類中的⽅法有可能是靜態的,也有可能是⾮靜態⽅法,當是⾮靜態⽅法時,即可 採⽤下⾯的配置⽅式
<bean id="beanFactory"
class="com.lagou.factory.instancemethod.BeanFactory"></bean> <bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>
Bean的作用範圍及生命週期
作⽤範圍的改變 在spring框架管理Bean物件的建立時,Bean物件預設都是單例的,但是它⽀持配置的⽅式改變作⽤範圍。作⽤範圍官⽅提供的說明如下圖:
<!--配置service物件-->
<bean id="transferService"
class="com.lagou.service.impl.TransferServiceImpl" scope="singleton"> </bean>
單例模式:singleton
物件出⽣:當建立容器時,物件就被建立了。
物件活著:只要容器在,物件⼀直活著。
物件死亡:當銷燬容器時,物件就被銷燬了。
⼀句話總結:單例模式的bean物件⽣命週期與容器相同。
多例模式:prototype
物件出⽣:當使⽤物件時,建立新的物件例項。
物件活著:只要物件在使⽤中,就⼀直活著。
物件死亡:當物件⻓時間不⽤時,被java的垃圾回收器回收了。
⼀句話總結:多例模式的bean物件,spring框架只負責建立,不負責銷燬。
Bean標籤屬性
依賴注入
方式一:建構函式注入,類中提供的建構函式引數個數必須和配置的引數個數⼀致,且資料型別匹配。同時需要注意的是,當沒有⽆參構造時,則必須提供建構函式引數的注⼊,否則Spring框架會報錯。
<bean id ="userDao" class="com.lgl.study.dao.impl.UserDaoImpl" scope="singleton"> <!-- 按照引數名稱--> <constructor-arg name="connectionUtils" ref="connectionUtils"> <constructor-arg name="money" value="100.6">
<!-- 按照引數位置--> <constructor-arg index="1" ref="connectionUtils"> <constructor-arg index="2" value="100.6">
</bean>
方式二:使用set方法注入
<bean id="userService" class="com.lgl.study.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
複雜型別屬性注入
方式三: 註解注入
@Autowired
@Qualifier("userDAO")
private UserDAO userDAO;
後置處理器
Spring提供了兩種後處理bean的擴充套件接⼝,分別為 BeanPostProcessor 和BeanFactoryPostProcessor,兩者在使⽤上是有所區別的。 ⼯⼚初始化(BeanFactory)—> Bean物件 在BeanFactory初始化之後可以使⽤BeanFactoryPostProcessor進⾏後置處理做⼀些事情 在Bean物件例項化(並不是Bean的整個⽣命週期完成)之後可以使⽤BeanPostProcessor進⾏後置處理做⼀些事情。
什麼是AOP
AOP:Aspect oriented Programming ⾯向切⾯程式設計/⾯向⽅⾯程式設計
解決的問題: 在不改變原有業務邏輯情況下,增強橫切邏輯程式碼,根本上解耦合,避免橫切邏輯程式碼重複。
為什麼叫面向切面程式設計:
「切」:指的是橫切邏輯,原有業務邏輯程式碼我們不能動,只能操作橫切邏輯程式碼,所以⾯向橫切邏輯 「⾯」:橫切邏輯程式碼往往要影響的是很多個⽅法,每⼀個⽅法都如同⼀個點,多個點構成⾯,有⼀個⾯的概念在⾥⾯
AOP術語:
Joinpoint(連線點):它指的是那些可以⽤於把增強程式碼加⼊到業務主線中的點,這些點指的就是⽅法。在⽅法執⾏的前後通過動態代理技術加⼊增強的程式碼。在Spring框架AOP思想的技術實現中,也只⽀持⽅法型別的連線點。
Pointcut(切⼊點):它指的是那些已經把增強程式碼加⼊到業務主線進來之後的連線點。由上圖中,我們看出表現層 transfer⽅法就只是連線點,因為判斷訪問許可權的功能並沒有對其增強。
Advice(通知/增強):它指的是切⾯類中⽤於提供增強功能的⽅法。並且不同的⽅法增強的時機是不⼀樣的。⽐如,開啟事務肯定要在業務⽅法執⾏之前執⾏;提交事務要在業務⽅法正常執⾏之後執⾏,⽽回滾事務要在業務⽅法執⾏產⽣異常之後執⾏等等。那麼這些就是通知的型別。其分類有:前置通知 後置通知 異常通知 最終通知 環繞通知。
Target(⽬標物件):它指的是代理的⽬標物件。即被代理物件。
Proxy(代理):它指的是⼀個類被AOP織⼊增強後,產⽣的代理類。即代理物件。
Weaving(織⼊):它指的是把增強應⽤到⽬標物件來建立新的代理物件的過程。spring採⽤動態代理織⼊,⽽AspectJ採⽤編譯期織⼊和類裝載期織⼊。
Aspect(切⾯):它指定是增強的程式碼所關注的⽅⾯,把這些相關的增強程式碼定義到⼀個類中,這個類就是切⾯類。例如,事務切⾯,它⾥⾯定義的⽅法就是和事務相關的,像開啟事務,提交事務,回滾事務等等,不會定義其他與事務⽆關的⽅法。我們前⾯的案例中 TrasnactionManager就是⼀個切⾯。
實現方式:
Spring 實現AOP思想使⽤的是動態代理技術
預設情況下,Spring會根據被代理物件是否實現接⼝來選擇使⽤JDK還是CGLIB。當被代理物件沒有實現任何接⼝時,Spring會選擇CGLIB。當被代理物件實現了接⼝,Spring會選擇JDK官⽅的代理技術,不過我們可以通過配置的⽅式,讓Spring強制使⽤CGLIB。
AOP核心配置及應用
<!--Spring基於XML的AOP配置前期準備: 在spring的配置⽂件中加⼊aop的約束 xmlns:aop="http://www.springframework.org/schema/aop" http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd Spring基於XML的AOP配置步驟: 第⼀步:把通知Bean交給Spring管理第⼆步:使⽤aop:config開始aop的配置第三步:使⽤aop:aspect配置切⾯ 第四步:使⽤對應的標籤配置通知的型別 ⼊⻔案例採⽤前置通知,標籤為aop:before --> <bean id="logUtil" class="com.lagou.utils.LogUtil"></bean> <!--開始aop的配置--> <aop:config> <!--配置切⾯--> <aop:aspect id="logAdvice" ref="logUtil"> <!--配置前置通知--> <aop:before method="printLog" pointcut="execution(public *com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou .pojo.Account))"></aop:before> </aop:aspect> </aop:config>
execution表示式使用示例
全限定⽅法名 訪問修飾符 返回值 包名.包名.包名.類名.⽅法名(引數列表) 全匹配⽅式: public void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c om.lagou.pojo.Account) 訪問修飾符可以省略 void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c om.lagou.pojo.Account) 返回值可以使⽤*,表示任意返回值 com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(c om.lagou.pojo.Account) 包名可以使⽤.表示任意包,但是有⼏級包,必須寫⼏個 ....TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account) 包名可以使⽤..表示當前包及其⼦包 ..TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account ) 類名和⽅法名,都可以使⽤.表示任意類,任意⽅法* ...(com.lagou.pojo.Account) 引數列表,可以使⽤具體型別 基本型別直接寫型別名稱 : 引⽤型別必須寫全限定類名:java.lang.String 引數列表可以使⽤*,表示任意引數型別,但是必須有引數 * *..*.*(*) 引數列表可以使⽤..,表示有⽆引數均可。有引數可以是任意型別 * *..*.*(..) 全通配⽅式: * *..*.*(..)
五種通知配置
1. aop:before標籤前置通知永遠都會在切⼊點⽅法(業務核⼼⽅法)執⾏之前執⾏。前置通知可以獲取切⼊點⽅法的引數,並對其進⾏增強。
<!-- 作⽤: ⽤於配置前置通知。 出現位置: 它只能出現在aop:aspect標籤內部 屬性: method:⽤於指定前置通知的⽅法名稱 pointcut:⽤於指定切⼊點表示式 pointcut-ref:⽤於指定切⼊點表示式的引⽤ --> <aop:before method="printLog" pointcut-ref="pointcut1"> </aop:before>
2.正常執⾏時通知
<!-- 作⽤: ⽤於配置正常執⾏時通知 出現位置: 它只能出現在aop:aspect標籤內部 屬性: method:⽤於指定後置通知的⽅法名稱 pointcut:⽤於指定切⼊點表示式 pointcut-ref:⽤於指定切⼊點表示式的引⽤ --> <aop:after-returning method="afterReturningPrintLog" pointcutref=" pt1"></aop:after-returning>
3. 異常通知執⾏時機是在切⼊點⽅法(業務核⼼⽅法)執⾏產⽣異常之後,異常通知執⾏。如果切⼊點⽅法執⾏沒有產⽣異常,則異常通知不會執⾏。
異常通知不僅可以獲取切⼊點⽅法執⾏的引數,也可以獲取切⼊點⽅法執⾏產⽣的異常資訊。
<!-- 作⽤: ⽤於配置異常通知。 出現位置: 它只能出現在aop:aspect標籤內部 屬性: method:⽤於指定異常通知的⽅法名稱 pointcut:⽤於指定切⼊點表示式 pointcut-ref:⽤於指定切⼊點表示式的引⽤ --> <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1" ></aop:after-throwing>
4.最終通知
最終通知的執⾏時機是在切⼊點⽅法(業務核⼼⽅法)執⾏完成之後,切⼊點⽅法返回之前執⾏。換句話說,⽆論切⼊點⽅法執⾏是否產⽣異常,它都會在返回之前執⾏。
最終通知執⾏時,可以獲取到通知⽅法的引數。同時它可以做⼀些清理操作。
<!-- 作⽤: ⽤於指定最終通知。 出現位置: 它只能出現在aop:aspect標籤內部 屬性: method:⽤於指定最終通知的⽅法名稱 pointcut:⽤於指定切⼊點表示式 pointcut-ref:⽤於指定切⼊點表示式的引⽤ --> <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
5.環繞通知
環繞通知,它是有別於前⾯四種通知型別外的特殊通知。前⾯四種通知(前置,後置,異常和最終)它們都是指定何時增強的通知型別。⽽環繞通知,它是Spring框架為我們提供的⼀種可以通過編碼的
⽅式,控制增強程式碼何時執⾏的通知型別。它⾥⾯藉助的ProceedingJoinPoint接⼝及其實現類,實現⼿動觸發切⼊點⽅法的調⽤
<!-- 作⽤: ⽤於配置環繞通知。 出現位置: 它只能出現在aop:aspect標籤的內部 屬性: method:⽤於指定環繞通知的⽅法名稱 pointcut:⽤於指定切⼊點表示式 pointcut-ref:⽤於指定切⼊點表示式的引⽤ --> <aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>