2015-02-10 15:52

JAX-WS RI + Spring 4 自動 Exporting Service

Spring 4 的部分我就不多說了,請自行配置 jar 檔,這裡我用 jaxws-ri-2.2.10.zipjaxws-spring-1.9.jar 進行配置。


AccountService.java
  1. package test.jaxws.webservice; 
  2.  
  3. import javax.jws.WebMethod; 
  4. import javax.jws.WebService; 
  5.  
  6. @WebService 
  7. public interface AccountService { 
  8.  
  9.    @WebMethod 
  10.    void insertAccount(String acc); 
  11.  
  12.    @WebMethod 
  13.    String[] getAccounts(String role); 
  14. } 
先來定義一個 interface,這主要是給 Client 用的。


AccountServiceImpl.java
  1. package test.jaxws.webservice; 
  2.  
  3. import javax.jws.WebMethod; 
  4. import javax.jws.WebService; 
  5. import org.springframework.stereotype.Component; 
  6.  
  7. @Component 
  8. @WebService(serviceName="AccountService") 
  9. public class AccountServiceImpl implements AccountService { 
  10.  
  11.    @Override 
  12.    @WebMethod 
  13.    public void insertAccount(String acc) { 
  14.    } 
  15.  
  16.    @Override 
  17.    @WebMethod 
  18.    public String[] getAccounts(String role) { 
  19.        return new String[] {"tom", "jax"}; 
  20.    } 
  21. } 
接者是 Web Service 的主體,@Component 是為了讓 Spring component-scan 自動建立 Bean,@WebService 則是用定義要 Exporting 到 JAX-WS 的 serviceName,由於是透過 Spring 去管理 bean 所以可以用 @Autowired 引用其他資源。


JaxWsRtServletServiceExporter.java
  1. package test.jaxws.webservice; 
  2.  
  3. import java.util.Arrays; 
  4. import java.util.LinkedHashSet; 
  5. import java.util.Set; 
  6. import javax.jws.WebService; 
  7. import javax.xml.ws.WebServiceProvider; 
  8.  
  9. import org.jvnet.jax_ws_commons.spring.SpringService; 
  10. import org.springframework.beans.factory.BeanFactory; 
  11. import org.springframework.beans.factory.BeanFactoryAware; 
  12. import org.springframework.beans.factory.CannotLoadBeanClassException; 
  13. import org.springframework.beans.factory.InitializingBean; 
  14. import org.springframework.beans.factory.ListableBeanFactory; 
  15. import org.springframework.beans.factory.config.ConfigurableBeanFactory; 
  16. import org.springframework.beans.factory.config.SingletonBeanRegistry; 
  17. import com.sun.xml.ws.transport.http.servlet.SpringBinding; 
  18.  
  19. public class JaxWsRtServletServiceExporter implements BeanFactoryAware, InitializingBean { 
  20.  
  21.    private ListableBeanFactory beanFactory; 
  22.    private String basePath; 
  23.  
  24.    public void setBasePath(String basePath) { 
  25.        this.basePath = basePath; 
  26.    } 
  27.  
  28.    @Override 
  29.    public void setBeanFactory(BeanFactory beanFactory) { 
  30.        if (!(beanFactory instanceof ListableBeanFactory)) { 
  31.            throw new IllegalStateException(getClass().getSimpleName() + " requires a ListableBeanFactory"); 
  32.        } 
  33.        this.beanFactory = (ListableBeanFactory) beanFactory; 
  34.    } 
  35.  
  36.    @Override 
  37.    public void afterPropertiesSet() throws Exception { 
  38.        if (this.beanFactory instanceof SingletonBeanRegistry == false) { return; } 
  39.  
  40.        SpringService service = new SpringService(); 
  41.  
  42.        Set<String> beanNames = new LinkedHashSet<String>(this.beanFactory.getBeanDefinitionCount()); 
  43.        beanNames.addAll(Arrays.asList(this.beanFactory.getBeanDefinitionNames())); 
  44.  
  45.        if (this.beanFactory instanceof ConfigurableBeanFactory) { 
  46.            beanNames.addAll(Arrays.asList(((ConfigurableBeanFactory) this.beanFactory).getSingletonNames())); 
  47.        } 
  48.  
  49.        SingletonBeanRegistry factory = (SingletonBeanRegistry)this.beanFactory; 
  50.  
  51.        for (String beanName : beanNames) { 
  52.            try { 
  53.                Class<?> type = this.beanFactory.getType(beanName); 
  54.                if (type == null || type.isInterface()) { continue; } 
  55.  
  56.                WebService wsAnnotation = type.getAnnotation(WebService.class); 
  57.                WebServiceProvider wsProviderAnnotation = type.getAnnotation(WebServiceProvider.class); 
  58.                if (wsAnnotation == null && wsProviderAnnotation == null) { continue; } 
  59.  
  60.  
  61.                String serviceName; 
  62.                if (wsAnnotation != null) { 
  63.                    serviceName = wsAnnotation.serviceName(); 
  64.                } else { 
  65.                    serviceName = wsProviderAnnotation.serviceName(); 
  66.                } 
  67.  
  68.                service.setImpl(type); 
  69.                service.setBean(this.beanFactory.getBean(beanName)); 
  70.  
  71.                SpringBinding springBinding = new SpringBinding(); 
  72.                springBinding.setBeanName(serviceName + "Binding"); 
  73.                springBinding.setUrl(this.basePath + "/" + serviceName); 
  74.                springBinding.setService(service.getObject()); 
  75.  
  76.                factory.registerSingleton(serviceName + "Binding", springBinding); 
  77.            } 
  78.            catch (CannotLoadBeanClassException ex) { 
  79.                // ignore beans where the class is not resolvable 
  80.            } 
  81.        } 
  82.    } 
  83. } 
