1. 程式人生 > 其它 >SSM自學筆記(四)

SSM自學筆記(四)

8、面向切面程式設計AOP

1.Spring 的 AOP 簡介

1.1 什麼是 AOP

AOPAspect Oriented Programming 的縮寫,意思為面向切面程式設計,是通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。

AOP 是 OOP 的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

1.2 AOP 的作用及其優勢
  • 作用:在程式執行期間,在不修改原始碼的情況下對方法進行功能增強

  • 優勢:減少重複程式碼,提高開發效率,並且便於維護

1.3 AOP 的底層實現

實際上,AOP 的底層是通過 Spring 提供的的動態代理技術實現的。在執行期間,Spring通過動態代理技術動態的生成代理物件,代理物件方法執行時進行增強功能的介入,在去呼叫目標物件的方法,從而完成功能的增強。

1.4 AOP 的動態代理技術

常用的動態代理技術

  • JDK 代理 : 基於介面的動態代理技術

  • cglib 代理:基於父類的動態代理技術

1.5 JDK 的動態代理
  1. 目標類介面
public interface TargetInterface {
    public void method();
}
  1. 目標類
public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}
  1. 動態代理程式碼
Target target = new Target(); //建立目標物件
//建立代理物件
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()
.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) 
                throws Throwable {
            System.out.println("前置增強程式碼...");
            Object invoke = method.invoke(target, args);
            System.out.println("後置增強程式碼...");
            return invoke;
        }
    }
);
  1. 呼叫代理物件的方法測試
// 測試,當呼叫介面的任何方法時,代理物件的程式碼都無序修改
proxy.method();
1.6 cglib 的動態代理
  1. 目標類
public class Target {
    public void method() {
        System.out.println("Target running....");
    }
}
  1. 動態代理程式碼
Target target = new Target(); //建立目標物件
Enhancer enhancer = new Enhancer();   //建立增強器
enhancer.setSuperclass(Target.class); //設定父類
enhancer.setCallback(new MethodInterceptor() { //設定回撥
    @Override
    public Object intercept(Object o, Method method, Object[] objects, 
    MethodProxy methodProxy) throws Throwable {
        System.out.println("前置程式碼增強....");
        Object invoke = method.invoke(target, objects);
        System.out.println("後置程式碼增強....");
        return invoke;
    }
});
Target proxy = (Target) enhancer.create(); //建立代理物件
  1. 呼叫代理物件的方法測試
//測試,當呼叫介面的任何方法時,代理物件的程式碼都無序修改
proxy.method();1
1.7 AOP 相關概念

Spring 的 AOP 實現底層就是對上面的動態代理的程式碼進行了封裝,封裝後我們只需要對需要關注的部分進行程式碼編寫,並通過配置的方式完成指定目標的方法增強。

在正式講解 AOP 的操作之前,我們必須理解 AOP 的相關術語,常用的術語如下:

  • Target(目標物件):代理的目標物件
  • Proxy (代理):一個類被 AOP 織入增強後,就產生一個結果代理類
  • Joinpoint(連線點):所謂連線點是指那些被攔截到的點。在spring中,這些點指的是方法,因為spring只支援方法型別的連線點
  • Pointcut(切入點):所謂切入點是指我們要對哪些 Joinpoint 進行攔截的定義
  • Advice(通知/ 增強):所謂通知是指攔截到 Joinpoint 之後所要做的事情就是通知
  • Aspect(切面):是切入點和通知(引介)的結合
  • Weaving(織入):是指把增強應用到目標物件來建立新的代理物件的過程。spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入
1.8 AOP 開發明確的事項
1. 需要編寫的內容
  • 編寫核心業務程式碼(目標類的目標方法)

  • 編寫切面類,切面類中有通知(增強功能方法)

  • 在配置檔案中,配置織入關係,即將哪些通知與哪些連線點進行結合

2.AOP 技術實現的內容

Spring 框架監控切入點方法的執行。一旦監控到切入點方法被執行,使用代理機制,動態建立目標物件的代理物件,根據通知類別,在代理物件的對應位置,將通知對應的功能織入,完成完整的程式碼邏輯執行。

3.AOP 底層使用哪種代理方式

在 spring 中,框架會根據目標類是否實現了介面來決定採用哪種動態代理的方式.

1.9 小結
  • aop:面向切面程式設計

  • aop底層實現:基於JDK的動態代理 和 基於Cglib的動態代理

  • aop的重點概念:

    ​ Pointcut(切入點):被增強的方法

    ​ Advice(通知/ 增強):封裝增強業務邏輯的方法

    ​ Aspect(切面):切點+通知

    ​ Weaving(織入):將切點與通知結合的過程

  • 開發明確事項:

    ​ 誰是切點(切點表示式配置)

    ​ 誰是通知(切面類中的增強方法)

    ​ 將切點和通知進行織入配置

2.基於 XML 的 AOP 開發

