电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

SpringBoot配置數據庫密碼加密的實現

瀏覽:12日期:2023-03-22 14:27:44

你在使用 MyBatis 的過程中,是否有想過多個數據源應該如何配置,如何去實現?出于這個好奇心,我在 Druid Wiki 的數據庫多數據源中知曉 Spring 提供了對多數據源的支持,基于 Spring 提供的 AbstractRoutingDataSource,可以自己實現數據源的切換。

一、配置動態數據源

下面就如何配置動態數據源提供一個簡單的實現:

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,代碼如下:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { @Nullable private Object defaultTargetDataSource; @Nullable private Map<Object, DataSource> resolvedDataSources; @Nullable private DataSource resolvedDefaultDataSource; @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); } protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, 'DataSource router not initialized'); // 確定當前要使用的數據源 Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException('Cannot determine target DataSource for lookup key [' + lookupKey + ']'); } return dataSource; } /** * Determine the current lookup key. This will typically be implemented to check a thread-bound transaction context. * <p> * Allows for arbitrary keys. The returned key needs to match the stored lookup key type, as resolved by the * {@link #resolveSpecifiedLookupKey} method. */ @Nullable protected abstract Object determineCurrentLookupKey(); // 省略相關代碼...}

重寫 AbstractRoutingDataSource 的 determineCurrentLookupKey() 方法,可以實現對多數據源的支持

思路:

重寫其 determineCurrentLookupKey() 方法,支持選擇不同的數據源 初始化多個 DataSource 數據源到 AbstractRoutingDataSource 的 resolvedDataSources 屬性中 然后通過 Spring AOP, 以自定義注解作為切點,根據不同的數據源的 Key 值,設置當前線程使用的數據源

接下來的實現方式是 Spring Boot 結合 Druid 配置動態數據源

(一)引入依賴

基于 3.繼承SpringBoot 中已添加的依賴再添加對AOP支持的依賴,如下:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>(二)開始實現

1. DataSourceContextHolder

DataSourceContextHolder 使用 ThreadLocal 存儲當前線程指定的數據源的 Key 值,代碼如下:

package cn.tzh.mybatis.config;import lombok.extern.slf4j.Slf4j;import java.util.HashSet;import java.util.Set;/** * @author tzh * @date 2021/1/4 11:42 */@Slf4jpublic class DataSourceContextHolder { /** * 線程本地變量 */ private static final ThreadLocal<String> DATASOURCE_KEY = new ThreadLocal<>(); /** * 配置的所有數據源的 Key 值 */ public static Set<Object> ALL_DATASOURCE_KEY = new HashSet<>(); /** * 設置當前線程的數據源的 Key * * @param dataSourceKey 數據源的 Key 值 */ public static void setDataSourceKey(String dataSourceKey) { if (ALL_DATASOURCE_KEY.contains(dataSourceKey)) { DATASOURCE_KEY.set(dataSourceKey); } else { log.warn('the datasource [{}] does not exist', dataSourceKey); } } /** * 獲取當前線程的數據源的 Key 值 * * @return 數據源的 Key 值 */ public static String getDataSourceKey() { return DATASOURCE_KEY.get(); } /** * 移除當前線程持有的數據源的 Key 值 */ public static void clear() { DATASOURCE_KEY.remove(); }}

2. MultipleDataSource

重寫其 AbstractRoutingDataSource 的 determineCurrentLookupKey() 方法,代碼如下:

package cn.tzh.mybatis.config;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * @author tzh * @date 2021/1/4 11:44 */public class MultipleDataSource extends AbstractRoutingDataSource { /** * 返回當前線程是有的數據源的 Key * * @return dataSourceKey */ @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceKey(); }}

3. DataSourceAspect切面

使用 Spring AOP 功能,定義一個切面,用于設置當前需要使用的數據源,代碼如下:

