[Spring+SpringMVC+Mybatis]框架學習筆記(四):Spring實現AOP

上一章:[Spring+SpringMVC+Mybatis]框架學習筆記(三):Spring實現JDBC
下一章:[Spring+SpringMVC+Mybatis]框架學習筆記(五):SpringAOP_顧問html

第4章 Spring實現AOP

4.1 AOP的概念

AOP: Aspect Oriented Programming 面向切面編程java

AOP的實現能夠把業務邏輯和系統級的服務進行隔離,使業務邏輯跟各個系統級服務的耦合度下降,提升系統的重用性和開發效率。spring

業務邏輯:某個類的某個方法自己要實現的功能。編程

系統級的服務:系統的日誌、事務、權限驗證安全

舉例:六七十年代,村支書要通知一個事情,原來要一家一家逐個告訴他們;有了廣播(AOP)之後,只須要用廣播,你們就知道了。app

4.2 AOP的實現

AOP底層實現的原理:動態代理框架

動態代理有兩種方式:ide

  • jdk動態代理(只能代理實現了接口的類)
  • cglib動態代理(均可以) ----> 字節碼生成器

4.3 AOP的一些專業術語

  • 切面(Aspect):交叉在各個業務邏輯中的系統服務,例如:安全驗證、事務處理、日誌記錄等
  • 織入(weaving):將切面代碼插入到目標對象的某個方法的過程,至關於jdk動態代理中的InvacationHandler接口方法的內容。
  • 鏈接點(JointPoint):理論上能夠被切面織入的全部方法,例如addStudent(),delStudent()...一般全部方法均可以被稱爲鏈接點。
  • 切入點(PointCut): 實際上被切面織入的方法。
  • 目標對象(target):就是切入點所屬的類,如:StudentService類
  • 通知(Advice):就是切面的實現,切面織入的目標對象,方法,時間點(方式執行前、後、先後、異常)以及內容。
  • 顧問(Advisor):就是通知的封裝和延伸。能夠將通知以更復雜的方式織入到某些方法中。

把前5個術語串聯起來:學習

將切面織入到目標對象的鏈接點方法中,使鏈接點成爲切入點。測試

4.4 Spring實現AOP的步驟

1)搭建SpringAOP開發環境須要引入的jar包;
aopalliance-1.0.jar
spring-aop-4.2.1.RELEASE.jar

2)實現通知

  • 前置通知 implements org.springframework.aop.MethodBeforeAdvice
  • 後置通知 implements org.springframework.aop.AfterReturningAdvice
  • 環繞通知 implements org.aopalliance.intercept.MethodInterceptor
  • 異常通知 implements org.springframework.aop.ThrowsAdvice

3)註冊目標類

4)註冊通知

5)註冊代理生產器,並注入目標類和通知

ps:通知的注意點:

  • 代理生產器配置時,目標類接口能夠省略;
  • 後置通知是在目標方法執行以後才執行,它能夠獲得目標方法的返回值,可是不能改變它;
  • 環繞通知有在目標方法執行以前的代碼,也有在目標方法執行以後的代碼;能夠獲得目標方法的返回值,且能夠改變它。

4.5 Spring實現AOP實例

4.5.1 目標類接口

package com.steven.spring.sysmgr.service;

import java.util.List;

import com.steven.spring.sysmgr.entity.Student;

public interface IStudentService {
    
    public boolean addStudent(Student student);
    
    public boolean delStudent(Integer studentId);
    
    public boolean updateStudent(Student student);
    
    public List<Student> getStudentList();
}

4.5.2 目標類

package com.steven.spring.sysmgr.service.impl;

import java.util.ArrayList;
import java.util.List;

import com.steven.spring.sysmgr.entity.Student;
import com.steven.spring.sysmgr.service.IStudentService;

public class StudentService implements IStudentService{

    @Override
    public boolean addStudent(Student student) {
        System.out.println("執行增長功能");
        return true;
    }

    @Override
    public boolean delStudent(Integer studentId) {
        //模擬異常,在異常通知中使用
        int i = 1 / 0;

        System.out.println("執行刪除功能");
        return true;
    }

    @Override
    public boolean updateStudent(Student student) {
        System.out.println("執行修改功能");
        return true;
    }

    @Override
    public List<Student> getStudentList() {
        System.out.println("執行增長功能");
        return new ArrayList<Student>();
    }

}

4.5.3 實現通知

a) 前置通知

package com.steven.spring.sysmgr.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 前置通知
 * @author chenyang
 *
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("這是前置通知,應該在方法執行以前打印出來");
    }
    
}

b) 後置通知

package com.steven.spring.sysmgr.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

