h

hitechr

V1

2022/08/30阅读:15主题:极客黑

Spring的事务管理

基于AOP实现一个自己的事务

假如有这样一个需求,在spring中实现@Hitx的事务支持。

AOP的代理过程

AOP会用到AnnotationAwareAspectJAutoProxyCreator类去解析方法上的注解,然后生成AspectJExpressionPointcut和方法生成Advice,然后再有Advice生成一个Advisor,在创建对象的过程中,对实例进行生成代理,添加回调。

分析

SpringAOP的三要素: Advicie、Pointcut、Advisor。

  1. Advice: 定义拦截到方法后要执行的逻辑
  2. Pointcut:拦截方法的表达式定义
  3. Advisor:spring中aop统一对外提供的增强器

有了这三要素后,还会往spring容器中注册代理类的生成器,可以使用AopConfigUtils这个工具类来注册。

实现步骤

定义Hitx注解

首先定义一个自定义的事务注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Hitx {}

定义Advice

然后定义自己的一个Advice通知器,用于在拦截到service方法时进行事务的开启、回滚、提交的过程;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class HitxMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //TODO 待完善
        return null;
    }
}

定义Pointcut

有了Advice后就得开始定义一个切面拦截,这个类实现了PointcutClassFilterMethodMatcher,也就是说这个实例对象即可以判断类是否满足条件、也可以判断某个方法是否满足条件。

public class HitxPointcut implements Pointcut,ClassFilter,MethodMatcher {
    @Override
    public ClassFilter getClassFilter() {
        return this;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return this;
    }
    /**
     * 当前类是否匹配
     */

    @Override
    public boolean matches(Class<?> clazz) {
        for (Method method : clazz.getMethods()) {
            Hitx hitx = AnnotationUtils.findAnnotation(method, Hitx.class);
            if (hitx!=null) {
                return true;
            }
        }
        return false;
    }
    /**
     * 当前方法是否匹配
     * @return
     */

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        Hitx hitx = AnnotationUtils.findAnnotation(method, Hitx.class);
        if (hitx!=null) {
            return true;
        }
        return false;
    }
    @Override
    public boolean isRuntime() {
        return true;
    }
    /**
     * 当前类,方法,参数 是否匹配
     * @return
     */

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        Hitx hitx = AnnotationUtils.findAnnotation(method, Hitx.class);
        if (hitx!=null) {
            return true;
        }
        return false;
    }
}

定义Advisor

开始定义Advisor对象

public class HitxAdvisor extends AbstractPointcutAdvisor {
    Advice advice;
    Pointcut pointcut;
 //从外部定义时获取
    public HitxAdvisor(Advice advice, Pointcut pointcut) {
        this.advice = advice;
        this.pointcut = pointcut;
    }
 //返回pointcut对象
    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }
 //返回advice对象
    @Override
    public Advice getAdvice() {
        return this.advice;
    }
    //上面的这两个方法都是AbstractPointcutAdvisor中的抽象方法
}

然后把我们的三要素定义在配置中

@Configuration
public class HitxConfiguration {
    @Bean
    public Pointcut pointcut(){
        return new HitxPointcut();
    }
    @Bean
    public MethodInterceptor methodInterceptor(){
        return new HitxMethodInterceptor();
    }
    @Bean
    public Advisor advisor(Pointcut pointcut,MethodInterceptor methodInterceptor){
        return new HitxAdvisor(methodInterceptor,pointcut);
    }
}

开启自动代理

定义一个BeanFactoryProcessor来往spring窗口中注册一个扩展器,只需要在配置类中引入EnableAspectJAutoProxy注解。

@Configuration
@EnableAspectJAutoProxy
public class HitxConfiguration {
    @Bean
    public Pointcut pointcut(){
        return new HitxPointcut();
    }
    @Bean
    public MethodInterceptor methodInterceptor(DataSource dataSource){
        return new HitxMethodInterceptor(dataSource);
    }
    @Bean
    public Advisor advisor(Pointcut pointcut,MethodInterceptor methodInterceptor){
        return new HitxAdvisor(methodInterceptor,pointcut);
    }
}

完善Advice

然后再扩展下HitxMethodInterceptor,在这个实现里我们从连接池中拿到连接,放到线程上下文中,业务类中同样从线程上下文中获取,用完后由拦截器类统一处理。

