WebService的发布及客户端的调用

一.目录

1.JAX-WS发布WebService

1.1 创建一个简单的WS

1.2 打包部署和发布

2.CXF+Spring发布WebService

3.客户端的调用方式

二.正文

1. JAX-WS发布WebService

JAX-WS (Java API for XML Web Services) 是一组专门用于实现 XML Web Services 的 Java API。JDK 1.6 自带 JAX-WS 版本为 2.1。不过,JAX-WS 只提供 web services 的基础功能,所以如果你希望实现 web services 的复杂功能,比如 WS-Security,WS-Policy,WS-RM 等,那就需要切换到 Apache CXF 、Metro 或者 Axis。

1.1 创建一个简单的WebService

首先,还是来看一下jdk API1.7中javax.jws包下面关于注解@WebService的描述吧。它是用来标记一个被定义为Web Service的实现类或接口的。元素endpointInterface是用来指明服务端接口的。这就是我们在定义接口和实现的时候要用的了。

WebService的发布及客户端的调用

接着,新建一个Java项目,定义好服务接口和具体实现类,如下:

package com.lglan.webservice.server;

import javax.jws.WebService;

@WebService
public interface GreetingService { public String greeting(String userName);
}
package com.lglan.webservice.server.impl;

import java.util.Calendar;

import javax.jws.WebService;

import com.lglan.webservice.server.GreetingService;

@WebService(endpointInterface="com.lglan.webservice.server.GreetingService")
public class GreetingServiceImpl implements GreetingService { public String greeting(String userName) {
// TODO Auto-generated method stub
return "Hello " + userName + ", currentTime is "
+ Calendar.getInstance().getTime();
} }

最后,就是来发布服务了,这里用到了javax.xml.ws.Endpoint类的publish方法,详见API文档

public static Endpoint publish(String address,
Object implementor)
Creates and publishes an endpoint for the specified implementor object at the given address.
package com.lglan.webservice.server.app;

import javax.xml.ws.Endpoint;

import com.lglan.webservice.server.impl.GreetingServiceImpl;

public class WebServiceMain {

    public static void main(String[] args) {
System.out.println("web service start");
GreetingServiceImpl implementor= new GreetingServiceImpl();
String address="http://localhost:8080/greetingService";
Endpoint.publish(address, implementor);
System.out.println("web service started");
} }

运行上面的类,在浏览器中请求http://localhost:8080/greetingService?wsdl就能看到我们发布的webservice接口了。

1.2 打包部署和发布

以上我们是通过在IDE中直接执行java application来发布服务的,那如何把项目进行打包部署呢?当然,最常见的方法就是把项目发布成web项目然后在web容器中启动(该方法在第二部分介绍)。这里,最简单的方法就是在jre中直接运行编译后的WebServiceMain.class文件,跟我们在IDE中一样,不需要容器,只要有jre环境就行。

用maven的assembly插件来打包是最方便的了(当然,要用maven来管理项目,用maven谁用谁知道),只要在项目的pom.xml中配置assembly插件,然后在assembly.xml中描述你想要怎么打包就行了。

pom.xml的<plugins></plugins>节点中加入:

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration> <!--描述文件路径-->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</configuration>
<executions> <!--执行器 mvn assembly:assembly-->
<execution>
<id>make-assembly</id> <!--名字任意 -->
<phase>package</phase> <!-- 绑定到package生命周期阶段上 -->
<goals>
<goal>single</goal> <!-- 只运行一次 -->
</goals>
</execution>
</executions>
</plugin>

assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>assembly</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/assembly/bin</directory><!-- 源路径 -->
<outputDirectory>bin</outputDirectory><!-- 输出路径 -->
<fileMode>0755</fileMode><!-- 文件权限rwx -->
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>