2.1 快速入門
  1. 匯入 AOP 相關座標

  2. 建立目標介面和目標類(內部有切點)

  3. 建立切面類(內部有增強方法)

  4. 將目標類和切面類的物件建立權交給 spring

  5. 在 applicationContext.xml 中配置織入關係

  6. 測試程式碼


  1. 匯入AOP相關座標
<!--匯入spring的context座標,context依賴aop-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的織入 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
  1. 建立目標介面和目標類(內部有切點)
public interface TargetInterface {
    public void method();
}
public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}
  1. 建立切面類(內部有增強方法)
public class MyAspect {
    //前置增強方法
    public void before(){
        System.out.println("前置程式碼增強.....");
    }
}
  1. 將目標類和切面類的物件建立權交給 spring
<!--配置目標類-->
<bean id="target" class="com.itheima.aop.Target"></bean>
<!--配置切面類-->
<bean id="myAspect" class="com.itheima.aop.MyAspect"></bean>
  1. 在 applicationContext.xml 中配置織入關係

    匯入aop名稱空間

    配置切點表示式和前置增強的織入關係

<aop:config>
    <!--引用myAspect的Bean為切面物件-->
    <aop:aspect ref="myAspect">
        <!--配置Target的method方法執行時要進行myAspect的before方法前置增強-->
        <aop:before method="before" pointcut="execution(public void com.itheima.aop.Target.method())"></aop:before>
    </aop:aspect>
</aop:config>
  1. 測試程式碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.method();
    }
}
  1. 測試結果
2.2 XML 配置 AOP 詳解
1.切點表示式的寫法

表示式語法:

execution([修飾符] 返回值型別 包名.類名.方法名(引數))

  • 訪問修飾符可以省略

  • 返回值型別、包名、類名、方法名可以使用星號* 代表任意

  • 包名與類名之間一個點 . 代表當前包下的類,兩個點 .. 表示當前包及其子包下的類

  • 引數列表可以使用兩個點 .. 表示任意個數,任意型別的引數列表

例如:

execution(public void com.itheima.aop.Target.method())	
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
2.通知的型別

通知的配置語法:

<aop:通知型別 method=“切面類中方法名” pointcut=“切點表示式"></aop:通知型別>

3.切點表示式的抽取

當多個增強的切點表示式相同時,可以將切點表示式進行抽取,在增強中使用 pointcut-ref 屬性代替 pointcut 屬性來引用抽取後的切點表示式

<aop:config>
    <!--引用myAspect的Bean為切面物件-->
    <aop:aspect ref="myAspect">
        <aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
    </aop:aspect>
</aop:config>
2.3 小結
  • aop織入的配置
<aop:config>
    <aop:aspect ref=“切面類”>
        <aop:before method=“通知方法名稱” pointcut=“切點表示式"></aop:before>
    </aop:aspect>
</aop:config>
  • 通知的型別:前置通知、後置通知、環繞通知、異常丟擲通知、最終通知

  • 切點表示式的寫法:

execution([修飾符] 返回值型別 包名.類名.方法名(引數))

3.基於註解的 AOP 開發

3.1 快速入門

基於註解的aop開發步驟:

  1. 建立目標介面和目標類(內部有切點)

  2. 建立切面類(內部有增強方法)

  3. 將目標類和切面類的物件建立權交給 spring

  4. 在切面類中使用註解配置織入關係

  5. 在配置檔案中開啟元件掃描和 AOP 的自動代理

  6. 測試


  1. 建立目標介面和目標類(內部有切點)
public interface TargetInterface {
    public void method();
}
public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}
  1. 建立切面類(內部有增強方法)
public class MyAspect {
    //前置增強方法
    public void before(){
        System.out.println("前置程式碼增強.....");
    }
}
  1. 將目標類和切面類的物件建立權交給 spring
@Component("target")
public class Target implements TargetInterface {
    @Override
    public void method() {
        System.out.println("Target running....");
    }
}
@Component("myAspect")
public class MyAspect {
    public void before(){
        System.out.println("前置程式碼增強.....");
    }
}
  1. 在切面類中使用註解配置織入關係
@Component(“myAspect”)
@Aspect //標註當前MyAspect是一個切面類
public class MyAspect {
    //配置前置通知
    @Before("execution(* com.itheima.aop.*.*(..))")
    public void before(){
        System.out.println("前置程式碼增強.....");
    }
}
  1. 在配置檔案中開啟元件掃描和 AOP 的自動代理
<!--元件掃描-->
<context:component-scan base-package="com.itheima.aop"/>
<!--aop的自動代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 測試程式碼
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.method();
    }
}
  1. 測試結果
3.2 註解配置 AOP 詳解
1.註解通知的型別

通知的配置語法:@通知註解(“切點表示式")

2. 切點表示式的抽取

同 xml 配置 aop 一樣,我們可以將切點表示式抽取。抽取方式是在切面內定義方法,在該方法上使用@Pointcut註解定義切點表示式,然後在在增強註解中進行引用。具體如下:

@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("MyAspect.myPoint()")
    public void before(){
        System.out.println("前置程式碼增強.....");
    }
    @Pointcut("execution(* com.itheima.aop.*.*(..))")
    public void myPoint(){}
}
3.3 小結
  • 註解aop開發步驟

    1. 使用@Aspect標註切面類
    2. 使用@通知註解標註通知方法
    3. 在配置檔案中配置aop自動代理<aop:aspectj-autoproxy/>
  • 通知註解型別

