tag:blogger.com,1999:blog-59465307047421309702024-03-06T16:20:07.273+08:00Jax 的工作紀錄除了在整理學習上的經驗,同時也能幫助其他需要的人Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.comBlogger1125tag:blogger.com,1999:blog-5946530704742130970.post-85327745699076224742015-02-10T15:52:00.000+08:002015-02-10T16:04:53.994+08:00JAX-WS RI + Spring 4 自動 Exporting ServiceSpring 4 的部分我就不多說了,請自行配置 jar 檔,這裡我用 <a href="https://jax-ws.java.net/" target="_blank">jaxws-ri-2.2.10.zip</a> 跟 <a href="https://jax-ws-commons.java.net/spring/" target="_blank">jaxws-spring-1.9.jar</a> 進行配置。<br />
<br />
<br />
<strong>AccountService.java</strong><br />
<pre class="java" name="code">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);
}
</pre>先來定義一個 interface,這主要是給 Client 用的。<br />
<br />
<br />
<strong>AccountServiceImpl.java</strong><br />
<pre class="java" name="code">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"};
}
}
</pre>接者是 Web Service 的主體,@Component 是為了讓 Spring component-scan 自動建立 Bean,@WebService 則是用定義要 Exporting 到 JAX-WS 的 serviceName,由於是透過 Spring 去管理 bean 所以可以用 @Autowired 引用其他資源。<br />
<br />
<br />
<strong>JaxWsRtServletServiceExporter.java</strong><br />
<pre class="java" name="code">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
}
}
}
}
</pre>為了可以自動 Exporting Service 到 JaxWs RI Servlet 需要建立一個 Exporter,這裡將具有 @WebService 或 @WebServiceProvider 的 Bean 包裝成 SpringBinding 再註冊到 Spring BeanFactory 裡,這樣 WSSpringServlet 就會取到這邊註冊 SpringBinding Bean 進行 Exporting。 <br />
<br />
<br />
<strong>web.xml</strong><br />
<pre class="xml" name="code"><?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>
</pre>在 web.xml 同時註冊兩個 servlet,分別是 Spring MVC 的 DispatcherServlet 跟 JAX-WS 的 WSSpringServlet,這邊主要是透過 ContextLoaderListener 去啟用全域的 ApplicationContext 去管理所有的 Bean。 <br />
<br />
<br />
<strong>jaxws.xml</strong><br />
<pre class="xml" name="code"><?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>
</pre>jaxws.xml 是給 ContextLoaderListener 的設定,這裡用 component-scan 去找出 test.jaxws.webservice 下有 @Component 的 Bean,以及用 JaxWsRtServletServiceExporter 去轉換所有的 @WebService 到 SpringBinding。<br />
<br />
<br />
<strong>dispatcher.xml</strong><br />
<pre class="xml" name="code"><?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>
</pre>在這裡用 JaxWsPortProxyFactoryBean 去建立 WebService Client,因為是同一個 Web Content 所以用 scope="request" 延遲建立。<br />
<br />
<br />
<strong>HomeController.java</strong><br />
<pre class="java" name="code">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;
}
}
</pre>接下來就是連接的 Client,這邊用 WebApplicationContext 延遲取得 AccountService,在 http://localhost:8080/test-jaxws-rt/service/AccountService 就可以看到 service 的主頁面。<br />
<br />
<br />
參考資料:<br />
<a href="http://www.mkyong.com/webservices/jax-ws/jax-ws-spring-integration-example/" target="_blank">JAX-WS + Spring integration example</a><br />
<a href="http://docs.spring.io/spring/docs/4.1.4.RELEASE/spring-framework-reference/html/remoting.html" target="_blank">22. Remoting and web services using Spring</a><br />
Jax Huhttp://www.blogger.com/profile/01953021685585893658noreply@blogger.com0