溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

【原創(chuàng)】003 | 搭上基于SpringBoot事務(wù)思想實(shí)戰(zhàn)專車

發(fā)布時(shí)間:2020-06-22 05:52:20 來源:網(wǎng)絡(luò) 閱讀:416 作者:師長學(xué)不動(dòng)了 欄目:編程語言

前言

如果這是你第二次看到師長,說明你在覬覦我的美色!

點(diǎn)贊+關(guān)注再看,養(yǎng)成習(xí)慣

沒別的意思,就是需要你的窺屏^_^

【原創(chuàng)】003 | 搭上基于SpringBoot事務(wù)思想實(shí)戰(zhàn)專車

專車介紹

該趟專車是開往基于Spring Boot事務(wù)思想實(shí)戰(zhàn)的專車,在上一篇 搭上SpringBoot事務(wù)源碼分析專車[1]中我們詳細(xì)介紹了Spring Boot事務(wù)實(shí)現(xiàn)的原理,這一篇是基于上一篇的實(shí)戰(zhàn)。

【原創(chuàng)】003 | 搭上基于SpringBoot事務(wù)思想實(shí)戰(zhàn)專車

在實(shí)戰(zhàn)之前,我們再次回顧下上篇文章講解的重點(diǎn):

  • 后置處理器:對Bean進(jìn)行攔截并處理
  • 切面:由切點(diǎn)和通知組成
  • 切點(diǎn):用于匹配符合的類和方法
  • 通知:用于代理處理

專車問題

  • 如何利用后置處理器對Bean進(jìn)行攔截并處理?
  • 如何定義切面?
  • 如何定義切點(diǎn)?
  • 如何定義通知?
  • 如何實(shí)現(xiàn)自動(dòng)配置?

專車分析

實(shí)現(xiàn)是以Spring Boot為基礎(chǔ),需要添加如下依賴

<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后置處理器,特別注意,如果項(xiàng)目中使用到了事務(wù)特性,就不需要重復(fù)定義

/**
 * 一定要聲明InfrastructureAdvisorAutoProxyCreator,用于實(shí)現(xiàn)bean的后置處理
 *
 * @return
 */
@Bean
public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
    return new InfrastructureAdvisorAutoProxyCreator();
}

定義切面

public class BeanFactorySystemLogAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    /**
    * 定義切點(diǎn)
    */
    private final SystemLogPointcut point = new SystemLogPointcut();

    @Override
    public Pointcut getPointcut() {
        return this.point;
    }
}

定義切點(diǎn)

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;
    }
}

自動(dòng)配置

@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,用于實(shí)現(xiàn)bean的后置處理
    *
    * @return
    */
    @Bean
    public InfrastructureAdvisorAutoProxyCreator infrastructureAdvisorAutoProxyCreator() {
        return new InfrastructureAdvisorAutoProxyCreator();
    }
}

定義注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLog {
}

專車集成業(yè)務(wù)

定義控制器

@RestController
public class SystemLogController {

    @Autowired
    private SystemLogService systemLogService;

    @GetMapping("/log")
    public String hello(@RequestParam("name") String name) throws InterruptedException {
        return systemLogService.log(name);
    }
}

定義業(yè)務(wù)方法

@Slf4j
@Service
public class SystemLogService {

    @SystemLog
    public String log(String name) throws InterruptedException {
        log.info("執(zhí)行業(yè)務(wù)方法");
        TimeUnit.SECONDS.sleep(1);
        return "hello " + name;
    }
}

定義啟動(dòng)類

@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  : 執(zhí)行業(yè)務(wù)方法2019-08-23 11:13:37.038  INFO 23227 --- [nio-8080-exec-1] c.b.example.config.SystemLogInterceptor  : ======[method execute time:1004]======

可以看到通過模擬@Transaction注解的實(shí)現(xiàn)方式,完成了日志切面功能。

專車總結(jié)

  • 首先我們需要定義一個(gè)Bean后置處理器,用于攔截處理Bean
  • 然后定義切面,在切面中定義切點(diǎn)
  • 切點(diǎn)中實(shí)現(xiàn)切入的邏輯,比如此處我們的實(shí)現(xiàn)邏輯就是查找類或方法上是否含有@SystemLog注解
  • 定義通知,完成代理工作
  • 自動(dòng)裝配,將我們的切面、通知、Bean后置處理器聲明在配置類中
  • 集成業(yè)務(wù)

專車回顧

回顧下開頭的五個(gè)問題:

  • 如何利用后置處理器對Bean進(jìn)行攔截并處理?直接在配置類中聲明后置處理器
  • 如何定義切面?繼承AbstractBeanFactoryPointcutAdvisor,并在配置類中中聲明
  • 如何定義切點(diǎn)?繼承StaticMethodMatcherPointcut,實(shí)現(xiàn)matches方法
  • 如何定義通知?實(shí)現(xiàn)MethodInterceptor接口,實(shí)現(xiàn)invoke方法
  • 如何實(shí)現(xiàn)自動(dòng)配置?自定義配置類,聲明所有需要加入容器的Bean

最后

師長,專注分享Java進(jìn)階、架構(gòu)技術(shù)、高并發(fā)、微服務(wù)、BAT面試、redis專題、JVM調(diào)優(yōu)、Springboot源碼、mysql優(yōu)化等20大進(jìn)階架構(gòu)專題。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI