Spring的cache实现一

背景

上篇我们说明了事务实现基本要素aop等等Spring的事务实现一

继续描述一下事务引入的几种方式

方式

注解

spring3.1之后引入了新的注解EnableTransactionManagement

为了不维护太多的xml Spring从3.1版本组件增加java的配置类

而EnableTransactionManagement就是其中一个开启事务的注解

SpringBoot是注解的集大成者

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
 
   /**
    * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
    * opposed to standard Java interface-based proxies ({@code false}). The default is
    * {@code false}. <strong>Applicable only if {@link #mode()} is set to
    * {@link AdviceMode#PROXY}</strong>.
    * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
    * Spring-managed beans requiring proxying, not just those marked with
    * {@code @Transactional}. For example, other beans marked with Spring's
    * {@code @Async} annotation will be upgraded to subclass proxying at the same
    * time. This approach has no negative impact in practice unless one is explicitly
    * expecting one type of proxy vs another, e.g. in tests.
    */
   boolean proxyTargetClass() default false;
 
   /**
    * Indicate how transactional advice should be applied. The default is
    * {@link AdviceMode#PROXY}.
    * @see AdviceMode
    */
   AdviceMode mode() default AdviceMode.PROXY;
 
   /**
    * Indicate the ordering of the execution of the transaction advisor
    * when multiple advices are applied at a specific joinpoint.
    * The default is {@link Ordered#LOWEST_PRECEDENCE}.
    */
   int order() default Ordered.LOWEST_PRECEDENCE;
 
}

一旦使用了该注解很明显会引入TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
 
   /**
    * {@inheritDoc}
    * @return {@link ProxyTransactionManagementConfiguration} or
    * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
    * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
    */
   @Override
   protected String[] selectImports(AdviceMode adviceMode) {
      switch (adviceMode) {
         case PROXY:
            return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
         case ASPECTJ:
            return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
         default:
            return null;
      }
   }
 
}

通常来说我们都是使用proxy 而使用aspectj相对来说比较麻烦需要在编译期支持

那么就会引入ProxyTransactionManagementConfiguration

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
 
   @Bean(name=TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
      BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
      advisor.setTransactionAttributeSource(transactionAttributeSource());
      advisor.setAdvice(transactionInterceptor());
      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() {
      TransactionInterceptor interceptor = new TransactionInterceptor();
      interceptor.setTransactionAttributeSource(transactionAttributeSource());
      if (this.txManager != null) {
         interceptor.setTransactionManager(this.txManager);
      }
      return interceptor;
   }
 
}

从这边可以知道advisor使用的是BeanFactoryTransactionAttributeSourceAdvisor===》this.enableTx.getNumber(“order”) 这个enableTX是从EnableTransactionManagement读取的order 默认为最低顺序?【思考是为什么】

默认情况下使用的是注解支持的attributeSource 事务注解与tx:attributes共存

同时我们使用真正的业务实现在此【事务切面】TransactionInterceptor

advisor可以告诉我们是如何进行拦截的

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
 
   private TransactionAttributeSource transactionAttributeSource;
 
   private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
      @Override
      protected TransactionAttributeSource getTransactionAttributeSource() {
         return transactionAttributeSource;
      }
   };
 
 
   /**
    * Set the transaction attribute source which is used to find transaction
    * attributes. This should usually be identical to the source reference
    * set on the transaction interceptor itself.
    * @see TransactionInterceptor#setTransactionAttributeSource
    */
   public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
      this.transactionAttributeSource = transactionAttributeSource;
   }
 
   /**
    * Set the {@link ClassFilter} to use for this pointcut.
    * Default is {@link ClassFilter#TRUE}.
    */
   public void setClassFilter(ClassFilter classFilter) {
      this.pointcut.setClassFilter(classFilter);
   }
 
   public Pointcut getPointcut() {
      return this.pointcut;
   }
 
}

这就是我们使用的pointCut可以看到这边的attributeSource是从传入的===》此处是AnnotationTransactionAttributeSource

XML

虽然Spring提供了注解形式开启事务的方式,但是还有好多小伙伴会使用xml方式

<aop:config proxy-target-class="true">
    <aop:pointcut id="serviceMethod"
                  expression=" execution(public *  com.air.tqb.service..*(..)) and !execution(public * com.air.tqb.service.report..*(..)) and !execution(public * com.air.tqb.service.saiku..*(..))"/>
    <aop:advisor order="1" pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="addBranch" read-only="false" rollback-for="java.lang.Exception"
                   timeout="100"/>
        <tx:method name="load*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="find*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="is*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="has*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="check*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="get*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="select*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="list*" read-only="true" propagation="SUPPORTS"
                   timeout="50"/>
        <tx:method name="loginCheck" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="selectDiff*" read-only="true" propagation="SUPPORTS"
                   timeout="120"/>
        <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"
                   timeout="50"/>
    </tx:attributes>
