【原创】003 | 搭上基于SpringBoot事务思想实战专车
前言
如果这是你第二次看到师长,说明你在觊觎我的美色!
点赞+关注再看,养成习惯
没别的意思,就是需要你的窥屏^_^
专车介绍
该趟专车是开往基于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大进阶架构专题。欢迎来稿。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
“物联网+区块链”市场研究报告出炉,竟有这么多企业已经用起来了?
物联网+区块链可以“链”动资本吗?农业、金融、工业、医疗、民生等应用场景,谁会成为区块链技术的下一个爆点? 12月4日,由物联网智库主办的「物联网+区块链在商业场景中智慧融合」主题沙龙在北京召开。来自信通院、蚂蚁金服、摩联科技、IOTA、DU Capital的几位嘉宾从不同维度探讨了区块链与物联网技术的结合。会上,物联网智库创始人彭昭发布并解读了《物链网市场研究报告》(以下简称《报告》)。 《报告》主要包含5个板块,本文带大家速读一下其中的核心要点。 物联网,是一种在互联网基础上延伸及扩展到物与物之间进行信息交换与通信的网络。根据Machina Research的数据,到2020年全球市场连接规模将达到1500亿,中国市场的连接数有望突破100亿,物联网应用进入快速增长期。 物联网产业潜力巨大,但伴随着发展,问题也相继出现。物联网当前存在设备安全、个人隐私、架构僵化、多主体协同、通信兼容、数据价值这样一些痛点: 一是物联网恶意软件Miral以IOT设备为感染目标发起DDOS攻击,形成僵尸网络,影响波及Twitter、Reddit等知名网站,造成恶劣影响;二是中央服务器管理者在未经授权的...
- 下一篇
Node.js 模块系统源码探微
Node.js 的出现使得前端工程师可以跨端工作在服务器上,当然,一个新的运行环境的诞生亦会带来新的模块、功能、抑或是思想上的革新,本文将带领读者领略 Node.js (以下简称 Node) 的模块设计思想以及剖析部分核心源码实现。 CommonJS 规范 Node 最初遵循 CommonJS 规范来实现自己的模块系统,同时做了一部分区别于规范的定制。CommonJS 规范是为了解决 JavaScript 的作用域问题而定义的模块形式,它可以使每个模块在它自身的命名空间中执行。 该规范强调模块必须通过module.exports导出对外的变量或函数,通过require()来导入其他模块的输出到当前模块作用域中,同时,遵循以下约定: 在模块中,必须暴露一个 require 变量,它是一个函数,require 函数接受一个模块标识符,require 返回外部模块的导出的 API。如果要求的模块不能被返回则 require 必须抛出一个错误。 在模块中,必须有一个自由变量叫做 exports,它是一个对象,模块在执行时可以在 exports 上挂载模块的属性。模块必须使用 exports 对...
相关文章
文章评论
共有0条评论来说两句吧...