Springboot整合CXF实例
添加依赖
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.5</version>
</dependency>
定义要发布的接口和实现类
@WebService(name = "CommonService", // 暴露服务名称
targetNamespace = "http://www.service.mf.com")// 命名空间,一般是接口的包名倒序
public interface CommonService {
@WebMethod
Map<String, Object> addBills(@WebParam(name="bills")InBills bills);
@WebMethod
Map<String, Object> deleteBills(@WebParam(name="billNo")String billNo);
}
@WebService(serviceName = "CommonService", // webService的服务名称,与前面接口一致
targetNamespace = "http://www.service.mf.com", // 与前面接口一致
endpointInterface = "com.mf.service.CommonService") // 当前需要实现接口的完全限定名,指定做SEI服务端点接口
@Service
@Transactional
public class CommonServiceImpl implements CommonService {
@Resource
private InBillsRepository billsRepository;
@Override
public Map<String, Object> addBills(InBills bills) {
Map<String, Object> map = new HashMap<String, Object>();
InBills b = billsRepository.findByBillNo(bills.getBillNo());
if (b != null) {
Integer rows = billsRepository.updateInBills(bills);
if (rows != 1) {
map.put("success", false);
map.put("errorInfo", "相同单据数据更新失败");
return map;
} else {
map.put("success", true);
return map;
}
} else {
try {
billsRepository.save(bills);
map.put("success", true);
return map;
} catch (Exception e) {
map.put("success", false);
map.put("errorInfo", "添加失败");
return map;
}
}
}
@Override
public Map<String, Object> deleteBills(String billNo) {
Map<String, Object> map = new HashMap<String, Object>();
Integer rows = billsRepository.deleteByBillNo(billNo);
if (rows != 1) {
map.put("success", false);
map.put("errorInfo", "删除失败");
return map;
} else {
map.put("success", true);
return map;
}
}
}
定义配置类
@Configuration
public class CxfWebServiceConfig {
@Autowired
private Bus bus;
@Autowired
CommonService service;
/**
* 配置ServletRegistrationBean时,需要注意设置方法的名称或者bean的名称时,
* 不要和默认的DispatcherServlet类重名了,会导致原先的mvc接口无法使用,因为被覆盖了
* 修改访问的路径可以通过设置ServletRegistrationBean来修改,但同时,要注意需要设置bean的名称为cxfServletRegistration,
* 不然会造成注册多个CXFServlet的。具体原因可查看自动配置类:org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration。
* @return
*/
// @Bean("cxfServletRegistration")
// public ServletRegistrationBean dispatcherServlet() {
// //注册servlet 拦截/ws 开头的请求 不设置 默认为:/services/*
// return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
// }
/**
* 申明业务处理类 当然也可以直接 在实现类上标注 @Service
* @return
*/
// @Bean
// public CommonService authorService() {
// return new CommonServiceImpl();
// }
/*
* 绑定接口类,描述接口实现方法,以及接口名称.如果有多个接口需要实现,可以添加多个该方法(设置方法名不同,发布路径不同即可)
* 发布endpoint
* 默认服务在Host:port/services/*路径下
* 修改路径为/services/CommonService
* wsdl文档路径为,http://localhost:8380/services/CommonService?wsdl
*/
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(bus, service);
endpoint.getInInterceptors().add(new AuthInterceptor());//添加校验拦截器
endpoint.publish("/CommonService");//接口发布在 /CommonService 目录下
return endpoint;
}
}
添加用户名密码校验
package com.mf.config;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import java.util.List;
import org.apache.cxf.headers.Header;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
// 由于AbstractPhaseInterceptor无无参数构造器,使用继承的方式,需要显示调用父类有参数的构造器
public AuthInterceptor() {
super(Phase.PRE_INVOKE);// 该拦截器将会调用之前拦截SOAP消息
}
// 实现自己的拦截器时,需要实现handleMessage方法。
// handleMessage方法中的形参就是被拦截到的Soap消息
// 一旦程序获取了SOAP消息,剩下的事情就可以解析SOAP消息或修改SOAP消息
@Override
public void handleMessage(SoapMessage msg) throws Fault {
// 从这里可以看出,我们已经拦截到了SOAP消息
System.out.println("-------" + msg);
// 得到SOAP消息所有Header
List<Header> headers = msg.getHeaders();
// 如果没有Header
if (headers == null || headers.size() < 1) {
throw new Fault(new IllegalArgumentException("请输入用户名密码,不能调用"));
}
// 假如要求第一个Header携带了用户名,密码信息
Header firstHeader = headers.get(0);
Element ele = (Element) firstHeader.getObject();
NodeList userName = ele.getElementsByTagName("userName");
NodeList password = ele.getElementsByTagName("password");
if (userName.getLength() != 1) {
throw new Fault(new IllegalArgumentException("用户名的格式不正确!"));
}
if (password.getLength() != 1) {
throw new Fault(new IllegalArgumentException("密码的格式不正确!"));
}
// 得到第一个userId元素里的文本内容,以该内容作为用户名字
String user = userName.item(0).getTextContent();
String pwd = password.item(0).getTextContent();
// 实际项目中,应该去查询数据库,该用户名密码是否被授权调用web service
if (!user.equals("root") || !pwd.equals("root")) {
throw new Fault(new IllegalArgumentException("用户名密码不正确!"));
}
}
}
生成客户端
-
创建新项目(该项目也需要导入cxf的依赖)
-
file–>new–>other–>Web Service Client
测试
@RunWith(SpringRunner.class) @SpringBootTest public class TestCase { public static void main(String[] args) { CommonService_Service service = new CommonService_Service(); // 此处返回的只是远程Web Service的代理 CommonService comm = service.getCommonServiceImplPort(); Client client = ClientProxy.getClient(comm); // 参数为输入的用户名,密码 client.getOutInterceptors().add(new LoginInterceptor("root","root")); //数据准备 InBills bills = new InBills(); bills.setBillNo("11"); bills.setCtnNo("e21313,412fsf,5436g,greg"); bills.setInstockTime("20200816"); bills.setOriginalWarehouse("r2ds123"); bills.setSingle("14dcf"); bills.setTargetWarehouse("421eder"); bills.setType("11"); //添加 System.err.println("添加:"+comm.addBills(bills).getEntry().get(0).getValue()); //System.err.println(comm.deleteBills("20").getEntry().get(0).getValue()); System.out.println("-------------------------------"); } }
服务端有用户名密码校验还需配置
package com.mf.config; import java.util.List; import javax.xml.namespace.QName; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.w3c.dom.Document; import org.w3c.dom.Element; public class LoginInterceptor extends AbstractPhaseInterceptor<SoapMessage>{ private String userName; private String password; public LoginInterceptor(String userName,String password) { super(Phase.PREPARE_SEND);//在准备发送SOAP消息时启用该拦截器 this.userName=userName; this.password=password; } @Override public void handleMessage(SoapMessage msg) throws Fault { List<Header> headers=msg.getHeaders(); //创建Document对象 Document doc=DOMUtils.createDocument(); Element ele=doc.createElement("authHeader"); //此处创建的元素应该按照服务器那边的要求 Element idEle=doc.createElement("userName"); idEle.setTextContent(userName); Element passEle=doc.createElement("password"); passEle.setTextContent(password); ele.appendChild(idEle); ele.appendChild(passEle); /** * 上面代码生成了一个如下XML文档片段 * <authHeader> * <userId>root</userId> * <userPass>root</userPass> * </authHeader> */ //把ele元素包装成Header,并添加到SOAP消息的Header列表中 headers.add(new Header(new QName("root"),ele)); } }
Good Luck