最近在给公司做一个基于webservice的数据提供接口,接口提供给第三方公司业务系统调用,完成数据对接。实现起来也相对简单,其实就是通过中间件服务提取内部系统授权数据组织相应的数据格式然后通过webservice的方式暴露获取数据接口给第三方应用,供其调用。要实现这样的需求每个公司的实现方式有可能不一样,根据每个公司实现中间件服务应用使用的实现语言而定。可选择的方案很多,比如如果你们公司的使用的C#的来实现,那么可选择的方案有WCF等技术方案;但是如果你们的中间件服务实现语言是java,那么实现的方案也很多,比如webservice,而webservice相对应的开源框架也很多,比如xfire,又或者你可以完全不适用开源框架来使用webservice技术完成跨平台的服务调用。这些根据每个公司的情况或者说是项目经理的需要而定。下面就针对xfire来讲这个案例。
下面我在一个大家或者大部分公司使用的开源架构SSJ来讲解这个案例。下面我们来准备一下这个案例需要的环境。首先我们准备两个项目,一个是服务端(提供[暴露]webservice服务接口端),一个是客户端(webservice 服务使用[消费、调用]端)。下面我们需要准备一下相应的案例环境,因此我创建了如下两个项目,如下图所示:
针对客户端项目我们只需要添加xfire下相关jar类库即可,而服务端SSJ架构应用需要添加的支持库除了xfire之外还包含struts2\spring\hibernate(jpa,hibernate便是jpa标准下的一个实现产品)的相关jar即可。好了以上两个截图便是集成好的两个项目。下面我开始讲解一些集成的细节。
首先是服务端集成说明如下:
web.xml的相关配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 应用显示的名称 --> <display-name>tsHisWeixinService</display-name> <description>tsHisWeixinService Application</description> <!-- WEB-INF/classes/tsTelemedicine*.xml, --> <!-- spring要读取的配置文件的上下文路径,这个上下文参数会放在servletContext范围内,这个参数也可以不知道,spring会从它默认的位置读取配置文件,来初始化spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> WEB-INF/classes/tsTelemedicine-core.xml </param-value> </context-param> <!-- 对Spring容器进行实例化--> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!--日志的配置--> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/classes/log4j.properties</param-value> </context-param> <listener> <listener-class> org.springframework.web.util.Log4jConfigListener </listener-class> </listener> <!-- 内存溢出 --> <listener> <listener-class> org.springframework.web.util.IntrospectorCleanupListener </listener-class> </listener> <filter> <filter-name>Spring OpenEntityManagerInViewFilter</filter-name> <filter-class> org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter </filter-class> <init-param> <!-- 指定org.springframework.orm.jpa.LocalEntityManagerFactoryBean在spring配置文件中的名称,默认值为entityManagerFactory 如果LocalEntityManagerFactoryBean在spring中的名称不是entityManagerFactory,该参数一定要指定,否则会出现找不到entityManagerFactory的例外 --> <param-name>entityManagerFactoryBeanName</param-name> <param-value>entityManagerFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>Spring OpenEntityManagerInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- struts2 核心filter --> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- webservice 相关配置1,这种配置方式xfire在启动时候会读取META-INFO/xfire/services.xml下面关于webservice bean相关配置 --> <!-- <servlet> <servlet-name>XFireServlet</servlet-name> <servlet-class> org.codehaus.xfire.transport.http.XFireConfigurableServlet </servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> --> <!-- webservice 相关配置2,这种配置方式xfire在启动时候根据spring配置文件关于xfire配置内容来启动 --> <servlet> <servlet-name>XFireServlet</servlet-name> <servlet-class> org.codehaus.xfire.spring.XFireSpringServlet </servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> </web-app>
spring的相关配置
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <aop:aspectj-autoproxy /> <context:component-scan base-package="com.tenshine" /> <!-- 使用数据源和指定persistence.xml位置的方式创建entityManagerFactory,如果使用的不是hibernate JPA实现, 需要在tomcat作一些特殊配置.具体参考手册 注意:使用该方式需要把persistence.xml中的hibernate.connection.driver_class,hibernate.connection.username,hibernate.connection.password,hibernate.connection.url配置删除 --> <!-- 在这里我使用c3p0作为数据库连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${driverClass}" /> <property name="jdbcUrl" value="${jdbcUrl}" /> <property name="user" value="${user}" /> <property name="password" value="${password}" /> <!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 --> <property name="initialPoolSize" value="1" /> <!-- 连接池中保留的最小连接数。 --> <property name="minPoolSize" value="1" /> <!-- 连接池中保留的最大连接数。Default: 15 --> <property name="maxPoolSize" value="100" /> <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> <property name="maxIdleTime" value="60" /> <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --> <property name="acquireIncrement" value="5" /> <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 --> <property name="idleConnectionTestPeriod" value="60" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" /> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" /> </property> </bean> <!-- 使用JPA方式的事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 使用注解的方式使用事务 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 指定相应的jdbc连接数据库的配置文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 基于注解配置XFire --> <import resource="classpath:org/codehaus/xfire/spring/xfire.xml" /> <bean id="webAnnotations" class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations" /> <bean id="jsr181HandlerMapping" class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping"> <property name="xfire" ref="xfire" /> <property name="webAnnotations" ref="webAnnotations" /> </bean> </beans>
以上便是服务端的关键配置信息了,配置好了以上内容我们就可以在我们服务端项目中基于注解的方式定于我们需要发布的服务了。下面举一个简单的例子如下:
定义WebService接口:
package com.tenshine.hisWeixin.service; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public interface ValidationLoginService { @WebMethod public boolean validateLogin(String userAccount,String passWord) throws Exception; }
实现WebService接口,并指定服务名:
package com.tenshine.hisWeixin.service.impl; import javax.jws.WebService; import org.springframework.stereotype.Service; import com.tenshine.hisWeixin.service.ValidationLoginService; @Service @WebService(serviceName="ValidationLoginService", endpointInterface = "com.tenshine.hisWeixin.service.ValidationLoginService") public class ValidationLoginServiceImpl implements ValidationLoginService{ public boolean validateLogin(String userAccount, String passWord) throws Exception { System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); System.out.println("远程调用服务端webservice 业务方法"); return false; } }
好了以上配置完之后我们就可以启动我们服务端,发布我们定义的服务,然后通过浏览器的访问相应的webservice服务连接地址看服务是否发布成功,或者我xfire是否集成SSJ成功。我们在浏览器地址栏输入:http://127.0.0.1:8080/tsweixin/services/ValidationLoginService?wsdl。出现如下内容说明集成成功:
其次是客户端调用服务说明如下:
一般我们有两种方式使用服务,一种是服务提供商直接提供服务接口源码给服务使用者。
客户端调用代码片段如下:
package com.tenshine.hisweixin.test; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.service.binding.ObjectServiceFactory; import org.junit.Test; //import com.tenshine.hisweixin.service.ValidationLoginService; public class HisWeixinServiceTest { @Test public void testBookService() { // Service serviceModel = new ObjectServiceFactory().create(ValidationLoginService.class); // String url = "http://127.0.0.1:8080/tsweixin/services/ValidationLoginService"; // ValidationLoginService service = null; // try { // service = (ValidationLoginService) new XFireProxyFactory().create(serviceModel, url); // System.out.println("返回值 "+service.validateLogin("admin", "admin")); // } catch (Exception e) { // e.printStackTrace(); // } } }
另外一种则是用过wsdl文件或者wsdl地址生成客户端代码(一般一些专业公司会手写webservice对应的wsdl规范[wsdl文件],也就是我们常听到的“契约优先”的概念,而不是“代码优先”,两种方式即可),我们可以使用jdk给我提供工具来生成webservice客户端代码【wsimport 】,wsimport
的使用详情大家自己百度去。在这里就不多说了。在控制台上出入如下命令:wsimport -keep -verbose -d D:\ http://127.0.0.1:8080/tsweixin/services/ValidationLoginService?wsdl
好了,通过以上这中方式生成客户端代码就可以被调用了。是不是很简单。当然可以通过myeclipse这个IDE里面生成客户端代码,操作如下:将普通的项目变成xfire项目,右键项目选择“Myeclipse”->"add XFire Web Service Capabilities",然后再进行新建xfire webservice客户端。操作为“右键->new->other->Web
services”,然后按照提示傻瓜式的操作即可。如下图所示:
生成完之后生成如下代码:
下面便是生成的客户端调用代码:
package com.tenshine.service.client; import java.net.MalformedURLException; import java.util.Collection; import java.util.HashMap; import javax.xml.namespace.QName; import org.codehaus.xfire.XFireRuntimeException; import org.codehaus.xfire.aegis.AegisBindingProvider; import org.codehaus.xfire.annotations.AnnotationServiceFactory; import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.jaxb2.JaxbTypeRegistry; import org.codehaus.xfire.service.Endpoint; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.soap.AbstractSoapBinding; import org.codehaus.xfire.transport.TransportManager; public class ValidationLoginServiceClient { private static XFireProxyFactory proxyFactory = new XFireProxyFactory(); private HashMap endpoints = new HashMap(); private Service service0; public ValidationLoginServiceClient() { create0(); Endpoint ValidationLoginServicePortTypeLocalEndpointEP = service0 .addEndpoint(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServicePortTypeLocalEndpoint"), new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServicePortTypeLocalBinding"), "xfire.local://ValidationLoginService"); endpoints.put(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServicePortTypeLocalEndpoint"), ValidationLoginServicePortTypeLocalEndpointEP); Endpoint ValidationLoginServiceHttpPortEP = service0 .addEndpoint(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServiceHttpPort"), new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServiceHttpBinding"), "http://127.0.0.1:8080/tsweixin/services/ValidationLoginService"); endpoints.put(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServiceHttpPort"), ValidationLoginServiceHttpPortEP); } public Object getEndpoint(Endpoint endpoint) { try { return proxyFactory.create((endpoint).getBinding(), (endpoint).getUrl()); } catch (MalformedURLException e) { throw new XFireRuntimeException("Invalid URL", e); } } public Object getEndpoint(QName name) { Endpoint endpoint = ((Endpoint) endpoints.get((name))); if ((endpoint) == null) { throw new IllegalStateException("No such endpoint!"); } return getEndpoint((endpoint)); } public Collection getEndpoints() { return endpoints.values(); } private void create0() { TransportManager tm = (org.codehaus.xfire.XFireFactory.newInstance().getXFire().getTransportManager()); HashMap props = new HashMap(); props.put("annotations.allow.interface", true); AnnotationServiceFactory asf = new AnnotationServiceFactory(new Jsr181WebAnnotations(), tm, new AegisBindingProvider(new JaxbTypeRegistry())); asf.setBindingCreationEnabled(false); service0 = asf.create((com.tenshine.service.client.ValidationLoginServicePortType.class), props); { AbstractSoapBinding soapBinding = asf.createSoap11Binding(service0, new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServiceHttpBinding"), "http://schemas.xmlsoap.org/soap/http"); } { AbstractSoapBinding soapBinding = asf.createSoap11Binding(service0, new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServicePortTypeLocalBinding"), "urn:xfire:transport:local"); } } public ValidationLoginServicePortType getValidationLoginServicePortTypeLocalEndpoint() { return ((ValidationLoginServicePortType)(this).getEndpoint(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServicePortTypeLocalEndpoint"))); } public ValidationLoginServicePortType getValidationLoginServicePortTypeLocalEndpoint(String url) { ValidationLoginServicePortType var = getValidationLoginServicePortTypeLocalEndpoint(); org.codehaus.xfire.client.Client.getInstance(var).setUrl(url); return var; } public ValidationLoginServicePortType getValidationLoginServiceHttpPort() { return ((ValidationLoginServicePortType)(this).getEndpoint(new QName("http://service.hisWeixin.tenshine.com", "ValidationLoginServiceHttpPort"))); } public ValidationLoginServicePortType getValidationLoginServiceHttpPort(String url) { ValidationLoginServicePortType var = getValidationLoginServiceHttpPort(); org.codehaus.xfire.client.Client.getInstance(var).setUrl(url); return var; } public static void main(String[] args) { ValidationLoginServiceClient client = new ValidationLoginServiceClient(); //create a default service endpoint ValidationLoginServicePortType service = client.getValidationLoginServiceHttpPort(); //TODO: Add custom client code here // //service.yourServiceOperationHere(); service.validateLogin("admin", "admin"); System.out.println("test client completed"); } }
调用结果如下:
客户端:
服务端:
好了一个完整案例就这样完了!希望以上内容对你有所帮助。