写在前面:
由于在当前项目中需要基于springboot实现webservice服务发布,甲方推荐使用cxf模式,但网络上的一些教程很多都相对复杂,此处力争使用通俗易懂的方式实现基于cxf的webservice服务发布。
发布期间遇到的一个大坑是集成了cxf以后,工程里面原有的http请求失效了,在启动类重写了dispatcherServlet()以后,发现http请求中的所有FormData数据都无法传入,这是因为springboot自动注册的ServletRegistrationBean默认名称就为dispatcherServlet(),重写以后,会使得原有的请求失效,只保留了webservice的请求,因此中级解决方案就是将webservice配置中的ServletRegistrationBean改名,使其与springboot自动注册的名称不一致即可实现webservice请求与http请求共存!
1. 实现思路
2. 具体实现
2.1 maven依赖
为springboot集成cxf添加必要的maven依赖
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.1.11</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>3.1.11</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.1.11</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.1.11</version>
</dependency>
2.2 编写CxfConfig.java
import com.csg.service.*;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
* @Author
* @Date: 2019/9/19 15:04
* @Description:
**/
@Configuration
public class CxfConfig {
//默认servlet路径/*,如果覆写则按照自己定义的来
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Autowired
private Bus bus;
@Autowired
UserWebService userWebService;
@Autowired
OrgWebService OrgService;
@Autowired
RoleWebServiceroleWebService;
@Autowired
UserRoleRelationWebService userRoleRelationWebService;
@Bean
public Endpoint endpointIscUserWebService() {
EndpointImpl endpoint = new EndpointImpl(bus,userWebService);
endpoint.publish("/WebServiceSyncUser");//接口发布在 /UserWebService 目录下
return endpoint;
}
@Bean
public Endpoint endpointIscBizOrgService() {
EndpointImpl endpoint = new EndpointImpl(bus,orgService);
endpoint.publish("/WebServiceSyncBizOrg");//接口发布在 /BizOrgService 目录下
return endpoint;
}
@Bean
public Endpoint endpointIscRoleService() {
EndpointImpl endpoint = new EndpointImpl(bus,roleWebService);
endpoint.publish("/WebServiceSyncBizRole");//接口发布在 /RoleService 目录下
return endpoint;
}
@Bean
public Endpoint endpointIscUserRoleRelationWebService() {
EndpointImpl endpoint = new EndpointImpl(bus,userRoleRelationWebService);
endpoint.publish("/WebServiceSyncUserBizRoleRelation");//接口发布在 /UserRoleRelationWebService 目录下
return endpoint;
}
}
2.3 编写功能代码
此处以UserWebService 为例:
- UserWebService 接口部分
@WebService
public interface UserWebService {
@WebMethod
boolean syncUserRequest(SyncUserPack syncPack);
}
- UserWebServiceImpl具体实现部分
//name暴露的服务名称, targetNamespace:命名空间,设置为接口的包名倒写(默认是本类包名倒写). endpointInterface接口地址
@WebService(serviceName = "WebServiceSyncUser" ,targetNamespace ="http://service.csg.com/" ,endpointInterface = "com.csg.service.UserWebService")
@Component
public class UserWebServiceImpl extends BaseServiceImpl implements UserWebService {
@Override
public boolean syncUserRequest(SyncUserPack syncPack) {
UserEntity userEntity = syncPack.getEntities();
if (iscUserEntity.equals("") || iscUserEntity == null) {
throw new RRException("操作对象为空!");
}
String operate = userEntity.getOperate();
boolean operateResult = false;
switch (operate) {
case "ADD":
System.out.println("test-add");
break;
case "UPD":
System.out.println("test-update");
break;
case "DEL":
System.out.println("test-delete");
break;
default:
System.out.println("此次操作在基本操作之外,请修改后重新操作!");
break;
}
return operateResult;
}
}
2.4 发布成功
直接访问:http://127.0.0.1:8080/services就能看到自己发布的webservice接口了
3. 踩坑记录:关于CxfConfig中的dispatcherServlet()
一开始是直接从github抄的CxfConfig,看着网络上大多也是写这个,就无脑copy,谁知这个是个大坑。
CxfConfig.java中的dispatcherServlet
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}
直接使用dispatcherServlet导致的问题第一个是,http请求失效,只有webservice请求能用。原因在前方已写,此处不再赘述。
经过多方百度以后,决定重写dispatcherServlet,于是乎,就在启动类底下多了这么个Bean:
package com.csg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
@SpringBootApplication
@EnableScheduling
public class CsgApplication {
public static void main(String[] args) {
SpringApplication.run(CorsApplication.class, args);
}
/**
* 重写ServletRegistrationBean,解决集成cxf以后,http请求失效情况
* @return
*/
@Bean
public ServletRegistrationBean restServlet() {
//注解扫描上下文
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
//项目包名
applicationContext.scan("com.csg");
DispatcherServlet rest_dispatcherServlet = new DispatcherServlet(applicationContext);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(rest_dispatcherServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/*");
return registrationBean;
}
}
重写了这个bean以后,http请求能用了,webservice请求也能用了!!!开心~但是!!!!!!!!!经过多方测试,发现所有的FormData请求,参数都传不进去!参数全报空……
测试出结果的我已经不是茫然能描述的了……
后来后来,经过多方查找以后,终于揪出了罪魁祸首!就是它:
CxfConfig.java中的dispatcherServlet
@Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}
因为和springboot自动注册重名了!!!
所以,终极解决方案就是——改名字!
把CxfConfig.java中的dispatcherServlet改成dispatcher,解决了!撒花儿!
@Bean
public ServletRegistrationBean dispatcher() {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}