9、宣告式事務控制

1.程式設計式事務控制相關物件

1.1 PlatformTransactionManager

PlatformTransactionManager 介面是 spring 的事務管理器,它裡面提供了我們常用的操作事務的方法。

注意:

PlatformTransactionManager 是介面型別,不同的 Dao 層技術則有不同的實現類,

例如:

Dao 層技術是jdbc 或 mybatis 時:org.springframework.jdbc.datasource.DataSourceTransactionManager

Dao 層技術是hibernate時:org.springframework.orm.hibernate5.HibernateTransactionManager

1.2 TransactionDefinition

TransactionDefinition 是事務的定義資訊物件,裡面有如下方法:

1. 事務隔離級別

設定隔離級別,可以解決事務併發產生的問題,如髒讀、不可重複讀和虛讀。

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ
  • ISOLATION_SERIALIZABLE
2.事務傳播行為
  • REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。一般的選擇(預設值)
  • SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行(沒有事務)
  • MANDATORY:使用當前的事務,如果當前沒有事務,就丟擲異常
  • REQUERS_NEW:新建事務,如果當前在事務中,把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
  • NEVER:以非事務方式執行,如果當前存在事務,丟擲異常
  • NESTED:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行 REQUIRED 類似的操作
  • 超時時間:預設值是-1,沒有超時限制。如果有,以秒為單位進行設定
  • 是否只讀:建議查詢時設定為只讀
1.3 TransactionStatus

TransactionStatus 介面提供的是事務具體的執行狀態,方法介紹如下

1.4 小結

程式設計式事務控制三大物件

  • PlatformTransactionManager

  • TransactionDefinition

  • TransactionStatus

2.基於 XML 的宣告式事務控制

2.1 什麼是宣告式事務控制

Spring 的宣告式事務顧名思義就是採用宣告的方式來處理事務。這裡所說的宣告,就是指在配置檔案中宣告,用在 Spring 配置檔案中宣告式的處理事務來代替程式碼式的處理事務。

宣告式事務處理的作用

  • 事務管理不侵入開發的元件。具體來說,業務邏輯物件就不會意識到正在事務管理之中,事實上也應該如此,因為事務管理是屬於系統層面的服務,而不是業務邏輯的一部分,如果想要改變事務管理策劃的話,也只需要在定義檔案中重新配置即可

  • 在不需要事務管理的時候,只要在設定檔案上修改一下,即可移去事務管理服務,無需改變程式碼重新編譯,這樣維護起來極其方便

注意:Spring 宣告式事務控制底層就是AOP

2.2 宣告式事務控制的實現

宣告式事務控制明確事項:

  • 誰是切點?

  • 誰是通知?

  • 配置切面?

  1. 引入tx名稱空間
  1. 配置事務增強
<!--平臺事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--事務增強配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
  1. 配置事務 AOP 織入
<!--事務的aop增強-->
<aop:config>
    <aop:pointcut id="myPointcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
  1. 測試事務控制轉賬業務程式碼
@Override
public void transfer(String outMan, String inMan, double money) {
    accountDao.out(outMan,money);
    int i = 1/0;
    accountDao.in(inMan,money);
}
2.3 切點方法的事務引數的配置
<!--事務增強配置-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

其中,<tx:method> 代表切點方法的事務引數的配置,例如:

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切點方法名稱

  • isolation:事務的隔離級別

  • propogation:事務的傳播行為

  • timeout:超時時間

  • read-only:是否只讀

2.4 小結

宣告式事務控制的配置要點

  • 平臺事務管理器配置

  • 事務通知的配置

  • 事務aop織入的配置

3.基於註解的宣告式事務控制

3.1 使用註解配置宣告式事務控制
  1. 編寫 AccoutDao
@Repository("accountDao")public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}
  1. 編寫 AccoutService
@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
}
  1. 編寫 applicationContext.xml 配置檔案
<!—之前省略datsSource、jdbcTemplate、平臺事務管理器的配置-->
<!--元件掃描-->
<context:component-scan base-package="com.itheima"/>
<!--事務的註解驅動-->
<tx:annotation-driven/>
3.2 註解配置宣告式事務控制解析
  1. 使用 @Transactional 在需要進行事務控制的類或是方法上修飾,註解可用的屬性同 xml 配置方式,例如隔離級別、傳播行為等。

  2. 註解使用在類上,那麼該類下的所有方法都使用同一套註解引數配置。

  3. 使用在方法上,不同的方法可以採用不同的事務引數配置。

  4. Xml配置檔案中要開啟事務的註解驅動<tx****:annotation-driven />

3.3 小結

註解宣告式事務控制的配置要點

  • 平臺事務管理器配置(xml方式)

  • 事務通知的配置(@Transactional註解配置)

  • 事務註解驅動的配置 <tx:annotation-driven/>