zoukankan      html  css  js  c++  java
  • Web Service 开发系列文章之四(手把手和你一起开发一个较完整的契约优先开发流程实例)

    Web Service 学习第四期(手把手和你一起开发一个较完整的契约优先开发流程实例)

    1、开始主题功能

    1.1、新建一个动态Web项目

    在JavaEE工作空间 下的项目视图

    在Java工作空间 下的项目视图

    1.2、依次编写schema(schema是标准然后根据schema编写wsdl)、wsdl;然后生成接口并且编写实现类

    1.2.1、新建文件夹META-INF、META-INFàwsdl和user.xsd

    注意:将schema统一加上命名空间前缀xsd

    <?xml version="1.0" encoding="UTF-8"?>

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"

    targetNamespace="http://service.zttc.edu.cn"

    xmlns:tns="http://service.zttc.edu.cn"

    elementFormDefault="qualified">

    <xsd:element name="add" type="tns:add"></xsd:element>

    <xsd:element name="addResponse" type="tns:addResponse"></xsd:element>

    <xsd:element name="delete" type="tns:delete"></xsd:element>

    <xsd:element name="deleteResponse" type="tns:deleteResponse"></xsd:element>

    <xsd:element name="list" type="tns:list"></xsd:element>

    <xsd:element name="listResponse" type="tns:listResponse"></xsd:element>

    <xsd:element name="login" type="tns:login"></xsd:element>

    <xsd:element name="loginResponse" type="tns:loginResponse"></xsd:element>

    <!-- 接下来定义上面引用到的那些类型 -->

    <xsd:complexType name="add">

         <xsd:sequence>

              <xsd:element name="user" type="tns:user"></xsd:element>

         </xsd:sequence>

    </xsd:complexType>

    <xsd:complexType name="addResponse">

         <xsd:sequence/>

    </xsd:complexType>

    <xsd:complexType name="delete">

         <xsd:sequence>

              <xsd:element name="username" type="xsd:string"></xsd:element>

         </xsd:sequence>

    </xsd:complexType>

    <xsd:complexType name="deleteResponse">

         <xsd:sequence/>

    </xsd:complexType>

    <xsd:complexType name="list">

         <xsd:sequence/>

    </xsd:complexType>

    <xsd:complexType name="listResponse">

    <xsd:sequence minOccurs="1" maxOccurs="unbounded">

    <xsd:element name="user"/>

    </xsd:sequence>

    </xsd:complexType>

    <xsd:complexType name="login">

    <xsd:sequence>

    <xsd:element name="username" type="xsd:string"/>

    <xsd:element name="password" type="xsd:string"/>

    </xsd:sequence>

    </xsd:complexType>

    <xsd:complexType name="loginResponse">

    <xsd:sequence>

    <xsd:element name="user" type="tns:user"/>

    </xsd:sequence>

    </xsd:complexType>

    <xsd:complexType name="user">

         <xsd:sequence>

              <xsd:element name="username" type="xsd:string"/>

              <xsd:element name="password" type="xsd:string"/>

              <xsd:element name="nickname" type="xsd:string"/>

         </xsd:sequence>

    </xsd:complexType>

    </xsd:schema>

    1.2.2、新建WSDL user.wsdl

    依次编写WSDL的五个部分

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>

    <wsdl:definitions

    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

    xmlns:tns="http://service.zttc.edu.cn"

    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

    xmlns:xsd="http://www.w3.org/2001/XMLSchema"

    name="UserService" targetNamespace="http://service.zttc.edu.cn">

    <wsdl:types>

    <xsd:schema targetNamespace="http://service.zttc.edu.cn">

    <!-- <xsd:import namespace="http://service.zttc.edu.cn" schemaLocation="user.xsd"></xsd:import> -->

    <xsd:include schemaLocation="user.xsd"></xsd:include>

    </xsd:schema>

    </wsdl:types>

    <wsdl:message name="add">

         <wsdl:part element="tns:add" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="addResponse">

    <wsdl:part element="tns:addResponse" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="delete">

         <wsdl:part element="tns:delete" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="deleteResponse">

    <wsdl:part element="tns:deleteResponse" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="list">

         <wsdl:part element="tns:list" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="listResponse">

    <wsdl:part element="tns:listResponse" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="login">

         <wsdl:part element="tns:login" name="parameters"/>

    </wsdl:message>

    <wsdl:message name="loginResponse">

    <wsdl:part element="tns:loginResponse" name="parameters"/>

    </wsdl:message>

    <!-- port是接口 -->

    <wsdl:portType name="IUserService">

    <!-- operation是方法 -->

    <wsdl:operation name="add">

    <wsdl:input message="tns:add"/>

    <wsdl:output message="tns:addResponse"/>

    </wsdl:operation>

    <wsdl:operation name="delete">

    <wsdl:input message="tns:delete"/>

    <wsdl:output message="tns:deleteResponse"/>

    </wsdl:operation>

    <wsdl:operation name="list">

    <wsdl:input message="tns:list"/>

    <wsdl:output message="tns:listResponse"/>

    </wsdl:operation>

    <wsdl:operation name="login">

    <wsdl:input message="tns:login"/>

    <wsdl:output message="tns:loginResponse"/>

    </wsdl:operation>

    </wsdl:portType>

    <!-- 通过binding可以设置传输的格式 -->

    <wsdl:binding name="userServiceSOAP" type="tns:IUserService">

    <!-- bindingtype是的name属性<wsdl:portType name="IUserService"> -->

    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

    <!-- 下面是操作 -->

    <wsdl:operation name="add">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="delete">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="list">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="login">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    </wsdl:operation>

    </wsdl:binding>

    <!-- 最后Service绑定 -->

    <wsdl:service name="UserService">

    <!-- <wsdl:service name="UserService">name属性是整个文档开头wsdl:definitionsname <wsdl:definitions name="UserService"-->

    <wsdl:port binding="tns:userServiceSOAP" name="UserServicePort">

    <soap:address location="http://localhost:9898/us/"/>

    </wsdl:port>

    </wsdl:service>

    </wsdl:definitions>

    1.2.3、通过刚刚写好的WSDL文件生成服务端的java接口文件

    进入WSDL文件所在的目录

    wsimport -d D:\wsimport\07s -keep -verbose user.wsdl

    将生成的代码拷贝到项目的src文件夹下

    之后删除除了IUserService.java的其它文件

    修改IUserService.java的代码:删除ObjectFactory

    @XmlSeeAlso({

    ObjectFactory.class

    })

    在项目中添加User类

    package org.zttc.vo;

    public class User {

        private String username;

        private String password;

        private String nickname;

        public String getUsername() {

            return username;

        }

        public void setUsername(String username) {

            this.username = username;

        }

        public String getPassword() {

            return password;

        }

        public void setPassword(String password) {

            this.password = password;

        }

        public String getNickname() {

            return nickname;

        }

        public void setNickname(String nickname) {

            this.nickname = nickname;

        }

        public User(String username, String password, String nickname) {

            super();

            this.username = username;

            this.password = password;

            this.nickname = nickname;

        }

        public User() {

            super();

        }

        

    }

    PS注意:其实像User这种类都是我们项目中本来就应该有的东西,我们刚刚编写WSDL也好,之前编写的以代码优先的服务也好,都只是为我们本来已有的项目提供了对外的Web服务接口而已。

    继续修改IUserService.java 添加对User的包引用

    1.2.4、编写实现类

    根据接口新建实现类UserServiceImpl.java

    很重要的一步是编写实现类的Web Service Annotation(用于绑定服务接口以及WSDL)

    @WebService(endpointInterface="cn.edu.zttc.service.IUserService",

             wsdlLocation="META-INF/wsdl/user.wsdl",

             serviceName="UserService",//wsdl<wsdl:definitions name="UserService"name

             portName="UserServicePort",//<wsdl:service--><wsdl:port name="UserServicePort">name

             targetNamespace="http://service.zttc.edu.cn" //IUserService的命名空间拷贝过来

             )

    1.2.5、新建MyService类发布服务

    package cn.edu.zttc.service;

    import javax.xml.ws.Endpoint;

    public class MyService {

        public static void main(String[] args) {

            Endpoint.publish("http://localhost:9898/us", new UserServiceImpl());

        }

    }

    1.2.6、导出客户端

    在MyService类中启动服务

    此时在浏览器中输入地址:http://localhost:9898/us?wsdl 既可以访问WSDL

    通过地址导出客户端代码

    wsimport -d D:\wsimport\07c -keep http://localhost:9898/us?wsdl

    此时因为使用JavaApplication发布的服务,所以如果WSDL和schema如果是分离的就会发生找不到schema的情况,如果改用Tomcat发布服务就没问题。为了继续,先将schema的内容拷贝到WSDL中

    之后再次生成客户端

    新建一个客户端项目并且将生成的客户端代码拷贝到src目录

    1.2.7、实现接口

    新建dao层包 org.zttc.dao

    在包中新建UserDao类

    编写UserDao

    package org.zttc.dao;

    import java.util.ArrayList;

    import java.util.HashMap;

    import java.util.List;

    import java.util.Map;

    import java.util.Set;

    import org.zttc.vo.User;

    public class UserDao {

        private static final Map<String, User> users = new HashMap<String, User>();

        

        private static UserDao dao = null;

        

        //写一个单例

        private UserDao() {

            users.put("admin", new User("admin", "123", "超级管理员"));

        }

        

        public static UserDao newInstance() {

            if(dao == null) {

                dao = new UserDao();

            }

            return dao;

        }

          

        

        public void add(User user) {

            users.put(user.getUsername(), user);

        }

        

        public void delete(String username) {

            users.remove(username);

        }

        

        public List<User> list() {

             Set<String> keys = users.keySet();

             List<User> list = new ArrayList<User>();

             for(String key : keys) {

                 list.add(users.get(key));

             }

             return list;    

        }

        

        public User login(String username, String password) {

            User u = users.get(username);

            return u;

        }

    }

    编写UserServiceImpl

    package cn.edu.zttc.service;

    import java.util.List;

    import javax.jws.WebService;

    import org.zttc.dao.UserDao;

    import org.zttc.vo.User;

    @WebService(endpointInterface="cn.edu.zttc.service.IUserService",

             wsdlLocation="META-INF/wsdl/user.wsdl",

             serviceName="UserService",//wsdl<wsdl:definitions name="UserService"name

             portName="UserServicePort",//<wsdl:service--><wsdl:port name="UserServicePort">name

             targetNamespace="http://service.zttc.edu.cn" //IUserService的命名空间拷贝过来

             )

    public class UserServiceImpl implements IUserService {

        

        private UserDao userDao = UserDao.newInstance();

          

          

        @Override

        public void add(User user) {

            userDao.add(user);

        }

        @Override

        public void delete(String username) {

            userDao.delete(username);

        }

        @Override

        public List<User> list() {

            

            return userDao.list();

        }

        @Override

        public User login(String username, String password) {

            return userDao.login(username, password);

        }

    }

    发布服务

    1.2.8、再次生成客户端并且编写TestService类

    wsimport -d D:\wsimport\07c -keep http://localhost:9898/us?wsdl

    新建包org.zttc.test和TestService类

    TestService的代码如下

    package org.zttc.test;

    import java.net.MalformedURLException;

    import java.net.URL;

    import java.util.List;

    import javax.xml.namespace.QName;

    import org.junit.Before;

    import org.junit.Test;

    import cn.edu.zttc.service.IUserService;

    import cn.edu.zttc.service.User;

    import cn.edu.zttc.service.UserService;

    public class TestService {

        private IUserService port;

        private UserService us;

        private String ns = "http://service.zttc.edu.cn";

        

        @Before

        public void init() {

            try {

                URL url = new URL("http://localhost:8888/us?wsdl");

                QName name = new QName(ns, "UserService");

                us = new UserService(url, name);

                port = us.getUserServicePort();

                

            } catch (MalformedURLException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

            //us = new UserService();

            //port = us.getUserServicePort();

        }

        

        @Test

        public void testList() {

            List<User> list = port.list();

            

            for(User u :list) {

                System.out.println(u.getNickname());

            }

        }

        

        @Test

        public void testAdd() {

            User u = new User();

            u.setNickname("张三");

            u.setPassword("123");

            u.setUsername("zs");

            port.add(u);

        }

        

        @Test

        public void testLogin() {

            User u = port.login("zs", "123");

            System.out.println(u.getUsername());

        }

        

        @Test

        public void testDelete() {

            port.delete("zs");

        }

    }

    2、善后处理

    2.1、异常处理

    2.2.1、处理代码端

    新建异常类

    UserException

    之后在UserException中生成超类的构造方法

    package org.zttc.dao;

    public class UserException extends RuntimeException {

        public UserException() {

            super();

            // TODO Auto-generated constructor stub

        }

        public UserException(String message, Throwable cause) {

            super(message, cause);

            // TODO Auto-generated constructor stub

        }

        public UserException(String message) {

            super(message);

            // TODO Auto-generated constructor stub

        }

        public UserException(Throwable cause) {

            super(cause);

            // TODO Auto-generated constructor stub

        }

        

    }

    在UserDao中的add函数和login添加异常处理

    public void add(User user) {

            if(users.containsKey(user.getUsername())) throw new UserException("用户已经存在");

            users.put(user.getUsername(), user);

    }

    public User login(String username, String password) {

            

            if(!users.containsKey(username)) throw new UserException("用户不存在");

            

            User u = users.get(username);

            

            if(!password.equals(u.getPassword())) throw new UserException("用户密码不正确");

            return u;

    }

    2.2.2、处理WSDL

    在schema中添加异常类型

    <xsd:element name="loginResponse" type="tns:loginResponse"></xsd:element>

    <!-- 异常处理信息 -->

    <xsd:element name="UserException" type="tns:UserException"></xsd:element>

    <xsd:complexType name="UserException">

    <xsd:sequence>

    <xsd:element name="message" type="xsd:string"></xsd:element>

    </xsd:sequence>

    </xsd:complexType>

    <!-- 异常处理信息 -->

    在WSDL中添加消息

    <wsdl:message name="UserException">

         <wsdl:part name="fault" element="tns:UserException"></wsdl:part>

    </wsdl:message>

    为portType设置异常

    add和login会抛出异常所以设置这两个方法

    <wsdl:operation name="add">

    <wsdl:input message="tns:add"/>

    <wsdl:output message="tns:addResponse"/>

    <wsdl:fault name="UserException" message="tns:UserException"></wsdl:fault>

    </wsdl:operation>

    <wsdl:operation name="login">

    <wsdl:input message="tns:login"/>

    <wsdl:output message="tns:loginResponse"/>

    <wsdl:fault name="UserException" message="tns:UserException"></wsdl:fault>

    </wsdl:operation>

    为binding添加异常(异常应该以什么样的格式来设置,以什么样的编码格式来传递呢)

    也是add和login可能会有异常

    <wsdl:operation name="add">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    <!-- 异常处理 -->

    <wsdl:fault name="UserException">

    <!-- use="literal"设置格式 -->

         <soap:fault name="UserException" use="literal"/>

    </wsdl:fault>

    </wsdl:operation>

    <wsdl:operation name="login">

    <!-- <soap:operation soapAction="http://service.zttc.edu.cn/NewOperation"/> -->

    <wsdl:input>

    <soap:body use="literal"/>

    </wsdl:input>

    <wsdl:output>

    <soap:body use="literal"/>

    </wsdl:output>

    <!-- 异常处理 -->

    <wsdl:fault name="UserException">

    <!-- use="literal"设置格式 -->

         <soap:fault name="UserException" use="literal"/>

    </wsdl:fault>

    </wsdl:operation>

    重新生成IUserService

    wsimport -d D:\wsimport\07s -keep -verbose user.wsdl

    去掉

    @XmlSeeAlso({

    ObjectFactory.class

    })

    UserException_Exception 换成UserException

    重新生成客户端

    wsimport -d D:\wsimport\07c -keep http://localhost:9898/us?wsdl

    可以看到再执行add和login方法的时候编译器会提示添加异常处理

    将异常处理加上

    @Test

        public void testAdd() {

            

            try{

                User u = new User();

                u.setNickname("张三");

                u.setPassword("123");

                u.setUsername("zs");

                port.add(u);

            }catch (UserException_Exception e) {

                System.out.println(e.getMessage());

            }

        }

        

        @Test

        public void testLogin() {

            try {

                User u = port.login("zs", "123");

                System.out.println(u.getUsername());

            } catch (UserException_Exception e) {

                System.out.println(e.getMessage());

            }

        }

    重新启动服务

    在客户端执行两次testAdd函数应该就会触发异常

    程序中断了,服务器端抛出了异常之后终止了;而我们想要的是服务器端抛出一个异常然后被JAX截获,创建一个fault对象出来,然后把fault对象通过SOAP传递到客户端,不应该在向服务器的上层抛了。之所以出现这种情况就是因为我们的UserException异常类继承自了RuntimeException了(运行时刻异常)

    将上述错误改正,让UserException继承自Exception

    之后在服务器端的org.zttc.dao.UserDao类和cn.edu.zttc.servic. UserServiceImpl类的方法中就要做相应的异常处理了

    public User login(String username, String password) {

            

            if(!users.containsKey(username)) throw new UserException("用户不存在");

            

            User u = users.get(username);

            

            if(!password.equals(u.getPassword())) throw new UserException("用户密码不正确");

            return u;

    }

    进行相应的异常处理

    之后进行测试

    执行两次testAdd

    客户端控制台输出

    说明异常被处理了

    一下是从Tcpmon中截获的有关异常处理的SOAP报文

    <?xml version="1.0" ?>

    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">

        <S:Body>

            12d

            <S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">

                <faultcode>S:Server</faultcode>

                <faultstring>鐢ㄦ埛宸茬粡瀛樺湪</faultstring>

                <detail>

                     <ns2:UserException xmlns:ns2="http://service.zttc.edu.cn">

                        <message>鐢ㄦ埛宸茬粡瀛樺湪</message>

                    </ns2:UserException>

                </detail>

            </S:Fault>

        </S:Body>

    </S:Envelope>

    虽然由于编码问题输出了乱码

    但是可以推测出 鐢ㄦ埛宸茬粡瀛樺湪应该是 "用户已存在"

    3、在Web服务器中发布服务

    3.1、将META-INFàwsdl的目录及其下面wsdl和xsd拷贝到我们Web项目的WEB-INF目录下

    之后就可以在WSDL中使用导入schema的方式了(之前是因为用JavaApplication发布的原因,所以将wsdl中types下面的schema全都写到了WSDL中)

    D:\项目\学习 workspace\workspace\07_soa\WebContent\WEB-INF\wsdl>wsimport -d D:\w

    simport\07s -keep user.wsdl

    将IUserService接口拷出来,覆盖下图中的

    3.2、修改实现类UserServiceImpl

    将webservice的annotation下的wsdlLocation属性目录修改为WEB-INF

    @WebService(endpointInterface="cn.edu.zttc.service.IUserService",

             wsdlLocation="META-INF/wsdl/user.wsdl",

    @WebService(endpointInterface="cn.edu.zttc.service.IUserService",

             wsdlLocation="WEN-INF/wsdl/user.wsdl",

    3.3、在WEB-INF下创建sun-jaxws.xml文件

    在这个文件下配置endpoint

    <?xml version="1.0" encoding="UTF-8"?>

    <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">

         <!-- 下边的name是我们对外发布的Service的名称(servlet中所需要对应的名称),implementation是实现类,

         url-pattern是在浏览器上访问的页面的地址 -->

         <endpoint name="UserServiceAaa"

         implementation="cn.edu.zttc.UserServiceImpl" url-pattern="/us"/>

    </endpoints>

    3.4、配置web.xml

    3.4.1、导入JAX-RI包

    将jaxws-ri-2.2.5包中lib目录下的文件拷贝到WEB-INF下的lib目录

    3.4.2、加入监听器以及servlet

    <?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_2_5.xsd" id="WebApp_ID" version="2.5">

        <listener>

         <!-- listener-classjax-ri中的一个类 -->

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

        </listener>

          

        <!-- 创建servlet -->    

        <servlet>

         <!-- servlet-name要和sun-jaxws.xml中的<endpoint name="UserServiceAaa"name一致 -->

            <servlet-name>UserServiceAaa</servlet-name>

            <!-- servlet-class也是jax-ri中的一个类 -->

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

        </servlet>

          

        <servlet-mapping>

         <servlet-name>UserServiceAaa</servlet-name>

            <url-pattern>/us</url-pattern>

        </servlet-mapping>

             

    </web-app>

    注意:cn.edu.zttc.service目录下不要有.class文件,否则在启动Tomcat时会报错

    之后启动Tomcat服务器

    在浏览器里访问发布出去的wsdl

    http://localhost:8080/07_soa/us?wsdl

    重新生成客户端测试

    D:\项目\学习 workspace\workspace\07_soa\WebContent\WEB-INF\wsdl>wsimport -d D:\w

    simport\07c -keep -verbose http://localhost:8080/07_soa/us?wsdl

  • 相关阅读:
    P1891 疯狂LCM
    P2568 GCD
    P1516 青蛙的约会和P2421 [NOI2002]荒岛野人
    P4168 蒲公英
    P5960 差分约束算法模板
    P2024 食物链(种类并查集)
    CF1328E Tree Queries
    CF1328B K-th Beautiful String
    dij-spfa乱搞
    P1993 小K的农场
  • 原文地址:https://www.cnblogs.com/decarl/p/2512736.html
Copyright © 2011-2022 走看看