前言

如果这是你第二次看到师长,说明你在觊觎我的美色!

点赞+关注再看,养成习惯

没别的意思,就是需要你的窥屏_

专车介绍

该趟专车是开往基于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 {
}

专车集成业务

定义控制器

@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

最后

师长,【java进阶架构师】号主,短短一年在各大平台斩获15W+程序员关注,专注分享Java进阶、架构技术、高并发、微服务、BAT面试、redis专题、JVM调优、Springboot源码、mysql优化等20大进阶架构专题,关注【java进阶架构师】回复【架构】领取2019架构师完整视频一套。

转载说明:请务必注明来源(本文首发于公众号:【java进阶架构师】)

【原创】003 | 搭上基于SpringBoot事务思想实战专车的更多相关文章

  1. 原创001 | 搭上SpringBoot自动注入源码分析专车

    前言 如果这是你第二次看到师长的文章,说明你在觊觎我的美色!O(∩_∩)O哈哈~ 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 本系列为SpringBoot深度源码专车系列,第一篇发车 ...

  2. 【原创】004 | 搭上SpringBoot事务诡异事件分析专车

    前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 本专车系列文章 目前连载到第四篇,本专题是深入讲解Springboot源码,毕竟是 ...

  3. 【原创】005 | 搭上SpringBoot请求处理源码分析专车

    前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot请求处理源码分析专车,主要用来分析S ...

  4. 基于springboot构建dubbo的入门demo

    之前记录了构建dubbo入门demo所需的环境以及基于普通maven项目构建dubbo的入门案例,今天记录在这些的基础上基于springboot来构建dubbo的入门demo:众所周知,springb ...

  5. 【原创】002 | 搭上SpringBoot事务源码分析专车

    前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍** 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为 ...

  6. 基于SpringBoot+SSM实现的Dota2资料库智能管理平台

    Dota2资料库智能管理平台的设计与实现 摘    要 当今社会,游戏产业蓬勃发展,如PC端的绝地求生.坦克世界.英雄联盟,再到移动端的王者荣耀.荒野行动的火爆.都离不开科学的游戏管理系统,游戏管理系 ...

  7. 基于spring-boot的应用程序的单元测试方案

    概述 本文主要介绍如何对基于spring-boot的web应用编写单元测试.集成测试的代码. 此类应用的架构图一般如下所示: 我们项目的程序,对应到上图中的web应用部分.这部分一般分为Control ...

  8. idea基于springboot搭建ssm(maven)

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/liboyang71/article/det ...

  9. MyBatis 进阶,MyBatis-Plus!(基于 Springboot 演示)

    这一篇从一个入门的基本体验介绍,再到对于 CRUD 的一个详细介绍,在介绍过程中将涉及到的一些问题,例如逐渐策略,自动填充,乐观锁等内容说了一下,只选了一些重要的内容,还有一些没提及到,具体可以参考官 ...

随机推荐

  1. 计划任务at和crontab

    目标:会看,会写计划任务时间,会制定计划任务 一次性:at yum -y install at #安装at systemctl start atd #启动at服务 systemctl enable a ...

  2. Ansible之常用模块(一)

    ansible之所以功能强大,不是ansible本身,是因为它有众多的模块,前文我们介绍了ansible的基础介绍,系列命令的用法以及选项的说明,通过前文的学习我们知道了ansible是基于pytho ...

  3. Redis实战--Jedis实现分布式锁

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 分布式 ...

  4. [LC]66题 Plus One (加1)

    ①英文题目 Given a non-empty array of digits representing a non-negative integer, plus one to the integer ...

  5. 全网阅读过20k的Java集合框架常见面试题总结!

    本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...

  6. JavaWeb核心知识点

    一:HTTP协议     一.概述 1. 概念:超文本传输协议 2. 作用:规范了客户端(浏览器)和服务器的数据交互格式 3. 特点 1. 简单快速:客户端向服务器请求服务时,仅通过键值对来传输请求方 ...

  7. A Lot of Games(Trie树 + 博弈)

    题目链接:http://codeforces.com/contest/455/problem/B 题意:n, k 分别表示 字符串组数 和 比赛次数.  从一个空单词开始, a,b二人分别轮流往单词后 ...

  8. 0xe7f001f0!?NDK调试过程,无故抛出SIGSEGV。

    arm调试过程,如果抛一个SIGSEGV,地址在 0xe7f001f0 附近,原因居然是因为我在调试.当我使用n指令跳到下一行代码时,往往变成了continue指令一样地执行.还不确定地抛出SIGSE ...

  9. drf组件之jwt认证

    drf组件之jwt认证模块 一.认证规则 全称:json web token 解释:加密字符串的原始数据是json,后台产生,通过web传输给前台存储 格式:三段式 - 头.载荷.签名 - 头和载荷才 ...

  10. python3 之 迭代器与生成器

    迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束. 迭代器只能往前不会后 ...