AOP多個切面執行順序實例

1 文章概述

AOP含義是面向切面編程,可以通過預編譯方式和運行期動態代理實現,可以在不修改源代碼情況下給程序動態統一添加功能。Spring對AOP支持非常友好,只需簡單配置即可實現切面功能。

如果系統中存在多個切面,這些切面執行順序是什麼,本文通過代碼實例分析多個切面執行順序問題。

2 一個切面

(1) 定義切面

<code>package com.xy.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import com.xy.domain.api.ErrorCodeBiz;
import com.xy.domain.api.MyBizException;
import com.xy.domain.api.ResultDTO;
import org.springframework.core.annotation.Order;

@Aspect
@Component
@Order(1)
public class MyServiceAspect1 {

\t@Around("execution(* com.xy.outter..*(..)))")
\tpublic Object exceptionHandler(ProceedingJoinPoint jp) throws Throwable {
\t\tString aspect = "切面1:";
\t\tString clazz = null;
\t\tString method = null;
\t\tObject result = null;
\t\tObject[] args = null;
\t\tStringBuilder params = new StringBuilder();
\t\tResultDTO resultDTO = new ResultDTO();
\t\ttry {
\t\t\tSystem.out.println("===" + aspect + "start");
\t\t\tclazz = jp.getTarget().getClass().getName();
\t\t\tmethod = jp.getSignature().getName();
\t\t\targs = jp.getArgs();
\t\t\tfor (Object arg : args) {
\t\t\t\tparams.append(arg + ",");
\t\t\t}
\t\t\tresult = jp.proceed();
\t\t\tSystem.out.println("===" + aspect + "end result:" + result);
\t\t} catch (MyBizException ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.getError(ex.getCode()));
\t\t\tresultDTO.setErrMsg(ex.getMsg());

\t\t\treturn resultDTO;
\t\t} catch (Exception ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.SYSTEM_ERROR);
\t\t\treturn resultDTO;
\t\t}
\t\treturn result;
\t}
}/<code>

(2) 服務實現

<code>package com.xy.outter;
import org.springframework.stereotype.Service;
import com.xy.domain.api.ResultDTO;

@Service
public class TestAspectServiceImpl implements TestAspectService {
\tpublic ResultDTO<string> test(String name) {
\t\tResultDTO<string> result = new ResultDTO<string>();
\t\tresult = result.successResult(name);
\t\tSystem.out.println("===方法執行:" + result);
\t\treturn result;
\t}
}/<string>/<string>/<string>/<code>

(3) 測試用例

<code>package com.xy.outter;
import javax.annotation.Resource;
import org.junit.Test;
import com.xy.base.BaseTest;
import com.xy.domain.api.ResultDTO;

public class TestAspectServiceImplTest extends BaseTest {

\t@Resource
\tprivate TestAspectService testAspectService;

\t@Test
\tpublic void testTest() {
\t\tResultDTO<string> result = testAspectService.test("xy");
\t\tSystem.out.println("===調用結果:" + result);
\t}
}/<string>/<code>

(4) 調用結果

<code>===切面1:start
===方法執行:com.xy.domain.api.ResultDTO@7a560583[module=xy,code=OK,errMsg=<null>]
===切面1:end result:com.xy.domain.api.ResultDTO@7a560583[module=xy,code=OK,errMsg=<null>]
===調用結果:com.xy.domain.api.ResultDTO@7a560583[module=xy,code=OK,errMsg=<null>]/<null>/<null>/<null>/<code>

3 兩個切面

(1) 新增切面

<code>package com.xy.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.xy.domain.api.ErrorCodeBiz;
import com.xy.domain.api.MyBizException;
import com.xy.domain.api.ResultDTO;

@Aspect
@Component
@Order(2)
public class MyServiceAspect2 {

\t@Around("execution(* com.xy.outter..*(..)))")
\tpublic Object exceptionHandler(ProceedingJoinPoint jp) throws Throwable {
\t\tString aspect = "切面2:";
\t\tString clazz = null;
\t\tString method = null;
\t\tObject result = null;
\t\tObject[] args = null;
\t\tStringBuilder params = new StringBuilder();
\t\tResultDTO resultDTO = new ResultDTO();
\t\ttry {
\t\t\tSystem.out.println("===" + aspect + "start");
\t\t\tclazz = jp.getTarget().getClass().getName();
\t\t\tmethod = jp.getSignature().getName();
\t\t\targs = jp.getArgs();
\t\t\tfor (Object arg : args) {
\t\t\t\tparams.append(arg + ",");
\t\t\t}
\t\t\tresult = jp.proceed();
\t\t\tSystem.out.println("===" + aspect + "end result:" + result);
\t\t} catch (MyBizException ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.getError(ex.getCode()));
\t\t\tresultDTO.setErrMsg(ex.getMsg());
\t\t\treturn resultDTO;

\t\t} catch (Exception ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.SYSTEM_ERROR);
\t\t\treturn resultDTO;
\t\t}
\t\treturn result;
\t}
}/<code>

