背景
由于 jar 包需要发布到 storm 分布式环境中运行,所以每次都要将配置文件打进 jar 包里,这样程序运行的时候,才能从 classpath 中找到配置文件。每次发布程序的时候,由于各项环境不同,都要修改配置文件,很麻烦。所以试想如何加载 jar 包外部配置文件。
配置文件
配置文件包括
- 程序配置文件 .properties
- spring 配置文件 .xml
其中 spring 配置文件引用了 .properties 文件
思路
- 将 .properties 配置读取,放到 stormConf 对象中,这样,当 jar 包提交到 storm 拓扑环境中时,也能从 stormConf 对象中加载配置
- 首先,去 xml 配置,spring 使用注解 @Configuration 配置类,然后通过工具类,从 stormConf 中获取需要的配置。
代码示例
本文只是演示思路,不介绍 spring 注解配置,请查阅其他资料
- 从外部文件读取配置,放入 stormConfig
public static void main(String[] args) {
final TopologyBuilder builder = new TopologyBuilder();
// 获取当前 jar 包路径
String programPath = ClassUtils.getCurrentProgramPath();
// 从外部读取 jdbc.properties 文件
String jdbcProperties = FileUtils.readFileContent(programPath + "/jdbc.properties");
Config conf = new Config();
// 放入 jdbc 配置
conf.put("jdbc.properties", jdbcProperties );
StormSubmitter.submitTopology("test", conf, builder.createTopology());
}
- spout、bolt 中获取配置对象
@Override
public void prepare(Map stormConf, TopologyContext context) {
// 获取 jdbc 配置
//String jdbcProperties = stormConf.get("jdbc.properties");
// 然后可以用于初始化 spring 容器
SpringContext.SpringContextInit(stormConf);
}
- ClassUtils 工具类
import java.io.File;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.util.Objects;
/**
* 类的工具类
*
* @author wangtan
* @date 2019-09-28 11:07:12
* @since 1.0
*/
public class ClassUtils {
/**
* Don't let anyone else instantiate this class
*/
private ClassUtils() {
}
/**
* 获取当前程序所在路径
*
* @return String
* @date 2019-09-28 11:08:39
* @since 1.0
*/
public static String getCurrentProgramPath() {
CodeSource codeSource = ClassUtils.class.getProtectionDomain().getCodeSource();
File jarFile = null;
try {
jarFile = new File(codeSource.getLocation().toURI().getPath());
} catch (URISyntaxException e) {
e.printStackTrace();
}
return Objects.requireNonNull(jarFile, "jar file should not be null.").getParentFile().getPath();
}
}
- FileUtils 文件工具类
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* 文件工具类
*
* @author wangtan
* @date 2020-01-07 17:08:21
* @since 1.0
*/
public class FileUtils {
/**
* Don't let anyone else instantiate this class
*/
private FileUtils() {
}
/**
* 读取文件内容
*
* @param filePath 文件路径
* @return 文件内容
* @date 2020-01-07 17:08:56
* @since 1.0
*/
public static String readFileContent(String filePath) {
File file = new File(filePath);
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
String tempStr;
while ((tempStr = reader.readLine()) != null) {
sb.append(tempStr).append(System.getProperty("line.separator"));
}
reader.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return sb.toString();
}
}
- spring 容器初始化
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Map;
/**
*
* 初始化Spring容器
*
* @author wangtan
* @date 2020-01-09 11:11:11
* @since 1.0
*/
public class SpringContext
{
private static ApplicationContext applicationContext;
public static synchronized void SpringContextInit(Map stormConf)
{
if (applicationContext == null)
{
//applicationContext = new ClassPathXmlApplicationContext("spring/application-context.xml","spring/application-redis.xml");
/* start: 使用 AnnotationConfigApplicationContext 注册配置类 @author wangtan @date 2020-01-09 11:11:11 */
// 加载配置
ConfigUtils.loadProps(stormConf);
/*
* spring 注册配置类
* Dependency Injection,依赖注入 注入方法:构造器注入
*
* spring 的依赖注入是有顺序的,当先注入的对象 A 依赖后注入的对象 B 时,会把 A 标记挂起,
* 等待所有的对象注入完毕后,再回过头来,重新注入被标记挂起的对象 A,而此时,A 依赖的 B 已经注入完毕,
* 所以此时可以注入 A。辅助理解配置类注入过程(其实和 xml 注入原理一样)。
*/
applicationContext = new AnnotationConfigApplicationContext(ApplicationContextConfiguration.class,
DataSourceConfiguration.class, SqlSessionFactoryConfiguration.class,
TransactionManagementConfiguration.class, SqlSessionTemplateConfiguration.class,
JedisPoolConfiguration.class, AiJedisConnectionFactoryConfiguration.class,
StringRedisTemplateConfiguration.class);
/* end: 使用 AnnotationConfigApplicationContext 注册配置类 @author wangtan @date 2020-01-09 11:11:30 */
}
}
}
- ConfigUtils 配置文具类,将 配置信息写入静态变量
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
/**
* 配置文件工具类
*
* @author wangtan
* @date 2020-01-08 11:03:23
* @since 1.0
*/
public class ConfigUtils {
/**
* jdbc 配置对象
*/
public static Properties jdbcProperties = new Properties();
/**
* Don't let anyone else instantiate this class
*/
private ConfigUtils() {
}
/**
* 加载配置
*
* @param stormConf storm 配置对象
* @date 2020-01-09 15:07:56
* @since 1.0
*/
public static void loadProps(Map stormConf) {
try {
jdbcProperties.load(new StringReader(StringUtils.getStr(stormConf.get("jdbc.properties"))));
} catch (IOException e) {
LOGGER.error("从 stormConf 加载配置失败。", e);
}
}
/**
* 根据 key 获取 jdbc 配置
*
* @param key 配置项名称
* @return 配置项值
* @date 2020-01-09 14:00:04
* @since 1.0
*/
public static String getJdbcProperty(String key) {
return jdbcProperties.getProperty(key);
}
}
- DataSource 配置类
import com.alibaba.druid.pool.DruidDataSource;
import com.****.ete.util.ConfigUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.sql.SQLException;
/**
* DataSource 配置类
*
* @author wangtan
* @date 2020-01-09 10:27:09
* @since 1.0
*/
@Configuration
public class DataSourceConfiguration {
/**
* 配置 DruidDataSource
* <p>
* 不可省略 (initMethod = "init", destroyMethod = "close")
*
* @return DruidDataSource 数据源对象
* @throws SQLException SQLException
* @date 2020-01-09 09:53:46
* @since 1.0
*/
@Bean(initMethod = "init", destroyMethod = "close")
@Primary
public DruidDataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
// 基本属性 url、user、password
druidDataSource.setUrl(ConfigUtils.getJdbcProperty("jdbc.url"));
druidDataSource.setUsername(ConfigUtils.getJdbcProperty("jdbc.username"));
druidDataSource.setPassword(ConfigUtils.getJdbcProperty("jdbc.password"));
// 配置初始化大小、最小、最大
druidDataSource.setInitialSize(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.initialSize")));
druidDataSource.setMinIdle(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.minIdle")));
druidDataSource.setMaxActive(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.maxActive")));
// 配置获取连接等待超时的时间
druidDataSource.setMaxWait(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.maxWait")));
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
druidDataSource.setTimeBetweenEvictionRunsMillis(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.timeBetweenEvictionRunsMillis")));
// 配置一个连接在池中最小生存的时间,单位是毫秒
druidDataSource.setMinEvictableIdleTimeMillis(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.minEvictableIdleTimeMillis")));
druidDataSource.setValidationQuery("select 'X' from dual");
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
// 打开 PSCache,并且指定每个连接上 PSCache 的大小 如果用 Oracle,
// 则把 poolPreparedStatements 配置为 true,mysql 可以配置为 false。
druidDataSource.setPoolPreparedStatements(BooleanUtils.toBoolean(ConfigUtils.getJdbcProperty("druid.poolPreparedStatements")));
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.maxPoolPreparedStatementPerConnectionSize")));
// 配置监控统计拦截的 filters
druidDataSource.setFilters(ConfigUtils.getJdbcProperty("druid.filters"));
return druidDataSource;
}
}
来源:CSDN
作者:王 坦
链接:https://blog.csdn.net/WTUDAN/article/details/104048024