Spring Boot集成webService
在pom添加依赖
<!--WerbService CXF依赖 start-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
</dependency>
<!--WerbService CXF依赖 end-->
提供webservice接口
@WebService(name = "MesService", // 暴露服务名称
targetNamespace = "http://mes.jingchuang.com/"// 命名空间,一般是接口的包名倒序
)
public interface T100Service {
/**
* 1.【sendMaterialInfo】接收T100料件信息
*
* @param requestMaterialInfoData
* @return Result
*/
@WebMethod
Result sendMaterialInfo(RequestMaterialInfoData requestMaterialInfoData);
}
实现webservice的方法
@Service
@Transactional
@WebService(
// 对外发布的服务名,指定Web Service的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service
serviceName = "MesService",
//portName:wsdl:portName的值。缺省值为WebService.name+Port
portName = "UserServiceImpl",
// 命名空间(默认的值为 “http://包名/” (也可为域名) ,可以通过此变量指定一个自定义的targetNamespace值)
targetNamespace = "http://mes.jingchuang.com/",
// 对外提供的接口
endpointInterface = "com.jingchuang.mes.t100service.service.T100Service")
@Component
@Slf4j
public class T100ServiceImpl implements T100Service {
/**
* 1.【sendMaterialInfo】接收****料件信息
*
* @param requestMaterialInfoData
* @return
*/
//region 接收料件信息
@Override
@Transactional
public Result sendMaterialInfo(RequestMaterialInfoData requestMaterialInfoData) {
return ResultGenerator.fail("请输入正确的操作码");
}
//endregion
}
配置并发布
@Configuration
public class WebServiceConfig {
// 注意更改此方法名:public ServletRegistrationBean dispatcherServlet()
// 把默认映射覆盖掉了,把这个名字改掉,控制类方法就能访问了,可以正常其他请求url,webservice服务也正常。
@Bean
public ServletRegistrationBean disServlet(){
return new ServletRegistrationBean(new CXFServlet(),"/service/*");//发布服务名称
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus()
{
return new SpringBus();
}
@Autowired
public T100Service t100Service;
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint=new EndpointImpl(springBus(), t100Service);//绑定要发布的服务
endpoint.publish("/zhlxmes"); //显示要发布的名称
endpoint.getInInterceptors().add(new AuthInterceptor()); //增加用户名密码验证
return endpoint;
}
}
增加用户名,密码验证配置
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
private SAAJInInterceptor saa = new SAAJInInterceptor();
private static final String USER_NAME = "t100";
private static final String USER_PASSWORD = "t100";
public AuthInterceptor() {
super(Phase.PRE_PROTOCOL);
getAfter().add(SAAJInInterceptor.class.getName());
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
SOAPMessage mess = message.getContent(SOAPMessage.class);
if (mess == null) {
saa.handleMessage(message);
mess = message.getContent(SOAPMessage.class);
}
SOAPHeader head = null;
try {
head = mess.getSOAPHeader();
} catch (Exception e) {
logger.error("getSOAPHeader error: {}", e.getMessage(), e);
}
if (head == null) {
throw new Fault(new IllegalArgumentException("找不到Header,无法验证用户信息"));
}
NodeList users = head.getElementsByTagName("username");
NodeList passwords = head.getElementsByTagName("password");
if (users.getLength() < 1) {
throw new Fault(new IllegalArgumentException("找不到用户信息"));
}
if (passwords.getLength() < 1) {
throw new Fault(new IllegalArgumentException("找不到密码信息"));
}
String userName = users.item(0).getTextContent().trim();
String password = passwords.item(0).getTextContent().trim();
if (USER_NAME.equals(userName) && USER_PASSWORD.equals(password)) {
logger.debug("admin auth success");
} else {
SOAPException soapExc = new SOAPException("认证错误");
logger.debug("admin auth failed");
throw new Fault(soapExc);
}
}
}
客户端
客户端可以通过两种方式调用服务端。
1 .通过JaxWsDynamicClientFactory方式
public static Object doPostSoap(Class<?> clazz, String postUrl, String methodName, String xml) {
try {
Object[] objects;
if (postUrl.equals("dev")) {
objects = new Object[]{TestConten};
} else {
log.info("【T00/doPostSoap发送】:\n" + xml);
// 创建动态客户端
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
Client client = dcf.createClient(postUrl);
// 设置超时单位为毫秒
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setConnectionTimeout(15000);
policy.setAllowChunking(false);
policy.setReceiveTimeout(30000);
conduit.setClient(policy);
// invoke("方法名",参数1,参数2,参数3....);
objects = client.invoke(methodName, xml);
log.info("【T00/doPostSoap接收】:\n" + objects[0].toString());
// 需要密码的情况需要加上用户名和密码
// client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME,PASS_WORD));
}
Log loginfo = new Log();
loginfo.setType("info");
loginfo.setName("【请求T100接口】-doPostSoap");
loginfo.setContent(xml);
loginfo.setMethodName(methodName);
loginfo.setCreateTime(new Date());
loginfo.setCreateUserId(BaseContextHandler.getUserID());
loginfo.setCreateUserName(BaseContextHandler.getUsername());
loginfo.setResult(objects[0].toString());
try {
logService.save(loginfo);
} catch (Exception ex) {
log.info("保存日志错误" + ex.getMessage());
}
if (objects.length <= 0) {
return null;
}
ReceivingData receivingData = (ReceivingData) XsteamUtil.toBean(ReceivingData.class, objects[0].toString());
// receivingData = (ReceivingData)XsteamUtil.toBean(ReceivingData.class,TestConten);
Object rulest = XsteamUtil.toBean(clazz, receivingData.getPayload().getParam().getData());
return rulest;
} catch (Exception e) {
System.out.println("错误信息:" + e.getMessage());
return null;
}
}
这种方式要注意的就是,如果调用的服务接口返回的是一个自定义对象,那么结果Object[]中的数据类型就成了这个自定义对象(组件帮你自动生成了这个对象),
但是你本地可能并没有这个类,所以需要自行转换处理,最简单的是新建一个跟返回结果一模一样的类进行强转。
客户端需要在header中添加用户信息,这样才能通过服务器的验证。
public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String username;
private String password;
public ClientLoginInterceptor(String username, String password) {
super(Phase.PREPARE_SEND);
this.username = username;
this.password = password;
}
@Override
public void handleMessage(SoapMessage soap) throws Fault {
List<Header> headers = soap.getHeaders();
Document doc = DOMUtils.createDocument();
Element auth = doc.createElement("authrity");
Element username = doc.createElement("username");
Element password = doc.createElement("password");
username.setTextContent(this.username);
password.setTextContent(this.password);
auth.appendChild(username);
auth.appendChild(password);
headers.add(0, new Header(new QName("tiamaes"),auth));
}
}
2、通过wsimport生成客户端代码
通过jdk自带的wsimport生成客户端代码。进入$JAVA_HOME/bin下,新建bin和src两个文件夹,执行以下命令:
wsimport -d ./bin -s ./src -keep http://localhost:8080/services/layout?wsdl
其中几个参数有以下几个,
-d:生成客户端执行类的class文件存放目录,
-s:生成客户端执行类的源文件存放目录,
-p:定义生成类的包名
-verbose:显示生成过程
需要注意的是:无论-d或是-s后的目录必须在文件系统中存在,否则报错。
将src下连同包一起复制到程序中,编写客户端入口类:
public class WSClient {
private static final Logger LOG = LoggerFactory.getLogger(WSClient.class);
private static final String USER_NAME = "admin";
private static final String PASS_WORD = "pass";
public static void main(String[] args) {
//方式二:通过wsimport生成客户端代码
LayoutServiceImpl impl = new LayoutServiceImpl();
impl.setHandlerResolver(new HandlerResolver() {
@Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new ClientHandler(USER_NAME, PASS_WORD));
return handlerList;
}
});
try {
LayoutService layoutService = impl.getLayoutImpl();
String result_1 = layoutService.sayHello("jack");
LOG.debug("[2]sayHello:" + result_1);
OperationResult result_2 = layoutService.addLayout("name", "content");
LOG.debug("[2]addLayout result_succeed:" + result_2.isSucceed());
LOG.debug("[2]addLayout result_msg:" + result_2.getMsg());
} catch (SOAPFaultException e) {
LOG.error("SOAPFaultException occurs:{}", e.getMessage());
}
}
该方式的好处是可以像调用本地接口一样调用服务端方法,简单明了。缺点就是会生成一堆文件。
用户的头信息设置类:
public class ClientHandler implements SOAPHandler<SOAPMessageContext> {
private String username;
private String password;
public ClientHandler(String username, String password) {
this.username = username;
this.password = password;
}
public boolean handleMessage(SOAPMessageContext ctx) {
//出站,即客户端发出请求前,添加表头信息
Boolean request_p = (Boolean)ctx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (request_p) {
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
if (hdr == null) hdr = env.addHeader();
//添加认证信息头
QName name = new QName("http://service.webservice.com/", "LayoutImpl");
SOAPHeaderElement header = hdr.addHeaderElement(name);
SOAPElement userElement = header.addChildElement("username");
userElement.addTextNode(username);
SOAPElement passElement = header.addChildElement("password");
passElement.addTextNode(password);
msg.saveChanges();
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
@Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
@Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}