springBoot项目中用策略模式结合工厂模式实践

网上很多设计模式,但是归根到底在实际的web项目中需要去转换,学习其设计的思想。参考了这篇文章,得到不少启示:https://www.douban.com/note/763178259/

项目需求:需要创建2种类型数据库的连接(mysql跟oracle)

  • 创建数据源的接口
public abstract class DataSourceContext {

	abstract DruidDataSource createDruidDataSource(DataSource dataSourceBean);

}
  • 创建数据源工厂
/**
 * @Description 应用工厂模式+策略模式,把根据不同数据库类型反射生成不同数据源
 **/
public class DataSourceFactory {
	
    /**
     * 定义包路径
     */
    private static final String package_path = "com.xxx.xxx.dataSource.";
    /**
     * 定义类的后缀名
     */
    private static final String class_suffix = "DataSource";
    
    /**
     * 根据传入的类型,反射创建对应类型的dataSource
     * @param type 数据源类型
     * @param dataSourceBean 数据源Bean
     */
    public static DruidDataSource createDruidDataSource(String type,DataSource dataSourceBean) throws Exception {
        StringBuilder dataSourceClassName = new StringBuilder();
    	dataSourceClassName.append(package_path).append(Character.toUpperCase(type.charAt(0))).append(type.substring(1)).append(class_suffix);
    	DataSourceContext dataSourceContext = (DataSourceContext) Class.forName(dataSourceClassName.toString()).newInstance();
        return dataSourceContext.createDruidDataSource(dataSourceBean);
    }
}
  • 创建数据源容器,启动初始化项目的时候把数据源通过创建数据源的工厂缓存起来
/**
 * 1.实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。
 * 为了解决这样的问题,Spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现。很简单,只需要一个类就可以,无需其他配置。
 * 创建实现接口 CommandLineRunner 的类
 * 2.服务启动,就会执行MyStartupRunner
 * 3.Spring Boot应用程序在启动后,会遍历CommandLineRunner接口的实例并运行它们的run方法。
 * 也可以利用@Order注解(或者实现Order接口)来规定所有CommandLineRunner实例的运行顺序, value 值越小,优先级越高
 *
 * @Description 项目启动时,读取数据源表,获取有效的数据源并缓存
 **/
@Component
@Order(1)
public class DataSourceContainer implements CommandLineRunner {

    private static final Logger LOG = LoggerFactory.getLogger(DataSourceContainer.class);

    /**
     * 定义全局共享的DruidDataSource容器,key:source_code数据源编码,唯一
     */
    public static volatile Map<String, DruidDataSource> datasourceContainerCode = new ConcurrentHashMap<>();
    

    @Autowired
    private DataSourceMapper dataSourceMapper;

    @Override
    public void run(String... args) {
        initDatasourceContainer();
    }

    /**
     * 查询有效的数据源,初始化数据源容器
     */
    private void initDatasourceContainer() {
        List<DataSource> dataSourcesList = dataSourceMapper.dataSourcesListAll();

        dataSourcesList.stream().forEach(dataSourceBean -> {
            DruidDataSource dataSource = null;
            try {
                dataSource = DataSourceFactory.createDruidDataSource(dataSourceBean.getDatabaseType().toLowerCase(), dataSourceBean);
            } catch (Exception e) { 
            }
            if (dataSourceBean.getSourceCode() != null) {         datasourceContainerCode.put(dataSourceBean.getSourceCode(), dataSource);
            }
        });
    }

    /**
     * 根据sourceCode从数据源容器中获取Connection
     *
     * @param sourceCode 数据源编码
     */
    public static Connection getConnectionFromContainer(String sourceCode) {
        try {
            return datasourceContainerCode.get(sourceCode).getConnection();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    

    /**
     * 获取datasourceContainerCode容器
     *
     * @return
     */
    public static Map<String, DruidDataSource> getdatasourceContainerCodeMap() {
        return datasourceContainerCode;
    }

    public static DruidDataSource getDatasource(String code) {
        return datasourceContainerCode.get(code);
    }
    
}
  • mysql数据源参考
/**
 * @Description mysql类型的数据源
 **/
public class MysqlDataSource extends DataSourceContext {

	@Override
	public DruidDataSource createDruidDataSource(DataSource dataSource) {
		DruidDataSource druidDataSource = new DruidDataSource();
		druidDataSource.setUrl(getUrl(dataSource));
		  if (dataSource.getPassWord().startsWith("@#")) {
	        	druidDataSource.setPassword(CryptoUtils.decrypt(dataSource.getPassWord()));
	        } else {
	        	druidDataSource.setPassword(dataSource.getPassWord());
	        }
	        if (dataSource.getUserName().startsWith("@#")) {
	        	druidDataSource.setUsername(CryptoUtils.decrypt(dataSource.getUserName()));
	        } else {
	        	druidDataSource.setUsername(dataSource.getUserName());
	        }
		druidDataSource.setDbType(dataSource.getDatabaseType());
		// 初始化连接大小
		druidDataSource.setInitialSize(1);
		// 连接池最小空闲
		druidDataSource.setMinIdle(1);
		// 连接池最大使用链接数量
		druidDataSource.setMaxActive(500);
		// 配置获取连接等待超时的时间
		druidDataSource.setMaxWait(8000);
		druidDataSource.setQueryTimeout(3600);
		druidDataSource.setValidationQuery("select 1");
		druidDataSource.setValidationQueryTimeout(15000);
		druidDataSource.setTestOnBorrow(false);
		druidDataSource.setTestOnReturn(false);
		druidDataSource.setTestWhileIdle(true);
		druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
		druidDataSource.setRemoveAbandoned(true);
		druidDataSource.setRemoveAbandonedTimeout(1800);
		return druidDataSource;
	}
	
	private String getUrl(DataSource dataSourceBean) {
		
		if (StringUtils.isNotEmpty(dataSourceBean.getJdbcUrl())) {
			return dataSourceBean.getJdbcUrl();
		} else {
			return "jdbc:mysql://"+dataSourceBean.getServiceIp()+":"+dataSourceBean.getServiceProt()
			+"/"+dataSourceBean.getServiceName()+"?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false";
		}
		
	}
}

  • 数据源实体类
public class DataSource implements Serializable {

    @ApiModelProperty(value = "主键id")
    private Long id;

    @ApiModelProperty(value = "数据源名称")
    private String sourceName;

    @ApiModelProperty(value = "数据源编码")
    private String sourceCode;
	
    @ApiModelProperty(value = "服务器IP")
    private String serviceIp;

    @ApiModelProperty(value = "服务器端口")
    private String serviceProt;

    @ApiModelProperty(value = "用户名")
    private String userName;

    @ApiModelProperty(value = "密码")
    private String passWord;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值