执行maven install 或 package命令后,项目会被打包成三个文件夹:/bin, /conf, /lib,其中/conf下面放的是配置文件,/lib下面放的是项目依赖的所有的jar包(包括项目自身的jar包)。那么,/bin目录下是什么呢?这里放的是我们自己编写的用来启动和停止服务的脚本,例如:windows下面的.bat文件和Unix或Linux下的.sh脚本文件。好吧,我们就来写启动脚本吧,在项目中新建src/main/assembly/bin路径,创建脚本如下:

start.bat (其中goto start … :start中间代码是被跳过的,这里用这种方法来达到注掉一段代码的目的了,WebService的发布及客户端的调用)

@echo off
setlocal enabledelayedexpansion goto start
::方法一,用start命令来启动java.exe
echo %JAVA_HOME%
set jre="%JAVA_HOME%\bin\java"
set tempclass="%JAVA_HOME%\lib\dt.jar";"%JAVA_HOME%\lib\tools.jar";
cd ..\lib
for %%i in (*) do set tempclass=!tempclass!;%%i;
start "GreetingService" %jre% -classpath !tempclass! com.lglan.webservice.server.app.WebServiceApp
:start ::方法二,本地配置好jdk的环境变量,直接执行java指令
set LIB_JARS=""
cd ..\lib
for %%i in (*) do set LIB_JARS=!LIB_JARS!;..\lib\%%i;
java -Xms64m -Xmx1024m -XX:MaxPermSize=64M -classpath ..\conf;%LIB_JARS% com.lglan.webservice.server.app.WebServiceApp endlocal

start.sh

#!/bin/bash
LIB_JARS=.
for i in 'ls lib/*.jar'
do
LIB_JARS=$LIB_JARS:./lib/$i
done
$JAVA_HOME/bin/java -classpath $LIB_JARS com.lglan.webservice.server.app.WebServiceApp

stop.sh