public class HitxMethodInterceptor implements MethodInterceptor {
    DataSource dataSource;

    public HitxMethodInterceptor(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Connection connection = dataSource.getConnection();
        try {
            System.out.println("事务开启");
            connection.setAutoCommit(false);
            ThreadLocalContext.put(connection);
            return mi.proceed();
        } catch (Throwable e) {
            System.out.println("事务回退");
            connection.rollback();
            connection.close();
        }finally {
            ThreadLocalContext.remove();
            if (!connection.isClosed()) {
                System.out.println("事务提交");
                connection.commit();
                connection.close();
            }
        }
        return null;
    }
}

因为spring的aop调用是基于链式调用的,当处理到自定义的拦截器类时,只需要调回链上即可mi.proceed()

线程上下文工具类

public class ThreadLocalContext {
    public static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
    public static void put(Connection connection){
        threadLocal.set(connection);
    }
    public static Connection get(){
        return threadLocal.get();
    }
    public static void remove() {
        threadLocal.remove();
    }
}

业务代码

service

@Component
public class BookService {
    @Hitx
    public void save(){
        Connection connection = ThreadLocalContext.get();
        try {
            String s = "insert INTO `book`(`name`, `price`, `reserve`, `version`) VALUES ('设计模式', 12, 10, 0)";
            PreparedStatement preparedStatement = connection.prepareStatement(s);
            preparedStatement.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Hitx
    public void saveRollback(){
        Connection connection = ThreadLocalContext.get();
        try {
            String s = "insert INTO `book`(`name`, `price`, `reserve`, `version`) VALUES ('设计模式', 12, 10, 0)";
            PreparedStatement preparedStatement = connection.prepareStatement(s);
            preparedStatement.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        System.out.println(1/0);
    }
}

配置连接池

@Configurable
@Component
public class TXConfiguration {
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("*****");
        dataSource.setPassword("*****");
        dataSource.setUrl("jdbc:mysql://******.rds.aliyuncs.com:3306/books");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setDefaultAutoCommit(false);
        return dataSource;
    }
}

测试

    @Test
    public void testInsert(){
        String basepackage="org.hitechr.spring.transaction";
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(basepackage);
        BookService bookService = (BookService) context.getBean("bookService");
        bookService.save();
        bookService.saveRollback();
    }

事务管理器

[地址](https://www.yuque.com/docs/share/95d47f4b-e5a6-458b-a0c3-01eb087e9eaf?# 《spring事务准备过程》)

当然,在实际的开发过程中,我们并不会这样自己去实现这么多的东西,只需要在项目的配置类中引入@EnableTransactionManager即可实现事务的自动开启,spring会帮我们把上面的所有步骤都给做完。

准备阶段

此阶段主要是定义Advisor、MethodInterceptor、TransactionAttributeSource。

首先引入@EnableTransactionManager,其实是导入TransactionManagementConfigurationSelector,它是一个实现了ImportSelector的类,所以再解析TransactionMangementConfigurationSelector时调用其getImports方法,返回两个配置类:AutoProxyRegistrarProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration中对AdvisorTransactionAttributeSourceTransactionInterceptor进行了定义。

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
 @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
   TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor)
 
{

  BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
  advisor.setTransactionAttributeSource(transactionAttributeSource);
  advisor.setAdvice(transactionInterceptor);
  if (this.enableTx != null) {
   advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
  }
  return advisor;
 }

 @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public TransactionAttributeSource transactionAttributeSource() {
  return new AnnotationTransactionAttributeSource();
 }
    //
 @Bean
 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
  TransactionInterceptor interceptor = new TransactionInterceptor();
  interceptor.setTransactionAttributeSource(transactionAttributeSource);
  if (this.txManager != null) {
   interceptor.setTransactionManager(this.txManager);
  }
  return interceptor;
 }
}

TransactionInterceptor: 定义一个TransactionInterceptor,其实就是MethodInterceptor的实现类,与我们自己定义的HitxMethodInterceptor的功能是一样的,在这个类中对事务进行开启、回退等操作。

BeanFactoryTransactionAttributeSourceAdvisor: 与我们定义的HitxAdvisor是一样的。

