SpringBoot根据yml配置信息动态生成bean并加入Spring容器
Posted 敲代码的小小酥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot根据yml配置信息动态生成bean并加入Spring容器相关的知识,希望对你有一定的参考价值。
背景
yml里配置经纬度信息,然后通过WebSocket接收配置的经纬度范围内的车辆数据。代码如下:
@Component
public class CarInitTask implements CommandLineRunner,WebSocketHandler {
Logger logger= LoggerFactory.getLogger(CarInitTask.class);
@Value("${websoket.car.url}")
private String webSocketUrl;
@Value("${websoket.car.zone}")
private String zone;//全局范围内车辆
@Autowired
ISjfzCarService carService;
@Autowired
ISjfzInformationboardService informationboardService;
@Autowired
CarMesageHandler carMesageHandler;
@Autowired
EmqClient emqClient;
Map<String,String> carsMap=new HashMap();
int count=0;
@Override
public void run(String... args) throws Exception {
try {
String timestap = DateUtils.dateTimeNow("yyyy-MM-dd");
String tablename="`sjfz_car_" + timestap+"`";
String Informationboardtablename = "`sjfz_informationboard_" + timestap+"`";
carService.createDayCarsTable(tablename);
informationboardService.createDayInformationboardTable(Informationboardtablename);
}catch (Exception e){
e.printStackTrace();
}
logger.error("全局连接车辆websocket...........");
System.out.println(zone);
WsWebSocketContainer wsWebSocketContainer = new WsWebSocketContainer();
wsWebSocketContainer.setDefaultMaxBinaryMessageBufferSize(5120000);
wsWebSocketContainer.setDefaultMaxTextMessageBufferSize(5120000);
// wsWebSocketContainer.setDefaultMaxSessionIdleTimeout(300000);
StandardWebSocketClient client = new StandardWebSocketClient(wsWebSocketContainer);
WebSocketHandler webSocketHandler=this;
String uriTemplate = webSocketUrl;
Object uriVars = null;
ListenableFuture<WebSocketSession> future =client.doHandshake(webSocketHandler, uriTemplate, uriVars);
try {
WebSocketSession session = future.get();
System.out.println(session);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
TextMessage message=new TextMessage(zone.getBytes());
webSocketSession.sendMessage(message);
}
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
count++;
Map map = JSONObject.fromObject(webSocketMessage.getPayload());
Map map1=JSONObject.fromObject( map.get("result"));
JSONArray jsonArray = JSONArray.fromObject(map1.get("perList"));
List<Map> perList = JSONArray.toList(jsonArray, Map.class);
for (int i=0;i<perList.size();i++){
JSONArray dataArray= JSONArray.fromObject(perList.get(i).get("data"));
List<Map> dataList = JSONArray.toList(dataArray,Map.class);
for(int j=0;j<dataList.size();j++){
String confidence=dataList.get(j).get("confidence").toString();
String devId=dataList.get(j).get("devId").toString();
String gpsTime=dataList.get(j).get("gpsTime").toString();
Date date = new Date();
date.setTime(Long.valueOf(gpsTime));
String temp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(date);
String heading=dataList.get(j).get("heading").toString();
String latitude=dataList.get(j).get("latitude").toString();
String longitude=dataList.get(j).get("longitude").toString();
String objColor=dataList.get(j).get("objColor").toString();
String plateNo=dataList.get(j).get("plateNo").toString();
String speed=dataList.get(j).get("speed").toString();
String targetType=dataList.get(j).get("targetType").toString();
String vehicleId=dataList.get(j).get("vehicleId").toString();
try {
// carMesageHandler.handleCarMessage(plateNo, vehicleId);
}catch (Exception e){
e.printStackTrace();
}
String tablename="`sjfz_car_" +DateUtils.parseDateToStr("yyyy-MM-dd",DateUtils.parseDate(temp,"yyyy-MM-dd HH:mm:ss SSS"))+"`";
SjfzCar car=new SjfzCar();
car.setConfidence(confidence);
car.setDevId(devId);
car.setGpsTime(temp);
car.setHeading(heading);
car.setLatitude(latitude);
car.setLongitude(longitude);
car.setObjColor(objColor);
car.setPlateNo(plateNo);
car.setSpeed(speed);
car.setTargetType(targetType);
car.setVehicleId(vehicleId);
car.setTablename(tablename);
String time=carsMap.get(plateNo);
if(StringUtils.isEmpty(time)){
carsMap.put(plateNo,temp);
carService.insertSjfzCar(car);
emqClient.publish("jd/12", JSON.toJSONString(car), QosEnum.Qos0,false);
}else{
if(DateUtils.parseDate(temp,"yyyy-MM-dd HH:mm:ss SSS").getTime()-DateUtils.parseDate(time,"yyyy-MM-dd HH:mm:ss SSS").getTime()>=500){
carsMap.put(plateNo,temp);
carService.insertSjfzCar(car);
emqClient.publish("jd/12", JSON.toJSONString(car), QosEnum.Qos0,false);
}
}
}
}
if(count==100){
carsMap.clear();
}
}
@Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
}
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
logger.info("断开了websocket连接:"+closeStatus.toString());
reconnect();
}
@Override
public boolean supportsPartialMessages() {
return false;
}
public void reconnect(){
WsWebSocketContainer wsWebSocketContainer = new WsWebSocketContainer();
wsWebSocketContainer.setDefaultMaxBinaryMessageBufferSize(5120000);
wsWebSocketContainer.setDefaultMaxTextMessageBufferSize(5120000);
// wsWebSocketContainer.setDefaultMaxSessionIdleTimeout(300000);
StandardWebSocketClient client = new StandardWebSocketClient(wsWebSocketContainer);
WebSocketHandler webSocketHandler=this;
String uriTemplate = webSocketUrl;
Object uriVars = null;
ListenableFuture<WebSocketSession> future =client.doHandshake(webSocketHandler, uriTemplate, uriVars);
try {
WebSocketSession session = future.get();
System.out.println(session);
}catch (Exception e){
e.printStackTrace();
reconnect();
}
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
}
上面的代码,将yml中经纬度的配置通过@Vaule注解注入了zone属性中,然后接收这个区间的webSocket数据。当经纬度区间写的很大时,数据会非常多,而且Websocket一个连接是单线程的,所以处理起来速度会很慢。
所以要把经纬度信息分成几段区间,进行数据接收。这样的话,分成了几段,就要写几个上述代码的类,来分别接收各自路段的zone经纬度范围。但是具体分成多少段,又不确定,每分一段,就添加一个类,也很麻烦。所以考虑根据yml里的配置,配置了几个路段,就动态生成几个上述类。
解决
动态生成bean,可以转化为动态的增加BeanDefinition对象。Spring容器中的bean都是由BeanDefinition来的。所以可以动态增加BeanDefinition。Spring为我们提供了这样的接口,来动态增加BeanDefinition对象,那就是BeanDefinitionRegistryPostProcessor接口。下面看代码:
@Component
public class CarInitPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
Environment env;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
String property = env.getProperty("websoket.car.zone");
List<String> zones = JSONObject.parseArray(property,String.class);
for(int i=0;i<zones.size();i++){
String zone=zones.get(i);
BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.rootBeanDefinition(CarInitTask.class);
beanDefinitionBuilder.addPropertyValue("zone",zone);
registry.registerBeanDefinition("carInitTask"+i,beanDefinitionBuilder.getBeanDefinition());
}
}
@Override
public void setEnvironment(Environment environment) {
this.env=environment;
}
}
上述该类,实现了BeanDefinitionRegistryPostProcessor接口和EnvironmentAware接口。
BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法用来注册新的BeanDefinition对象。
EnvironmentAware接口用来获取Environment对象。Environment对象获取yml里的配置信息,相关代码如下:
String property = env.getProperty("websoket.car.zone");
List<String> zones = JSONObject.parseArray(property,String.class);
可以看到,将yml中的经纬度转成了List集合。然后遍历这个集合,根据每个经纬度配置,动态生成一个BeanDefinition对象。
这里用到了BeanDefinitionBuilder类来创建BeanDefinition对象,参数就是我们要动态创建的类的class对象。如下代码:
BeanDefinitionBuilder beanDefinitionBuilder=BeanDefinitionBuilder.rootBeanDefinition(CarInitTask.class);
这里需要注意的是,我们动态创建CarInitTask的BeanDefinition对象,则CarInitTask类本身就无需再加@Component注解了,而且也无需用@Value注入zone属性了,而是需要动态添加zone属性,代码如下:
beanDefinitionBuilder.addPropertyValue("zone",zone);
通过addPropertyValue方法添加成员属性的值。这里也需要注意一点,用addPropertyValue方法注入值时,需要提供zone属性的get和set方法。
最后,将动态生成的BeanDefinition对象注册到BeanFactory中:
registry.registerBeanDefinition("carInitTask"+i,beanDefinitionBuilder.getBeanDefinition());
至此,完成了类的动态生成。在Spring初始化的后续过程,生成这几个类的实例,并执行里面的方法,进行数据的接收。
总结
这里几个知识点需要注意:
1.BeanDefinitionBuilder类生成BeanDefinition对象。
2.BeanDefinition可以通过addPropertyValue方法设置对象的成员属性和值。
以上是关于SpringBoot根据yml配置信息动态生成bean并加入Spring容器的主要内容,如果未能解决你的问题,请参考以下文章
springboot 整合RabbitMQ yml配置文件配置交换机 队列信息
spring boot 无法从 application.yml 获取配置