`
ywu
  • 浏览: 457331 次
  • 性别: Icon_minigender_1
  • 来自: 无锡
社区版块
存档分类
最新评论

webservice发布使用spring的SpringBeanAutowiringSupport自动装配不了属性

阅读更多

    同事将开发好的webservice服务发布到测试环境后,使用客户端去访问时发现,服务提供类中使用spring容器注入的属性都为空,配置片段如下:

 

@javax.jws.WebService(endpointInterface = "com.mipo.webservice.service.impl.URInterfaceServletmplDelegate", targetNamespace = "http://impl.service.webservice.mipo.com/", serviceName = "URInterfaceServletService", portName = "URInterfaceServletPort")
public class URInterfaceServletPortImpl extends SpringBeanAutowiringSupport{
	@Autowired
	private URInventoryInService  urInventoryInService;

 服务提供类URInterfaceServletPortImpl继承了spring的SpringBeanAutowiringSupport实现自动装配,web.xml中配置片段如下:

<listener>
  	<listener-class>
  		com.sun.xml.ws.transport.http.servlet.WSServletContextListener
  	</listener-class>
  </listener>

 

<servlet>
  	<description>JAX-WS endpoint - URInterfaceServlet</description>
  	<display-name>URInterfaceServlet</display-name>
  	<servlet-name>URInterfaceServlet</servlet-name>
  	<servlet-class>
  		com.sun.xml.ws.transport.http.servlet.WSServlet
  	</servlet-class>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>URInterfaceServlet</servlet-name>
  	<url-pattern>/URInterfaceServletPort</url-pattern>
  </servlet-mapping>

WEB-INF目录下有一个sun-jaxws.xml文件,表面上看没啥问题,启动过程也没有报错,但是使用webservice客户端访问的时候,就报出上面服务类中注入的属性urInventoryInService为空,抛出空指针异常。

    既然为空,说明spring没有装配进这个属性,才会导致执行的时候属性为空,查看下spring处理这个类的过程,看到这个服务类URInterfaceServletPortImpl,以往我们使用spring时,会把这个bean先交给spring容器管理(类上使用注解@Service等,并使用自动扫描component-scan指定包,或者是在xml文件中配置bean),然后在使用@Autowired等注解注入依赖的属性。但是这个类URInterfaceServletPortImpl上面即没有@Service等注解,也没在xml中配置,只是继承了SpringBeanAutowiringSupport,然后就直接使用@Autowired注入依赖的属性了,spring怎么知道这个类需要注入属性呢?按照我的想法,肯定得在配置文件中说明,这个类需要依赖注入,然后spring容器启动的时候读取到这个类,然后通过反射取得类的属性,检测属性是否需要注入,然后去容器中获取指定的bean,注入进来。

    但是全局搜索了一下,并没有看到在配置文件中有配置URInterfaceServletPortImpl这个bean,看了下它集成的SpringBeanAutowiringSupport这个类的源代码,只有一个无参的构造函数

	public SpringBeanAutowiringSupport() {
		processInjectionBasedOnCurrentContext(this);
	}

 

public static void processInjectionBasedOnCurrentContext(Object target) {
		Assert.notNull(target, "Target object must not be null");
		WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();
		if (cc != null) {
			AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
			bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());
			bpp.processInjection(target);
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Current WebApplicationContext is not available for processing of " +
						ClassUtils.getShortName(target.getClass()) + ": " +
						"Make sure this class gets constructed in a Spring web application. Proceeding without injection.");
			}
		}
	}

 在这个地方去注入了URInterfaceServletPortImpl依赖的bean,在java中,子类创建时会先创建父类,执行父类默认的构造函数,而在SpringBeanAutowiringSupport的构造函数中处理了子类属性的注入,唉 ,master is master,思想比我高明多了!!在这个类中打了几个断点,调试了一下,发现都是OK的,并没有出什么错,开源软件就是好啊,一言不合就可以关联源代码调试。生成webservice客户端,访问了一下,也是OK的,说明注入没问题,但是在同时环境中还是不行。

    问了同事是不是在服务没启动完就访问的,没启动完就访问有可能spring容器中还没有依赖的bean,同事说不是,那就奇怪了,理论上注入是没问题的,而且在我环境上是OK的。在同事环境中,在其他类中new了一个服务类URInterfaceServletPortImpl的实例,然后访问,发现这回依赖的属性urInventoryInService是有值的,说明注入是没问题的,很奇怪,猜测是webservice在启动初始化URInterfaceServletPortImpl的时候有问题。怀疑是启动顺序的原因,调整了下listener、servlet在web.xml中的顺序,修改了servlet的load-on-startup参数,发现在同事的环境中都不行,奇了怪了。

    跟同事统一了下环境,我的环境是6版本的tomcat6.0.39,同事使用的是tomcat7版本的7.0.69,但是不应该跟服务器有关啊,同事切换到tomcat6版本,发现竟然OK了,好神奇啊。难道tomcat7解析web.xml的顺序改了?查了下,发现并没有变,是在没辙,我的环境切换到tomcat7后也报空指针了,tomcat7上的确有问题,来看看URInterfaceServletPortImpl是在什么时候初始化的。在web的xml中把相关的listener、servlet注释掉,启动了下,发现在tomcat6中URInterfaceServletPortImpl没有被初始化,但是在tomcat7中,这个类居然还是被初始化了,明明web.xml中已经注释了相关配置,把WEB-INF目录下的sun-jaxws.xml文件也删了,发现在tomcat6和tomcat7下都不初始化URInterfaceServletPortImpl了,难道tomcat7会自动读取WEB-INF目录下的sun-jaxws.xml?应该不可能。

    没办法,还是来看源代码,com.sun.xml.ws.transport.http.servlet.WSServletContextListener的方法中

 

void parseAdaptersAndCreateDelegate(ServletContext context){
        //The same class can be invoked via @WebListener discovery or explicit configuration in deployment descriptor
        // avoid redoing the processing of web services.
        String alreadyInvoked = (String) context.getAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED);
        if(Boolean.valueOf(alreadyInvoked)) {
            return;
        }
        context.setAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED, "true");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = getClass().getClassLoader();
        }
        try {
            URL sunJaxWsXml = context.getResource(JAXWS_RI_RUNTIME);
            if(sunJaxWsXml==null) {
                    throw new WebServiceException(WsservletMessages.NO_SUNJAXWS_XML(JAXWS_RI_RUNTIME));                 
            }

            // Parse the descriptor file and build endpoint infos
            DeploymentDescriptorParser<ServletAdapter> parser = new DeploymentDescriptorParser<ServletAdapter>(
                classLoader,new ServletResourceLoader(context), createContainer(context), new ServletAdapterList(context));
            adapters = parser.parse(sunJaxWsXml.toExternalForm(), sunJaxWsXml.openStream());
            registerWSServlet(adapters, context);
            delegate = createDelegate(adapters, context);

            context.setAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO,delegate);

        } catch (Throwable e) {
            logger.log(Level.SEVERE,
                WsservletMessages.LISTENER_PARSING_FAILED(e),e);
            context.removeAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO);
            throw new WSServletException("listener.parsingFailed", e);
        }

    }

 读取了WEB-INF下的sun-jaxws.xml,打了几个断点,debug一下,发现在tomcat7下,会两次走进这个方法,查看了一下,这个方法被两个地方调用:



 一个是listener本身,一个是
WSServletContainerInitializer的onStartup方法,listener已经被注释了,应该不会执行,那应该是WSServletContainerInitializer的onStartup方法导致URInterfaceServletPortImpl被初始化了,WSServletContainerInitializer代码如下:

@HandlesTypes({WebService.class, WebServiceProvider.class})
public class WSServletContainerInitializer implements ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        //Called with null, when there are no matching classes as per Servlet 3.0 spec
        try {
            if (c != null && !c.isEmpty()) {
                URL sunJaxWsXml = ctx.getResource(WSServletContextListener.JAXWS_RI_RUNTIME);
                //Don't register a listener, when there is no sun-jaxws.xml, let 109 impl  handle it.
                if (sunJaxWsXml != null) {
                    WSServletContextListener listener = new WSServletContextListener();
                    listener.parseAdaptersAndCreateDelegate(ctx);
                    ctx.addListener(listener);
                }
            }
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
}

 这边创建了WSServletContextListener,也读取了WEB-INF目录下的sun-jaxws.xml,断点打了一下,的确是执行了,那这个类的onStartup方法是谁来执行的,什么时候执行的,查看了相关资料,这个类实现了ServletContainerInitializer,是servlet3.0中新增的功能,支持servlet3.0的容器在启动的时候会自动执行实现了ServletContainerInitializer接口的类的onStartup方法,但是有个前提,就是要执行的实现了ServletContainerInitializer接口的类必须在META-INF/services目录下配置javax.servlet.ServletContainerInitializer文件,查看了下WSServletContainerInitializer类所在包的META-INF目录,的确是有这么一个文件



 把这个包中的这个文件删除,在tomcat7下终于也OK 了。那看来就是这个原因导致的,tomcat容器在启动的时候会扫描jar包的META-INF目录,执行那些配置了的,并实现了ServletContainerInitializer接口的类的onStartup,所以这时候会执行WSServletContextListener的parseAdaptersAndCreateDelegate,然后web.xml中配置了com.sun.xml.ws.transport.http.servlet.WSServletContextListener,导致parseAdaptersAndCreateDelegate方法又执行了一遍,看下parseAdaptersAndCreateDelegate方法

void parseAdaptersAndCreateDelegate(ServletContext context){
        //The same class can be invoked via @WebListener discovery or explicit configuration in deployment descriptor
        // avoid redoing the processing of web services.
        String alreadyInvoked = (String) context.getAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED);
        if(Boolean.valueOf(alreadyInvoked)) {
            return;
        }
        context.setAttribute(WSSERVLET_CONTEXT_LISTENER_INVOKED, "true");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = getClass().getClassLoader();
        }
        try {
            URL sunJaxWsXml = context.getResource(JAXWS_RI_RUNTIME);
            if(sunJaxWsXml==null) {
                    throw new WebServiceException(WsservletMessages.NO_SUNJAXWS_XML(JAXWS_RI_RUNTIME));                 
            }

            // Parse the descriptor file and build endpoint infos
            DeploymentDescriptorParser<ServletAdapter> parser = new DeploymentDescriptorParser<ServletAdapter>(
                classLoader,new ServletResourceLoader(context), createContainer(context), new ServletAdapterList(context));
            adapters = parser.parse(sunJaxWsXml.toExternalForm(), sunJaxWsXml.openStream());
            registerWSServlet(adapters, context);
            delegate = createDelegate(adapters, context);

            context.setAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO,delegate);

        } catch (Throwable e) {
            logger.log(Level.SEVERE,
                WsservletMessages.LISTENER_PARSING_FAILED(e),e);
            context.removeAttribute(WSServlet.JAXWS_RI_RUNTIME_INFO);
            throw new WSServletException("listener.parsingFailed", e);
        }

    }

 第一次执行后alreadyInvoked已经为true,因此第二次由listener初始化是不在后续处理,查了下,说tomcat初始化ServletContainerInitializer并执行onStartup还是比较靠前,先于listener初始化,因此WSServletContainerInitializer的onStartup执行时spring容器还没有初始化,因此URInterfaceServletPortImpl中的属性装配进来都为空,而在第二次listener初始化时,由于第一次已经初始化过了,虽然这时候spring容器已经初始化了,但是URInterfaceServletPortImpl不再被初始化,因此后续访问的时候提示注入的属性为空。

    解决办法,一个就是把jar包中META-INF目录下的javax.servlet.ServletContainerInitializer文件删除,虽然凑效,但未免有点暴力,往上看到有说使用web.xml中配置metadata-complete="true",这个是在web.xml中配置让servlet容不扫描servlet3.0规范中定义的类,需要在web.xml中使用servlet3.0规范的申明,但是我在测试中发现并没有卵用,还一个方法是在tomcat的conf/catalina.properties文件中,配置tomcat.util.scan.DefaultJarScanner.jarsToSkip属性,在其值最后加上,*



 其目的是指示tomcat扫描是跳过指定jar,这里最后跳过所有jar,原文链接如下(http://blog.sina.com.cn/s/blog_4d0855690102whto.html)

但是个人感觉虽然解决了问题,但不是很优雅。

  • 大小: 11 KB
  • 大小: 10.8 KB
  • 大小: 26 KB
分享到:
评论

相关推荐

    发布webService服务接口与spring整合教程

    Spring支持基于注解的Web Service发布,如使用`@WebService`和`@WebServiceClient`。你也可以使用Spring-WS或Apache CXF等库来创建WSDL并部署服务。 4. **测试和调试**:确保编写了单元测试来验证Web Service接口的...

    WebService验证与Spring整合

    配置文件中还需要设置服务发布和消费的相关属性。 6. **测试Web服务**:在Spring中,可以使用MockMVC或WebServiceTemplate来测试Web服务的端点。这有助于确保服务按预期工作,并且验证逻辑正确无误。 7. **安全...

    axis2+spring webservice

    标题中的“axis2+spring webservice”指的是使用Apache Axis2框架与Spring框架集成来开发Web服务。Apache Axis2是Java环境中广泛使用的Web服务引擎,它提供了高性能、灵活且可扩展的架构。Spring框架则是一个全面的...

    Spring+webservice例子

    本示例"Spring+webservice例子"聚焦于如何结合Spring框架来实现Web服务,特别是侧重于提供全代码实现,不依赖外部库(LIB)。下面将详细介绍这两个技术及其结合使用的要点。 Spring框架是一个开源的应用框架,它...

    webservice客户端,整合spring

    - **使用Spring自动装配**:通过`@Autowired`注解,可以将CXF客户端注入到需要调用Web Service的类中,实现自动化管理。 5. **手动配置与自动配置**: - **手动配置**:在没有使用Spring Boot或Spring的自动配置...

    使用spring远程调用服务端接口实现WebService功能

    在Java EE平台上,Spring框架提供了一种强大的方式来实现远程服务调用,特别是通过其HttpInvokerServiceExporter组件来实现基于HTTP的WebService功能。这个技术允许客户端和服务端通过HTTP协议进行通信,实现远程...

    webservice xfire整合spring(webservice配置采用注解)例子

    【标题】中的“webservice xfire整合spring(webservice配置采用注解)”是指将Xfire,一个早期的Web服务框架,与Spring框架结合使用,其中Web服务的配置通过Spring的注解方式进行。这种方式简化了配置,提高了开发...

    webservice CXF结合Spring所需jar包

    在标题中提到的"webservice CXF结合Spring所需jar包",这是开发过程中必不可少的依赖。以下是一些关键的jar包及其作用: 1. **cxf-rt-frontend-jaxws.jar**: 这个jar包包含了CXF处理JAX-WS(Java API for XML Web ...

    spring集成cxf,server发布webservice,client调用webservice

    使用spring集成cxf,在两个web project里发布及调用webservice server端使用spring+springmvc+mybatis+cxf,client端使用struts2+spring+hibernate+cxf 两个工程均为myeclipse project,包含所有除myeclipse自带以外...

    springcloud feign 服务消费者 类似 webservice

    在传统的Web Service(WebService)中,我们通常使用SOAP协议来交换数据,通过WSDL(Web Service Description Language)文件来描述服务接口。而Spring Cloud Feign则是面向RESTful API的,它使用HTTP协议进行通信,...

    Axis2与Spring整合发布多个WebService

    6. **发布多个WebService**:重复上述步骤,只需更改接口、实现类和Spring配置文件,就可以发布多个独立的WebService。 **项目管理** 由于项目使用Maven进行管理,这意味着可以利用Maven的生命周期和插件来自动化...

    Apache CXF2+Spring2.5轻松实现WebService

    完成这些配置后,只需启动Spring容器,Apache CXF就会自动发布Web服务,并处理来自客户端的请求。客户端可以通过WSDL文档来发现和调用服务。 在实际项目中,可能还需要处理安全、事务、异常处理等问题。Apache CXF...

    webservice cxf+spring maven项目

    总结来说,"webservice cxf+spring maven项目"是一个适合初学者的示例,它展示了如何利用CXF、Spring和Maven构建、部署和测试Web服务。这个项目涵盖了Web服务的基本概念,以及CXF和Spring在Web服务中的实际应用,为...

    webservice源代码Spring+JDBC

    在本示例中,"webservice源代码Spring+JDBC" 提供了一种使用Spring框架和JDBC(Java Database Connectivity)来实现Web服务的实践教程。下面将详细阐述这两个关键组件以及它们在Web服务中的作用。 **Spring框架**:...

    webservice7 spring的bean发布为webservice

    ### 使用Spring框架与Axis2发布JavaBean为WebService 在现代Web开发中,Spring框架因其强大的依赖注入和面向切面编程支持而备受青睐。通过结合Axis2的Spring感知特性,可以非常简便地将Spring管理下的JavaBean发布...

    jws与spring发布WebService

    【标题】"jws与spring发布WebService"涉及的是在Java Web Service(JWS)和Spring框架结合下,如何创建和部署Web服务。Web服务是一种基于网络的、平台无关的交互方式,它允许不同系统间的应用进行数据交换。JWS是...

    webservice cxf 整合spring例子源代码

    4. **发布Web服务**:在Spring配置完成后,可以通过Spring启动CXF的Bus,使Web服务自动发布到指定的地址。这通常通过`&lt;jaxws:endpoint&gt;`标签的`address`属性完成。 5. **客户端调用**:在Spring环境中,也可以方便...

    Restful WebService + Spring

    在IT行业中,RESTful Web Service和Spring框架的集成是一个广泛使用的解决方案,特别是在构建现代、可扩展的分布式系统中。REST(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,基于...

    webservice集成spring框架

    在做四川电信项目时搭建的一个webservice集成spring的服务端框架,里面有服务端的实现,运行后在浏览器总输入:http://localhost:端口/ismpbJOA_me/services/BnetForJOA?wsdl 便可以看到wsdl文件,希望对大家有用

    WebService的CXF整合Spring

    2. **配置Spring**:创建一个Spring配置文件(如`cxf-servlet.xml`),在这个文件中定义CXF的服务发布和消费者配置。你可以使用Spring的`jaxws:endpoint`或`jaxrs:server`标签来声明服务端点,`jaxws:client`或`jax...

Global site tag (gtag.js) - Google Analytics