/**
 * 後置通知
 * @author chenyang
 *
 */
public class MyAfterAdvice implements AfterReturningAdvice {
    //該方法中的代碼就是要織入到目標類方法的切面代碼
    @Override
    public void afterReturning(Object returnValue, Method paraMethod, 
            Object[] paramArrayOfObject, Object target) throws Throwable {
        System.out.println("方法執行的結果:" + returnValue);
        System.out.println("這是後置通知,應該在方法執行以後打印出來");
        
    }

}

c) 環繞通知

package com.steven.spring.sysmgr.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 環繞通知
 * @author chenyang
 *
 */
public class MyAroundAdvice implements MethodInterceptor {
    // 環繞通知能夠改變方法的返回值
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("這是環繞通知的前面執行部分");
        Object result = invocation.proceed();
        System.out.println("方法的執行結果是:" + result);
        System.out.println("這是環繞通知的後面執行部分");
        result = false;
        return result;
    }
    
}

d) 異常通知

package com.steven.spring.sysmgr.advice;

import org.springframework.aop.ThrowsAdvice;

/**
 * 異常通知
 * @author chenyang
 *
 */
public class MyThrowingAdvice implements ThrowsAdvice {
    public void afterThrowing(Exception e){
        System.out.println("發生的異常:" + e);
    }
    
}

4.5.4 配置文件:註冊目標類、通知、代理生成器

<?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">

    <!-- 註冊目標類,並描述依賴關係 -->
    <bean id="studentService" class="com.steven.spring.sysmgr.service.impl.StudentService"></bean>
    
    <!-- 註冊前置通知 -->
    <bean id="beforeAdvice" class="com.steven.spring.sysmgr.advice.MyBeforeAdvice"></bean>
    
    <!-- 註冊後置通知 -->
    <bean id="afterAdvice" class="com.steven.spring.sysmgr.advice.MyAfterAdvice"></bean>
    
    <!-- 註冊環繞通知 -->
    <bean id="aroundAdvice" class="com.steven.spring.sysmgr.advice.MyAroundAdvice"></bean>
    
    <!-- 註冊異常通知 -->
    <bean id="throwingAdvice" class="com.steven.spring.sysmgr.advice.MyThrowingAdvice"></bean>
    
    <!-- 註冊前置通知的代理生成器,注入目標類接口、目標類、通知 -->
    <bean id="myBeforeProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interfaces" value="com.steven.spring.sysmgr.service.IStudentService"/>
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>
    
    <!-- 註冊後置通知的代理生成器,注入目標類、通知 -->
    <bean id="myAfterProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <!-- 這裏省略了目標類接口的實現類,由於系統能本身找到 -->
        <property name="interceptorNames" value="afterAdvice"/>
    </bean>
    
    <!-- 註冊環繞通知的代理生成器,注入目標類、通知 -->
    <bean id="myAroundProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interceptorNames" value="aroundAdvice"/>
    </bean>
    
    <!-- 註冊異常通知的代理生成器,注入目標類、通知 -->
    <bean id="myThrowingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interceptorNames" value="throwingAdvice"/>
    </bean>
    
</beans>

ps:

教你何時用value,何時用ref?

ref是引用(指針),value是布爾、string等得值。

用ref仍是value,須要看源碼中這個name是什麼類型。 全選name的引號中的值,alt + / , 若彈出的類的參數是對象,就用ref;若不是,就用value。

4.5.5 測試

package com.steven.spring.sysmgr.test;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmgr.entity.Student;
import com.steven.spring.sysmgr.service.IStudentService;

/**
 * 測試各類通知
 * @author chenyang
 *
 */
public class AdviceTest {
    private ApplicationContext ac = null;
    
    @Before
    public void init(){
        ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    
    //測試前置通知,預期目標:將前置通知打印的內容出如今目標方法內容的前面
    @Test
    public void testBeforeAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myBeforeProxy");
        studentService.addStudent(new Student());
    }
    
    //測試後置通知
    @Test
    public void testAfterAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myAfterProxy");
        studentService.addStudent(new Student());
    }
    
    //測試環繞通知
    @Test
    public void testAroundAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myAroundProxy");
        studentService.addStudent(new Student());
    }
        
    //測試異常通知
        @Test
        public void testThrowingAdvice(){
            IStudentService studentService = (IStudentService) ac.getBean("myThrowingProxy");
            studentService.delStudent(1);
        }   
}

上一章:[Spring+SpringMVC+Mybatis]框架學習筆記(三):Spring實現JDBC
下一章:[Spring+SpringMVC+Mybatis]框架學習筆記(五):SpringAOP_顧問

相關文章
相關標籤/搜索