package cn.tzh.mybatis.config;import lombok.extern.log4j.Log4j2;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import java.lang.reflect.Method;/** * @author tzh * @date 2021/1/4 11:46 */@Aspect@Component@Log4j2public class DataSourceAspect { @Before('@annotation(cn.tzh.mybatis.config.TargetDataSource)') public void before(JoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); if (method.isAnnotationPresent(TargetDataSource.class)) { TargetDataSource targetDataSource = method.getAnnotation(TargetDataSource.class); DataSourceContextHolder.setDataSourceKey(targetDataSource.value()); log.info('set the datasource of the current thread to [{}]', targetDataSource.value()); } else if (joinPoint.getTarget().getClass().isAnnotationPresent(TargetDataSource.class)) { TargetDataSource targetDataSource = joinPoint.getTarget().getClass().getAnnotation(TargetDataSource.class); DataSourceContextHolder.setDataSourceKey(targetDataSource.value()); log.info('set the datasource of the current thread to [{}]', targetDataSource.value()); } } @After('@annotation(cn.tzh.mybatis.config.TargetDataSource)') public void after() { DataSourceContextHolder.clear(); log.info('clear the datasource of the current thread'); }}

4. DruidConfig

Druid 配置類,代碼如下:

package cn.tzh.mybatis.config;import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @author tzh * @date 2021/1/4 11:49 */@Configurationpublic class DruidConfig { @Bean(value = 'druid-stat-interceptor') public DruidStatInterceptor druidStatInterceptor() { return new DruidStatInterceptor(); } @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); beanNameAutoProxyCreator.setProxyTargetClass(true); // 設置要監控的bean的id beanNameAutoProxyCreator.setInterceptorNames('druid-stat-interceptor'); return beanNameAutoProxyCreator; }}

5. MultipleDataSourceConfig

MyBatis 的配置類,配置了 2 個數據源,代碼如下:

