前言
如果这是你第二次看到师长, 说明你在觊觎我的美色!
点赞 + 关注再看, 养成习惯
没别的意思, 就是需要你的窥屏 ^_^
专车介绍
该趟专车是开往基于 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 大进阶架构专题.
来源: http://www.bubuko.com/infodetail-3326877.html