AccountService.java
package test.jaxws.webservice; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public interface AccountService { @WebMethod void insertAccount(String acc); @WebMethod String[] getAccounts(String role); }先來定義一個 interface,這主要是給 Client 用的。
AccountServiceImpl.java
package test.jaxws.webservice; import javax.jws.WebMethod; import javax.jws.WebService; import org.springframework.stereotype.Component; @Component @WebService(serviceName="AccountService") public class AccountServiceImpl implements AccountService { @Override @WebMethod public void insertAccount(String acc) { } @Override @WebMethod public String[] getAccounts(String role) { return new String[] {"tom", "jax"}; } }接者是 Web Service 的主體,@Component 是為了讓 Spring component-scan 自動建立 Bean,@WebService 則是用定義要 Exporting 到 JAX-WS 的 serviceName,由於是透過 Spring 去管理 bean 所以可以用 @Autowired 引用其他資源。
JaxWsRtServletServiceExporter.java
package test.jaxws.webservice; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import javax.jws.WebService; import javax.xml.ws.WebServiceProvider; import org.jvnet.jax_ws_commons.spring.SpringService; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.CannotLoadBeanClassException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.SingletonBeanRegistry; import com.sun.xml.ws.transport.http.servlet.SpringBinding; public class JaxWsRtServletServiceExporter implements BeanFactoryAware, InitializingBean { private ListableBeanFactory beanFactory; private String basePath; public void setBasePath(String basePath) { this.basePath = basePath; } @Override public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ListableBeanFactory)) { throw new IllegalStateException(getClass().getSimpleName() + " requires a ListableBeanFactory"); } this.beanFactory = (ListableBeanFactory) beanFactory; } @Override public void afterPropertiesSet() throws Exception { if (this.beanFactory instanceof SingletonBeanRegistry == false) { return; } SpringService service = new SpringService(); Set<String> beanNames = new LinkedHashSet<String>(this.beanFactory.getBeanDefinitionCount()); beanNames.addAll(Arrays.asList(this.beanFactory.getBeanDefinitionNames())); if (this.beanFactory instanceof ConfigurableBeanFactory) { beanNames.addAll(Arrays.asList(((ConfigurableBeanFactory) this.beanFactory).getSingletonNames())); } SingletonBeanRegistry factory = (SingletonBeanRegistry)this.beanFactory; for (String beanName : beanNames) { try { Class<?> type = this.beanFactory.getType(beanName); if (type == null || type.isInterface()) { continue; } WebService wsAnnotation = type.getAnnotation(WebService.class); WebServiceProvider wsProviderAnnotation = type.getAnnotation(WebServiceProvider.class); if (wsAnnotation == null && wsProviderAnnotation == null) { continue; } String serviceName; if (wsAnnotation != null) { serviceName = wsAnnotation.serviceName(); } else { serviceName = wsProviderAnnotation.serviceName(); } service.setImpl(type); service.setBean(this.beanFactory.getBean(beanName)); SpringBinding springBinding = new SpringBinding(); springBinding.setBeanName(serviceName + "Binding"); springBinding.setUrl(this.basePath + "/" + serviceName); springBinding.setService(service.getObject()); factory.registerSingleton(serviceName + "Binding", springBinding); } catch (CannotLoadBeanClassException ex) { // ignore beans where the class is not resolvable } } } }為了可以自動 Exporting Service 到 JaxWs RI Servlet 需要建立一個 Exporter,這裡將具有 @WebService 或 @WebServiceProvider 的 Bean 包裝成 SpringBinding 再註冊到 Spring BeanFactory 裡,這樣 WSSpringServlet 就會取到這邊註冊 SpringBinding Bean 進行 Exporting。
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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" > <display-name>test-jaxws-rt</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/jaxws.xml</param-value> </context-param> <context-param> <param-name>webAppRootKey</param-name> <param-value>test-jaxws-rt.root</param-value> </context-param> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <servlet-name>jaxws-servlet</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jaxws-servlet</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping> </web-app>在 web.xml 同時註冊兩個 servlet,分別是 Spring MVC 的 DispatcherServlet 跟 JAX-WS 的 WSSpringServlet,這邊主要是透過 ContextLoaderListener 去啟用全域的 ApplicationContext 去管理所有的 Bean。
jaxws.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" > <context:component-scan base-package="test.jaxws.webservice" /> <bean id="jaxWsRtServletServiceExporter" class="test.jaxws.webservice.JaxWsRtServletServiceExporter"> <property name="basePath" value="/service"/> </bean> </beans>jaxws.xml 是給 ContextLoaderListener 的設定,這裡用 component-scan 去找出 test.jaxws.webservice 下有 @Component 的 Bean,以及用 JaxWsRtServletServiceExporter 去轉換所有的 @WebService 到 SpringBinding。
dispatcher.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" > <context:component-scan base-package="test.jaxws.controller" /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean" scope="request"> <property name="serviceInterface" value="test.jaxws.webservice.AccountService"/> <property name="wsdlDocumentUrl" value="http://localhost:8090/test-jaxws-rt/service/AccountService?wsdl"/> <property name="namespaceUri" value="http://webservice.jaxws.test/"/> <property name="serviceName" value="AccountService"/> <property name="portName" value="AccountServiceImplPort"/> </bean> </beans>在這裡用 JaxWsPortProxyFactoryBean 去建立 WebService Client,因為是同一個 Web Content 所以用 scope="request" 延遲建立。
HomeController.java
package test.jaxws.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.ModelAndView; import test.jaxws.webservice.AccountService; @Controller public class HomeController { @Autowired private WebApplicationContext appContent; @RequestMapping("/") public ModelAndView index() { AccountService service = appContent.getBean("accountWebService", AccountService.class); String[] users = service.getAccounts("user"); ModelAndView model = new ModelAndView("index"); model.addObject("users", users); return model; } }接下來就是連接的 Client,這邊用 WebApplicationContext 延遲取得 AccountService,在 http://localhost:8080/test-jaxws-rt/service/AccountService 就可以看到 service 的主頁面。
參考資料:
JAX-WS + Spring integration example
22. Remoting and web services using Spring
沒有留言:
張貼留言
你好!歡迎你在我的 Blog 上留下你寶貴的意見。