1. 程式人生 > 實用技巧 >Spring 學習筆記一 IOC AOP

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>