WebService学习之路四

 

 

WebService学习之路一 :http://trylin.iteye.com/blog/1906819

 

WebService学习之路二 :http://trylin.iteye.com/blog/1907883

 

WebService学习之路三 :http://trylin.iteye.com/blog/1908269

 

SOAP 的理解使用

 

不论是以那种形式实现WS服务,axis也好,xfire,CXF也好 ,服务进行消息传替都是基于SOAP格式的。

 

这里简单理解一下SOAP信息,和实现使用SOAP格式的调用我们发布的WS.

 

1、SOAP消息格式。

打开之前的第一个简单示例的服务。

 

eclipse的视图模式改为javaee,并且打开wsdl浏览



 选择WSDL视图

 



 

 

选择一个WSDL MAIN,并输入我们发布的WSDL地址。
WebService学习之路四_第1张图片
 看到我们发布的两个方法了吧,选择add然后输入两个参数,点击go,然后点击下发status项的 Source,

 

 


WebService学习之路四_第2张图片
 
WebService学习之路四_第3张图片

 

 

这样就可以看到我们请求WS的请求和返回的SOAP消息,上面是请求,下面是返回 。请求的参数都是被包裹在一个Body的对象中,然后是方法,再是方法下的参数。

 


WebService学习之路四_第4张图片
 

 看到SOAP消息,有没有觉得和我们发布的WSDL的types的格式很像,下面截图做个简单的比较,一目了然。

 

 

 


WebService学习之路四_第5张图片
 

2、使用SOAP消息访问WS

 

上面看到了我们请求返回的SOAP消息,那当然我们也可以根据SOAP的消息格式访问我们发布的WS服务,这就要使用到jdk扩展包下的 soap 包。

 

 

 

在我们客户端的项目中新建一个类 SOAPImpl(具体代码有注释,更多的可以看视频的中的解释哦)

学习视频地址:http://trylin.iteye.com/blog/1907289

 

WS服务端代码:(由于之前有测试过命名空间相关的东西,下面的测试都是基于默认命名空间,如果按照第一章的示例来直接用下面的客户端访问 可能会出现找不到方法的情况,或者你可以把我的测试代码的命名空间修改下,都是可以的,这里我把除去命名空间的服务端代码也贴上。。。)

 

@WebService()
public interface ITest {
	public int add(@WebParam(name="a")int a, @WebParam(name="b")int b);
	public int minus(@WebParam(name="a")int a, @WebParam(name="b")int b);
}
@WebService(endpointInterface="com.trylin.ws.service.ITest")
public class TestImpl implements ITest{
	public int add(int a, int b) {
		System.out.println(a+"+"+b+"="+(a+b));
		return a+b;
	}
	public int minus(int a, int b) {
		return a-b;
	}
}

 

 客户端:

public class SOAPImpl {
	
	/** 服务地址 */
	public URL url = null;
	/** 服务命名空间 */
	public String ns = "http://impl.service.ws.trylin.com/";
	/** 命名空间变量 */
	public String xlns = "nn";
	
