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尽量小)例如限流切面可以放在第一层,超出流量直接返回不再进行后续流程,从而在第一层就减小系统压力。


分享到:


相關文章: