【原创】003 | 搭上基于SpringBoot事务思想实战专车
程序员文章站
2022-09-27 16:11:32
前言 如果这是你 第二次 看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往基于Spring Boot事务思想实战的专车,在上一篇 搭上SpringBoot事务源码分析专车 [1]中我们详细介绍了Spring Boot事务实现的原 ......
前言
如果这是你第二次看到师长,说明你在觊觎我的美色!
点赞+关注再看,养成习惯
没别的意思,就是需要你的窥屏^_^
专车介绍
该趟专车是开往基于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进阶架构师】)