您的位置:首页 > 编程语言 > Java开发

spring-事务源码解析(一)

2020-02-11 19:14 204 查看

今天我们来说一下spring的事务功能实现,测试环境如下:

[code]    <!-- Spring Boot jdbc 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- Mysql 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

/**
* Service层
*/
@Service
public class CityServiceImpl implements ICityService{

@Resource
private JdbcTemplate jdbcTemplate;

/**
* 注解方式添加事务
*/
@Transactional(rollbackFor=Exception.class)
@Override
public void create(Long id,String cityName) {
jdbcTemplate.update("insert into city(id,province_id,city_name) values(?,1,?)", id,cityName);
throw new IllegalArgumentException();
}
}

/**
* 单元测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class CityTests {

@Autowired
private ICityService cityService;

@Test
public void test() {
cityService.create(3L,"北京");
}
}

还记得我们在之前一文spring-boot bean初始化源码分析(二)中提到doCreateBean方法,该方法在bean实例化后会调用initializeBean方法对相关模板方法进行调用,其中一个就是触发BeanPostProcessor类的postProcessAfterInitialization方法,具体代码如下:

[code]/**
* AbstractAutowireCapableBeanFactory
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

try {
......
exposedObject = initializeBean(beanName, exposedObject, mbd);
......
}
catch (Throwable ex) {
}

return exposedObject;
}

/**
* AbstractAutowireCapableBeanFactory
*/
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
......
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

/**
* AbstractAutowireCapableBeanFactory
*/
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
// 其中有一个InfrastructureAdvisorAutoProxyCreator类,这个类就是spring对bean进行事务代理检测和代理的核心类
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

下面我们就一起分析一下InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization方法

[code]
/**
* AbstractAutoProxyCreator
*/
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

/**
* AbstractAutoProxyCreator
*/
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 如果缓存中没有,就检测是否需要代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

/**
* AbstractAutoProxyCreator
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 通过缓存判断该对象是否已经检测过,并且检测结果是不需要代理,直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 判断当前bean是否是aop相关基类(如:Pointcut/Advisor/AopInfrastructureBean),或者当前bean不需要被代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 检测当前bean对象中的方法或所在类是否有@Transactional注解
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 如果有符合条件的specificInterceptors,就对bean进行代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

/**
* AbstractAutoProxyCreator
*/
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

// 查询
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}

/**
* AbstractAutoProxyCreator
*/
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 从spring容器中查询Advisor对象
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 检测这些Advisor对象是否适用当前bean对象
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}

/**
* AbstractAutoProxyCreator
*/
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 检测这些Advisor对象是否适用当前bean对象
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}

/**
* AopUtils
*/
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<>();
for (Advisor candidate : candidateAdvisors) {
// 检测candidate是否适用当前bean
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 检测candidate是否适用当前bean
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}

/**
* AopUtils
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
// IntroductionAdvisor类型检测
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
// PointcutAdvisor类型检测
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}

/**
* AopUtils
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 如果ClassFilter不匹配,直接返回
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}

MethodMatcher methodMatcher = pc.getMethodMatcher();
// 如果methodMatcher是MethodMatcher.TRUE(一直返回true),直接返回
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}

IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}

Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
// 如果targetClass不是代理类,就获取用户class
classes.add(ClassUtils.getUserClass(targetClass));
}
// 获取targetClass所有接口
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

for (Class<?> clazz : classes) {
// 获取该类(或接口)所有方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
// 如果这个方法或类测试通过,就返回true
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}

return false;
}

/**
* BeanFactoryTransactionAttributeSourceAdvisor类中继承了TransactionAttributeSourcePointcut类的内部类
*/
public boolean matches(Method method, @Nullable Class<?> targetClass) {
if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
// 此处tas是AnnotationTransactionAttributeSource
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

/**
* AbstractFallbackTransactionAttributeSource
*/
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 如果该类是Object,直接返回null
if (method.getDeclaringClass() == Object.class) {
return null;
}

// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
Object cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// 如果缓存存在,并且之前检测的是不匹配,直接返回null
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
// 直接返回
return (TransactionAttribute) cached;
}
}
else {
// 检测该方法或类上是否添加了@Transactional注解
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
// 添加缓存
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
}
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}

/**
* AbstractFallbackTransactionAttributeSource
*/
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// no-public方法不允许代理
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}

// 如果该方法已经是代理类方法,就获取最原始的方法
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

// 首先查询该方法上是否有@Transactional注解
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}

// 如果方法上没有@Transactional注解,再查询所在类上面是否有@Transactional注解
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}

if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}

return null;
}

再回过头来看wrapIfNecessary方法,如果当前bean有对应的Advisor,就对bean进行代理

[code]/**
* AbstractAutoProxyCreator
*/
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

ProxyFactory proxyFactory = new ProxyFactory();
// 属性复制
proxyFactory.copyFrom(this);

// 判断当前是否用哪种方式进行代理(CGLIB/JDK)
if (!proxyFactory.isProxyTargetClass()) {
// 判断当前BeanDefinition上是否有preserveTargetClass属性,且属性值是否为true
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 判断beanClass是否实现了用户接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 对拦截器进行包装
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);

proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 通过代理工厂获取代理
return proxyFactory.getProxy(getProxyClassLoader());
}

/**
* AbstractAutoProxyCreator
*/
protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
}

/**
* 判断BeanDefinition是否有preserveTargetClass属性
* AutoProxyUtils
*/
public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
}
return false;
}

/**
* 检测代理类是否有用户接口(包括代理类本身)
*  如果有:proxyTargetClass为false
*  如果没有: proxyTargetClass为true
* ProxyProcessorSupport
*/
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
// 获取当前类及其父类上所有接口,不会获取接口上的接口
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {

// 不能是InitializingBean,DisposableBean这些配置回调接口
// 不能是groovy.lang.GroovyObject,cglib.proxy.Factory这些内部语言接口
// 不能是空的标记接口
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
// 如果有符合条件的接口,则说明存在合适的代理接口
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// 将接口添加到proxyFactory中,注意此时的proxyTargetClass为false,这对后面选择代理方式很重要
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}

/**
* ProxyProcessorSupport
*/
protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
return (InitializingBean.class == ifc || DisposableBean.class == ifc || Closeable.class == ifc ||
AutoCloseable.class == ifc || ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}

/**
* ProxyProcessorSupport
*/
protected boolean isInternalLanguageInterface(Class<?> ifc) {
return (ifc.getName().equals("groovy.lang.GroovyObject") ||
ifc.getName().endsWith(".cglib.proxy.Factory") ||
ifc.getName().endsWith(".bytebuddy.MockAccess"));
}

/**
* ProxyFactory
*/
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

/**
* ProxyCreatorSupport
*/
protected final synchronized AopProxy createAopProxy() {
// 当AOP代理第一次被创建时,active会被设置为true
if (!this.active) {
// 设置active为true,激活代理配置
activate();
}
// 默认ProxyFactory为DefaultAopProxyFactory
return getAopProxyFactory().createAopProxy(this);
}

/**
* 选择代理方式
* 1. 代理类本身是接口,用JDK
* 2. 代理类本身是类,没有实现接口,用CGLIB
* 3. 代理类本身是类,且实现了接口,看proxyTargetClass值
* DefaultAopProxyFactory
*/
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 1. 判断当前isOptimize,默认false,不太清除
// 2.判断proxyTargetClass,这个比较重要
// 3.代理类接口不存在,或只有一个继承了SpringProxy的接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果targetClass是一个接口,或已经被Proxy代理过
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

到这里就已经选择了具体的代理方式(CGLIB或JDK),后面就是调用各自的getProxy方法去创建代理了,最后我们再思考一个问题:InfrastructureAdvisorAutoProxyCreator和BeanFactoryTransactionAttributeSourceAdvisor是在什么时候注入的呢?答案就在下面:

[code]
/**
* 这是一个事务相关的配置类,该类是通过spring-boot-autoconfigure-xxx.jar下面"META-INF/spring-autoconfigure-metadata.properties"文件方式注入的
*/
@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public TransactionManagerCustomizers platformTransactionManagerCustomizers(
ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) {
return new TransactionManagerCustomizers(customizers.getIfAvailable());
}

@Configuration
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {

private final PlatformTransactionManager transactionManager;

public TransactionTemplateConfiguration(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}

@Bean
@ConditionalOnMissingBean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(this.transactionManager);
}
}

/**
* 当我们没有添加@EnableTransactionManagement注解时,这里会默认开启事务的
* 当我们没有配置spring.aop.proxy-target-class属性时,默认proxyTargetClass为true,默认优先适用CGLIB
*
*/
@Configuration
@ConditionalOnBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {

@Configuration
@EnableTransactionManagement(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}

@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
}

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

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// 默认是PROXY
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}

/**
* AutoProxyRegistrar
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
// 这里会注入org.springframework.aop.config.internalAutoProxyCreator=InfrastructureAdvisorAutoProxyCreator
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
// 如果proxyTargetClass为true,会在InfrastructureAdvisorAutoProxyCreator添加proxyTargetClass=true属性
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
}

/**
* 这个类会注入:
* BeanFactoryTransactionAttributeSourceAdvisor
* AnnotationTransactionAttributeSource
* TransactionInterceptor
*/
@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());
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() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}

 

  • 点赞
  • 收藏
  • 分享
  • 文章举报
yiyefuchen 发布了23 篇原创文章 · 获赞 1 · 访问量 376 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: