Apache CXF实现Web Service(4)——Tomcat容器和Spring实现JAX-RS(RESTful) web service

准备

我们仍然使用 Apache CXF实现Web Service(2)——不借助重量级Web容器和Spring实现一个纯的JAX-RS(RESTful) web service 中的代码作为基础,并引入spring来进行RESTful web service的配置和管理。

项目目录结构如下图

首先我们要在web.xml中加入通过Spring的ContextLoaderListener加载的Spring运行时环境以及CXF的Spring配置文件

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" 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">
   <display-name>cxf</display-name>  

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/cxf-servlet.xml</param-value>
    </context-param>  

    <!--设置一起动当前的Web应用,就加载Spring,让Spring管理Bean -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>  

  <servlet>
    <description>m CXF Endpoint</description>
    <display-name>cxf</display-name>
    <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>
  <session-config>
    <session-timeout>60</session-timeout>
  </session-config>
</web-app>

需要注意的是Spring配置文件的命名和路径"/WEB-INF/cxf-servlet.xml",如果将项目文件重命名为applicationContext.xml,同时修改web.xml里面为"/WEB-INF/applicationContext.xml",Tomcat服务器启动的时候会提示错误信息:(稍后我们来讲如何解决这个问题)

org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/cxf-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/cxf-servlet.xml]
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:341)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4793)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5236)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/cxf-servlet.xml]
    at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:140)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:328)
    ... 21 more

在/WEB-INF目录下,我们加入文件cxf-servlet.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:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="
    	http://www.springframework.org/schema/beans
	    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	    http://www.springframework.org/schema/context
	    http://www.springframework.org/schema/context/spring-context-3.0.xsd
	    http://cxf.apache.org/jaxrs
	    http://cxf.apache.org/schemas/jaxrs.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>  

    <bean id="roomService"
    	class="com.cnblog.richaaaard.cxftest.spring.rs.helloworld.service.RoomService">
    </bean>  

	<jaxrs:server id="restContainer" address="/">
        <jaxrs:serviceBeans>
            <ref bean="roomService" />
        </jaxrs:serviceBeans>
         <jaxrs:providers>
        	<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
    	</jaxrs:providers>
<!--         <jaxrs:extensionMappings> -->
<!--         <entry key="json" value="application/json" /> -->
<!--         <entry key="xml" value="application/xml" /> -->
<!--     </jaxrs:extensionMappings> -->
    </jaxrs:server>
</beans>

  注意

文件中的Spring支持,还有http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd(如果是ws类型的web service则需要引入jaxws的规范)

我们还可以看到,文件通过Spring bean的配置将roomService的实例注入到了jaxrs:server之中。

红色高亮的"import resource"部分也是必须的,否则在Tomcat服务器启动的时候会出错:

信息: Loading XML bean definitions from URL [file:/Users/Richard/Documents/Dev/workspace/eclipse/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/cxf-test-rs-spring-helloworld/WEB-INF/cxf-servlet.xml]
十二月 02, 2015 4:34:02 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.s[email protected]58318a06: defining beans [roomService,restContainer]; parent: org.s[email protected]22252012
566 [localhost-startStop-1] INFO org.apache.cxf.endpoint.ServerImpl - Setting the server‘s publish address to be /
十二月 02, 2015 4:34:02 下午 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
信息: Destroying singletons in org.s[email protected]58318a06: defining beans [roomService,restContainer]; parent: org.s[email protected]22252012
十二月 02, 2015 4:34:02 下午 org.apache.catalina.core.ApplicationContext log
严重: StandardWrapper.Throwable
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘restContainer‘: Invocation of init method failed; nested exception is org.apache.cxf.service.factory.ServiceConstructionException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1482)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.apache.cxf.transport.servlet.CXFServlet.createSpringContext(CXFServlet.java:151)
    at org.apache.cxf.transport.servlet.CXFServlet.loadBus(CXFServlet.java:74)
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.init(CXFNonSpringServlet.java:77)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4978)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5270)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.cxf.service.factory.ServiceConstructionException
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:219)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)

"import resource"起什么作用?

待研

最后

我们在Tomcat中运行Web项目,并通过浏览器访问

*扩展

  • 为什么使用applicationContext.xml服务器会出错,从错误信息看,它仍然尝试去寻找cxf-servlet.xml文件?

    在查看项目结构后发现在/WebContent/WEB-INF中还有一个web.xml里面指定的文件是cxf-servlet.xml,这个web.xml和我们在/src/main/webapp/WEB-INF下的文件不同步,Tomcat启动时读取的文件在WebContent下,修改后服务器就正常了。

    

为什么会这样?那么又如何使src下的文件与WebContent中的文件保持同步呢?(其实这都是网上转来转去的例子惹得祸)

我们右键查看整个项目的属性Properties->Web Deployment Assembly 发现在部署配置下 /src/main/webapp 和 /WebContent 都输出到服务器部署目标的根目录下 "/" 且 "/WebContent" 在 "/src/main/webapp" 之后输出,所以会覆盖之前webapp下的内容,所以我们要做的是只要保留一个输出——删除webapp这行配置,然后将原来webapp下的applicationContext.xml文件拷贝到WebContent下。

经过试验,server可以正常启动。

  • "import resource"起什么作用?为什么它的路径是"classpath:META-INF/cxf/cxf.xml"与"classpath:META-INF/cxf/cxf-servlet.xml"

我们发现cxf.xml和cxf-servlet.xml两个文件并不在我们项目源码的路径中,那么是不是在cxf相关的jar包中呢?

