TransactionManager Spring 启动的动态选择
Dynamic Selection of TransactionManager Spring Boot
我正在将我现有的 Spring 应用程序转换为 Spring 启动应用程序。在我现有的应用程序中,我们需要连接到多个数据库,我们通过定义多个数据源并根据条件获取相应的 bean 来实现这一点。还使用 TransactionInterceptor 的自定义实现选择了事务管理器。
@Override
public TransactionAttributeSource getTransactionAttributeSource() {
final TransactionAttributeSource origTxAttrSource = super.getTransactionAttributeSource();
return new TransactionAttributeSource() {
@Override
public TransactionAttribute getTransactionAttribute(final Method method, final Class<?> targetClass) {
TransactionAttribute txAttr = origTxAttrSource.getTransactionAttribute(method, targetClass);
String database = (String) ThreadContext.get("database");
if (database != null && StringUtils.isNotBlank(database)) {
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setQualifier("txManager" + database);
}
}
return txAttr;
}
};
}
通过 BeanFactoryPostProcessor
我们包含了这个拦截器
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = beanFactory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
}
}
这在 Spring 4.X 中运行得非常好。
既然我们正在转向 Spring 引导,我正在尝试转换相同的方法。我可以看到正在调用 bean 工厂,但我没有发现自定义拦截器发生调用 class。这导致我的 @Transactional
失败,因为有不止一个符合条件的 bean。
关于 Spring 引导配置,我是否遗漏了什么?
(这种动态事务管理的方法是通过参考博客http://blog.tirasa.net/dynamic-springs--at-transactional.html)
最后的答案是将工厂 类 和工厂 bean 名称设置为 null,这导致事务拦截器被调用。我还没有弄清楚这如何影响拦截器调用以及这些字段中的值(它们指向 ProxyTransaction 类 因为 transactionInterceptor bean 是由它创建的)。
最终代码的形式为 -
事务拦截器Class
@Component
public class TransactionInterceptorReplacer implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory) throws BeansException {
String[] names = factory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = factory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
bd.setFactoryBeanName(null);
bd.setFactoryMethodName(null);
}
}
}
我正在将我现有的 Spring 应用程序转换为 Spring 启动应用程序。在我现有的应用程序中,我们需要连接到多个数据库,我们通过定义多个数据源并根据条件获取相应的 bean 来实现这一点。还使用 TransactionInterceptor 的自定义实现选择了事务管理器。
@Override
public TransactionAttributeSource getTransactionAttributeSource() {
final TransactionAttributeSource origTxAttrSource = super.getTransactionAttributeSource();
return new TransactionAttributeSource() {
@Override
public TransactionAttribute getTransactionAttribute(final Method method, final Class<?> targetClass) {
TransactionAttribute txAttr = origTxAttrSource.getTransactionAttribute(method, targetClass);
String database = (String) ThreadContext.get("database");
if (database != null && StringUtils.isNotBlank(database)) {
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setQualifier("txManager" + database);
}
}
return txAttr;
}
};
}
通过 BeanFactoryPostProcessor
我们包含了这个拦截器
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = beanFactory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
}
}
这在 Spring 4.X 中运行得非常好。
既然我们正在转向 Spring 引导,我正在尝试转换相同的方法。我可以看到正在调用 bean 工厂,但我没有发现自定义拦截器发生调用 class。这导致我的 @Transactional
失败,因为有不止一个符合条件的 bean。
关于 Spring 引导配置,我是否遗漏了什么?
(这种动态事务管理的方法是通过参考博客http://blog.tirasa.net/dynamic-springs--at-transactional.html)
最后的答案是将工厂 类 和工厂 bean 名称设置为 null,这导致事务拦截器被调用。我还没有弄清楚这如何影响拦截器调用以及这些字段中的值(它们指向 ProxyTransaction 类 因为 transactionInterceptor bean 是由它创建的)。
最终代码的形式为 -
事务拦截器Class
@Component
public class TransactionInterceptorReplacer implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory) throws BeansException {
String[] names = factory.getBeanNamesForType(TransactionInterceptor.class);
for (String name : names) {
BeanDefinition bd = factory.getBeanDefinition(name);
bd.setBeanClassName(MyTransactionInterceptor.class.getName());
bd.setFactoryBeanName(null);
bd.setFactoryMethodName(null);
}
}
}