(2) 執行結果

<code>===切面1:start
===切面2:start
===方法執行:com.xy.domain.api.ResultDTO@784c3487[module=xy,code=OK,errMsg=<null>]
===切面2:end result:com.xy.domain.api.ResultDTO@784c3487[module=xy,code=OK,errMsg=<null>]
===切面1:end result:com.xy.domain.api.ResultDTO@784c3487[module=xy,code=OK,errMsg=<null>]
===調用結果:com.xy.domain.api.ResultDTO@784c3487[module=xy,code=OK,errMsg=<null>]/<null>/<null>/<null>/<null>/<code>

(3) 執行順序


AOP多個切面執行順序實例

4 實現類發生異常

(1) 實現類修改

<code>package com.xy.outter;
import org.springframework.stereotype.Service;
import com.xy.domain.api.ErrorCodeBiz;
import com.xy.domain.api.MyBizException;
import com.xy.domain.api.ResultDTO;

@Service
public class TestAspectServiceImpl implements TestAspectService {
\tpublic ResultDTO<string> test(String name) {
\t\tResultDTO<string> result = new ResultDTO<string>();
\t\tif ("xy".equals(name)) {
\t\t\tthrow new MyBizException(ErrorCodeBiz.USER_MOBILE_NULL);
\t\t}
\t\tresult = result.successResult(name);
\t\tSystem.out.println("===方法執行:" + result);
\t\treturn result;
\t}
}/<string>/<string>/<string>/<code>

(2) 執行結果

<code>===切面1:start
===切面2:start
===切面2:error clazz:com.xy.outter.TestAspectServiceImpl,method:test,params:xy,ex:com.xy.domain.api.MyBizException: 空號
===切面1:end result:com.xy.domain.api.ResultDTO@40dff0b7[module=<null>,code=USER_MOBILE_NULL,errMsg=空號]
===調用結果:com.xy.domain.api.ResultDTO@40dff0b7[module=<null>,code=USER_MOBILE_NULL,errMsg=空號]/<null>/<null>/<code>

(3) 關鍵說明

我們發現異常在最後一個切面被捕獲處理,處理完成後(包裝成ResultDTO)最後一個切面將結果返回至上一層,結果信息由下往上返回。

5 請求被切面攔截

(1) 模擬限流切面

<code>package com.xy.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.xy.domain.api.ErrorCodeBiz;
import com.xy.domain.api.MyBizException;
import com.xy.domain.api.ResultDTO;

@Aspect
@Component
@Order(1)
public class MyServiceAspect1 {

\t@Around("execution(* com.xy.outter..*(..)))")
\tpublic Object exceptionHandler(ProceedingJoinPoint jp) throws Throwable {
\t\tString aspect = "切面1:";
\t\tString clazz = null;
\t\tString method = null;
\t\tObject result = null;
\t\tObject[] args = null;
\t\tStringBuilder params = new StringBuilder();
\t\tResultDTO resultDTO = new ResultDTO();
\t\ttry {
\t\t\tSystem.out.println("===" + aspect + "start");
\t\t\tclazz = jp.getTarget().getClass().getName();
\t\t\tmethod = jp.getSignature().getName();
\t\t\targs = jp.getArgs();
\t\t\tfor (Object arg : args) {
\t\t\t\tparams.append(arg + ",");
\t\t\t}
\t\t\t/** 模擬限流功能 **/
\t\t\tif (params.toString().contains("xy")) {
\t\t\t\tthrow new MyBizException(ErrorCodeBiz.LIMIT_FLOW);
\t\t\t}
\t\t\tresult = jp.proceed();
\t\t\tSystem.out.println("===" + aspect + "end result:" + result);
\t\t} catch (MyBizException ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.getError(ex.getCode()));
\t\t\tresultDTO.setErrMsg(ex.getMsg());
\t\t\treturn resultDTO;
\t\t} catch (Exception ex) {
\t\t\tSystem.out.println("===" + aspect + "error,clazz:" + clazz + ",method:" + method + ",params:" + params + "ex:" + ex);
\t\t\tresultDTO.setCode(ErrorCodeBiz.SYSTEM_ERROR);
\t\t\treturn resultDTO;
\t\t}
\t\treturn result;

\t}
}/<code>

(2) 執行結果

<code>===切面1:start
===切面1:error,clazz:com.xy.outter.TestAspectServiceImpl,method:test,params:xy,ex:com.xy.domain.api.MyBizException: 流量攔截
===調用結果:com.xy.domain.api.ResultDTO@12bd8a64[module=<null>,code=FLOW_LIMIT,errMsg=流量攔截]/<null>/<code>

(3) 關鍵說明

我們在設計多個切面時,需要立即攔截的可以放在第一層(Order儘量小)例如限流切面可以放在第一層,超出流量直接返回不再進行後續流程,從而在第一層就減小系統壓力。


分享到:


相關文章: