spring老項(xiàng)目升級(jí)spring-boot之dubbo升級(jí)
巨大的建筑,總是由一木一石疊起來(lái)的恒水,我們何妨做做這一木一石呢?我時(shí)常做些零碎事浑度,就是為此寇窑。
這是對(duì)的,但是我沒(méi)有說(shuō)過(guò)這句話箩张! —— 魯迅
問(wèn)題的開(kāi)始
之前老的spring項(xiàng)目使用dubbo的時(shí)候甩骏,都是使用的xml
的方式。這篇文章主要是站在consumer
端的角度出發(fā)先慷,也就是 provider
不變的情況下(仍然是xml),怎樣先升級(jí)consumer
端饮笛,來(lái)實(shí)現(xiàn)項(xiàng)目的正常運(yùn)行。
我相信论熙,使用xml
的老項(xiàng)目的配置文件一般長(zhǎng)這樣.
<?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
中引入這個(gè)文件福青,會(huì)出現(xiàn)報(bào)錯(cuò)
@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);
}
}
報(bào)錯(cuò)如下:
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)
也就是說(shuō),相應(yīng)的 ${zookeeper.hosts}
類似于這樣的值脓诡,無(wú)法注入.
針對(duì)xml
中${zookeeper.hosts}
無(wú)法注入的思考
無(wú)法注入无午,是否可以通過(guò)使用 @Configuration
注解的方式,來(lái)注入相應(yīng)的值
新增配置類祝谚,但是不完全取代xml
在xml中宪迟,有兩個(gè)標(biāo)簽如下:
<!-- 對(duì)應(yīng)java類是 ApplicationConfig -->
<dubbo:application name="api-consumer" owner="ennz"/>
<!-- 對(duì)應(yīng)java類是 RegistryConfig -->
<dubbo:registry group="${dubbo.group}" check="false" protocol="zookeeper"
address="${zookeeper.hosts}"/>
把這兩個(gè)用java配置類來(lái)實(shí)現(xiàn),至于有很多已經(jīng)寫(xiě)好的<dubbo:reference />
則繼續(xù)使用xml的方式交惯,通過(guò)@ImportResource
來(lái)實(shí)現(xiàn)次泽。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;
/*相當(dāng)于consumer.xml中的:<dubbo:application name="consumer"/>*/
@Bean
public ApplicationConfig gpsApplicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("api-consumer");
applicationConfig.setOwner("ennz");
return applicationConfig;
}
/*相當(dāng)于:<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
中穿仪,去掉這兩個(gè)類的配置,只剩下主要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>
但是運(yùn)行之后意荤,仍然報(bào)錯(cuò)啊片,是dubbo中注入的service為空。我這里是生成shiro的時(shí)候玖像,用到了某個(gè)dubbo服務(wù)紫谷。報(bào)錯(cuò):register dubbo:null
Caused by: java.lang.IllegalStateException: registry address == null
然后,輸出了一下 registryConfig.getAddress()
的值御铃,是null,也就是@Value("${dubbo.zookeepers}")
失效碴里,并沒(méi)有注入成功.
分析一下原因,應(yīng)該跟加載順序有關(guān).
- 先加載bean
- 加載配置類上真,加載@Value值咬腋。
- 執(zhí)行第一步的時(shí)候,發(fā)現(xiàn)有的是dubbo的睡互,但是想要加載dubbo的根竿,就需要先找到dubbo的配置,就先加載了dubbo的配置類.但是就珠,此時(shí)@Value還未生效寇壳,是null.
使用最基礎(chǔ)的方法,讀取文件妻怎,提前找到配置壳炎。
配置文件一般都提取出來(lái),放到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是一個(gè)靜態(tài)變量,簡(jiǎn)單的存儲(chǔ)器.
public class CacheManager {
/**
* config中的變量.
*/
public static Properties configProperties;
}
使用注入@Value的地方榛丢,不使用@Value來(lái)注入铲球,直接取值
@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;
}
運(yùn)行,可以啟動(dòng)成功.
有沒(méi)有更好的方法
使用 EnvironmentPostProcessor
晰赞,這里會(huì)在加載配置類之前執(zhí)行稼病。增加類,實(shí)現(xiàn)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并不會(huì)加載它然走,還需要配置一下
在resources
下面增加文件夾META-INF/spring.factories
,增加配置
org.springframework.boot.env.EnvironmentPostProcessor=\
com.xxxx.CustomerConfigLoadProcessor
去掉AdminApiApplication
中的setConfigProperties()
,運(yùn)行,成功啟動(dòng).