</tx:advice>

这是一种方式用来提供NameMatch

还有一种注解形式的事务注解与tx:attributes共存【这个注解是指具体使用什么事务】

通常来说我们使用

tx:advice

但是使用advice是远远不够的 这个advice需要和pointCut集合起来组成advisor

/**
 * Create a {@link RootBeanDefinition} for the advisor described in the supplied. Does <strong>not</strong>
 * parse any associated '{@code pointcut}' or '{@code pointcut-ref}' attributes.
 */
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
   RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
   advisorDefinition.setSource(parserContext.extractSource(advisorElement));
 
   String adviceRef = advisorElement.getAttribute(ADVICE_REF);
   if (!StringUtils.hasText(adviceRef)) {
      parserContext.getReaderContext().error(
            "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
   }
   else {
      advisorDefinition.getPropertyValues().add(
            ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
   }
 
   if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
      advisorDefinition.getPropertyValues().add(
            ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
   }
 
   return advisorDefinition;
}

很明显 我们使用的就是DefaultBeanFactoryPointcutAdvisor

public class DefaultBeanFactoryPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {

   private Pointcut pointcut = Pointcut.TRUE;


   /**
    * Specify the pointcut targeting the advice.
    * <p>Default is {@code Pointcut.TRUE}.
    * @see #setAdviceBeanName
    */
   public void setPointcut(Pointcut pointcut) {
      this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
   }

   public Pointcut getPointcut() {
      return this.pointcut;
   }


   @Override
   public String toString() {
      return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice bean '" + getAdviceBeanName() + "'";
   }

}

此处可以看到如果不设置PointCut那么使用的就是PointCut.True

class TruePointcut implements Pointcut, Serializable {

   public static final TruePointcut INSTANCE = new TruePointcut();

   /**
    * Enforce Singleton pattern.
    */
   private TruePointcut() {
   }

   public ClassFilter getClassFilter() {
      return ClassFilter.TRUE;
   }

   public MethodMatcher getMethodMatcher() {
      return MethodMatcher.TRUE;
   }

   /**
    * Required to support serialization. Replaces with canonical
    * instance on deserialization, protecting Singleton pattern.
    * Alternative to overriding {@code equals()}.
    */
   private Object readResolve() {
      return INSTANCE;
   }

   @Override
   public String toString() {
      return "Pointcut.TRUE";
   }

}

换言之将所有情况下都会proxy

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java开发

SpringBoot集成Druid不支持多条SQL

2173
来自专栏流柯技术学院

CentOS升级Python2.7导致使用pip等命令安装模块失败

出现这个问题是因为:虽然已经把Python升级到了2.7版本,但是pip仍然是原来的版本,仍在原来python的site-package里面

1413
来自专栏一枝花算不算浪漫

SpringBoot自定义序列化的使用方式--WebMvcConfigurationSupport

1451
来自专栏服务端技术杂谈

dubbo源码学习笔记----registry

注册工厂 public interface RegistryFactory { Registry getRegistry(URL url); } ...

2484
来自专栏码匠的流水账

聊聊resilience4j的CircuitBreakerConfig

本文主要研究一下resilience4j的CircuitBreakerConfig

2392
来自专栏java、Spring、技术分享

Spring Import 三种用法与源码解读

  最近在看Spring Cloud相关的源码,每次引入一个新的starter,发现都会加一些enable的注解,比如:@EnableDiscoveryClie...

2284
来自专栏菩提树下的杨过

JAVA CDI 学习(5) - 如何向RESTFul Service中注入EJB实例

RESTFul Service中如果要注入EJB实例,常规的@Inject将不起作用,在Jboss中,应用甚至都启动不起来(因为@Inject注入失败),解决方...

2217
来自专栏一枝花算不算浪漫

[Spring框架]Spring JDBCTmplate基础入门总结.

3408
来自专栏技术小黑屋

Android中一个简单有用的发现性能问题的方法

在Android中,性能优化是我们持之不懈的工作。这其中,在主线程执行耗时的任务,可能会导致界面卡顿,甚至是ANR(程序未响应)。当然Android提供了很多优...

931
来自专栏Android知识点总结

SpringBoot-15-之整合MyBatis-注解篇+分页

1.2K1

扫码关注云+社区