zoukankan      html  css  js  c++  java
  • 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.springframework.beans.factory.support.DefaultListableBeanFactory@58318a06: defining beans [roomService,restContainer]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@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.springframework.beans.factory.support.DefaultListableBeanFactory@58318a06: defining beans [roomService,restContainer]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@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/users@cxf.apache.org/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

  • 相关阅读:
    浅谈函数式编程
    理解Spark SQL(三)—— Spark SQL程序举例
    理解Spark SQL(二)—— SQLContext和HiveContext
    理解Spark SQL(一)—— CLI和ThriftServer
    理解Spark运行模式(三)(STANDALONE和Local)
    理解Spark运行模式(二)(Yarn Cluster)
    理解Spark运行模式(一)(Yarn Client)
    Java操作Excel:POI和EasyExcel
    MyBatis-Plus入门,看这一篇就足够了
    50道SQL面试题
  • 原文地址:https://www.cnblogs.com/richaaaard/p/5013700.html
Copyright © 2011-2022 走看看