应用场景:
想动态调整Spirngboot的yml配置文件内容。比如数据库链接信息,或者说mq链接信息、监听队列的调整。
本人这里以rabbitmq消息队列为例,@RabbitListener(queues = "xxx") 监听者的这个队列名,每次在修改消息队列名称时都要手动调整yml然后重启项目,为了解决这个问题看了两天的源码才知道可以这样调整。
1、将mq的所有信息都保存在数据库里,项目初始化的时候从数据库读取。在Springboot中,需要通过ApplicationListener 接口来获取配置信息,onApplicationEvent这个方法执行的时候所有信息都是空的,所以需要在此方法里进行数据库连接。
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
@Configuration
public class RabbitMqConfig implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
private String host;
private String port;
private String username;
private String password;
@PostConstruct
public void init() {
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession();
YumingMapper mapper = sqlSession.getMapper(YumingMapper.class);
Map<String, String> config = mapper.queryRabbitMqConfigInfo();
host = config.get("host");
port = config.get("port");
username = config.get("username");
password = config.get("password");
}
@Bean
public ConnectionFactory connectionFactory() {
// 重置yml 里的配置文件
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(Integer.parseInt(port));
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
return connectionFactory;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
// 获取spring Environment
MutablePropertySources propertySources = event.getEnvironment().getPropertySources();
// 配置放在了application-pro或者是application-dev 中 赋值复制需要在其中赋值
for (PropertySource<?> propertySource : propertySources) {
boolean applicationConfig = propertySource.getName().contains("application");
if (!applicationConfig) {
continue;
}
// 获取上文的application集合中获取数据库连接
Map<String, OriginTrackedValue> dataBaseSource =
(Map<String, OriginTrackedValue>)propertySource.getSource();
// String driverClass = String.valueOf(dataBaseSource.get("spring.datasource.driver-class-name").getValue());
String url = String.valueOf(dataBaseSource.get("spring.datasource.url").getValue());
String user = String.valueOf(dataBaseSource.get("spring.datasource.username").getValue());
String password = String.valueOf(dataBaseSource.get("spring.datasource.password").getValue());
// 因为在spring初始化之前 所有不能使用注解 所以需要jdbc直接连接数据库 首先建立驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(url, user, password);
// 1、获取连接对象
// 2、创建statement类对象,用来执行SQL语句!!
st = conn.createStatement();
// 3、创建sql查询语句
String sql = "select * from jl_config";
// 4、执行sql语句并且换回一个查询的结果集
rs = st.executeQuery(sql);
while (rs.next()) {
// 获取数据库中的数据
String jlappid = rs.getString("jlappid");
int jlstore_id = rs.getInt("jlstore_id");
String goodsQueneName = "goods_list_" + jlappid;
String orderQueneName = "xxxie_store_gongban" + jlstore_id;
String orderRoutingKey = "order_" + jlstore_id;
ConfigurableEnvironment environment = event.getEnvironment();
Properties props = new Properties();
props.put("mq.goods.queneName", goodsQueneName);
props.put("mq.order.queneName", orderQueneName);
props.put("mq.order.routingKey", orderRoutingKey);
environment.getPropertySources().addFirst(new PropertiesPropertySource("decrypted_properties", props));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2、新建resources/META_INF/下新建spring.factories文件,输入以下内容.就是类路径。
org.springframework.context.ApplicationListener = xxxx.RabbitMqConfig
3、重启项目就能达到通过数据获取覆盖Springbootymp配置文件的覆盖操作。