spring-tx源码分析(1)_Import注解

本文介绍一下EnableTransactionManagement和Import注解,还会编写一个Import示例程序,为后续的spring-tx源码分析做基础。

在分析spring-tx之前,我们先介绍一下@Import注解,因为@EnableTransactionManagement注解使用到了这个注解。

@EnableTransactionManagement注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}

作用:启用Spring的注解驱动事务管理功能。

API文档

Enables Spring’s annotation-driven transaction management capability, similar to the support found in Spring’s <tx:*> XML namespace. To be used on @Configuration classes to configure traditional, imperative transaction management or reactive transaction management.

The following example demonstrates imperative transaction management using a PlatformTransactionManager. For reactive transaction management, configure a ReactiveTransactionManager instead.

启用Spring的注解驱动事务管理功能,类似于Spring的<tx:*>XML命名空间。用于@Configuration类以配置传统的命令式事务管理器或响应式事务管理器。

以下示例演示了使用PlatformTransactionManager的命令式事务管理器。对于响应式事务管理器,请改为配置ReactiveTransactionManager。

@Configuration
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public FooRepository fooRepository() {
        // configure and return a class having @Transactional methods
        return new JdbcFooRepository(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // configure and return the necessary JDBC DataSource
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

For reference, the example above can be compared to the following Spring XML configuration:

<beans>
    <tx:annotation-driven/>
    <bean id="fooRepository" class="com.foo.JdbcFooRepository">
        <constructor-arg ref="dataSource"/>
    </bean>
    <bean id="dataSource" class="com.vendor.VendorDataSource"/>
    <bean id="transactionManager" class="org.sfwk...DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>
</beans>

In both of the scenarios above, @EnableTransactionManagement and <tx:annotation-driven/> are responsible for registering the necessary Spring components that power annotation-driven transaction management, such as the TransactionInterceptor and the proxy- or AspectJ-based advice that weaves the interceptor into the call stack when JdbcFooRepository’s @Transactional methods are invoked.

(不翻译了)

A minor difference between the two examples lies in the naming of the TransactionManager bean: In the @Bean case, the name is “txManager” (per the name of the method); in the XML case, the name is “transactionManager”. <tx:annotation-driven/> is hard-wired to look for a bean named “transactionManager” by default, however @EnableTransactionManagement is more flexible; it will fall back to a by-type lookup for any TransactionManager bean in the container. Thus the name can be “txManager”, “transactionManager”, or “tm”: it simply does not matter.

(不翻译了)

For those that wish to establish a more direct relationship between @EnableTransactionManagement and the exact transaction manager bean to be used, the TransactionManagementConfigurer callback interface may be implemented - notice the implements clause and the @Override-annotated method below:

@Configuration
@EnableTransactionManagement
public class AppConfig implements TransactionManagementConfigurer {

    @Bean
    public FooRepository fooRepository() {
        // configure and return a class having @Transactional methods
        return new JdbcFooRepository(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // configure and return the necessary JDBC DataSource
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager();
    }
}

This approach may be desirable simply because it is more explicit, or it may be necessary in order to distinguish between two TransactionManager beans present in the same container. As the name suggests, the annotationDrivenTransactionManager() will be the one used for processing @Transactional methods. See TransactionManagementConfigurer Javadoc for further details.

这种方法可能是可取的,因为它更明确、或者为了区分同一容器中存在的两个TransactionManager bean可能是必要的。 顾名思义annotationDrivenTransactionManager()将用于处理@Transactional方法。

The mode() attribute controls how advice is applied: If the mode is AdviceMode.PROXY (the default), then the other attributes control the behavior of the proxying. Please note that proxy mode allows for interception of calls through the proxy only; local calls within the same class cannot get intercepted that way.

mode()属性控制如何应用建议:如果模式是AdviceMode.PROXY(默认),则其他属性控制代理的行为。请注意,代理模式只允许通过代理拦截调用; 不能以这种方式拦截同一类中的本地调用。

Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath, with compile-time weaving or load-time weaving applying the aspect to the affected classes. There is no proxy involved in such a scenario; local calls will be intercepted as well.

@Transactional注解

@Import注解

标注在@Configuration类上面,导入的一个或多个组件类,可以是@Configuration类、ImportSelector和ImportBeanDefinitionRegistrar实现。

API文档

Indicates one or more component classes to import — typically @Configuration classes.

指示要导入的一个或多个组件类 - 通常是@Configuration类。

Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register(java.lang.Class<?>…)).

提供与Spring XML中的<import/>元素等效的功能。允许导入@Configuration类、ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从4.2开始;类似于AnnotationConfigApplicationContext.register(java.lang.Class<?>…))。

@Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.

在导入的@Configuration类中声明的@Bean定义应该通过使用@Autowired注入来访问。bean本身可以自动装配,或者声明bean的配置类实例可以自动装配。后一种方法允许在@Configuration类方法之间进行显式的、IDE友好的导航。

May be declared at the class level or as a meta-annotation.

If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.

ImportBeanDefinitionRegistrar接口

Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.

Along with @Configuration and ImportSelector, classes of this type may be provided to the @Import annotation (or may also be returned from an ImportSelector).

An ImportBeanDefinitionRegistrar may implement any of the following Aware interfaces, and their respective methods will be called prior to registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.beans.factory.support.BeanNameGenerator):

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

