轉載:https://blog.csdn.net/Daybreak1209/article/details/80591566
應用一:方法入參校驗
由於系統多個方法入參均對外封裝了統一的Dto,其中Dto中幾個必傳參數在每個方法中都會進行相同的校驗邏輯。筆者考慮采用Spring AOP進行優化,攔截方法進行參數校驗。測試case實現如下:
Before
-
/**
-
* 被代理的目標類
-
*/
-
-
public class PayOrderTarget {
-
-
private PaymentOrderService paymentOrderService;
-
-
public Result testQuery(QueryPaymentDto paymentOrderDto){
-
PaymentOrderDto paymentOrderDto1 = paymentOrderService.queryPaymentOrder(paymentOrderDto);
-
return ResultWrapper.success(paymentOrderDto1);
-
}
-
}
-
/**
-
* 通知類,橫切邏輯
-
*/
-
-
-
public class Advices {
-
-
-
public Result before(JoinPoint proceedingJoinPoint) {
-
// 攔截獲取入參
-
Object[] args = proceedingJoinPoint.getArgs();
-
// 入參校驗
-
if (args ==null){
-
return ResultWrapper.fail();
-
}
-
-
String input = JSON.toJSON(args).toString();
-
Map<String, String> map = Splitter.on( ",").withKeyValueSeparator(":").split(input);
-
if (map.containsKey("businessId") || map.containsKey("payOrderId")){
-
System.out.println( "key null");
-
return ResultWrapper.fail();
-
}
-
if (map.get("businessId")==null || map.get("payOrderId")==null ){
-
System.out.println( "value null");
-
return ResultWrapper.fail();
-
}
-
-
System.out.println( "----------前置通知----------");
-
System.out.println(proceedingJoinPoint.getSignature().getName());
-
return ResultWrapper.success();
-
}
-
}
測試類正常調用查詢方法
-
-
-
public class Test {
-
-
-
private PayOrderTarget payOrderTarget;
-
-
-
public void test(){
-
QueryPaymentDto paymentOrderDto= new QueryPaymentDto();
-
paymentOrderDto.setPayOrderId( 11l);
-
paymentOrderDto.setBusinessId( 112L);
-
paymentOrderDto.setPageSize( 1);
-
payOrderTarget.testQuery(paymentOrderDto);
-
}
-
}
執行結果可對入參Dto進行攔截,執行before中的校驗邏輯。但即便return fail之后,目標方法還是會被執行到。筆者是想實現參數校驗失敗,則直接返回,不執行接下來查詢db的操作。此時則需要使用Around切入。
Around
-
-
public Result around(ProceedingJoinPoint proceedingJoinPoint) {
-
// 獲取java數組
-
Object[] args = proceedingJoinPoint.getArgs();
-
JSONArray jsonArray = JSONArray.parseArray(JSONArray.toJSONString(args));
-
String businessId = null;
-
String payOrderId = null;
-
for (int i = 0; i < jsonArray.size(); i++) {
-
businessId = jsonArray.getJSONObject( 0).getString("businessId");
-
payOrderId = jsonArray.getJSONObject( 0).getString("payOrderId");
-
}
-
;
-
//參數校驗
-
if (businessId == null || payOrderId == null) {
-
System.out.println( "value null");
-
return ResultWrapper.fail();
-
}
-
//執行目標方法
-
try {
-
proceedingJoinPoint.proceed();
-
} catch (Throwable throwable) {
-
throwable.printStackTrace();
-
}
-
System.out.println( "----------Around通知----------");
-
return ResultWrapper.success();
-
}
簡單介紹下,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 | 在方法拋出異常后調用通知 |
-
/**
-
* login接口類 被代理接口類
-
*/
-
public interface ILoginService {
-
boolean login(String userName, String password);
-
void quary() throws Exception;
-
}
-
-
/**
-
* login實現類,被代理目標類
-
*/
-
-
public class LoginServiceImpl implements ILoginService {
-
-
public boolean login(String userName, String password) {
-
System.out.println( "login:" + userName + "," + password);
-
return true;
-
}
-
-
public void quary() throws Exception{
-
System.out.println( "******quary*******");
-
// 測試方法拋出異常后,解注After-throwing配置,會執行logArg切入,不拋異常不執行切點
-
// int i=10/0;
-
}
-
}
日志包裝類,將各類日志情況進行方法封裝
-
/**
-
* 日志處理類
-
*/
-
public interface ILogService {
-
//無參的日志方法
-
void log();
-
-
//有參的日志方法
-
void logArg(JoinPoint point);
-
-
//有參有返回值的方法
-
void logArgAndReturn(JoinPoint point, Object returnObj);
-
}
-
-
/**
-
* 日志實現類
-
*/
-
-
-
public class LogServiceImpl implements ILogService {
-
-
-
public void log() {
-
System.out.println( "*************Log*******************");
-
}
-
-
public void logArg(JoinPoint point) {
-
System.out.println( "方法:"+point.getSignature().getName());
-
Object[] args = point.getArgs();
-
System.out.println( "目標參數列表:");
-
if (args != null) {
-
for (Object obj : args) {
-
System.out.println(obj + ",");
-
}
-
}
-
}
-
-
public void logArgAndReturn(JoinPoint point, Object returnObj) {
-
//此方法返回的是一個數組,數組中包括request以及ActionCofig等類對象
-
Object[] args = point.getArgs();
-
System.out.println( "目標參數列表:");
-
if (args != null) {
-
for (Object obj : args) {
-
System.out.println(obj + ",");
-
}
-
System.out.println( "執行結果是:" + returnObj);
-
}
-
}
-
}
配置(注釋着重看下):
-
<aop:config>
-
<!-- 切入點 LoginServiceImpl類中所有方法都會被攔截,執行增強方法-->
-
<aop:pointcut expression="execution(* com.payment.util.aoplogger.LoginServiceImpl.*(..))" id="myPointcut" />
-
<!-- 切面-->
-
<aop:aspect id="dd" ref="logService">
-
<!-- LoginServiceImpl類方法執行前,增強執行日志service的log方法-->
-
<!--<aop:before method="log" pointcut-ref="myPointcut" />-->
-
-
<!-- LoginServiceImpl類方法執行后,增強執行日志service的logArg方法-->
-
<!--<aop:after method="logArg" pointcut-ref="myPointcut"/>-->
-
<!-- LoginServiceImpl類方法執行拋異常后,增強執行日志service的logArg方法-->
-
<aop:after-throwing method="logArg" pointcut-ref="myPointcut"/>
-
<!--<aop:after-returning method="logArgAndReturn" returning="returnObj" pointcut-ref="myPointcut"/>-->
-
</aop:aspect>
-
</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 等同於其他增強方式
|