在WildFly中集成EJB与JAX-WS:解决部署与访问难题

本文详细介绍了在WildFly应用服务器上,将E

JB(Enterprise JavaBeans)与JAX-WS(Java API for XML Web Services)项目整合到EAR(Enterprise Archive)中的实践。教程涵盖了多模块Maven项目结构、依赖管理、以及如何解决部署过程中常见的NoClassDefFoundError类加载问题,并指导读者正确访问已部署Web服务的WSDL定义。

概述:EJB与JAX-WS在EAR中的集成

在企业级java应用开发中,将业务逻辑封装为ejb,并通过jax-ws暴露为web服务是一种常见模式。为了更好地管理和部署这些组件,通常会将它们打包到一个ear文件中。本教程将以一个典型的多模块maven项目为例,演示如何配置项目、解决部署时可能遇到的类加载问题,并最终成功访问web服务。

项目结构与Maven依赖管理

一个典型的EJB与JAX-WS集成项目通常包含以下Maven模块:

  • 父项目 (myapp-app): 管理所有子模块的公共依赖和版本。
  • EJB模块 (myapp-ejb): 包含业务逻辑的EJB接口和实现。
  • Web服务模块 (myapp-ws): 包含JAX-WS服务接口和实现,并通过@EJB注解注入EJB。
  • EAR模块 (myapp-ear): 将EJB模块和Web服务模块打包在一起,形成一个可部署的企业级应用。

以下是相关pom.xml文件的关键配置片段,展示了这些模块之间的依赖关系:

1. 父项目 pom.xml (myapp-app)


    4.0.0
    org.myapp
    myapp-app
    pom
    0.1.0
    
        myapp-ejb
        myapp-web      
        myapp-ear
        myapp-ws       
    
    
        UTF-8
        1.8
        1.8
        18.0.1.Final
    
    
        
            
                org.wildfly.bom
                wildfly-jakartaee8-with-tools
                ${version.server.bom}
                pom
                import
                           
            
                ${project.groupId}
                myapp-ejb
                ${project.version}
                ejb
            
            
                ${project.groupId}
                myapp-ws
                ${project.version}
                war
                compile
                               
        
    
    

2. EAR项目 pom.xml (myapp-ear)

EAR项目负责将WAR和EJB打包。请确保在dependencies中正确引用了EJB和Web服务模块。


    4.0.0
    org.myapp
    myapp-ear
    ear
    0.1.0
    myapp-ear
    
        org.myapp
        myapp-app
        0.1.0
    
    
        
            ${project.groupId}
            myapp-ejb
            ejb
            0.1.0
        
        
            ${project.groupId}
            myapp-ws
            war
            0.1.0 
               
    
    

3. Web服务项目 pom.xml (myapp-ws)

Web服务模块需要EJB接口来进行编译和类型检查,但在运行时,EJB实例将由容器提供。因此,将EJB模块的依赖范围设置为provided是关键,这可以避免将EJB JAR重复打包到WAR中,并依赖EAR容器的类加载机制。


    4.0.0
    org.myapp
    myapp-ws
    war
    0.1.0
    myapp-ws Maven Soap WS
    
        org.myapp
        myapp-app
        0.1.0
    
    
        
        
            ${project.groupId}
            myapp-ejb
            ejb
            provided
        
    
    

解决部署时的NoClassDefFoundError

在WildFly中,当一个WAR包部署在EAR内部时,它通常可以访问EAR中其他模块(如EJB JAR)的类。然而,有时会遇到java.lang.NoClassDefFoundError或java.lang.ClassNotFoundException,特别是在Web服务模块尝试通过反射获取EJB接口信息时。错误信息通常类似:

