转载

Spring Boot 基于@Transaction思想实现日志功能

通过之前对@Transaction原理分析,可以得出实现事务的要点如下:

  • 需要定义一个后置处理器,用来拦截bean
  • 需要定义一个切面
  • 需要定义切面的切点
  • 需要定义切面的通知,用作代理处理

基于以上思想,我们来实现一个类似的功能:系统日志

系统日志实现

实现是以Spring Boot为基础,需要添加如下依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
复制代码

按照如上提到的要点依次定义

  • 定义bean后置处理器,特别注意,如果项目中使用到了事务特性,就不需要重复定义
/**
 * 一定要声明InfrastructureAdvisorAutoProxyCreator,用于实现bean的后置处理
 *
 * @return
 */
@Bean
public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
    return new InfrastructureAdvisorAutoProxyCreator();
}
复制代码
  • 定义切面
public class BeanFactorySystemLogAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    /**
     * 定义切点
     */
    private final SystemLogPointcut point = new SystemLogPointcut();

    @Override
    public Pointcut getPointcut() {
        return this.point;
    }
}
复制代码
  • 定义切点
public class SystemLogPointcut extends StaticMethodMatcherPointcut {

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                targetClass, SystemLog.class, false, false);
        if (Objects.nonNull(attributes)) {
            return true;
        }

        attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                method, SystemLog.class, false, false);
        return Objects.nonNull(attributes);
    }
}
复制代码
  • 定义通知
@Slf4j
public class SystemLogInterceptor implements MethodInterceptor, Serializable {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        String className = method.getDeclaringClass().getSimpleName();
        String methodName = method.getName();
        log.info("======[" + className + "#" + methodName + " method begin execute]======");
        Arrays.stream(invocation.getArguments()).forEach(argument -> log.info("======[execute method argument:" + argument + "]======"));
        Long time1 = Clock.systemDefaultZone().millis();
        Object result = invocation.proceed();
        Long time2 = Clock.systemDefaultZone().millis();
        log.info("======[method execute time:" + (time2 - time1) + "]======");
        return result;
    }
}
复制代码
  • 自动配置
@Configuration
public class ProxySystemLogConfiguration {

    /**
     * 定义切面
     * 此处一定要指定@Role注解
     *
     * @return
     */
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    @Bean
    public BeanFactorySystemLogAdvisor beanFactorySystemLogAdvisor() {
        BeanFactorySystemLogAdvisor advisor = new BeanFactorySystemLogAdvisor();
        advisor.setAdvice(systemLogInterceptor());
        return advisor;
    }

    /**
     * 定义通知
     *
     * @return
     */
    @Bean
    public SystemLogInterceptor systemLogInterceptor() {
        return new SystemLogInterceptor();
    }

    /**
     * 一定要声明InfrastructureAdvisorAutoProxyCreator,用于实现bean的后置处理
     *
     * @return
     */
    @Bean
    public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
        return new InfrastructureAdvisorAutoProxyCreator();
    }
}
复制代码
  • 定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
}
复制代码

集成到业务

  • 定义控制器
@RestController
public class SystemLogController {

    @Autowired
    private SystemLogService systemLogService;

    @GetMapping("/log")
    public String hello(@RequestParam("name") String name) throws InterruptedException {
        return systemLogService.log(name);
    }
}
复制代码
  • 定义业务方法
@Slf4j
@Service
public class SystemLogService {

    @SystemLog
    public String log(String name) throws InterruptedException {
        log.info("执行业务方法");
        TimeUnit.SECONDS.sleep(1);
        return "hello " + name;
    }
}
复制代码
  • 定义启动类
@SpringBootApplication
public class TransactionImitateApplication {

    public static void main(String[] args) {
        SpringApplication.run(TransactionImitateApplication.class, args);
    }
}
复制代码
  • 访问http://localhost:8080/log?name=advisor
  • 查看控制台
2019-08-23 11:13:36.029  INFO 23227 --- [nio-8080-exec-1] c.b.example.config.SystemLogInterceptor  : ======[SystemLogService#log method begin execute]======
2019-08-23 11:13:36.030  INFO 23227 --- [nio-8080-exec-1] c.b.example.config.SystemLogInterceptor  : ======[execute method argument:advisor]======
2019-08-23 11:13:36.038  INFO 23227 --- [nio-8080-exec-1] c.boot.example.service.SystemLogService  : 执行业务方法
2019-08-23 11:13:37.038  INFO 23227 --- [nio-8080-exec-1] c.b.example.config.SystemLogInterceptor  : ======[method execute time:1004]======
复制代码

可以看到通过模拟@Transaction注解的实现方式,完成了日志切面功能

原文  https://juejin.im/post/5d64a48ef265da03cb1248bb
正文到此结束
Loading...