就剩一个TransactionAttributeSource我们之前没有去定义,TransactionAttributeSource定义后发现在Advisor中有使用,

我们是直接定义了Pointcut这么一个类,而在spring中他是通过TransactionAttributeSource来间接定义Pointcut的。

实例化过程

  1. 首先实例化的是代理的创建对象InfrastructAdvisorAutoProxyCreator,在spring启动过程中的registerBeanPostProcessors的方法里。
  1. 然后开始创建所有的单例对象,调用初始化方法里对新创建的对象进行代理的包装,当第一次调用InfrastructAdvisorAutoProxyCreator的postProcessorAfterInstation方法时对Advisor对象进行了创建。
  1. 所有实例化后的对象都要经过代理类的一个包装过程,在这个包装过程中会从容器中去获取所有的Advisor对象,然后再筛选出适合的Advisor。
  1. 在获取Advisor对象时,因为第一次调用所有的对象还没有被创建,所以会先去创建Advisor对象(BeanFactoryTransactionAttributeSouceAdvisor),因为这个对象依赖TransactionAttributeSourceTransactionInterceptor所以得先去创建这两个对象。
  1. BeanFactoryTransactionAttributeSourceAdvisor对象创建完后,还要调用findAdvisorsThatCanApply方法,筛选出适合的Advisor集合

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
      //判断当前类是否满足条件
      if (!pc.getClassFilter().matches(targetClass)) {
       return false;
      }
      //判断方法是否为MethodMatcher.TRUE
      MethodMatcher methodMatcher = pc.getMethodMatcher();
      if (methodMatcher == MethodMatcher.TRUE) {
       return true;
      }

      IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
      if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
       introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
      }
      //获取类上所有的接口
      Set<Class<?>> classes = new LinkedHashSet<>();
      if (!Proxy.isProxyClass(targetClass)) {
       classes.add(ClassUtils.getUserClass(targetClass));
      }
      classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
      //遍历每个接口中每个方法是否能匹配
      for (Class<?> clazz : classes) {
       Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
       for (Method method : methods) {
        if (introductionAwareMethodMatcher != null ?
          introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
          methodMatcher.matches(method, targetClass)) {
         return true;
        }
       }
      }

      return false;
     }

筛选Advisor的过程

  1. Pointcut是通过Advisor调用getPoint返回的一个内部类
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
  @Override
  @Nullable
  protected TransactionAttributeSource getTransactionAttributeSource() {
   return transactionAttributeSource;
  }
 };
public Pointcut getPointcut() {
    return this.pointcut;
}
  1. 通过pointcut获取方法匹配对象MethodMatcher,也就是当前的Pointcut,然后调用获取类的所有方法进行遍历匹配
  1. 是否能获取方法上的属性Transaction注解。先拿到TransactionAttributeSource对象,然后通过这个对象去获取方法上的注解。

    public boolean matches(Method method, Class<?> targetClass) {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }
  2. 在getTransactionAttribute中通过computeTransactionAttribute返回方法上的注解

  1. 在提取方法上属性的过程中使用提交定义好的解析器进行解析的
  1. 这个方法会去查询方法上的@Transaction注解,然后获取属性,封装为TransactionAttribute对象进行返回
  1. 拿到Advisor后就可以去创建一个代理对象了,与AOP创建代理对象的过程一样。

总结

  1. spring事务需要三要素对象BeanFactoryTransactionAttributeSourceAdvisorTransactionAttributeSourceTransactionInterceptor

  2. 还需要一个代理生成对象InfrastructureAdvisorAutoProxyCreator

  3. 在生成一个对象后,对其进行代理时,在InfrastructureAdvisorAutoProxyCreator#postprocessorAfterInitialization中首先去创建事务的三要素对象

  4. 然后在去筛选适合的Advisor集合,判断类是否满足条件,然后再去遍历类中每个方法

  5. 在对方法进行匹配时,通过pointcut获取到TransactionAttributeSource对象,通过这个对象去获取方法上的@Transaction注解信息,注解信息封装为TransactionAttribute对象,如果有对象则表示匹配,只要有一个方法匹配,当前类就满足被代理的条件。

分类:

后端

标签:

Java

作者介绍

h
hitechr
V1