问题描述
某天,业务要求我们对某个webservice
接口做个开关,实现动态控制接口的放行与拦截。我们的实现方案是在nacos
上增加一个配置,同时在项目中获取该配置判断是否执行后续业务逻辑。看上去很简单的一个流程(后面却坑的我们好惨),我一顿操作就写完了代码通知测试同学可以提测了。结果过了一段时间后测试同学过来说我这个开关不好使,不能动态感知到配置变更。。。没办法了,只能去看看什么原因导致的。
相关代码
webservice
实现类
package com.sunl888.nacossync.webservice.impl;
import com.sunl888.nacossync.config.AppConfig;
import com.sunl888.nacossync.webservice.TestWebService;
import jakarta.annotation.Resource;
import jakarta.jws.WebService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@WebService(
serviceName = "testWebService",
targetNamespace = "https://service.nacos.sync.sunl888.com/",
endpointInterface = "com.sunl888.nacossync.webservice.TestWebService")
@Service
@Slf4j
public class TestWebServiceImpl implements TestWebService {
@Resource
private AppConfig appConfig;
@Override
public String sync(String request) {
if (!Boolean.TRUE.equals(appConfig.getEnabled())) {
return "未执行业务逻辑";
}
return "OK";
}
}
应用配置类
package com.sunl888.nacossync.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
@Data
@RefreshScope
@Configuration
public class AppConfig {
@Value("${switch.enabled:}")
private Boolean enabled;
}
webservice
配置类
package com.sunl888.nacossync.config;
import com.sunl888.nacossync.client.TestWebServiceRemote;
import com.sunl888.nacossync.webservice.TestWebService;
import jakarta.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.jaxws.JaxWsClientFactoryBean;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebServiceConfig {
@Bean
public ServletRegistrationBean<CXFServlet> cxfServletServletRegistrationBean() {
return new ServletRegistrationBean<>(new CXFServlet(), "/ws/*");
}
@Bean
public Endpoint testEndpoint(Bus bus, TestWebService testWebService) {
Endpoint endpoint = new EndpointImpl(bus, testWebService);
endpoint.publish("/testWebService");
return endpoint;
}
}
问题排查
测试
以上就是关键代码,启动后我们就可以通过构建 JaxWsProxyFactoryBean
来调用这个 webservice
接口了,我测试了一下,系统确实没有获取到 appConfig
的最新配置。
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("http://localhost:8088/ws/testWebService?wsdl");
jaxWsProxyFactoryBean.setServiceClass(TestWebServiceRemote.class);
TestWebServiceRemote remote = jaxWsProxyFactoryBean.create(TestWebServiceRemote.class);
@RefreshScope知识
通过翻阅相关资料,我们知道被 @RefreshScope
注解的实例,在扫描生成 BeanDefiniton
时被动态修改了,实际上是向 spring ioc
中注册了两个 Bean
定义,一个是与 beanName
同名但类型是 LockedScopedProxyFactoryBean.class
的代理工厂 Bean
,另一个是 scopedTarget.beanName
的 Bean。当我们修改了nacos配置后,spring
实际上只会修改其中一个 bean
,那么问题就出在webservice
引用的AppConfig
实例上了,很可能引用的是原始实例。
调试
我们从webservice
服务的publish
流程开始debug
,经过一段时间的排查发现 webservice
在 publish
服务时会自动从 spring
中获取依赖并注入到服务中,见如下图:
从图中我们可以看出来webservice框架默认是根据type取第一个name对应的bean,那么我们只要指定@Resource(name="appConfig")是不是就可以了呢?接下来我把代码改成如下方式:
package com.sunl888.nacossync.webservice.impl;
import com.sunl888.nacossync.config.AppConfig;
import com.sunl888.nacossync.webservice.TestWebService;
import jakarta.annotation.Resource;
import jakarta.jws.WebService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@WebService(
serviceName = "testWebService",
targetNamespace = "https://service.nacos.sync.sunl888.com/",
endpointInterface = "com.sunl888.nacossync.webservice.TestWebService")
@Service
@Slf4j
public class TestWebServiceImpl implements TestWebService {
@Resource(name="appConfig")
private AppConfig appConfig;
@Override
public String sync(String request) {
if (!Boolean.TRUE.equals(appConfig.getEnabled())) {
return "未执行业务逻辑";
}
return "OK";
}
}
经过一番测试发现果然可以正常感知到 nacos
的变更了。
结尾
这是测试项目地址:github.com/sunl888/nac… 希望能给大家提供一些帮助。