spring-AOP(面向切面編程)-xml方式配置


AOP是針對面向對象編程的一種補充,有時使用面向對象不能很好完成一些額外的功能業務時,可以采用AOP來進行補充。

AOP術語:

  1. 切面(Aspect)

    切面是用於編寫切面邏輯的一個類,這個類很類似於JDK動態代理中的回調處理器或者cglib中的方法攔截器,主要就是將需要增強目標對象的功能代碼編寫在這個類中,而這些功能增強的代碼就是切面邏輯。

  2. 切入點(Pointcut)

    切入點類似一個切入的坐標,目的就是要找到目標對象的哪些方法。

    通常切入點都是以一種表達式的形式來描述

  3. 通知/增強(Advice)

    通知就是切面中具體的增強邏輯,總共分為五種:

    1)前置通知(在目標方法調用之前執行)

    2)后置通知(在目標方法正確返回之后執行)

    3)環繞通知(在目標方法調用前后執行)

    4)異常通知(當目標方法拋出異常時執行,並且不會執行后置通知)

    5)最終通知(不管目標方法有無異常都會執行)

  4. 連接點(Joinpoint)

    目標對象的方法就稱之為連接點,一個切入點可以對應目標對象的的多個連接點。

  5. 代理(Proxy)

    在運行時動態創建的對象,稱之為代理對象,負責調用目標對象的方法,並執行增強功能

  6. 目標(Target)

    被代理的對象就是目標對象

  7. 織入(Weaver)

    將切面中的通知應用到目標對象上並且產生代理的過程稱之為織入。

    因此通常描述為“將通知織入到具體的目標”。


 

項目結構:

 

代碼示例:

applicationContext.xml配置:

<?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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 裝備UserServiceImpl -->
    <bean id="userService" class="edu.nf.ch11.service.impl.UserServiceImpl"/>

    <!-- 裝配自定義的切面-->
    <bean id="userServiceAspect" class="edu.nf.ch11.service.aspect.UserServiceAspect"/>

    <!-- 配置AOP, proxy-target-class用於設置是否強制使用cglib進行動態代理
         true表示強制使用,false則表示spring會根據目標對象有無實現接口來決定
         使用jdk動態代理還是cglib代理。默認值就是false-->
    <aop:config>
        <!-- 配置切入點,id屬性給切入點定義一個唯一標識,expression用於編寫切入點表達式-->
        <!-- execution表達式的使用:切入范圍是在方法級別-->
        <!-- 表達式語法[訪問修飾符] 返回值類型 [完整類名].方法名(參數)-->
        <!-- *號表示通配所有,方法的參數可以使用".."來代表任意個數和類型的參數-->
        <aop:pointcut id="myCut" expression="execution(* edu.nf.ch11.service.*.*(..))"/>

        <!-- within表達式的使用:切入范圍是在類級別-->
        <!--<aop:pointcut id="myCut" expression="within(edu.nf.ch11.service.impl.*)"/>-->

        <!-- 引用上面裝配的切面類的id -->
        <aop:aspect ref="userServiceAspect">
            <!-- 配置具體的通知方法,method指定通知的方法名
            pointcut-ref引用上面定的切入點的id,也可以通過pointcut來編寫相應的切入點表達式 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="myCut"/>
            <!-- 環繞通知-->
            <aop:around method="around" pointcut-ref="myCut"/>
            <!-- 后置通知,returning指定后置通知的參數名,用於獲取目標方法的返回值-->
            <aop:after-returning method="afterReturn" pointcut-ref="myCut" returning="returnVal"/>
            <!-- 異常通知,如果要獲取目標方法拋出的異常對象,需要指定throwing屬性,value對應異常通知的參數名-->
            <aop:after-throwing method="throwAdvice" pointcut-ref="myCut" throwing="e"/>
            <!-- 最終通知-->
            <aop:after method="after" pointcut-ref="myCut"/>
        </aop:aspect>

    </aop:config>

</beans>

 

切面(增強)類:

package edu.nf.ch11.service.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import javax.xml.crypto.dsig.SignatureMethod;

/**
 * @author wangl
 * @date 2018/10/23
 * 編寫一個UserService的切面
 * 在切面中編寫的方法都稱之為通知或者是增強
 * 在AOP當中,通知有五種類型
 * 1. 前置通知
 * 2. 后置通知
 * 3. 環繞通知
 * 4. 最終通知
 * 5. 異常通知
 * 在一個切面中,任何一種類型的通知都可以定義多個,當有多個通知存在的時候
 * 就會形成一個通知棧
 */
public class UserServiceAspect {

    /**
     * 前置通知
     * 在執行目標方法之前執行,攔截目標方法的參數
     * 可以通過JoinPoint獲得目標方法的參數信息
     */
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知...");
        //通過連接點獲得目標對象
        //joinPoint.getTarget();
        //獲取目標方法的參數信息
        Object[] params = joinPoint.getArgs();
        for (Object param : params) {
            System.out.println(param);
        }
    }

    /**
     * 后置通知
     * 在目標方法執行完並且return之后執行
     */
    public void afterReturn(String returnVal){
        System.out.println("后置通知..."+returnVal);
    }

    /**
     * 環繞通知
     * @param pjp 連接點處理器,由它負責調用目標對象的具體方法
     *
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("環繞通知前...");
        //調用目標對象的方法
        Object returnVal = pjp.proceed();
        //獲取目標方法的參數
        System.out.println(pjp.getArgs()[0]);
        //通過MethodSignature獲取連接點方法信息
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        //獲取正在調用的目標方法
        System.out.println(ms.getMethod().getName());
        //獲取目標方法的返回類型
        System.out.println(ms.getReturnType());
        System.out.println("環繞通知后...");
        return returnVal;
    }

    /**
     * 異常通知
     * @param e 獲取目標方法拋出的異常對象
     */
    public void throwAdvice(Throwable e){
        System.out.println("異常通知..."+e.getMessage());
    }

    /**
     * 最終通知
     */
    public void after(){
        System.out.println("最終通知...");
    }
}

 

 

 UserService接口:

package edu.nf.ch11.service;

/**
 * @author wangl
 * @date 2018/10/23
 */
public interface UserService {

    /**
     * 添加用戶
     */
    void addUser();

    /**
     * 刪除用戶
     * @param uid
     */
    void deleteUser(String uid);

    /**
     * 查詢用戶
     * @param uid
     * @return
     */
    String getUserNameById(String uid);
}

 

UserServiceImpl實現類:

package edu.nf.ch11.service.impl;

import edu.nf.ch11.service.UserService;

/**
 * @author wangl
 * @date 2018/10/23
 * 目標對象(被代理的對象)
 * Spring的AOP中需要代理的所有目標對象都應該歸納在ioc容器中管理
 */
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("保存用戶信息");
    }

    @Override
    public void deleteUser(String uid) {
        System.out.println("刪除用戶,ID:" + uid);
    }

    @Override
    public String getUserNameById(String uid) {
        System.out.println("根據ID查詢用戶名");
        //System.out.println(10/0);
        return "user1";
    }
}

 

程序測試類:

package edu.nf.ch11.test;

import edu.nf.ch11.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author wangl
 * @date 2018/10/23
 */
public class UserServiceAspectTest {

    @Test
    public void testAddUser(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //這里從容器中獲取的對象是一個代理對象
        UserService service = context.getBean("userService", UserService.class);
        //service.addUser();
        //System.out.println("----------------------");
        //service.deleteUser("10001");
        //System.out.println("----------------------");
        service.getUserNameById("10001");
    }
}

 

 運行結果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM