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

第6章 Spring_AspectJ實現AOP

6.1 什麼是AspectJ

對於AOP的這種編程思想,有不少框架或者組件進行了實現,spring實現AOP就是其中的一種。java

AspectJ也實現了AOP,並且實現方式更爲簡單,使用起來更爲方便,因此spring將AspectJ對於AOP的實現引入了本身的框架。spring

AspectJ是一個面向切面的框架,它定義了AOP的一些語法,有一個專門的字節碼生成器來生成遵照java規範的class文件。express

AspectJ的通知類型:編程

  • 前置通知
  • 後置通知
  • 環繞通知
  • 異常通知
  • 最終通知:不管程序是否正常執行,最終通知的代碼會獲得執行,相似於try...catch...finally

6.2 切入點表達式

切入點表達式做用:標識切面織入到哪些類的哪些方法當中。app

  • 語法格式以下:
public boolean com.steven.spring.service.impl.StudentService.addStudent(Student student) execution(
    modifiers-pattern?          //訪問權限匹配   如public、protected   問號表明能夠省略
    ret-type-pattern            //返回值類型匹配  
    declaring-type-pattern?     //全限定性類名
    name-pattern(param-pattern) //方法名(參數名)
    throws-pattern?             //拋出異常類型
)

注意:中間以空格隔開,有問號的屬性能夠省略框架

  • 特殊符號
a: * 表明0到多個任意字符
b: .. 放在方法參數中 ,表明任意個參數 ,放在包名後面表示當前包及其全部子包路徑
c: + 放在類名後,表示當前類及其子類,放在接口後,表示當前接口及其實現類
  • 例如:
a:execution(public * *(..))   表示任意的public方法

b:execution(* set*(..))       表示任意包含以set字符開頭的方法

c:execution(* com.steven.spring.service.impl.*.*(..)) 表示com.steven.spring.service.impl的任意類的任意方法

d:execution(* com.steven.spring.service..*.*(..)) 表示com.steven.spring.service包下面的全部方法以及全部子包下面的全部方法

e:execution(* com.steven.spring.service.IStudentService+.*(..))     

f:execution(* add(String,int))  帶包任意返回類型的add方法 有兩個參數,類型分別爲String,int

g:execution(* add(String,*))

6.3 AspectJ+Spring的環境搭建

  • 引入jar包jsp

    經測試,能夠不用另外加上spring-aspects-4.2.1.RELEASE.jar。但aspectjweaver必定不能少。ide

  • 引入aop的約束測試

6.4 Aspect的配置方式

6.4.1 基於xml的方式

實現步驟:this

  • 編寫切面類
  • 在切面類裏面定義各類通知的實現方法
  • 在配置文件裏面對aop進行配置 ,詳見applicationContext.xml

實例:

1)實體類

package com.steven.spring.sysmanage.entity;

/**
 * 學生實體類
 * @author Administrator
 *
 */
public class Student implements java.io.Serializable{

    private static final long serialVersionUID = -3875695558042397898L;
    
    private Integer id;
    private String name;
    private Integer age;
     
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    
}

2)服務類接口

package com.steven.spring.sysmanage.service;

import java.util.List;

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

/**
 * 用於對外提供學生服務類的增刪改查接口
 * @author Administrator
 *
 */
public interface IStudentService {
    
    public boolean addStudent(Student student);
    
    public boolean delStudent(Integer studentId);
    
    public boolean updateStudent(Student student);
    
    public List<Student> getStudentList();
        
}

3)服務類

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

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

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

/**
 * 用於對外提供學生服務類的增刪改查實現
 * @author Administrator
 *
 */
public class StudentService implements IStudentService{

     
    @Override
    public boolean addStudent(Student student) {
        //System.out.println("進行權限驗證");
        
        System.out.println("執行增長功能");
        
        //System.out.println("進行日誌記錄");
        return true;
    }

    @Override
    public boolean delStudent(Integer studentId) {
        //System.out.println("進行權限驗證");
        int i= 1/0;
        System.out.println("執行刪除功能");
        //System.out.println("進行日誌記錄");

        return true;

    }

    @Override
    public boolean updateStudent(Student student) {
        //System.out.println("進行權限驗證");
        System.out.println("執行修改功能");
        //System.out.println("進行日誌記錄");

        return true;

    }

    @Override
    public List<Student> getStudentList() {
        //System.out.println("進行權限驗證");
        System.out.println("執行查詢功能");
        //System.out.println("進行日誌記錄");
        return new ArrayList<Student>();

    }

}

4)定義通知

package com.steven.spring.sysmanage.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 基於xml配置方式實現aspectJ 定義通知
 * @author chenyang
 *
 */
public class MyAspect {
    //定義前置通知
    public void beforeAdvice(){
        System.out.println("這是一個前置通知,應該在目標方法以前打印出來");
    }
    
    //定義後置通知
    public void afterAdvice(){
        System.out.println("這是一個後置通知,應該在目標方法以後打印出來");
    }
    
    //定義後置通知,包含返回值
    public void afterAdvice(Object result){
        System.out.println("這是一個後置通知,應該在目標方法以後打印出來;目標方法的返回值爲:" + result.toString());
    }
    
    //定義環繞通知
    public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("這是一個環繞通知,應該在目標方法以前打印出來");
        Object result = pjp.proceed();
        System.out.println("這是一個環繞通知,應該在目標方法以後打印出來");
    }
    
    //定義異常通知
    public void afterThrowingAdvice(){
        System.out.println("這是一個異常通知,應該在目標方法出現異常以後打印出來");
    }
    
    //定義異常通知,包含返回值
    public void afterThrowingAdvice(Exception ex){
        System.out.println("這是一個異常通知,應該在目標方法出現異常以後打印出來,異常爲:" + ex);
    }
    
    //定義最終通知
    public void lastAdvice(){
        System.out.println("這是一個最終通知,不管如何都會執行");
    }
    
}

