SpringBoot 實現註解日誌功能 並打包進本地Maven倉庫
需求
能夠記錄請求的參數,請求的返回值,請求出現的異常。
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
測試結果爲:
記錄異常