#!/bin/sh
APP_MAIN=com.lglan.webservice.server.app.WebServiceApp tradePortalPID= getTradeProtalPID(){
javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAIN`
if [ -n "$javaps" ]; then
tradePortalPID=`echo $javaps | awk '{print $1}'`
else
tradePortalPID=
fi
} shutdown(){
getTradeProtalPID
echo "========================================================================================"
if [ $tradePortalPID -ne ]; then
echo -n "Stopping $APP_MAIN(PID=$tradePortalPID)..."
kill - $tradePortalPID
if [ $? -eq ]; then
echo "[Success]"
echo "================================================================================"
else
echo "[Failed]"
echo "================================================================================"
fi
getTradeProtalPID
if [ $tradePortalPID -ne ]; then
shutdown
fi
else
echo "$APP_MAIN is not running"
echo "===================================================================================="
fi
} shutdown

好了,按照上面的方法用maven打个包,解压之后,在相应的操作系统下执行start脚本就可以发布服务了。

2.CXF+Spring发布WebService

首先,要导入依赖的jar包,直接在pom.xml的<dependencies></dependencies>中添加

<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-api</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>2.4.6</version>
</dependency>

通过Spring来管理服务类,由cxf的jaxws.xsd定义了服务端的节点jaxws:endpoint,配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.lglan.webservice.server"/> <jaxws:endpoint id="GreetingService" implementor="com.lglan.webservice.server.impl.GreetingServiceImpl" address="http://localhost:8080/greetingService" /> </beans>

配置完成了,接下来写个main方法来启动服务吧。

package com.lglan.webservice.server.app;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class WebServiceStart {

    public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:cxf-spring.xml");
ac.start();
System.in.read();
} }

直接运行上面的类,在浏览器中输入http://localhost:8080/greetingService?wsdl就能看到发布成功了。

当然,这里还可以通过cxf-rt-frontend-jaxws包下面的org.apache.cxf.jaxws.JaxWsServerFactoryBean来创建服务,不需要在spring配置中添加服务类的bean了,代码如下:

package com.lglan.webservice.server.app;

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import com.lglan.webservice.server.impl.GreetingServiceImpl; public class WebServiceMain2 { public static void main(String[] args) {
//工厂模式创建jax
JaxWsServerFactoryBean jwsFactoryBean = new JaxWsServerFactoryBean();
jwsFactoryBean.setServiceClass(GreetingServiceImpl.class);
jwsFactoryBean.setAddress("http://localhost:8080/greetingService");
//获取一个server对象,并启动
Server server = jwsFactoryBean.create();
server.start(); }
}

最后再讲一下怎么在web容器中发布Web Service :

第一步:将原来的Java项目转成Web项目,具体步骤请参考:

http://blog.sina.com.cn/s/blog_7deb4bd601019llp.html

第二步:项目的根路径下(这里对应webapp目录)添加web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:cxf-spring.xml</param-value><!-- 这里指定spring配置文件 -->
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping> <welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>

还有,不要忘记修改原endpoint节点的address,在cxf-spring.xml配置文件中,修改后如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.lglan.webservice.server"/> <jaxws:endpoint id="GreetingService" implementor="com.lglan.webservice.server.impl.GreetingServiceImpl" address="/greetingService" /> </beans>

第三步,在tomcat中启动项目,访问配置的服务路径http://localhost:8080/cxf_ws_demo/services/greetingService?wsdl,发布成功!

WebService的发布及客户端的调用

二.客户端的调用方式

上面讲服务发布的方法,提到了三种:javax.xml.ws.Endpoint类的publish方法;cxf-rt-frontend-jaxws包下面的org.apache.cxf.jaxws.JaxWsServerFactoryBean类来创建服务;在spring配置文件中配置服务类。那么,客户端的调用也有类似的几种方法。

在写实现调用的方法之前,我们先来搭建一个客户端项目,在IDE中建一个maven管理的Java项目,pom文件中加了cxf依赖的jar包(同服务端,可选),cmd中执行cxf指令:wsdl2java –d D:\client –client http://localhost:8080/greetingService?wsdl,导入刚才cxf生成的客户端依赖包,准备工作完成。

方法1:继承javax.xml.ws.service类来创建service实例,该service类为我们提供了一个service()方法来获得服务,然后提供getPort()方法来动态调用服务接口。可喜的是,这个类完全不用我们自己来写,通过上面cxf指令生成的客户端代码包中已经给我们建好了,我们只要拿来用就行了。此例中,service类代码如下:

package com.lglan.webservice.server.impl;

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
import com.lglan.webservice.server.GreetingService;
import javax.xml.ws.Service; /**
* This class was generated by Apache CXF 2.2.8
* Sat Nov 15 20:17:20 CST 2014
* Generated source version: 2.2.8
*
*/ @WebServiceClient(name = "GreetingServiceImplService",
wsdlLocation = "http://localhost:8080/greetingService?wsdl",
targetNamespace = "http://impl.server.webservice.lglan.com/")
public class GreetingServiceImplService extends Service { public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://impl.server.webservice.lglan.com/", "GreetingServiceImplService");
public final static QName GreetingServiceImplPort = new QName("http://impl.server.webservice.lglan.com/", "GreetingServiceImplPort");
static {
URL url = null;
try {
url = new URL("http://localhost:8080/greetingService?wsdl");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from http://localhost:8080/greetingService?wsdl");
// e.printStackTrace();
}
WSDL_LOCATION = url;
} public GreetingServiceImplService(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
} public GreetingServiceImplService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
} public GreetingServiceImplService() {
super(WSDL_LOCATION, SERVICE);
} /**
*
* @return
* returns GreetingService
*/
@WebEndpoint(name = "GreetingServiceImplPort")
public GreetingService getGreetingServiceImplPort() {
return super.getPort(GreetingServiceImplPort, GreetingService.class);
} /**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values.
* @return
* returns GreetingService
*/
@WebEndpoint(name = "GreetingServiceImplPort")
public GreetingService getGreetingServiceImplPort(WebServiceFeature... features) {
return super.getPort(GreetingServiceImplPort, GreetingService.class, features);
} }

好了,我们可以通过以上类来获得服务接口并调用了,方法如下:

package com.lglan.webservice.client;

import java.net.URL;

import com.lglan.webservice.server.GreetingService;
import com.lglan.webservice.server.impl.GreetingServiceImplService; public class TestGreetingService3 {
public static void main(String[] args) throws Exception {
GreetingServiceImplService gs = new GreetingServiceImplService(new URL("http://localhost:8080/cxf_ws_demo/services/greetingService?wsdl"));
GreetingService port = gs.getGreetingServiceImplPort();
System.out.println(port.greeting("World"));
} }

方法2:通过cxf-rt-frontend-jaxws包下面的org.apache.cxf.jaxws.JaxWsProxyFactoryBean类来获取客户端的代理类并创建服务接口,实现方法如下:

package com.lglan.webservice.client;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;  

import com.lglan.webservice.server.GreetingService;

public class TestGreetingService {
public static void main(String[] args) {
//创建WebService客户端代理工厂
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
//注册WebService接口
factory.setServiceClass(GreetingService.class);
//设置WebService地址
factory.setAddress("http://localhost:8080/cxf_ws_demo/services/greetingService?wsdl");
GreetingService greetingService = (GreetingService)factory.create();
System.out.println("invoke webservice...");
System.out.println("message context is:"+greetingService.greeting("World"));
}
}

方法3:spring配置服务接口的bean

客户端添加spring的配置文件wsdl-service.xml,在<jaxws:client/>节点中配置服务地址和接口,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.lglan.webservice.server"/> <jaxws:client id="GreetingService" serviceClass="com.lglan.webservice.server.GreetingService"
address="http://localhost:8080/cxf_ws_demo/services/greetingService?wsdl"/> <!-- <bean id="proxyFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="com.lglan.webservice.server.GreetingService"/>
<property name="address" value="http://localhost:8080/cxf_ws_demo/services/greetingService?wsdl"/>
</bean> <bean id="GreetingService" class="com.lglan.webservice.server.GreetingService"
factory-bean="proxyFactory" factory-method="create"/> --> </beans>

最后,通过读spring的配置文件来实例化服务接口类并调用服务提供的方法:

package com.lglan.webservice.client;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lglan.webservice.server.GreetingService; public class TestGreetingService2 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("wsdlService.xml");
GreetingService greeting = (GreetingService) ac.getBean("GreetingService");
System.out.println(greeting.greeting("World"));
} }

总之,客户端调用服务的方式有很多,可以按照自己的喜欢来,不用关心服务端是如何实现的。关于客户端的调用最近计划整理一篇接口测试客户端搭建的文章,更多内容就在那里详述吧。

附:demo源码地址 http://pan.baidu.com/s/1pJt52Rp

三.参考

webservice:

http://cxf.apache.org/docs/a-simple-jax-ws-service.html

http://cxf.apache.org/docs/writing-a-service-with-spring.html

http://blessht.iteye.com/blog/1105562/

http://www.ithov.com/linux/125942.shtml

http://www.cnblogs.com/doosmile/archive/2012/06/21/2557351.html

http://www.blogjava.net/icewee/archive/2012/07/06/382399.html

http://www.cnblogs.com/frankliiu-java/articles/1641949.html

打包部署:

http://blog.csdn.net/WANGYAN9110/article/details/38646677

http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html

http://menjoy.iteye.com/blog/382200

http://blog.csdn.net/junmuzi/article/details/12239303

http://blog.csdn.net/jadyer/article/details/7960802

http://blog.sina.com.cn/s/blog_7deb4bd601019llp.html

全文完…

上一篇:c++类模板之分文件编写问题及解决


下一篇:用JAX-WS在Tomcat中公布WebService