一.目录
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是用来指明服务端接口的。这就是我们在定义接口和实现的时候要用的了。
接着,新建一个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中间代码是被跳过的,这里用这种方法来达到注掉一段代码的目的了,)
@echo off setlocal enabledelayedexpansion goto start ::方法一,用start命令来启动java.exe echo %JAVA_HOME% set jre="%JAVA_HOME%injava" set tempclass="%JAVA_HOME%libdt.jar";"%JAVA_HOME%lib ools.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=0 getTradeProtalPID(){ javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAIN` if [ -n "$javaps" ]; then tradePortalPID=`echo $javaps | awk '{print $1}'` else tradePortalPID=0 fi } shutdown(){ getTradeProtalPID echo "========================================================================================" if [ $tradePortalPID -ne 0 ]; then echo -n "Stopping $APP_MAIN(PID=$tradePortalPID)..." kill -9 $tradePortalPID if [ $? -eq 0 ]; then echo "[Success]" echo "================================================================================" else echo "[Failed]" echo "================================================================================" fi getTradeProtalPID if [ $tradePortalPID -ne 0 ]; 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,发布成功!
二.客户端的调用方式
上面讲服务发布的方法,提到了三种: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
全文完…