【原创】003 | 搭上基于SpringBoot事务头脑实战专车

发车啦,发车啦,上车要求:
点击左上方的“java进阶架构师”进入页面
选择右上角的“置顶民众号”上车!

专车先容
该趟专车是开往基于Spring Boot事务头脑实战的专车,在上一篇 搭上SpringBoot事务源码剖析专车[1]中我们详细先容了Spring Boot事务实现的原理,这一篇是基于上一篇的实战。

在实战之前,我们再次回首下上篇文章解说的重点:

  • 后置处置器:对Bean举行阻挡并处置
  • 切面:由切点和通知组成
  • 切点:用于匹配相符的类和方式
  • 通知:用于署理处置
    专车问题
  • 若何行使后置处置器对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) {
        // 查找类上@SystemLog注解属性
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                targetClass, SystemLog.class, false, false);
        if (Objects.nonNull(attributes)) {
            return true;
        }
        // 查找方式上@SystemLog注解属性
        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 {
}

专车集成营业
界说控制器

,

联博统计

www.u-healer.com采用以太坊区块链高度哈希值作为统计数据,联博以太坊统计数据开源、公平、无任何作弊可能性。联博统计免费提供API接口,支持多语言接入。

,
@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注解的实现方式,完成了日志切面功效。

专车总结

  • 首先我们需要界说一个Bean后置处置器,用于阻挡处置Bean
  • 然后界说切面,在切面中界说切点
  • 切点中实现切入的逻辑,好比此处我们的实现逻辑就是查找类或方式上是否含有@SystemLog注解
  • 界说通知,完成署理事情
  • 自动装配,将我们的切面、通知、Bean后置处置器声明在设置类中
  • 集成营业
    专车回首
    回首下开头的五个问题:

若何行使后置处置器对Bean举行阻挡并处置?直接在设置类中声明后置处置器
若何界说切面?继续AbstractBeanFactoryPointcutAdvisor,并在设置类中中声明
若何界说切点?继续StaticMethodMatcherPointcut,实现matches方式
若何界说通知?实现MethodInterceptor接口,实现invoke方式
若何实现自动设置?自界说设置类,声明所有需要加入容器的Bean
参考资料

1]搭上SpringBoot事务源码剖析专车: https://github.com/a601942905git/learning-notes/blob/master/springboot/搭上SpringBoot事务源码剖析专车.md

———— e n d ————
微服务、高并发、JVM调优、面试专栏等20大进阶架构师专题请关注民众号【Java进阶架构师】后在菜单栏查看。

看到这里,说明你喜欢本文
你的转发,是对我最大的激励!在看亦是支持↓