為了可以自動 Exporting Service 到 JaxWs RI Servlet 需要建立一個 Exporter,這裡將具有 @WebService 或 @WebServiceProvider 的 Bean 包裝成 SpringBinding 再註冊到 Spring BeanFactory 裡,這樣 WSSpringServlet 就會取到這邊註冊 SpringBinding Bean 進行 Exporting。


web.xml
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3. xmlns="http://java.sun.com/xml/ns/javaee"  
  4. xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
  6. version="3.0" 
  7. > 
  8. <display-name>test-jaxws-rt</display-name> 
  9.  
  10. <listener> 
  11.  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
  12. </listener> 
  13. <context-param> 
  14.  <param-name>contextConfigLocation</param-name> 
  15.  <param-value>/WEB-INF/jaxws.xml</param-value> 
  16. </context-param> 
  17.  
  18. <context-param> 
  19.     <param-name>webAppRootKey</param-name> 
  20.     <param-value>test-jaxws-rt.root</param-value> 
  21.   </context-param>  
  22.  
  23. <servlet> 
  24.  <servlet-name>dispatcher</servlet-name> 
  25.  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
  26.  <init-param> 
  27.   <param-name>contextConfigLocation</param-name> 
  28.   <param-value>/WEB-INF/dispatcher.xml</param-value> 
  29.  </init-param> 
  30.  <load-on-startup>1</load-on-startup> 
  31. </servlet> 
  32. <servlet-mapping> 
  33.  <servlet-name>dispatcher</servlet-name> 
  34.  <url-pattern>/</url-pattern> 
  35. </servlet-mapping> 
  36.  
  37.    <servlet> 
  38.        <servlet-name>jaxws-servlet</servlet-name> 
  39.        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class> 
  40.  <load-on-startup>2</load-on-startup> 
  41.    </servlet> 
  42.    <servlet-mapping> 
  43.        <servlet-name>jaxws-servlet</servlet-name> 
  44.        <url-pattern>/service/*</url-pattern> 
  45.    </servlet-mapping> 
  46. </web-app> 
在 web.xml 同時註冊兩個 servlet,分別是 Spring MVC 的 DispatcherServlet 跟 JAX-WS 的 WSSpringServlet,這邊主要是透過 ContextLoaderListener 去啟用全域的 ApplicationContext 去管理所有的 Bean。


jaxws.xml
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.    xmlns:context="http://www.springframework.org/schema/context" 
  5.    xsi:schemaLocation="http://www.springframework.org/schema/beans 
  6.                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
  7.                        http://www.springframework.org/schema/context 
  8.                        http://www.springframework.org/schema/context/spring-context-4.0.xsd" 
  9. > 
  10.    <context:component-scan base-package="test.jaxws.webservice" /> 
  11.  
  12.    <bean id="jaxWsRtServletServiceExporter" class="test.jaxws.webservice.JaxWsRtServletServiceExporter"> 
  13.        <property name="basePath" value="/service"/> 
  14.    </bean> 
  15. </beans> 
jaxws.xml 是給 ContextLoaderListener 的設定,這裡用 component-scan 去找出 test.jaxws.webservice 下有 @Component 的 Bean,以及用 JaxWsRtServletServiceExporter 去轉換所有的 @WebService 到 SpringBinding。


dispatcher.xml
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.    xmlns:context="http://www.springframework.org/schema/context" 
  5.    xsi:schemaLocation="http://www.springframework.org/schema/beans 
  6.                        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
  7.                        http://www.springframework.org/schema/context 
  8.                        http://www.springframework.org/schema/context/spring-context-4.0.xsd" 
  9. > 
  10.    <context:component-scan base-package="test.jaxws.controller" /> 
  11.  
  12.    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
  13.        <property name="prefix" value="/WEB-INF/views/" /> 
  14.        <property name="suffix" value=".jsp" /> 
  15.    </bean> 
  16.  
  17.    <bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean" scope="request"> 
  18.        <property name="serviceInterface" value="test.jaxws.webservice.AccountService"/> 
  19.        <property name="wsdlDocumentUrl" value="http://localhost:8090/test-jaxws-rt/service/AccountService?wsdl"/> 
  20.        <property name="namespaceUri" value="http://webservice.jaxws.test/"/> 
  21.        <property name="serviceName" value="AccountService"/> 
  22.        <property name="portName" value="AccountServiceImplPort"/> 
  23.    </bean> 
  24. </beans> 
在這裡用 JaxWsPortProxyFactoryBean 去建立 WebService Client,因為是同一個 Web Content 所以用 scope="request" 延遲建立。


HomeController.java
  1. package test.jaxws.controller; 
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired; 
  4. import org.springframework.stereotype.Controller; 
  5. import org.springframework.web.bind.annotation.RequestMapping; 
  6. import org.springframework.web.context.WebApplicationContext; 
  7. import org.springframework.web.servlet.ModelAndView; 
  8.  
  9. import test.jaxws.webservice.AccountService; 
  10.  
  11. @Controller 
  12. public class HomeController { 
  13.  
  14.    @Autowired 
  15.    private WebApplicationContext appContent; 
  16.  
  17.    @RequestMapping("/") 
  18.    public ModelAndView index() { 
  19.        AccountService service = appContent.getBean("accountWebService", AccountService.class); 
  20.        String[] users = service.getAccounts("user"); 
  21.  
  22.        ModelAndView model = new ModelAndView("index"); 
  23.        model.addObject("users", users); 
  24.  
  25.        return model; 
  26.    } 
  27. } 
接下來就是連接的 Client,這邊用 WebApplicationContext 延遲取得 AccountService,在 http://localhost:8080/test-jaxws-rt/service/AccountService 就可以看到 service 的主頁面。


參考資料:
JAX-WS + Spring integration example
22. Remoting and web services using Spring

0 回應: