CXF+Spring 开发WebService

接口定义: 
Java代码 
@WebService(name = "UserService",targetNamespace = Constants.WS_NAMESPACE)    
public interface UserWebService {    
    @WebResult(name = "user")    
    public UserDTO getUser(@WebParam(name = "userId") Integer userId) throws FaultException;    
}   
@WebService(name = "UserService",targetNamespace = Constants.WS_NAMESPACE)   public interface UserWebService {       @WebResult(name = "user")       public UserDTO getUser(@WebParam(name = "userId") Integer userId) throws FaultException;   }   

 


@WebService 必须,(name="UserService")可选,配置Service的名称,默认为类名。targetNamespace可选,默认为http:// package的倒序,可在一个自己的Constants里定义统一的namespace. 
默认接口的所有方法均输出为WebService。 
@WebResult 可选,配置方法的返回值在WSDL的名称,CXF默认为result。 
@WebParam 可选,配置参数在WSDL的名称,CX比较笨,不会反射,默认为arg0,arg1.... 
@WebMethod 可选,有一个方法有此注释,则其他无注释的方法非WebService。 


接口实现:


Java代码 
@WebService(endpointInterface = "xxx.xxx.service.UserWebService",targetNamespace = Constants.WS_NAMESPACE)    
public class UserWebServiceImpl {   
    //...   
}  
@WebService(endpointInterface = "xxx.xxx.service.UserWebService",targetNamespace = Constants.WS_NAMESPACE)   public class UserWebServiceImpl {      //...  }    

 
@WebService(endpointInterface="")  可选,指定实现的接口。接口是对外的,必须通过注释来声明,而Impl是内部的,声明是可选的。 
DTO:


Java代码 
@XmlType(name = "User")    
public class UserDTO {    
    //属性及setter/getter方法...    
}   
@XmlType(name = "User")   public class UserDTO {       //属性及setter/getter方法...   }     

@XmlType(name = "User")  可选的  
JAXB的智能化较高,基本上不需要手工映射。  
默认的@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER )根据公共getter/setter与公共属性反射(XmlAccessType.PROPERTY根据getter/setter反射,XmlAccessType.FIELD 根据成员变量反射) 
如果有需要注释@XMLElement,@XMLAttribute,需配合XmlAccessorType定义,默认的PUBLIC_MEMBER,需要在getter/setter上定义,如果要写在成员变量上定义,则XmlAccessType改为FIELD。 
@XmlType(name = "User") 指定WSDL上的类型名称,否则CXF奇怪,DTO的类名是小写开头的。 
@XmlTransient 可以注释某个字段,取消该字段的反射。 


总结一下: 
    1.统一:每一个接口函数,每一个DTO类都需要定义NameSpace,否则CXF会以类的pacage名倒序作为namespace,大幅提高引发命名空间的复杂度。最好在Constants类里统一定义;另外可以在包下建立package-info.java文件(下面会说明)。 
    2.接口: CXF比较笨,所以还需要注释每个方法的参数名。否则会以arg0,arg1命名,用SoapUI 等工具看SOAP包时比较痛苦。(Axis2也是arg0,arg1) 
        在ServiceImpl中,最好命名serviceName,否则默认会议类名+Service来命名,如UserManagerServiceImplService,比较难看 
    3.DTO:用@XmlType定义 DTO名,否则名称默认小写,在.Net下生成的代码,类名都是小写的,超奇怪。 
最后要着重指出一点,在网上能搜索到的N多帖子都是如出一辙,但是估计第一个人犯了错误,而后来者都是不负责任的粘贴!!估计他们都是没有经过自己测试的!太误人子弟了!! 
这就是 Server端 作为Spring和CXF桥梁的那个xml配置文件中的写法,引用一段网上的帖子: 
在spring-cxf.xml配置发布的web service


Xml代码 
<!-- 上半段省略 -->  
    <bean id="hello" class="test.HelloWorldImpl" />     
    <jaxws:endpoint id="helloWorld" implementor="#hello"     
        address="/HelloWorld" />     
</beans>  
注意: id:指在spring配置的bean的ID. <P>Implementor:指明具体的实现类. </P><P>Address:指明这个web service的相对地址   
</P>  
<!-- 上半段省略 -->      <bean id="hello" class="test.HelloWorldImpl" />        <jaxws:endpoint id="helloWorld" implementor="#hello"            address="/HelloWorld" />    </beans>  注意: id:指在spring配置的bean的ID. 

Implementor:指明具体的实现类.

Address:指明这个web service的相对地址

 
 
请注意这里一共犯了两个错误: 
1、id,作为标签的id,它当然是这个endpoint的id,不可能是“指在spring配置的bean的ID” 
2、implementor用#引用了上面声明的一个bean,其实implementor=的引用才应该指向在spring配置的bean的ID,spring-cxf.xml中不用也不该再定义一个bean了 
正确的写法是: 
假设Spring的配置文件applicationContext.xml中定义了一个bean 


Xml代码 
<bean id="helloWorld" class="test.HelloWorldImpl">  
        <property name="userDao">  
            <ref bean="userDao" />  <!-- 这里注入一个DAO -->  
        </property>  
</bean>  
<bean id="helloWorld" class="test.HelloWorldImpl">          <property name="userDao">              <ref bean="userDao" />  <!-- 这里注入一个DAO -->          </property>  </bean>  

 
spring-cxf.xml应该这样写:


Xml代码 
<jaxws:endpoint id="helloWorldEndpoint"  
    implementor="#helloWorld"  <!-- 这里是与spring定义的bean呼应的 -->  
    address="/HelloWorldService" />       
t;/beans>   
    <jaxws:endpoint id="helloWorldEndpoint"       implementor="#helloWorld"  <!-- 这里是与spring定义的bean呼应的 -->          address="/HelloWorldService" />        </beans> 

 
试想,如果在implementor参数里指定具体实现类,那spring注入时的“接口指向用具体实现类”还有什么意义呢? 
最初的作者仅仅调试了一个HelloWorld就草草落笔,他们并没有仔细观察,当客户端来访时,服务器是新实例化了一个服务呢?还是从spring已经注入好的bean中get一个呢? 
显然我们要的是后者的效果,而错误的写法确实前者的效果,虽然HelloWorld能通,但是稍复杂一点,比如sping的bean中又注入了其他bean,那么一下就能发现错误了,因为错误的写法是另外实例化了一个bean,由于他是在错误的spring-cxf.xml中定义的,自然没有写property。

 

附:关于package-info.java 我们可以通过这个文件解决命名空间问题

首先,它不能随便被创建。在Eclipse中, package-info文件不能随便被创建,会报“Type name is notvalid”错误,类名无效,Java变量定义规范是:字母、数字、下划线,还有那个不怎么常用的$符号(顺带说下,Java是支持中文名称的变量,习惯挑战的同学可以尝试下,分享一下这方面的经验),这个中划线可不再之列,那怎么创建这个文件呢?

很简单,用记事本创建一个,然后拷贝进去再改一下就成了,更直接的办法就是从别的项目中拷贝过来一个,这更方便。

