SpringBoot2 集成 Axis2
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
SpringBoot2 与 axis2 集成,采用 axis2-spring 包实现服务的注册,包括服务端和客户端。具体版本 SpringBoot 2.7.16,Axis2 1.7.9。
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
在已有的 SpringBoot 工程里添加 Axis2的包,提供 WebService 的服务,服务端结合 spring 并提供两种客户端的调用方式,一种是方案标准的拼 OMElement 方式;一种是通过纯 xml 字符串转 OMElement 的方式,各有各的使用场景。
需要对 springboot, servlet 有些了解。
提示:以下是本篇文章正文内容,下面案例可供参考
一、添加依赖 pom.xml
添加axis2的依赖包
1. 指定 axis2 的版本及 jar 包
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<dubbo.version>3.2.6</dubbo.version>
<spring-boot.version>2.7.16</spring-boot.version>
<!-- mac 需要指定 classifier -->
<os.detected.classifier>osx-x86_64</os.detected.classifier>
<!-- axis2的版本 -->
<axis2.version>1.7.9</axis2.version>
</properties>
<!-- axis -->
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb</artifactId>
<version>${axis2.version}</version>
<!-- 在 Spring mvc 已经依赖了 servlet 包,这里需要排除 -->
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-spring</artifactId>
<version>${axis2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-jaxws</artifactId>
<version>${axis2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-xmlbeans</artifactId>
<version>${axis2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-api</artifactId>
<version>1.2.22</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-http</artifactId>
<version>${axis2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>${axis2.version}</version>
</dependency>
<!-- axis2 services/listServices jsp -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
二、服务端代码
1.接口及实现类
接口类 HelloService:
public interface HelloService {
public String hello(String name);
}
实现类 HelloServiceImpl:
import com.example.dubbo.grpc.manage.ws.services.HelloService;
import org.springframework.stereotype.Service;
/**
* 简单实现示例
*/
// helloService 必须与 services.xml 里定义的 SpringBeanName 一致
@Service("helloService")
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "Hello " + name;
}
}
2.注入 Axis 的 Servlet
Axis 的服务端是通过 Servlet 提供服务的,SpringBoot 提供了注入 Bean 的方式注入 Servlet,源代码如下:
import lombok.extern.log4j.Log4j;
import org.apache.axis2.transport.http.AxisServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Objects;
@Configuration
@Log4j
public class Axis2WebServiceConfiguration {
/**
* 注册 Axis 的Servlet,指定 url-mapping 为 /service
* @return ServletRegistrationBean
*/
@Bean
public ServletRegistrationBean<AxisServlet> axisServlet() {
ServletRegistrationBean<AxisServlet> bean = new ServletRegistrationBean<>(new AxisServlet(), "/services/*");
String path = Objects.requireNonNull(getClass().getResource("/WEB-INF")).getPath();
log.info("axis2.repository.path:" + path);
bean.addInitParameter("axis2.repository.path", path);
bean.setLoadOnStartup(1);
return bean;
}
}
通过 /services/helloService?wsdl 访问时能进入该 servlet 。
三.Aixs2的约定配置
Axis2有自己约定的配置,主要是 目录结构 和 services.xml 文件,必须要按约定,否则会导致服务无法启动或启动后找不到服务。
1.目录结构
1.1 配置文件目录
- WEB-INF/conf/axis2.xml 这个文件启动时会有提示,但并不影响运行,找不到会有默认的。
- resources 是指 springboot 下的资源文件
resources
├── WEB-INF
│ ├── conf
│ │ └── axis2.xml
│ └── services
│ └── axis2
│ └── META-INF
│ └── services.xml
├── application.yml
├── log4j.properties
1.2 管理页面目录
有这些文件后访问 /services/listServices 会出现管理界面。
- 官网下载 http://archive.apache.org/dist/axis/axis2/java/core/1.7.9/axis2-1.7.9-src.zip,解压后把 modules/webapp/src/main/webapp 复制到工程里,复制后的目录结构如下图:
webapp
├── WEB-INF
│ ├── include
│ │ ├── LeftFrame.jsp
│ │ ├── adminfooter.inc
│ │ ├── adminfooter.jsp
│ │ ├── adminheader.jsp
│ │ ├── footer.inc
│ │ ├── header.inc
│ │ ├── httpbase.jsp
│ │ ├── link-footer.inc
│ │ └── link-footer.jsp
│ ├── tags
│ │ └── status.tag
│ └── views
│ ├── admin
│ │ ├── Login.jsp
│ │ ├── SelectService.jsp
│ │ ├── activateService.jsp
│ │ ├── admin.jsp
│ │ ├── deactivateService.jsp
│ │ ├── editServiceParameters.jsp
│ │ ├── engageGlobally.jsp
│ │ ├── engageToOperation.jsp
│ │ ├── engageToService.jsp
│ │ ├── engageToServiceGroup.jsp
│ │ ├── errorModule.jsp
│ │ ├── globalModules.jsp
│ │ ├── listModules.jsp
│ │ ├── listServiceGroups.jsp
│ │ ├── listServices.jsp
│ │ ├── listSingleService.jsp
│ │ ├── upload.jsp
│ │ ├── viewContexts.jsp
│ │ ├── viewGlobalChains.jsp
│ │ ├── viewOperationSpecificChains.jsp
│ │ ├── viewServiceContext.jsp
│ │ ├── viewServiceGroupContext.jsp
│ │ └── viewphases.jsp
│ ├── listFaultyService.jsp
│ └── listServices.jsp
└── axis2-web
├── Error
│ ├── AuthError.html
│ ├── GenError.html
│ ├── error404.jsp
│ └── error500.jsp
├── HappyAxis.jsp
├── MainFrame.jsp
├── css
│ └── axis-style.css
├── error.jsp
├── images
│ ├── asf-logo.gif
│ ├── axis.gif
│ ├── axis.jpg
│ └── axis_l.jpg
└── index.jsp
1.3 工程的完整目录
我这的工程里还有 dubbo,这个不影响,关注 axis2 相关的即可,工程目录结构如下图:
2 配置文件 services.xml
services.xml 的文件位置一定要正确,src/main/resources/WEB-INF/services/axis2/META-INF/services.xml,否则会找不到注册的服务
<?xml version="1.0" encoding="UTF-8"?>
<serviceGroup>
<!-- 定义服务名 helloService -->
<service name="helloService" scope="application">
<description>Hello Service Demo</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<!-- 指定 class 的方式同样可以使用
<parameter name="ServiceClass">com.example.manage.ws.services.impl.HelloServiceImpl</parameter>
-->
<!-- 需要在 HelloServiceImpl 添加注解 @Service("helloService") -->
<parameter name="SpringBeanName">helloService</parameter>
<parameter name="ServiceObjectSupplier">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
</service>
</serviceGroup>
四.启动 WebService 服务
和正常的 SpringBoot的启动方式一致
SpringBoot启动类
Spring Boot的启动类如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Axis2Application {
public static void main(String[] args) {
SpringApplication.run(Axis2Application.class, args);
}
}
SpringBoot启动日志
1. 启动成功的日志如下图
2. 访问 http://127.0.0.1:8080/services/listServices 如下图:
3. 访问 http://127.0.0.1:8080/services/helloService?wsdl 如下图:
五.客户端实现
提供两个客户端类,一个参考官方标准,一个通过 xml 的纯字符串。
客户端-标准玩法
import lombok.extern.log4j.Log4j;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;
/**
* 通过拼 Dom 实现服务调用,官方推荐的标准玩法
* 发出的报文如下:
* <code><?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><hello xmlns="http://impl.services.ws.manage.grpc.dubbo.example.com"><arg0>world</arg0></hello></soapenv:Body></soapenv:Envelope></code>
*/
@Log4j
public class Axis2StandardClient {
/**
* 服务端调用地址,最后面的 "?wsdl" 有没有都可以
*/
private static final String TARGET_URL = "http://127.0.0.1:8080/services/helloService?wsdl";
/**
* 服务调用
* @param url 服务地址
* @param targetNamespace 命名空间,可通过 wsdl 查看
* @param method 方法名
* @param args 方法入参
* @return 返回报文
* @throws AxisFault 标准异常
*/
public static String invoke(String url, String targetNamespace, String method, Object... args) throws AxisFault {
RPCServiceClient serviceClient = new RPCServiceClient();
Options options = serviceClient.getOptions();
EndpointReference targetEPR = new EndpointReference(url);
options.setTimeOutInMilliSeconds(10*1000);
options.setTo(targetEPR);
OMElement methodElement = getMethodElement(targetNamespace, method, args);
OMElement resElement = serviceClient.sendReceive(methodElement);
return resElement.getFirstElement().toString();
// 以下通过 QName 的方式调用,也是标准玩法之一
/*QName qName = new QName(targetNamespace, method);
Object[] params = new Object[]{"world"};
Class<?>[] types = new Class[] {String.class};
Object[] response = serviceClient.invokeBlocking(qName, params, types);
return response[0].toString();*/
}
/**
* 根据参数个数拼报文
* @param targetNamespace 命名空间
* @param method 方法名
* @param args 方法入参
* @return OMElement 对象
*/
private static OMElement getMethodElement(String targetNamespace, String method, Object[] args) {
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace omNamespace = factory.createOMNamespace(targetNamespace, "");
OMElement methodElement = factory.createOMElement(method, omNamespace);
for (int i = 0,len = args.length; i < len; i++) {
OMElement element = factory.createOMElement("arg" + i, omNamespace);
element.setText(String.valueOf(args[i]));
methodElement.addChild(element);
}
methodElement.build();
return methodElement;
}
public static void main(String[] args) throws Exception {
// NameSpace 的地址可通过访问wsdl获取
// impl.services.ws.manage.grpc.dubbo.example.com
String response = invoke(TARGET_URL, "http://impl.services.ws.manage.grpc.dubbo.example.com", "hello", "world");
log.debug("response:" + response);
}
}
客户端-xml字符串玩法
import lombok.extern.log4j.Log4j;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
/**
* 简单暴力的 xml 字符串报文调用
*/
@Log4j
public class AxisSimpleStringClient {
/**
* 服务端调用地址,最后面的 "?wsdl" 有没有都可以
*/
private static final String TARGET_URL = "http://127.0.0.1:8080/services/helloService?wsdl";
/**
* 通过模板拼 xml 报文,模板化程度需要根据使用情况调整,这里只是用来做为示例,仅控制了一个入参
* @param param
* @return SOAPEnvelope
*/
private static SOAPEnvelope createMessage(String param) {
// NameSpace 的地址可通过访问wsdl获取
// impl.services.ws.manage.grpc.dubbo.example.com
String xml = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header/><soapenv:Body><hello xmlns=\"http://impl.services.ws.manage.grpc.dubbo.example.com\"><arg0>%s</arg0></hello></soapenv:Body></soapenv:Envelope>";
String message = String.format(xml, param);
// 关键是这段代码,能将 xml 字符串转换成 SOAPEnvelope 对象
return OMXMLBuilderFactory.createSOAPModelBuilder(
new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)),
StandardCharsets.UTF_8.displayName()).getSOAPEnvelope();
}
public static void sendMessage() throws Exception {
EndpointReference target = new EndpointReference(TARGET_URL);
RPCServiceClient client = new RPCServiceClient();
Options options = client.getOptions();
options.setTimeOutInMilliSeconds(10*1000);
options.setTo(target);
SOAPEnvelope message = createMessage("world");
// sendReceive 时不能直接使用 SOAPEnvelope 对象,否则会抛出以下异常
// org.apache.axis2.AxisFault: Can not output XML declaration, after other output has already been done.
OMElement request = message.getBody().getFirstElement();
if (log.isDebugEnabled()) {
log.debug(message.toString());
}
OMElement response = client.sendReceive(request);
if (log.isDebugEnabled()) {
log.debug(response.toString());
}
}
public static void main(String[] args) throws Exception {
sendMessage();
}
}
总结
提示:这里对文章进行总结:
参考了一些同学的内容,或多或少有点不足,就自己整理了一个,比较完整,完整工程代码https://gitee.com/yifur/dubbo-grpc.git。