5)配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
   
    <!-- 註冊目標類 -->
    <bean id="studentService" class="com.steven.spring.sysmanage.service.impl.StudentService"></bean>
    
    <!-- 註冊切面(通知) -->
    <bean id="myAspect" class="com.steven.spring.sysmanage.aspect.MyAspect"></bean>
    
    <!-- AspectJ的aop配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* add*(..))" id="beforePointCut"/>
        <aop:pointcut expression="execution(* update*(..))" id="afterPointCut"/>
        <aop:pointcut expression="execution(* get*(..))" id="aroundPointCut"/>
        <aop:pointcut expression="execution(* del*(..))" id="afterThrowingPointCut"/>
        <aop:pointcut expression="execution(* del*(..))" id="lastPointCut"/>
        
        <aop:aspect ref="myAspect">
            <aop:before method="beforeAdvice" pointcut-ref="beforePointCut"/>
            
            <aop:after-returning method="afterAdvice" pointcut-ref="afterPointCut" />
            <!-- 方法參數必須是全路徑類名  返回值參數名必須與方法中參數名一致 -->
            <aop:after-returning method="afterAdvice(java.lang.Object)"
                pointcut-ref="afterPointCut" returning="result" />
            
            <aop:around method="aroundAdvice" pointcut-ref="aroundPointCut"/>
            
            <aop:after-throwing method="afterThrowingAdvice" pointcut-ref="afterThrowingPointCut"/>
            <aop:after-throwing method="afterThrowingAdvice(java.lang.Exception)"
                pointcut-ref="afterThrowingPointCut" throwing="ex" />
            
            <aop:after method="lastAdvice" pointcut-ref="lastPointCut"/>
        </aop:aspect>
    </aop:config>
    
</beans>

6)測試

package com.steven.spring.sysmanage.test;

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

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

public class AspectXmlTest {
    private ApplicationContext ac = null;
    private IStudentService studentService = null;
    
    @Before
    public void init(){
        ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        studentService = (IStudentService) ac.getBean("studentService");
    }
    
    //測試前置通知
    @Test
    public void testBeforeAdvice(){
        studentService.addStudent(new Student());
    }
    //測試後置通知
    @Test
    public void testAfterAdvice(){
        studentService.updateStudent(new Student());
    }
    //測試環繞通知
    @Test
    public void testAroundAdvice(){
        studentService.getStudentList();
    }
    //測試異常通知
    @Test
    public void testAfterThrowingAdvice(){
        studentService.delStudent(1);
    }
    //測試最終通知
    @Test
    public void testLastAdvice(){
        studentService.delStudent(1);
    }
}

6.4.2 基於註解的方式

註解方式有必定的侵入性(在代碼中加入本來不屬於代碼的部分)。

實現步驟:

  • 編寫切面類,加上@Aspect註解
  • 實現各類通知,在實現通知的方法上加上通知的註解以及切入點表達式的註解
  • 在配置文件註冊切面,且加上aspectJ的自動代理

實例:

在6.4.1實例的基礎上更改通知類和配置文件,最後在測試類中更改配置文件名稱便可。

1)通知類

package com.steven.spring.sysmanage.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 基於xml配置方式來實現AspectJ 定義通知
 * @author Administrator
 *
 */
@Aspect
public class MyAspectAnnotation {
    //定義前置通知
    @Before(value="execution(* add*(..))")
    public void beforeAdvice(){
        System.out.println("這是一個前置通知,應該在目標方法以前打印出來");
    }
    
    //定義後置通知
    @AfterReturning(value="execution(* update*(..))")
    public void afterAdvice(){
        System.out.println("這是一個後置通知,應該在目標方法以後打印出來");
    }
    
    //定義後置通知包含返回值
    @AfterReturning(value="execution(* update*(..))",returning="result")
    public void afterAdvice(Object result){
        System.out.println("這是一個後置通知,應該在目標方法以後打印出來");
        System.out.println("後置通知獲得目標方法的返回值="+result.toString());
    }
    
    //定義環繞通知
    @Around(value="execution(* get*(..))")
    public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("這是一個環繞通知,應該在目標方法以前打印出來");
        Object result = pjp.proceed();
        System.out.println("這是一個環繞通知,應該在目標方法以後打印出來");
    }
    
    //定義異常通知
    @AfterThrowing(value="execution(* del*(..))")
    public void afterThrowingAdvice(){
        System.out.println("這是一個異常通知,應該在目標方法出現異常時候打印出來");
    }
    
    //定義異常通知
    @AfterThrowing(value="execution(* del*(..))",throwing="ex")
    public void afterThrowingAdvice(Exception ex){
        System.out.println("這是一個異常通知,應該在目標方法出現異常時候打印出來 ex="+ex);
    }
    
    
    //定義最終通知 finally
    @After(value="execution(* del*(..))")
    public void lastAdvice(){
        System.out.println("這是一個最終通知,不管如何會執行 ");
    }
    
}

2)配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
   
   <!--註冊studentService  -->
   <bean id = "studentService" class = "com.steven.spring.sysmanage.service.impl.StudentService">
   </bean>
  
    <!--註冊切面  -->
    <bean id= "myAspect" class = "stevenm.tz.spring.sysmanage.aspect.MyAspectAnnotation"></bean>
     
    <aop:aspectj-autoproxy/>
</beans>
相關文章
相關標籤/搜索