spring老项目升级spring-boot之dubbo升级
巨大的建筑,总是由一木一石叠起来的,我们何妨做做这一木一石呢?我时常做些零碎事,就是为此。
这是对的,但是我没有说过这句话! —— 鲁迅
问题的开始
之前老的spring项目使用dubbo的时候,都是使用的xml
的方式。这篇文章主要是站在consumer
端的角度出发,也就是 provider
不变的情况下(仍然是xml),怎样先升级consumer
端,来实现项目的正常运行。
我相信,使用xml
的老项目的配置文件一般长这样.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="api-consumer" owner="ennz"/>
<dubbo:registry group="${dubbo.group}" check="false" protocol="zookeeper"
address="${zookeeper.hosts}"/>
<dubbo:reference version="0.0.1" check="false" id="service1"
interface="com.tms.bl.service.Service1"/>
<dubbo:reference version="0.0.1" check="false" id="service2"
interface="com.tms.bl.service.Service2"/>
</beans>
在Application
中引入这个文件,会出现报错
@SpringBootApplication
@ImportResource(locations = {"classpath:dubbo_consumer.xml"})
public class AdminApiApplication {
private static final Logger logger = LoggerFactory.getLogger(AdminApiApplication.class);
public static void main(String[] args) {
SpringApplication.run(AdminApiApplication.class, args);
}
}
报错如下:
2023-03-30 16:52:14.323 [main-SendThread()] WARN org.apache.zookeeper.ClientCnxn - Session 0x0 for server ${zookeeper.hosts}:9090, unexpected error, closing socket connection and attempting reconnect
java.lang.IllegalArgumentException: named capturing group is missing trailing '}'
at java.util.regex.Matcher.appendReplacement(Matcher.java:841)
at java.util.regex.Matcher.replaceAll(Matcher.java:955)
at java.lang.String.replaceAll(String.java:2223)
at org.apache.zookeeper.ClientCnxn$SendThread.startConnect(ClientCnxn.java:997)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1064)
也就是说,相应的 ${zookeeper.hosts}
类似于这样的值,无法注入.
针对xml
中${zookeeper.hosts}
无法注入的思考
无法注入,是否可以通过使用 @Configuration
注解的方式,来注入相应的值
新增配置类,但是不完全取代xml
在xml中,有两个标签如下:
<!-- 对应java类是 ApplicationConfig -->
<dubbo:application name="api-consumer" owner="ennz"/>
<!-- 对应java类是 RegistryConfig -->
<dubbo:registry group="${dubbo.group}" check="false" protocol="zookeeper"
address="${zookeeper.hosts}"/>
把这两个用java配置类来实现,至于有很多已经写好的<dubbo:reference />
则继续使用xml的方式,通过@ImportResource
来实现。java配置类如下:
@Configuration
@ImportResource("classpath:dubbo-consumer.xml")
public class DubboAdasConsumerConfig {
private Logger logger = LoggerFactory.getLogger(DubboAdasConsumerConfig.class);
@Value("${dubbo.zookeepers}")
private String dubboZookeepers;
@Value("${dubbo.group}")
private String dubboGroup;
/*相当于consumer.xml中的:<dubbo:application name="consumer"/>*/
@Bean
public ApplicationConfig gpsApplicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("api-consumer");
applicationConfig.setOwner("ennz");
return applicationConfig;
}
/*相当于:<dubbo:registry address="39.108.125.227:2181" protocol="zookeeper"/>*/
@Bean
public RegistryConfig adasRegistryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setCheck(false);
registryConfig.setAddress(dubboZookeepers);
registryConfig.setGroup(dubboGroup);
logger.info("adasRegistryConfig:{}", registryConfig.getAddress());
return registryConfig;
}
}
其中 dubbo-consumer.xml
中,去掉这两个类的配置,只剩下主要service的注入:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:reference version="0.0.1" check="false" id="service1"
interface="com.tms.bl.service.Service1"/>
<dubbo:reference version="0.0.1" check="false" id="service2"
interface="com.tms.bl.service.Service2"/>
</beans>
但是运行之后,仍然报错,是dubbo中注入的service为空。我这里是生成shiro的时候,用到了某个dubbo服务。报错:register dubbo:null
Caused by: java.lang.IllegalStateException: registry address == null
然后,输出了一下 registryConfig.getAddress()
的值,是null,也就是@Value("${dubbo.zookeepers}")
失效,并没有注入成功.
分析一下原因,应该跟加载顺序有关.
- 先加载bean
- 加载配置类,加载@Value值。
- 执行第一步的时候,发现有的是dubbo的,但是想要加载dubbo的,就需要先找到dubbo的配置,就先加载了dubbo的配置类.但是,此时@Value还未生效,是null.
使用最基础的方法,读取文件,提前找到配置。
配置文件一般都提取出来,放到config文件夹下面.
直接读取它,获取属性配置。
@SpringBootApplication
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class AdminApiApplication {
private static final Logger logger = LoggerFactory.getLogger(AdminApiApplication.class);
public static void main(String[] args) {
try {
setConfigProperties();
SpringApplication.run(AdminApiApplication.class, args);
System.out.println("print start");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setConfigProperties() {
Properties config = new Properties();
try {
File file = new File("/data/config/config.properties");
FileInputStream fileInputStream = new FileInputStream(file);
InputStream is = fileInputStream;
config.load(is);
} catch (IOException var5) {
throw new RuntimeException("An error occurred while reaed exceptions", var5);
}
CacheManager.configProperties = config;
logger.info("config:{}", JSON.toJSONString(config));
}
}
其中 CacheManager.configProperties是一个静态变量,简单的存储器.
public class CacheManager {
/**
* config中的变量.
*/
public static Properties configProperties;
}
使用注入@Value的地方,不使用@Value来注入,直接取值
@Bean
public RegistryConfig gpsRegistryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setCheck(false);
registryConfig.setAddress(CacheManager.configProperties.getProperty("dubbo.zookeepers"));
registryConfig.setGroup(CacheManager.configProperties.getProperty("dubbo.group"));
return registryConfig;
}
运行,可以启动成功.
有没有更好的方法
使用 EnvironmentPostProcessor
,这里会在加载配置类之前执行。增加类,实现AdminApiApplication
中的setConfigProperties
方法.
/**
*
* @author liuzhenning
* @version 0.0.1
* @since 0.0.1 2022-11-25
*/
public class CustomerConfigLoadProcessor implements EnvironmentPostProcessor {
private Logger logger = LoggerFactory.getLogger(CustomerConfigLoadProcessor.class);
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
System.out.println("postProcessEnvironment begin=====================================");
Properties config = new Properties();
this.addProperties(config, "/data/config/config.properties");
this.addProperties(config, "E://temp/config/config.properties");
CacheManager.configProperties = config;
System.out.println("postProcessEnvironment done");
}
private void addProperties(Properties config, String fileName) {
try {
Properties properties = new Properties();
File file = new File(fileName);
if (!file.exists()) {
return;
}
InputStream is = new FileInputStream(file);
properties.load(is);
config.putAll(properties);
is.close();
} catch (IOException var5) {
logger.error("error", var5);
}
}
}
增加该类后,spring并不会加载它,还需要配置一下
在resources
下面增加文件夹META-INF/spring.factories
,增加配置
org.springframework.boot.env.EnvironmentPostProcessor=\
com.xxxx.CustomerConfigLoadProcessor
去掉AdminApiApplication
中的setConfigProperties()
,运行,成功启动.