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
测试结果为:
记录异常