解耦atomikos

This commit is contained in:
Dftre 2024-10-07 01:50:35 +08:00
parent ac40c0f92f
commit 85992058e2
7 changed files with 164 additions and 72 deletions

View File

@ -0,0 +1,9 @@
package com.ruoyi.common.service.datasource;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
public interface CreateDataSource {
DataSource createDataSource(String name, DataSourceProperties dataSourceProperties);
}

View File

@ -1,5 +1,8 @@
package com.ruoyi.framework.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@ -8,7 +11,9 @@ import org.springframework.transaction.jta.JtaTransactionManager;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import jakarta.annotation.PreDestroy;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
@ -18,11 +23,9 @@ import jakarta.transaction.UserTransaction;
* @author ruoyi
*/
@Configuration
public class AtomikosConfig
{
public class AtomikosConfig {
@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable
{
public UserTransaction userTransaction() throws Throwable {
UserTransaction userTransaction = new UserTransactionImp();
// 设置事务超时时间为10000毫秒
userTransaction.setTransactionTimeout(10000);
@ -30,8 +33,7 @@ public class AtomikosConfig
}
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable
{
public TransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager userTransactionManager = new UserTransactionManager();
// 设置是否强制关闭事务管理器为false
userTransactionManager.setForceShutdown(false);
@ -40,10 +42,22 @@ public class AtomikosConfig
@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable
{
public PlatformTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
private List<AtomikosDataSourceBean> atomikosDataSourceBeans = new ArrayList<>();
public List<AtomikosDataSourceBean> getAtomikosDataSourceBeans() {
return atomikosDataSourceBeans;
}
@PreDestroy
public void destroy() {
for (AtomikosDataSourceBean aDataSourceBean : this.atomikosDataSourceBeans) {
aDataSourceBean.close();
}
}
}

View File

@ -0,0 +1,48 @@
package com.ruoyi.framework.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.ruoyi.common.service.datasource.CreateDataSource;
@Component
@DependsOn({ "transactionManager" })
public class AtomikosDataSourceCreate implements CreateDataSource {
private static final Logger logger = LoggerFactory.getLogger(AtomikosDataSourceCreate.class);
@Autowired
private DynamicDataSourceProperties properties;
@Autowired
private DruidConfig druidConfig;
@Autowired
private AtomikosConfig atomikosConfig;
public DataSource createDataSource(String name, DataSourceProperties dataSourceProperties) {
Properties prop = properties.build(dataSourceProperties);
DruidXADataSource dataSource = new DruidXADataSource();
druidConfig.getDruidDataSources().add(dataSource);
dataSource.setConnectProperties(prop);
properties.setProperties(dataSource, prop);
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
atomikosConfig.getAtomikosDataSourceBeans().add(ds);
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(name);
ds.setXaProperties(prop);
ds.setXaDataSource(dataSource);
properties.validateDataSource(ds);
logger.info("数据源:{} 链接成功", name);
return ds;
}
}

View File

@ -1,10 +1,8 @@
package com.ruoyi.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -13,12 +11,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.ruoyi.framework.datasource.DynamicDataSource;
import jakarta.annotation.PreDestroy;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@ -36,18 +34,9 @@ public class DruidConfig {
@Autowired
DynamicDataSourceProperties dataSourceProperties;
Logger logger = LoggerFactory.getLogger(DruidConfig.class);
private List<DruidDataSource> druidDataSources = new ArrayList<>();
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> objectMap = new HashMap<>();
Map<String, DataSource> targetDataSources = dataSourceProperties.getTargetDataSources();
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
objectMap.put(entry.getKey(), entry.getValue());
}
return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap);
}
Logger logger = LoggerFactory.getLogger(DruidConfig.class);
/**
* 去除监控页面底部的广告
@ -91,4 +80,15 @@ public class DruidConfig {
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
public List<DruidDataSource> getDruidDataSources() {
return druidDataSources;
}
@PreDestroy
public void destroy() {
for (DruidDataSource druidDataSource : druidDataSources) {
druidDataSource.close();
}
}
}

View File

@ -4,56 +4,27 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties;
@Configuration
@DependsOn({ "transactionManager" })
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
public class DynamicDataSourceProperties implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceProperties.class);
public class DynamicDataSourceProperties {
private Map<String, DataSourceProperties> datasource;
private String primary;
private Map<String, DataSource> targetDataSources = new HashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
datasource.forEach((name, props) -> targetDataSources.put(name, createDataSource(name, props)));
}
protected DataSource createDataSource(String name, DataSourceProperties dataSourceProperties) {
Properties prop = build(dataSourceProperties);
DruidXADataSource dataSource = new DruidXADataSource();
dataSource.setConnectProperties(prop);
setProperties(dataSource, prop);
validateDataSource(dataSource);
logger.info("数据源:{} 链接成功", name);
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(name);
ds.setXaProperties(prop);
ds.setXaDataSource(dataSource);
return ds;
}
private void validateDataSource(DataSource dataSource) {
public void validateDataSource(DataSource dataSource) {
try (Connection conn = dataSource.getConnection()) {
String validationQuery = "SELECT 1";
try (Statement stmt = conn.createStatement();
@ -143,12 +114,4 @@ public class DynamicDataSourceProperties implements InitializingBean {
this.primary = primary;
}
public Map<String, DataSource> getTargetDataSources() {
return targetDataSources;
}
public void setTargetDataSources(Map<String, DataSource> targetDataSources) {
this.targetDataSources = targetDataSources;
}
}

View File

@ -11,8 +11,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import com.atomikos.util.IntraVmObjectRegistry;
import com.ruoyi.common.service.mybatis.CreateSqlSessionFactory;
import com.ruoyi.framework.datasource.DataSourceManagement;
import com.ruoyi.framework.datasource.DynamicSqlSessionTemplate;
@Configuration
@ -24,18 +24,16 @@ public class SqlSessionFactoryConfig {
@Autowired
DynamicDataSourceProperties dataSourceProperties;
@Autowired
DataSourceManagement dataSourceManagement;
@Bean(name = "sqlSessionTemplate")
public DynamicSqlSessionTemplate sqlSessionTemplate(Environment env) throws Exception {
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
Map<String, DataSource> targetDataSources = dataSourceProperties.getTargetDataSources();
Map<String, DataSource> targetDataSources = dataSourceManagement.getDataSourcesMap();
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
SqlSessionFactory sessionFactory = createSqlSessionFactory.createSqlSessionFactory(env, entry.getValue());
sqlSessionFactoryMap.put(entry.getKey(), sessionFactory);
// 应对热重载的特殊处理
Object ret = com.atomikos.icatch.config.Configuration.removeResource(entry.getKey());
if (ret != null) {
IntraVmObjectRegistry.removeResource(entry.getKey());
}
}
SqlSessionFactory factoryMaster = sqlSessionFactoryMap.get(dataSourceProperties.getPrimary());
if (factoryMaster == null) {

View File

@ -0,0 +1,60 @@
package com.ruoyi.framework.datasource;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.ruoyi.common.service.datasource.CreateDataSource;
import com.ruoyi.framework.config.DynamicDataSourceProperties;
@Component
public class DataSourceManagement implements InitializingBean {
private Map<String, DataSource> targetDataSources = new HashMap<>();
@Autowired
private DynamicDataSourceProperties dataSourceProperties;
@Autowired
private CreateDataSource c;
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> objectMap = new HashMap<>();
Map<String, DataSource> targetDataSources = this.getDataSourcesMap();
for (Map.Entry<String, DataSource> entry : targetDataSources.entrySet()) {
objectMap.put(entry.getKey(), entry.getValue());
}
return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap);
}
@Override
public void afterPropertiesSet() throws Exception {
dataSourceProperties.getDatasource()
.forEach((name, props) -> this.putDataSource(name, c.createDataSource(name, props)));
}
public DataSource getDataSource(String name) {
return targetDataSources.get(name);
}
public Collection<DataSource> getDataSources() {
return targetDataSources.values();
}
public Map<String, DataSource> getDataSourcesMap() {
return targetDataSources;
}
public void putDataSource(String name, DataSource dataSource) {
targetDataSources.put(name, dataSource);
}
}