SpringBoot,實現AOP通過註解,對指定方法進行記錄入參出參和異常,並打包

需求

能夠記錄請求的參數,請求的返回值,請求出現的異常。
Log日誌工具任意。

實現思路

需要的依賴

打包選項

<packaging>jar</packaging>
		<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

類上需要的註解

@Aspect
@Configuration
public class LoggerAspect {
......................
}

切入點

這裏使用annotation的方式,對所有打上註解的方法進行攔截。
Log 註解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
}

切入點

@Pointcut("@annotation(annotation.Log)")
    public void logCut(){}

入參記錄

首先使用@Before註解,將進入方法前的參數進行攔截並記錄

@Before("logCut()")
    public void logRequest(JoinPoint joinPoint){
        System.out.println("記錄中");
        logger.info("記錄開始------------------------------------------------------");
        for (Object arg:joinPoint.getArgs()) {
            logger.info("請求參數爲:"+arg.toString());
        }
        logger.info("請求的方法爲:"+joinPoint.getSignature().getName());
        logger.info("請求記錄完成");
    }

出參記錄

其中@AfterReturning中“ returning ” 參數指定命名爲 “result”,這樣就能在下面方法的參數中添加result形參。

@AfterReturning(value = "logCut()",returning = "result")
    public void logResponse(JoinPoint joinPoint,Object result){
        logger.info(joinPoint.getSignature().getName()+" 執行完畢");
        logger.info("執行結果爲:"+result);
    }

異常記錄

這裏的BaseException是我自定義的異常,不影響理解。

@AfterThrowing(value = "logCut()",throwing = "e")
    public void logException(JoinPoint joinPoint, Exception e){
        logger.error(joinPoint.getSignature().getName()+" 方法執行異常");
        //如果是定義的異常體系,則打出異常碼
        if (e instanceof BaseException){
            BaseException exception = (BaseException) e;
            logger.error("異常爲: "+e.getMessage()+" 異常碼爲:  "+((BaseException) e).getErrorCode()+"  異常堆棧打印:"+e.getStackTrace());
        }
        logger.error("異常爲: "+e.getMessage()+"    "+e.getStackTrace());
    }

打包

使用mvn install進行打包並放置在本地maven倉庫
IDEA則可以使用右側窗口的Maven工具,執行install即可
在這裏插入圖片描述
執行完成後在本地maven庫中會出現對應的jar包
在這裏插入圖片描述

打包出來的jar包引用方式

在你的項目裏之間導入即可

<dependency>
   <groupId>com.example</groupId>
    <artifactId>boss-bes-common-logging</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

然後ExternalLibraries中就有對應的jar包了。

日誌功能的測試

注意!!

對於新手來說,容易搞混@ComponentScan()和SpringBootApplication(scanBasePackages={})

@SpringBootApplication和@ComponentScan

進入@SpringBootApplication源碼可以看到,@SpringBootApplication實際上是集成了@EnableAutoConfiguration,@ComponentScan。所以,不要使用了@ComponentScan又使用@SpringBootApplication(scanBasePackage={})。

public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

回到正題,啓動類上應該加上@EnableAspectJAutoProxy,如果沒有則無法使用切面。
@EnableAspectJAutoProxy源碼,其中第一個參數是指定動態代理的方式,默認爲false
即使用JDKProxy進行動態代理,如果設置爲true則使用CgLib進行動態代理。關於動態代
理內容見:
第二個參數爲控制代理的暴露方式,解決內部調用不能使用代理的場景,默認爲false.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {

	/**
     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
     * to standard Java interface-based proxies. The default is {@code false}.
     */
    boolean proxyTargetClass() default false;

	/**
     * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
     * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
     * Off by default, i.e. no guarantees that {@code AopContext} access will work.
     * @since 4.3.1
     */
    boolean exposeProxy() default false;
}

這裏在啓動類上加了@RestController,這是爲了便於測試。
@RestContoller源碼,不難看出,@RestController = @Controller + @ResponseBody

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

正式開始測試
在hello方法上打上我定義的註解@Log
同時scanBasePackage指定jar中Log所在的包名,不然無法掃到這個Aspect

@EnableAspectJAutoProxy
@SpringBootApplication(scanBasePackages = {"logexample"})
@RestController
public class DemoApplication {
 	@Log
    @RequestMapping("/hello")
    public String hello(@RequestParam Integer id){
        if (id==1){
            throw new BusinessException(EnumException.SERVICE_INVALID_STATUS);
        }
        return id+"dd";
    }
}

測試工具爲PostMan
測試結果爲:
在這裏插入圖片描述
在這裏插入圖片描述
記錄異常
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章