SpringBoot系列之AOP實現的兩種方式

AOP經常使用的實現方式有兩種,一種是採用聲明的方式來實現(基於XML),一種是採用註解的方式來實現(基於AspectJ)java

首先複習下AOP中一些比較重要的概念:正則表達式

Joinpoint(鏈接點):程序執行時的某個特定的點,在Spring中就是某一個方法的執行 。
Pointcut(切點):說的通俗點,spring中AOP的切點就是指一些方法的集合,而這些方法是須要被加強、被代理的。通常都是按照必定的約定規則來表示的,如正則表達式等。切點是由一類鏈接點組成。 
Advice(通知):仍是說的通俗點,就是在指定切點上要幹些什麼。 
Advisor(通知器):其實就是切點和通知的結合 。spring

1、基於XML配置的Spring AOPexpress

採用聲明的方式實現(在XML文件中配置),大體步驟爲:配置文件中配置pointcut, 在java中用編寫實際的aspect 類, 針對對切入點進行相關的業務處理。springboot

業務接口:ide

package com.springboottime.time.service;

public interface AdviceService {
    /*查找用戶*/
    public String findUser();

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

業務實現:測試

package com.springboottime.time.service.serviceImpl;

import com.springboottime.time.service.AdviceService;
import lombok.Data;

@Data
public class AdviceServiceImpl implements AdviceService {

    private String name;

    @Override
    public String findUser() {
        System.out.println("***************執行業務方法findUser,查找的用戶名字爲:"+name+"****************");
        return name;
    }

    @Override
    public void addUser() {
        System.out.println("***************執行業務方法addUser****************");
        throw new RuntimeException();
    }
}

切面類:spa

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AopAspect {

    /**
     * 前置通知:目標方法調用以前執行的代碼
     * @param jp
     */
    public void doBefore(JoinPoint jp){
        System.out.println("===========執行前置通知============");
    }

    /**
     * 後置返回通知:目標方法正常結束後執行的代碼
     * 返回通知是能夠訪問到目標方法的返回值的
     * @param jp
     * @param result
     */
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========執行後置通知============");
        System.out.println("返回值result==================="+result);
    }

    /**
     * 最終通知:目標方法調用以後執行的代碼(不管目標方法是否出現異常均執行)
     * 由於方法可能會出現異常,因此不能返回方法的返回值
     * @param jp
     */
    public void doAfter(JoinPoint jp){
        System.out.println("===========執行最終通知============");
    }

    /**
     *
     * 異常通知:目標方法拋出異常時執行的代碼
     * 在目標方法執行的時候,若是拋出異常,當即進入此方法
     * 能夠訪問到異常對象
     * @param jp
     * @param ex
     */
    public void doAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println("===========執行異常通知============");
    }

    /**
     * 環繞通知:目標方法調用先後執行的代碼,能夠在方法調用先後完成自定義的行爲。
     * 包圍一個鏈接點(join point)的通知。它會在切入點方法執行前執行同時方法結束也會執行對應的部分。
     * 主要是調用proceed()方法來執行切入點方法,來做爲環繞通知先後方法的分水嶺。
     *
     * 環繞通知相似於動態代理的全過程:ProceedingJoinPoint類型的參數能夠決定是否執行目標方法。
     * 並且環繞通知必須有返回值,返回值即爲目標方法的返回值
     * @param pjp
     * @return
     * @throws Throwable
     */
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("======執行環繞通知開始=========");
        // 調用方法的參數
        Object[] args = pjp.getArgs();
        // 調用的方法名
        String method = pjp.getSignature().getName();
        // 獲取目標對象
        Object target = pjp.getTarget();
        // 執行完方法的返回值
        // 調用proceed()方法,就會觸發切入點方法執行
        Object result=pjp.proceed();
        //若是調用pjp.proceed()執行業務方法的時候拋出異常,那麼下面的代碼將不會執行
        System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result);
        System.out.println("======執行環繞通知結束=========");
        return result;
    }
}

Spring的AOP配置:spring-aop.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-3.0.xsd
        http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- 聲明一個業務類 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 聲明通知類 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspect" />

    <aop:config>
        <aop:aspect ref="aspectBean">
            <aop:pointcut id="pointcut" expression="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))"/>

            <aop:before method="doBefore" pointcut-ref="pointcut"/>
            <aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/>
            <aop:after method="doAfter" pointcut-ref="pointcut" />
            <aop:around method="doAround" pointcut-ref="pointcut"/>
            <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/>
        </aop:aspect>
    </aop:config>
</beans>

springboot啓動類設置:注意要引入xml文件code

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource("classpath:spring-aop.xml")
//@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

測試類:

package com.springboottime.time;

import com.springboottime.time.service.AdviceService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TimeApplicationTests {

    @Autowired
    private AdviceService adviceService;

    @Test
    public void contextLoads() {
    }

    @Test
    public void test(){
        String user = adviceService.findUser();
        System.out.println("<><><><><><><><><><><><><>");
        adviceService.addUser();
    }
}