Alternatively, the class may provide a single constructor with one or more of the following supported parameter types:

  • Environment
  • BeanFactory
  • ClassLoader
  • ResourceLoader

ImportSelector接口

Interface to be implemented by types that determine which @Configuration class(es) should be imported based on a given selection criteria, usually one or more annotation attributes.

An ImportSelector may implement any of the following Aware interfaces, and their respective methods will be called prior to selectImports(org.springframework.core.type.AnnotationMetadata):

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

Alternatively, the class may provide a single constructor with one or more of the following supported parameter types:

  • Environment
  • BeanFactory
  • ClassLoader
  • ResourceLoader

ImportSelector implementations are usually processed in the same way as regular @Import annotations, however, it is also possible to defer selection of imports until all @Configuration classes have been processed (see DeferredImportSelector for details).

@Import注解示例

我们写一个示例说明一下这个注解的使用方式。

示例需求

使用通知方式打印业务方法的入参、返回值以及执行时长等日志。

  1. 通过一个注解标注在业务方法上,表示该业务方法需要打印日志
  2. 可以通过一个开关注解控制全局日志的开启/关闭

两个注解

给用户提供两个注解:

  • @ServiceLog注解:标注在需要打印业务日志的目标方法上
  • @EnableServiceLog注解:标注在Configuration类上面,控制全局日志的开启或关闭

@EnableServiceLog注解的定义:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 会导入ServiceLogConfigurationSelector.selectImports方法返回的组件
@Import(ServiceLogConfigurationSelector.class)
public @interface EnableServiceLog {

}

用户只需要使用这两个注解即可控制日志的打印,其余的框架细节都不需要关心。

ServiceLogConfigurationSelector类

负责导入配置类:

public class ServiceLogConfigurationSelector implements ImportSelector {

  @Override
  public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[]{
        AutoProxyRegistrar.class.getName(),
        ServiceLogConfiguration.class.getName()
    };
  }
}

ServiceLogConfiguration类

就是一个普通的Configuration类:

@Configuration
public class ServiceLogConfiguration {

  @Bean
  // 标识为框架内部组件
  // 这里参考了ProxyTransactionManagementConfiguration类
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public ServiceLogAdviser serviceLogAdviser() {
    return new ServiceLogAdviser();
  }
}

ServiceLogAdviser类

APO通知:

public class ServiceLogAdviser extends AbstractPointcutAdvisor {

  @Override
  public Pointcut getPointcut() {
    // 这个就是切面
    return new ServiceLogPointcut();
  }

  @Override
  public Advice getAdvice() {
    // 这个就是拦截到目标方法之后执行的方法
    return new ServiceLogInterceptor();
  }
}

我们的示例只是为了说明相关组件的作用,所以内部实现没有设计的很复杂。

ServiceLogPointcut类

public class ServiceLogPointcut extends StaticMethodMatcherPointcut {

  @Override
  public boolean matches(Method method, Class<?> targetClass) {
    // 判断一下目标方法是否标注了@ServiceLog注解
    return method.getAnnotation(ServiceLog.class) != null;
  }
}

ServiceLogInterceptor类

public class ServiceLogInterceptor implements MethodInterceptor {

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    // 前置日志
    System.out.println(String.format("Service log before: %s", invocation.getMethod().toString()));
    Object result = invocation.proceed();
    // 后置日志
    System.out.println(String.format("Service log after: %s", invocation.getMethod().toString()));
    return result;
  }
}

如何使用

使用@EnableServiceLog注解开启全局日志功能

使用@ServiceLog注解开启方法日志功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值