答案是肯定的

cxf.xml存在于cxf-core.jar中 /META-INF/cxf/cxf.xml

<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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--  For Testing using the Spring commons processor, uncomment one of:-->
    <!--
                <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
                <context:annotation-config/>
        -->
    <bean id="cxf" class="org.apache.cxf.bus.spring.SpringBus" destroy-method="shutdown"/>
    <bean id="org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor" class="org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor"/>
    <bean id="org.apache.cxf.bus.spring.Jsr250BeanPostProcessor" class="org.apache.cxf.bus.spring.Jsr250BeanPostProcessor"/>
    <bean id="org.apache.cxf.bus.spring.BusExtensionPostProcessor" class="org.apache.cxf.bus.spring.BusExtensionPostProcessor"/>
</beans>

  cxf-servlet.xml存在与cxf-rt-transports-http.jar中

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:foo="http://cxf.apache.org/configuration/foo" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

奇怪的是这里的cxf-servlet.xml里面什么都没有申明,那么我们是否可以将其去掉呢?(答案???自行验证)

所以之前在Spring中配置的cxf-servlet.xml只是与这里的import resource里面的恰好相同而已,两个文件不是同一个,路径也不一样。

那么cxf-core.jar里面的这个文件cxf.xml里面的配置有什么用呢?

后文分解:)

参考:

https://www.mail-archive.com/[email protected]/msg00488.html

http://www.cnblogs.com/hoojo/archive/2011/03/30/1999563.html

http://www.kuqin.com/shuoit/20140716/341250.html

https://cwiki.apache.org/confluence/display/CXF20DOC/JAXRS+Services+Configuration

时间: 2024-10-29 04:05:27

Apache CXF实现Web Service(4)——Tomcat容器和Spring实现JAX-RS(RESTful) web service的相关文章

Apache CXF实现Web Service(3)——Tomcat容器和不借助Spring的普通ServletJAX-RS(RESTful) web service

起步 参照这一系列的另外一篇文章: Apache CXF实现Web Service(2)——不借助重量级Web容器和Spring实现一个纯的JAX-RS(RESTful) web service 首先在eclipse中新建一个Dynamic Web Project,然后实现上篇文章中的所有类,唯一不同的是这里,我们不需要一个Server.java来启动一个Web Service.我们用CXF自带的org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServ

Spring 4.x实现Restful web service

首先我们还是跟之前一样,创建一个maven项目,不过因为Spring Restful web service是基于Spring 4.x版本的,所以我在这里就直接将Spring升级到了4.0.8,下面我贴出我的pom文件主要的依赖: <properties> <spring.version>4.0.8.RELEASE</spring.version> </properties> <dependencies> <dependency> &

【转】Spring 4.x实现Restful web service

http://my.oschina.net/yuyidi/blog/352909 首先我们还是跟之前一样,创建一个maven项目,不过因为Spring Restful web service是基于Spring 4.x版本的,所以我在这里就直接将Spring升级到了4.0.8,下面我贴出我的pom文件主要的依赖: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 3

使用 Spring 3 来创建 RESTful Web Services(转)

使用 Spring 3 来创建 RESTful Web Services 在 Java™ 中,您可以使用以下几种方法来创建 RESTful Web Service:使用 JSR 311(311)及其参考实现 Jersey.使用 Restlet 框架和从头开始开发.Spring 是流行的 Java EE 应用开发框架,现在它的 MVC 层也支持 REST 了.本文将介绍使用 Spring 开发 RESTful Web Services 的方法.读者将了解如何使用 Spring API 和注释来开发

Spring 3 来创建 RESTful Web Services

Spring 3 创建 RESTful Web Services 在 Java™ 中,您可以使用以下几种方法来创建 RESTful Web Service:使用 JSR 311(311)及其参考实现 Jersey.使用 Restlet 框架和从头开始开发.Spring 是流行的 Java EE 应用开发框架,现在它的 MVC 层也支持 REST 了.本文将介绍使用 Spring 开发 RESTful Web Services 的方法.读者将了解如何使用 Spring API 和注释来开发 RES

CRUD using Spring MVC 4.0 RESTful Web Services and AngularJS

原文: http://www.tuicool.com/articles/MneeU3f Based on the requests from many readers, I am now presenting an article on how to make CRUD operations using Spring MVC 4.0 RESTFul web services and AngularJS. I had already written few articles on Spring M

SpringBoot实战(十)之使用Spring Boot Actuator构建RESTful Web服务

一.导入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.

基于Spring设计并实现RESTful Web Services(转)

基于Spring设计并实现RESTful Web Services 在本教程中,你将会使用Spring来创建一个具有生产力的RESTful网络服务. 为什么用RESTful网络服务? 从和Amazon Web Services的整合,到聚合多个数据源,RESTful网络服务遵从了Roy Fielding的架构风格的指导方针,提供了简单.高效的web APIs,支持的API用户数量从少量到百万级别. 你要实现一个RESTful风格的网络服务,可能会是因为: 你正在创建一个API,而客户端需要通过网

使用 Spring 3 来创建 RESTful Web Services

来源于:https://www.ibm.com/developerworks/cn/web/wa-spring3webserv/ 在 Java? 中,您可以使用以下几种方法来创建 RESTful Web Service:使用 JSR 311(311)及其参考实现 Jersey.使用 Restlet 框架和从头开始开发.Spring 是流行的 Java EE 应用开发框架,现在它的 MVC 层也支持 REST 了.本文将介绍使用 Spring 开发 RESTful Web Services 的方法