測試結果:

===========執行前置通知============
======執行環繞通知開始=========
***************執行業務方法findUser,查找的用戶名字爲:lixiaoxi****************
輸出,方法名:findUser;目標對象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======執行環繞通知結束=========
===========執行最終通知============
===========執行後置通知============
返回值result===================lixiaoxi
<><><><><><><><><><><><><>
===========執行前置通知============
======執行環繞通知開始=========
***************執行業務方法addUser****************
===========執行異常通知============
===========執行最終通知============

java.lang.RuntimeException
	at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)

2、使用註解配置AOP

採用註解來作aop, 主要是將寫在spring 配置文件中的鏈接點寫到註解裏面。

業務接口和業務實現與上邊同樣,具體切面類以下:

package com.springboottime.time.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AopAspectJ {

    /**
     * 必須爲final String類型的,註解裏要使用的變量只能是靜態常量類型的
     */
    public static final String EDP="execution(* com.springboottime.time.service.serviceImpl.AdviceServiceImpl..*(..))";

    /**
     * 切面的前置方法 即方法執行前攔截到的方法
     * 在目標方法執行以前的通知
     * @param jp
     */
    @Before(EDP)
    public void doBefore(JoinPoint jp){

        System.out.println("=========AopAspectJ執行前置通知==========");
    }


    /**
     * 在方法正常執行經過以後執行的通知叫作返回通知
     * 能夠返回到方法的返回值 在註解後加入returning
     * @param jp
     * @param result
     */
    @AfterReturning(value=EDP,returning="result")
    public void doAfterReturning(JoinPoint jp,String result){
        System.out.println("===========AopAspectJ執行後置通知============");
    }

    /**
     * 最終通知:目標方法調用以後執行的通知(不管目標方法是否出現異常均執行)
     * @param jp
     */
    @After(value=EDP)
    public void doAfter(JoinPoint jp){
        System.out.println("===========AopAspectJ執行最終通知============");
    }

    /**
     * 在目標方法非正常執行完成, 拋出異常的時候會走此方法
     * @param jp
     * @param ex
     */
    @AfterThrowing(value=EDP,throwing="ex")
    public void doAfterThrowing(JoinPoint jp,Exception ex) {
        System.out.println("===========AopAspectJ執行異常通知============");
    }

    /**
     * 環繞通知:目標方法調用先後執行的通知,能夠在方法調用先後完成自定義的行爲。
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(EDP)
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{

        System.out.println("======AopAspectJ執行環繞通知開始=========");
        // 調用方法的參數
        Object[] args = pjp.getArgs();
        // 調用的方法名
        String method = pjp.getSignature().getName();
        // 獲取目標對象
        Object target = pjp.getTarget();
        // 執行完方法的返回值
        // 調用proceed()方法,就會觸發切入點方法執行
        Object result=pjp.proceed();
        System.out.println("輸出,方法名:" + method + ";目標對象:" + target + ";返回值:" + result);
        System.out.println("======AopAspectJ執行環繞通知結束=========");
        return result;
    }

}

spring的配置:spring-aop-aspectJ.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-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- 聲明spring對@AspectJ的支持 -->
    <aop:aspectj-autoproxy/>

    <!-- 聲明一個業務類 -->
    <bean id="userManager" class="com.springboottime.time.service.serviceImpl.AdviceServiceImpl">
        <property name="name" value="lixiaoxi"></property>
    </bean>

    <!-- 聲明通知類 -->
    <bean id="aspectBean" class="com.springboottime.time.aop.AopAspectJ" />
</beans>

springboot啓動類配置:

package com.springboottime.time;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

//@ImportResource("classpath:spring-aop.xml")
@ImportResource("classpath:spring-aop-aspectJ.xml")
@SpringBootApplication
public class TimeApplication {

    public static void main(String[] args) {
        SpringApplication.run(TimeApplication.class, args);
    }

}

測試結果:

======AopAspectJ執行環繞通知開始=========
=========AopAspectJ執行前置通知==========
***************執行業務方法findUser,查找的用戶名字爲:lixiaoxi****************
輸出,方法名:findUser;目標對象:AdviceServiceImpl(name=lixiaoxi);返回值:lixiaoxi
======AopAspectJ執行環繞通知結束=========
===========AopAspectJ執行最終通知============
===========AopAspectJ執行後置通知============
<><><><><><><><><><><><><>
======AopAspectJ執行環繞通知開始=========
=========AopAspectJ執行前置通知==========
***************執行業務方法addUser****************
===========AopAspectJ執行最終通知============
===========AopAspectJ執行異常通知============

java.lang.RuntimeException
	at com.springboottime.time.service.serviceImpl.AdviceServiceImpl.addUser(AdviceServiceImpl.java:20)
相關文章
相關標籤/搜索