WFLYSRV0153: Failed to process phase POST_MODULE of deployment "myapp-ws.war"
Caused by: java.lang.RuntimeException: WFLYSRV0177: Error getting reflective information for class org.ws.MyAppWSImpl with ClassLoader ModuleClassLoader for Module "deployment.myapp-ws.war"
Caused by: java.lang.NoClassDefFoundError: Lorg/myapp/MyAppStatelessLocal;
Caused by: java.lang.ClassNotFoundException: org.myapp.MyAppStatelessLocal from [Module "deployment.myapp-ws.war"]

这表明尽管myapp-ejb被包含在EAR中,但myapp-ws.war的类加载器在部署阶段未能找到MyAppStatelessLocal接口。为了明确告知WildFly myapp-ws.war依赖于myapp-ejb.jar,可以在myapp-ws.war的WEB-INF/目录下添加一个jboss-deployment-structure.xml文件。

1. 创建 jboss-deployment-structure.xml

在myapp-ws项目的src/main/webapp/WEB-INF/目录下创建或编辑jboss-deployment-structure.xml文件,内容如下:



    
        
            
            
        
    

这里的module name格式deployment..ear.是WildFly中引用EAR内部子部署的约定。这会确保myapp-ws.war的类加载器能够正确地访问myapp-ejb.jar中的类。

2. Web服务实现 (MyAppWSImpl.java)

Web服务实现类通过@EJB注解注入EJB,这是Java EE规范的标准做法。

package myapp.ws;

import javax.ejb.EJB;
import javax.jws.WebMethod;
import javax.jws.WebService;
import myapp.lab.MyAppStatelessLocal; // 确保导入EJB接口

@WebService(endpointInterface = "myapp.ws.MyAppWS")
public class MyAppWSImpl implements MyAppWS {
    @EJB
    MyAppStatelessLocal masl; // 注入EJB本地接口

    @WebMethod()
    public String predict(int value) {
        return masl.getPrediction(value);
    }
}

3. Web服务配置 (web.xml)

web.xml用于配置Servlet和Servlet映射,JAX-WS服务通常通过Servlet暴露。



  myapp-ws
  
    index.html
  

  
    myappws
    myapp.ws.MyAppWSImpl
  
  
    myappws
    /myappws
  

这里的定义了Web服务的访问路径,这对于后续访问WSDL至关重要。

访问已部署Web服务的WSDL

在成功部署EAR文件到WildFly服务器后,Web服务即可通过HTTP协议访问。为了获取Web服务的WSDL(Web Services Description Language)定义,需要使用正确的URL。

根据web.xml中定义的,Web服务的访问路径是/myappws。因此,正确的WSDL访问URL应为:

http://localhost:8080/myapp-ws/myappws?wsdl

注意事项:

  • localhost:8080是WildFly服务器的默认地址和端口,如果你的配置不同,请相应修改。
  • myapp-ws是Web服务模块的上下文根(通常与WAR文件的名称相同)。
  • myappws是web.xml中定义的url-pattern。
  • ?wsdl是标准后缀,用于请求Web服务的WSDL定义。

错误地使用服务实现类名(例如http://localhost:8080/myapp-ws/MyAppWSImpl?wsdl)将无法获取WSDL,因为JAX-WS运行时通常根据web.xml中的Servlet映射来暴露WSDL,而不是直接基于实现类名。

总结与最佳实践

在WildFly中集成EJB与JAX-WS并将其打包到EAR中,需要细致的Maven配置和对Java EE类加载机制的理解。

  1. Maven依赖管理:确保EJB模块在Web服务模块中被声明为provided范围,避免JAR文件重复打包。
  2. EAR打包:myapp-ear的pom.xml必须正确引用所有内部模块(EJB JAR和WAR)。
  3. WildFly类加载:当遇到NoClassDefFoundError时,jboss-deployment-structure.xml是解决WAR内部模块对EAR中其他模块(如EJB)显式依赖的有效方法。
  4. Web服务配置:web.xml中的定义了Web服务的实际访问路径,这直接影响WSDL的URL。

遵循这些步骤和最佳实践,可以有效地在WildFly环境中构建和部署健壮的企业级Web服务应用。