	public SOAPImpl(){
		try {
			url = new URL("http://localhost:8888/WS");
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 创建add方法的SOAP消息
	 * @return
	 * @throws Exception
	 */
	protected SOAPMessage add() throws Exception{
		MessageFactory factory = MessageFactory.newInstance();//创建消息工厂
		SOAPMessage message = factory.createMessage();//创建消息类型
		SOAPPart part = message.getSOAPPart();//创建Part元素
		SOAPEnvelope envelope = part.getEnvelope();//创建envelope元素   SOAP的根节点
		SOAPBody body = envelope.getBody();//body对象
		//指定body下节点 请求的方法add元素
		SOAPBodyElement bodyElement = body.addBodyElement(new QName("http://service.ws.trylin.com/","add",xlns));
		//设置add方法的参数
		bodyElement.addChildElement("a").setValue("1");
		bodyElement.addChildElement("b").setValue("2");
		message.writeTo(System.out);
		return message;
	}
	
	/**
	 * 通过Service将SOAP消息提交到WS服务端,并返回服务端的SOAPMessage
	 * @return
	 * @throws Exception
	 */
	public SOAPMessage getSOAPWS() throws Exception{
		//创建发送Service  设定Qname TestImplService  服务名
		Service service = Service.create(url, new QName(ns,"TestImplService"));
		//通过dispatch发送请求  设置请求格式为Service.Mode.MESSAGE
		Dispatch<SOAPMessage> dispath = service.createDispatch(new QName(ns,"TestImplPort"),
				SOAPMessage.class, Service.Mode.MESSAGE);
		SOAPMessage request = this.add();
		System.out.println("\n.......");
		SOAPMessage response = dispath.invoke(request);
		response.writeTo(System.out);
		return response;
	}
	
	/**
	 * 解析服务端的SOAPMessage
	 * @param message
	 * @throws Exception
	 */
	public void parseSOAPMessage(SOAPMessage message) throws Exception{
		System.out.println();
		Document document = message.getSOAPPart()
			.getEnvelope().getBody().extractContentAsDocument();//获取Body节点元素文本对象
		Element element = document.getDocumentElement();
		System.out.println((element.getElementsByTagName("return").item(0).getTextContent()));//打印返回值
	}
}

 

 

然后在写客户端测试用例:

 

public class TestSOAP {
	@Test
	public void test01() throws Exception{
		SOAPImpl soap = new SOAPImpl();
		soap.parseSOAPMessage(soap.getSOAPWS());
	}
}

 运行后,查看我们定义的soap输出和服务的返回 soap 还有返回的计算结果

 

 

 




 这个是简单的服务访问,我们添加一个自定义的User对象,在服务中再添加一个用户登录的方法,然后发布后,通过WSDL浏览器,请求login服务,看下这时的soap消息是啥样的。

首先创建一个User

 

public class User {
	private String userName; //名字
	private int age;//年龄
	private boolean isBoy;//
	private double money;//
……省略get set
}

 服务类中 添加login方法

 

 

@WebService()
public interface ITest {
	public int add(@WebParam(name="a")int a, @WebParam(name="b")int b);
	public int minus(@WebParam(name="a")int a, @WebParam(name="b")int b);
	@WebMethod(operationName="login")
	public @WebResult(name="user")User login(@WebParam(name="user")User user);
}
@WebService(endpointInterface="com.trylin.ws.service.ITest")
public class TestImpl implements ITest{
	public int add(int a, int b) {
		System.out.println(a+"+"+b+"="+(a+b));
		return a+b;
	}
	public int minus(int a, int b) {
		return a-b;
	}
	public User login(User user){
		if("hiboy".equals(user.getUserName())){
			user.setAge(12);
			user.setBoy(true);
			user.setMoney(100);
			return user;
		}
		return null;
	}
}

 这里用到了几个注释,简单说明一下:

 

 

@WebService()  标记创建WS服务,可以设置命名空间,实现接口,服务名称等。

 

@WebParam() 表示参数在wsdl中显示的名称,默认参数名是按照arg0.. argN,可以手动设定,如@WebParam(name="a")int a   这样a元素在wsdl中就显示成a而不是 arg0

 

@WebResult这个表示返回参数在wsdl显示的名称,默认返回参数名称为return,如@WebResult(name="user")User 这样在wsdl中login的返回参数名就是user了

 

@WebMethod 表示wsdl中显示的方法名,默认是按照原方法名显示,这里可以修改。

 

 

 

然后启动服务,通过WSDL浏览器访问我们的login方法,查看SOAP消息。仔细看下User的格式哦。


WebService学习之路四_第6张图片

 

 

看到SOAP消息了,然后我们在用客户端去按照SOAP消息的格式访问WS。

 

这里的访问有两种方法,第一种就是前面说的,给body对像手动的添加节点,这样比较死,一个一个的添加,也比较累。第二种方法,可以通过JAXB 将一个User对象格式化成类似上面soap消息的xml。可以先试下第一种方法,我这里写第二种。

 

 在客户端,我们也要生成XMl对应的user对象

 

@XmlRootElement//注意这个注解  不加下面使用jaxB的时候会报错
public class User {
	
	private String userName; //名字
	private int age;//年龄
	private boolean isBoy;//是否
	private double money;//
     //...get  set
}

 在SOAPImpl类中新增加几个方法

 

 

 

/**
	 * 设置Login的 Source对象  Source简单理解为数据源
	 * @return
	 * @throws Exception
	 */
	protected Source login() throws Exception{
		User user = new User();
		user.setUserName("hiboy");//设置参数 User
		JAXBContext context = JAXBContext.newInstance(User.class);//创建JAXB文档对戏那个
		Marshaller mar = context.createMarshaller();
		mar.setProperty(Marshaller.JAXB_FRAGMENT, true);//设置生成的xml不包含xml头  
		StringWriter writer = new StringWriter();
		mar.marshal(user, writer);//处理User
		//JAXBContext  只处理User对像 所以方法声明的部分 还是的自己手写
		String payLoad = "<"+xlns+":login xmlns:"+xlns+"=\""+"http://service.ws.trylin.com/"+"\">"+writer.toString()+"</nn:login>";
		System.out.println(payLoad);
		//穿件 Source流对象
		Source source = new StreamSource(new StringReader(payLoad));
		return source;
	}
	/**
	 * 通过Service将Source发送到服务端  并且返回服务端的Source
	 * @return
	 * @throws Exception
	 */
	public Source getSOAPLoginWS() throws Exception{
		Service service = Service.create(url, new QName(ns,"TestImplService"));
		Dispatch<Source> dispath = service.createDispatch(new QName(ns,"TestImplPort"), 
				Source.class, Service.Mode.PAYLOAD);
		Source request = this.login();
		Source response = dispath.invoke(request);
		return response;
	}
	/**
	 * 解析Source
	 * @param source
	 * @throws Exception
	 */
	public void parseSOAPSource(Source source) throws Exception{
		System.out.println();
		//解析的时候 需要获得节点对象  所以此处用 Transformer转换
		Transformer transformer  = TransformerFactory.newInstance().newTransformer();
		DOMResult result = new DOMResult();
		transformer.transform(source, result);
		//第一层 获取response节点 
		Node nl =  result.getNode().getFirstChild().getFirstChild();
		JAXBContext context = JAXBContext.newInstance(User.class);
		Unmarshaller umar = context.createUnmarshaller();
		User user = (User)umar.unmarshal(nl);
		System.out.println(user.getMoney());
	}

 

编写测试类:

@Test
public void test02() throws Exception{
	SOAPImpl soap = new SOAPImpl();
	soap.parseSOAPSource(soap.getSOAPLoginWS());
}

 运行 输出 :  100.0就是我们在服务端 给User加的一个值,这样表示通过SOAP调用成功。



 

还是那句话,本人技术不牢靠,可能整理的这些有点乱,也没有按照视频上的原样规规矩矩的记录所有。也有一些内容都是自己的理解,肯定有理解有误的地方。这里解释不详细的,有错误的,还行看到博客的大神在回复中留下你们的间接,如果有幸有童鞋模仿上面的示例,有问题的也可以提出,我们一起解决。。。

 

测试代码在附件中,不嫌弃可以下载运行下大笑。。。

 


 
 

 

 

 

 

你可能感兴趣的:(webservice,wsdl,SOAP,JAXBContext)