    其次,服务的对象很特殊。一个类是一类或一组事物的描述,比如Dog这个类,就是描述旺财的,那package-info这个类是描述啥的呢?它总要有一个被描述或被陈述的对象,它是描述和记录本包信息。

    最后,类不能带有public、private访问权限。package-info.java再怎么特殊,也是一个类文件,也会被编译成package-info.class,但是在package-info.java中只能声明默认访问权限的类,也就是友好类。

其实还有几个特殊的地方,比如不可以继承,没有接口,没有类间关系(关联、组合、聚合等等)等。

这个文件的特殊性说完了,那再说说它有什么作用,它有三个作用:

1、为标注在包上Annotation提供便利;

2、声明友好类和包常量;

3、提供包的整体注释说明。

这与包下放置package.htm没啥区别,只是package-info可以更好的在代码中维护文档的完整性,并且可以实现代码与文档同步更新,package.htm也可以做到,不争论,建议是Java 1.5以上版本都使用package-info.java来注释。

 

     与package-info相关的问题

     在项目开发中,可以放置在包上的常用注解有:Struts的@namespace、Hibernate的@FilterDef和@TypeDef等等。在包下,随便一个类中的包名前加这些注解,Eclipse会提示“Package annotations must be in file package-info.java”,在该包下建立package-info.java文件,把注解移到这里即可。

    使用Checkstyle插件做代码检查时,会报一个警告“Missing package-info.java file.”也是这个package-info文件惹的祸,在各个包下创建一个即可。

所以,我们可以在需要的包下建立此文件并通过@XmlSchema来设定命名空间,如下

@javax.xml.bind.annotation.XmlSchema(   namespace = "http://www.laune.at/hospital",    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)  package hospital;  

这样就不需要在DTO中都定义NameSpace可,简单吧

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值