package cn.tzh.mybatis.config;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import org.apache.ibatis.mapping.DatabaseIdProvider;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.scripting.LanguageDriver;import org.apache.ibatis.session.ExecutorType;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.type.TypeHandler;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;import org.mybatis.spring.boot.autoconfigure.MybatisProperties;import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;import org.springframework.beans.BeanWrapperImpl;import org.springframework.beans.factory.ObjectProvider;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ResourceLoader;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.util.CollectionUtils;import org.springframework.util.ObjectUtils;import org.springframework.util.StringUtils;import javax.sql.DataSource;import java.beans.FeatureDescriptor;import java.util.*;import java.util.stream.Collectors;import java.util.stream.Stream;/** * @author tzh * @projectName code-demo * @title MultipleDataSourceConfig * @description * @date 2021/1/4 13:43 */@Configuration@EnableConfigurationProperties({MybatisProperties.class})public class MultipleDataSourceConfig { private final MybatisProperties properties; private final Interceptor[] interceptors; private final TypeHandler[] typeHandlers; private final LanguageDriver[] languageDrivers; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer> configurationCustomizers; public MultipleDataSourceConfig(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { this.properties = properties; this.interceptors = (Interceptor[]) interceptorsProvider.getIfAvailable(); this.typeHandlers = (TypeHandler[]) typeHandlersProvider.getIfAvailable(); this.languageDrivers = (LanguageDriver[]) languageDriversProvider.getIfAvailable(); this.resourceLoader = resourceLoader; this.databaseIdProvider = (DatabaseIdProvider) databaseIdProvider.getIfAvailable(); this.configurationCustomizers = (List) configurationCustomizersProvider.getIfAvailable(); } @Bean(name = 'master', initMethod = 'init', destroyMethod = 'close') @ConfigurationProperties(prefix = 'spring.datasource.druid.master') public DruidDataSource master() { return DruidDataSourceBuilder.create().build(); } @Bean(name = 'slave', initMethod = 'init', destroyMethod = 'close') @ConfigurationProperties(prefix = 'spring.datasource.druid.slave') public DruidDataSource slave() { return DruidDataSourceBuilder.create().build(); } @Bean(name = 'dynamicDataSource') public DataSource dynamicDataSource() { MultipleDataSource dynamicRoutingDataSource = new MultipleDataSource(); Map<Object, Object> dataSources = new HashMap<>(); dataSources.put('master', master()); dataSources.put('slave', slave()); dynamicRoutingDataSource.setDefaultTargetDataSource(master()); dynamicRoutingDataSource.setTargetDataSources(dataSources); DataSourceContextHolder.ALL_DATASOURCE_KEY.addAll(dataSources.keySet()); return dynamicRoutingDataSource; } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dynamicDataSource()); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } this.applyConfiguration(factory); if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (this.properties.getTypeAliasesSuperType() != null) { factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.typeHandlers)) { factory.setTypeHandlers(this.typeHandlers); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } Set<String> factoryPropertyNames = (Set) Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if (factoryPropertyNames.contains('scriptingLanguageDrivers') && !ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); if (defaultLanguageDriver == null && this.languageDrivers.length == 1) { defaultLanguageDriver = this.languageDrivers[0].getClass(); } } if (factoryPropertyNames.contains('defaultScriptingLanguageDriver')) { factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); } return factory.getObject(); } private void applyConfiguration(SqlSessionFactoryBean factory) { org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new org.apache.ibatis.session.Configuration(); } if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { Iterator var3 = this.configurationCustomizers.iterator(); while (var3.hasNext()) { ConfigurationCustomizer customizer = (ConfigurationCustomizer) var3.next(); customizer.customize(configuration); } } factory.setConfiguration(configuration); } @Bean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory); } @Bean public PlatformTransactionManager masterTransactionManager() { // 配置事務管理器 return new DataSourceTransactionManager(dynamicDataSource()); }}

6. 添加配置

server: port: 9092 servlet: context-path: /mybatis-springboot-demo tomcat: accept-count: 200 min-spare-threads: 200spring: application: name: mybatis-springboot-demo profiles: active: test servlet: multipart: max-file-size: 100MB max-request-size: 100MB datasource: type: com.alibaba.druid.pool.DruidDataSource druid: master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mybatis-demo username: root password: root initial-size: 5 # 初始化時建立物理連接的個數 min-idle: 20 # 最小連接池數量 max-active: 20 # 最大連接池數量 slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mybatis-demo1 username: root password: root initial-size: 5 # 初始化時建立物理連接的個數 min-idle: 20 # 最小連接池數量 max-active: 20 # 最大連接池數量mybatis: type-aliases-package: cn.tzh.mybatis.entity mapper-locations: classpath:cn/tzh/mybatis/mapper/*.xml config-location: classpath:mybatis-config.xmlpagehelper: helper-dialect: mysql reasonable: true # 分頁合理化參數 offset-as-page-num: true # 將 RowBounds 中的 offset 參數當成 pageNum 使用 supportMethodsArguments: true # 支持通過 Mapper 接口參數來傳遞分頁參數

其中分別定義了 master 和 slave 數據源的相關配置

這樣一來,在 DataSourceAspect 切面中根據自定義注解,設置 DataSourceContextHolder 當前線程所使用的數據源的 Key 值,MultipleDataSource 動態數據源則會根據該值設置需要使用的數據源,完成了動態數據源的切換

7. 使用示例

在 Mapper 接口上面添加自定義注解 @TargetDataSource,如下:

package cn.tzh.mybatis.mapper;import cn.tzh.mybatis.config.TargetDataSource;import cn.tzh.mybatis.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;/** * @author tzh * @date 2020/12/28 14:29 */@Mapperpublic interface UserMapper { User selectUserOne(@Param('id') Long id); @TargetDataSource('slave') User selectUserTwo(@Param('id') Long id);} 總結

上面就如何配置動態數據源的實現方式僅提供一種思路,其中關于多事務方面并沒有實現,采用 Spring 提供的事務管理器,如果同一個方法中使用了多個數據源,并不支持多事務的,需要自己去實現(筆者能力有限),可以整合JAT組件,參考:SpringBoot2 整合JTA組件多數據源事務管理

分布式事務解決方案推薦使用 Seata 分布式服務框架

到此這篇關于SpringBoot配置數據庫密碼加密的實現的文章就介紹到這了,更多相關SpringBoot 數據庫密碼加密內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 天一线缆邯郸有限公司_煤矿用电缆厂家_矿用光缆厂家_矿用控制电缆_矿用通信电缆-天一线缆邯郸有限公司 | 学生作文网_中小学生作文大全与写作指导| 捆扎机_气动捆扎机_钢带捆扎机-沈阳海鹞气动钢带捆扎机公司 | 喷播机厂家_二手喷播机租赁_水泥浆洒布机-河南青山绿水机电设备有限公司 | 金刚网,金刚网窗纱,不锈钢网,金刚网厂家- 河北萨邦丝网制品有限公司 | 深圳富泰鑫五金_五金冲压件加工_五金配件加工_精密零件加工厂 | 锥形螺带干燥机(新型耙式干燥机)百科-常州丰能干燥工程 | 手板-手板模型-手板厂-手板加工-生产厂家,[东莞创域模型] | CNC机加工-数控加工-精密零件加工-ISO认证厂家-鑫创盟 | 月嫂_保姆_育婴_催乳_母婴护理_产后康复_养老护理-吉祥到家家政 硫酸亚铁-聚合硫酸铁-除氟除磷剂-复合碳源-污水处理药剂厂家—长隆科技 | 消电检公司,消电检价格,北京消电检报告-北京设施检测公司-亿杰(北京)消防工程有限公司 | 温泉机设备|温泉小镇规划设计|碳酸泉设备 - 大连连邦温泉科技 | ptc_浴霸_大巴_干衣机_呼吸机_毛巾架_电动车加热器-上海帕克 | 钢衬玻璃厂家,钢衬玻璃管道 -山东东兴扬防腐设备有限公司 | 广西资质代办_建筑资质代办_南宁资质代办理_新办、增项、升级-正明集团 | 成都顶呱呱信息技术有限公司-贷款_个人贷款_银行贷款在线申请 - 成都贷款公司 | 石栏杆_青石栏杆_汉白玉栏杆_花岗岩栏杆 - 【石雕之乡】点石石雕石材厂 | 分子精馏/精馏设备生产厂家-分子蒸馏工艺实验-新诺舜尧(天津)化工设备有限公司 | 西安耀程造价培训机构_工程预算实训_广联达实作实操培训 | 上海律师咨询_上海法律在线咨询免费_找对口律师上策法网-策法网 广东高华家具-公寓床|学生宿舍双层铁床厂家【质保十年】 | 联系我们-腾龙公司上分客服微信19116098882 | 全国冰箱|空调|洗衣机|热水器|燃气灶维修服务平台-百修家电 | 消防设施操作员考试报名时间,报名入口,报考条件 | 代做标书-代写标书-专业标书文件编辑-「深圳卓越创兴公司」 | 书法培训-高考书法艺考培训班-山东艺霖书法培训凭实力挺进央美 | 南京PVC快速门厂家南京快速卷帘门_南京pvc快速门_世界500强企业国内供应商_南京美高门业 | 油冷式_微型_TDY电动滚筒_外装_外置式电动滚筒厂家-淄博秉泓机械有限公司 | 北京印刷厂_北京印刷_北京印刷公司_北京印刷厂家_北京东爵盛世印刷有限公司 | 管家婆-管家婆软件-管家婆辉煌-管家婆进销存-管家婆工贸ERP | 铸钢件厂家-铸钢齿轮-减速机厂家-淄博凯振机械有限公司 | 电镀标牌_电铸标牌_金属标贴_不锈钢标牌厂家_深圳市宝利丰精密科技有限公司 | 天津次氯酸钠酸钙溶液-天津氢氧化钠厂家-天津市辅仁化工有限公司 | 中空玻璃生产线,玻璃加工设备,全自动封胶线,铝条折弯机,双组份打胶机,丁基胶/卧式/立式全自动涂布机,玻璃设备-山东昌盛数控设备有限公司 | 呼末二氧化碳|ETCO2模块采样管_气体干燥管_气体过滤器-湖南纳雄医疗器械有限公司 | 不锈钢管件(不锈钢弯头,不锈钢三通,不锈钢大小头),不锈钢法兰「厂家」-浙江志通管阀 | 蒜肠网-动漫,二次元,COSPLAY,漫展以及收藏型模型,手办,玩具的新媒体.(原变形金刚变迷TF圈) | 土壤肥料养分速测仪_测土配方施肥仪_土壤养分检测仪-杭州鸣辉科技有限公司 | 氧化铝球_高铝球_氧化铝研磨球-淄博誉洁陶瓷新材料有限公司 | 春腾云财 - 为企业提供专业财税咨询、代理记账服务 | 成都竞价托管_抖音代运营_网站建设_成都SEM外包-成都智网创联网络科技有限公司 | 钢制拖链生产厂家-全封闭钢制拖链-能源钢铝拖链-工程塑料拖链-河北汉洋机械制造有限公司 |