Spring AOP項目應用——方法入參校驗 & 日志橫切


轉載:https://blog.csdn.net/Daybreak1209/article/details/80591566

應用一:方法入參校驗

由於系統多個方法入參均對外封裝了統一的Dto,其中Dto中幾個必傳參數在每個方法中都會進行相同的校驗邏輯。筆者考慮采用Spring AOP進行優化,攔截方法進行參數校驗。測試case實現如下:

Before

  1.  
    /**
  2.  
    * 被代理的目標類
  3.  
    */
  4.  
    @Service
  5.  
    public class PayOrderTarget {
  6.  
    @Autowired
  7.  
    private PaymentOrderService paymentOrderService;
  8.  
     
  9.  
    public Result testQuery(QueryPaymentDto paymentOrderDto){
  10.  
    PaymentOrderDto paymentOrderDto1 = paymentOrderService.queryPaymentOrder(paymentOrderDto);
  11.  
    return ResultWrapper.success(paymentOrderDto1);
  12.  
    }
  13.  
    }
  1.  
    /**
  2.  
    * 通知類,橫切邏輯
  3.  
    */
  4.  
    @Component
  5.  
    @Aspect
  6.  
    public class Advices {
  7.  
     
  8.  
    @Before("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
  9.  
    public Result before(JoinPoint proceedingJoinPoint) {
  10.  
    // 攔截獲取入參
  11.  
    Object[] args = proceedingJoinPoint.getArgs();
  12.  
    // 入參校驗
  13.  
    if (args ==null){
  14.  
    return ResultWrapper.fail();
  15.  
    }
  16.  
     
  17.  
    String input = JSON.toJSON(args).toString();
  18.  
    Map<String, String> map = Splitter.on( ",").withKeyValueSeparator(":").split(input);
  19.  
    if (map.containsKey("businessId") || map.containsKey("payOrderId")){
  20.  
    System.out.println( "key null");
  21.  
    return ResultWrapper.fail();
  22.  
    }
  23.  
    if (map.get("businessId")==null || map.get("payOrderId")==null ){
  24.  
    System.out.println( "value null");
  25.  
    return ResultWrapper.fail();
  26.  
    }
  27.  
     
  28.  
    System.out.println( "----------前置通知----------");
  29.  
    System.out.println(proceedingJoinPoint.getSignature().getName());
  30.  
    return ResultWrapper.success();
  31.  
    }
  32.  
    }

測試類正常調用查詢方法

  1.  
    @RunWith(SpringJUnit4ClassRunner.class)
  2.  
    @ContextConfiguration(locations = {"classpath*:spring/spring-context.xml"})
  3.  
    public class Test {
  4.  
     
  5.  
    @Autowired
  6.  
    private PayOrderTarget payOrderTarget;
  7.  
     
  8.  
    @org.junit.Test
  9.  
    public void test(){
  10.  
    QueryPaymentDto paymentOrderDto= new QueryPaymentDto();
  11.  
    paymentOrderDto.setPayOrderId( 11l);
  12.  
    paymentOrderDto.setBusinessId( 112L);
  13.  
    paymentOrderDto.setPageSize( 1);
  14.  
    payOrderTarget.testQuery(paymentOrderDto);
  15.  
    }
  16.  
    }

執行結果可對入參Dto進行攔截,執行before中的校驗邏輯。但即便return fail之后,目標方法還是會被執行到。筆者是想實現參數校驗失敗,則直接返回,不執行接下來查詢db的操作。此時則需要使用Around切入。

Around

  1.  
    @Around("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")
  2.  
    public Result around(ProceedingJoinPoint proceedingJoinPoint) {
  3.  
    // 獲取java數組
  4.  
    Object[] args = proceedingJoinPoint.getArgs();
  5.  
    JSONArray jsonArray = JSONArray.parseArray(JSONArray.toJSONString(args));
  6.  
    String businessId = null;
  7.  
    String payOrderId = null;
  8.  
    for (int i = 0; i < jsonArray.size(); i++) {
  9.  
    businessId = jsonArray.getJSONObject( 0).getString("businessId");
  10.  
    payOrderId = jsonArray.getJSONObject( 0).getString("payOrderId");
  11.  
    }
  12.  
    ;
  13.  
    //參數校驗
  14.  
    if (businessId == null || payOrderId == null) {
  15.  
    System.out.println( "value null");
  16.  
    return ResultWrapper.fail();
  17.  
    }
  18.  
             //執行目標方法
  19.  
    try {
  20.  
    proceedingJoinPoint.proceed();
  21.  
    } catch (Throwable throwable) {
  22.  
    throwable.printStackTrace();
  23.  
    }
  24.  
    System.out.println( "----------Around通知----------");
  25.  
    return ResultWrapper.success();
  26.  
    }

簡單介紹下,before和after切入都是接收JoinPoint對象,該對象可獲取切點(即被代理對象)的入參、方法名等數據。

方法名 功能
Signature getSignature(); 獲取封裝了署名信息的對象,在該對象中可以獲取到目標方法名,所屬類的Class等信息
Object[] getArgs(); 獲取傳入目標方法的參數對象
Object getTarget(); 獲取被代理的對象
Object getThis(); 獲取代理對象

而Around接收ProceedingJoinPoint該接口繼承自JoinPoint,新增了如下兩個方法,通過調用proceedingJoinPoint.proceed方法才控制目標方法的調用。則在如上around中,進行參數校驗,不合法則return,未進入到proceedingJoinPoint.proceed()處,達到方法不合規直接返回不調用查詢邏輯。

方法名 功能
Object proceed() throws Throwable  執行目標方法
Object proceed(Object[] var1) throws Throwable 傳入的新的參數去執行目標方法

注:本case采用注解聲明,直接可運行。xml中增加如下配置(支持自動裝配@Aspect注解的bean)即可。

<aop:aspectj-autoproxy proxy-target-class="true"/>

 

應用二:日志處理

實現將日志打印抽取,成為一個公共類,切入到目標類的方法入口、出口處打印方法名+參數信息;這個case重點引用了幾種不同的AOP增強方式,簡單介紹如下:

Before  在方法被調用之前調用通知
Around  通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為
After  在方法完成之后調用通知,無論方法執行是否成功
After-returning 在方法返回結果后執行通知
After-throwing 在方法拋出異常后調用通知
  1.  
    /**
  2.  
    * login接口類 被代理接口類
  3.  
    */
  4.  
    public interface ILoginService {
  5.  
    boolean login(String userName, String password);
  6.  
    void quary() throws Exception;
  7.  
    }
  8.  
     
  9.  
    /**
  10.  
    * login實現類,被代理目標類
  11.  
    */
  12.  
    @Service
  13.  
    public class LoginServiceImpl implements ILoginService {
  14.  
     
  15.  
    public boolean login(String userName, String password) {
  16.  
    System.out.println( "login:" + userName + "," + password);
  17.  
    return true;
  18.  
    }
  19.  
    @Override
  20.  
    public void quary() throws Exception{
  21.  
    System.out.println( "******quary*******");
  22.  
    // 測試方法拋出異常后,解注After-throwing配置,會執行logArg切入,不拋異常不執行切點
  23.  
    // int i=10/0;
  24.  
    }
  25.  
    }

日志包裝類,將各類日志情況進行方法封裝

  1.  
    /**
  2.  
    * 日志處理類
  3.  
    */
  4.  
    public interface ILogService {
  5.  
    //無參的日志方法
  6.  
    void log();
  7.  
     
  8.  
    //有參的日志方法
  9.  
    void logArg(JoinPoint point);
  10.  
     
  11.  
    //有參有返回值的方法
  12.  
    void logArgAndReturn(JoinPoint point, Object returnObj);
  13.  
    }
  14.  
     
  15.  
    /**
  16.  
    * 日志實現類
  17.  
    */
  18.  
    @Component("logService")
  19.  
    @Aspect
  20.  
    public class LogServiceImpl implements ILogService {
  21.  
     
  22.  
    @Override
  23.  
    public void log() {
  24.  
    System.out.println( "*************Log*******************");
  25.  
    }
  26.  
    @Override
  27.  
    public void logArg(JoinPoint point) {
  28.  
    System.out.println( "方法:"+point.getSignature().getName());
  29.  
    Object[] args = point.getArgs();
  30.  
    System.out.println( "目標參數列表:");
  31.  
    if (args != null) {
  32.  
    for (Object obj : args) {
  33.  
    System.out.println(obj + ",");
  34.  
    }
  35.  
    }
  36.  
    }
  37.  
    @Override
  38.  
    public void logArgAndReturn(JoinPoint point, Object returnObj) {
  39.  
    //此方法返回的是一個數組,數組中包括request以及ActionCofig等類對象
  40.  
    Object[] args = point.getArgs();
  41.  
    System.out.println( "目標參數列表:");
  42.  
    if (args != null) {
  43.  
    for (Object obj : args) {
  44.  
    System.out.println(obj + ",");
  45.  
    }
  46.  
    System.out.println( "執行結果是:" + returnObj);
  47.  
    }
  48.  
    }
  49.  
    }

配置(注釋着重看下):

  1.  
    <aop:config>
  2.  
    <!-- 切入點 LoginServiceImpl類中所有方法都會被攔截,執行增強方法-->
  3.  
    <aop:pointcut expression="execution(* com.payment.util.aoplogger.LoginServiceImpl.*(..))" id="myPointcut" />
  4.  
    <!-- 切面-->
  5.  
    <aop:aspect id="dd" ref="logService">
  6.  
    <!-- LoginServiceImpl類方法執行前,增強執行日志service的log方法-->
  7.  
    <!--<aop:before method="log" pointcut-ref="myPointcut" />-->
  8.  
     
  9.  
    <!-- LoginServiceImpl類方法執行后,增強執行日志service的logArg方法-->
  10.  
    <!--<aop:after method="logArg" pointcut-ref="myPointcut"/>-->
  11.  
    <!-- LoginServiceImpl類方法執行拋異常后,增強執行日志service的logArg方法-->
  12.  
    <aop:after-throwing method="logArg" pointcut-ref="myPointcut"/>
  13.  
    <!--<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>-->
  14.  
    </aop:aspect>
  15.  
    </aop:config>

注意:采用xml配置與使用@注解二選一(都寫上即執行兩次增強方法),對等關系如下:

<aop:aspect ref="advices">

@Aspect

public class Advices

<aop:pointcut expression="execution(* com.payment.util.springaop.PayOrderTarget.*(..))" id="pointcut1"/>

 

<aop:before method="before" pointcut-ref="pointcut1"/>

@Before("execution(* com.payment.util.springaop.PayOrderTarget.*(..))")      

<aop:after

@After  等同於其他增強方式

 


免責聲明!

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



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