SOA(面向服务的架构)
Service1,Service2,Service3--所有组件都是“即插即用”的
IBM提倡的SOA架构,希望以“组装电脑”的方式来开发软件。
1,各种提供服务的组件。
2,企业服务总线(EnterpriseServiceBus,ESB)
CXF号称是SOA框架
CXF(Apache)
cxf框架的使用
1,下载cxf
2,配置cxf下bin的path环境变量
3,测试环境变量是否配置正确
cxf框架目录介绍
/bin常用命令
/doc开发文档
/libjar包
cxf内置了一个JettyWeb服务器
(tomcat的开发者就是javaee规范的制定者)
使用cxf开发webservice服务端
1,开发一个webservice接口
加上@WebService注释
2,开发实现类
加上@WebService()注释
加上属性endpointInterface="com.kenan.cxf.HelloWorld"指定实现的接口
serviceName=“HelloworldService”
3,加入jar包
cxf servlet3.0 jetty-*(JettyWeb服务器) asm common-logging neethi xmlschema-core wsdl4j
4,创建测试类,发布ws(需要在main方法中)
HelloWorldhw=newHImpl(); Endpoint.publish("http://localhost:9999/hello",hw);
5,访问http://localhost/hello?wsdl
使用cxf开发webservice客户端
1,使用wsdl2java将服务端暴露的wsdl文档转换为java代码
2,找到实现了service接口的类,该类可以当成工厂来使用
HelloWorldImplfac=new();
3,生产HelloWorldhello=fac.getHelloWorldWsPort();
调用远程方法
hello.say("sdf");
1,当形参,返回值为String的时候,cxf可以轻松的处理
2,当形参返回值类型为javabean的复合类,List集合,数组,cxf也可以很好的处理
wsdl介绍
wsdl:webservicedefinationlanguage
wsdl是一个xml文档
xml中的两个属性
targetNamespace(规定自己这个文档的命名空间)相当于java中的包名
xmlns(命名空间:xmlnamespace)相当于java中要引入的
WSDL文档
1,WebService接口
一次webservice的调用,其实不是方法调用,而是发送SOAP消息(xml文档片段)
要调用的方法,和传入的参数都写在xml文档中,也就是一个SOAP消息
SOAPsimpleobjectaccessproto简单对象协议
调用一次webservice的本质
1,客户端把要调用方法参数,转换成xml文档片段(SOAP消息),该文档必须符合wsdl文档规范
2,通过网络,把xml文档传给服务器
3,服务器接受到xml文档
4,服务器解析xml文档片段,提取其中的数据。
并把数据转换调用webservice所需要的参数类型
5,服务器执行方法
6,把执行方法得到的返回值,再次转换成xml文档片段
7,通过网络,把xml文档片段传给客户端
8,客户端接受到XML文档
9,客户端解析到XML文档,提取其中的数据
并把数据转换成调用webservice的返回值
从上面调用的本质来看,要一个语言支持webservice
唯一的要求是,该语言支持xml文档解析,声称,支持网络传输
如果遇到cxf无法转换的类型,就需要我们自定义转换
如对于map对象
1,使用@XmlJavaTypeAdapter注释无法处理的类型
通过(value=)XmlAdapter指定一个转换器
转换器在这里就可以把cxf搞不定的类型搞定
2,实现自己的转换器,开发一个cxf搞得定的类型需要实现XmlAdapter抽象类
这里有两个参数,第一个是搞得定的类型
第二个是搞不定的类型
extendsXmlAdapter<StringCat,Map<string,Cat>>
SOAP协议:简单对象传输协议
传入消息:
header:默认情况下,header元素不是强制出现的,header元素
有程序员控制添加,主要用于携带一些额外的信息
2013-1-2822:13:26org.apache.cxf.services.HelloWorldImpl.HelloWorldImplPort.HelloWorld
信息:InboundMessage
----------------------------
ID:3
Address:http://localhost:9999/hello Encoding:UTF-8 Http-Method:POST Content-Type:text/xml;charset=UTF-8 Headers: {Accept=[text/xml,multipart/related,text/html,image/gif,image/jpeg,*;q=.2,*/*;q=.2], connection=[keep-alive], Content-Length=[195], content-type=[text/xml; charset=UTF-8], Host=[localhost:9999], SOAPAction=[""], User-Agent=[Java/1.6.0_13]} Payload: <?xmlversion="1.0"?> <S:Envelopexmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:helloxmlns:ns2="http://ws.cxf.kenan.com/"> <arg0>你好</arg0> </ns2:hello> </S:Body> </S:Envelope>
--------------------------------------
传出消息:
2013-1-2822:13:26org.apache.cxf.services.HelloWorldImpl.HelloWorldImplPort.HelloWorld
信息:OutboundMessage
---------------------------
ID:3
Encoding:UTF-8 Content-Type:text/xml Headers:{} Payload: <soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:helloResponsexmlns:ns2="http://ws.cxf.kenan.com/"> <return>你好</return> </ns2:helloResponse> </soap:Body> </soap:Envelope>
--------------------------------------
webservice急需解决的问题:如何收钱?
--如何进行权限控制?
为了进行权限控制,需要:
客户端发送soap消息的时候,必须携带用户名和密码信息
如果没有,或者不正确,拒绝调用
拦截器
为了让程序员放访问,并修改cxf声称的xml文档,cxf提供的拦截器
分为In拦截器
Out拦截器
服务器端添加拦截器:
1,在发布的时候
EndPointImplep=En...publish();
添加in拦截器
ep.getInInterceptors().add(e);
添加out拦截器
ep.getOutInterceptors().add(e);
2,系统的拦截器
这个是日志的拦截器
newLoggingInInterceptor();(默认输出到控制台)
newLoggingOutInterceprot();
3,创建自定义拦截器需要实现Interceptor
一般我们会继承AbstractPhaseInterceptor
客户端添加拦截器
1,倒入jar包(zxf。。。)
2,Clientclient=ClientProxy.getClient(hi);
hi为webserice接口类对象
client.getInInterceptors().add();
自定义拦截器
1,继承AbstractPhaseInterceptor
extendsAbstractPhaseInterceptor<SoapMessage>
2,实现handlMessage方法
这个方法中的形参就是被拦截到的soap消息
解析soap消息,或者修改soap消息
3,写构造函数,显式调用父类构造函数,程序将不会隐式调用父类无参数的构造函数
super(Phase.PRE_INVOKE)在调用之前拦截soap消息
4,消息处理
参数SoapMessagemsg System.our.println(msg); List<Header>headers=msg.getHeaders(); if(headers!=null&&headers.size()>0){ //加入要求第一个header存放用户名和密码 Headerh=headers.get(0); Elementele=(Element)h.getObject(); NodeListuserIds=ele.getElementsByTagName("userId"); NodeListuserPasses=ele.getElementsByTagName("userPass"); if(userIds.getLength==1&&userPasses.getLength()==1){ StringuserId=userIds.item)0).getTextContent(); StringuserPass=userPasses.item(0).getTextContent(); if(){ //验证通过 return; } } }
自定义客户端的OutInterceptor
1,同上
2,构造器
publicConstructor(StringuserId,StringuserPass){ super(Phase.PREPARE_SEND);//在发送soap消息之前调用该拦截器 this.userId= this.userPass.. }
3,HandleMessage
List<Header>headers=msg.getHeaders(); Documentdoc=DOMUtils.createDocument(); Elementele=doc.createElement("authHeader"); ElementidEle=doc.createElement("userId"); idEle.setTextContent(userId); ElementpassEle=doc.createElement("userPass"); passEle.setTextContent(userPass); ele.appenChild(idEle); ele.appenChild(passEle); 生成如下文档 <authHeader> <userId></userId> <userPass></userPass> </authHeader> //把ele元素包装成header,并添加到soap消息中 Headerheader=newHeader(newQName("qname"),ele); headers.add(header); 客户端利用拦截器在发功的soap消息中写入数据后的消息 <soap:Envelopexmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <authHeader> <userId>23</userId> <userPass>sdr</userPass> </authHeader> </soap:Header> <soap:Body> <ns2:helloxmlns:ns2="http://ws.cxf.kenan.com/"> <arg0>你好</arg0></ns2:hello ></soap:Body> </soap:Envelope>
服务器端代码:
转换器:用于转换Map数据类型
packagecom.kenan.cxf.adapter; importjava.util.HashMap; importjava.util.Map; importjavax.xml.bind.annotation.adapters.XmlAdapter; importcom.kenan.cxf.adapter.domain.StringCat; importcom.kenan.cxf.adapter.domain.StringCat.Entry; importcom.kenan.cxf.domain.Cat; publicclassMapXmlAdapterextendsXmlAdapter<StringCat,Map<String,Cat>>{ @Override publicMap<String,Cat>unmarshal(StringCatv)throwsException{ Map<String,Cat>m=newHashMap<String,Cat>(); for(Entryentry:v.getEntries()){ m.put(entry.getKey(),entry.getValue()); } returnm; } @Override publicStringCatmarshal(Map<String,Cat>v)throwsException{ StringCats=newStringCat(); for(Stringkey:v.keySet()){ Entrye=newEntry(key,v.get(key)); s.getEntries().add(e); } returns; } }
用户map转换器的实体
packagecom.kenan.cxf.adapter.domain; importjava.util.ArrayList; importjava.util.List; importcom.kenan.cxf.domain.Cat; publicclassStringCat{ publicstaticclassEntry{ privateStringkey; privateCatvalue; publicEntry(){ } publicEntry(Stringkey,Catvalue){ super(); this.key=key; this.value=value; } publicStringgetKey(){ returnkey; } publicvoidsetKey(Stringkey){ this.key=key; } publicCatgetValue(){ returnvalue; } publicvoidsetValue(Catvalue){ this.value=value; } } privateList<Entry>entries=newArrayList<Entry>(); publicList<Entry>getEntries(){ returnentries; } publicvoidsetEntries(List<Entry>entries){ this.entries=entries; } }
进行权限验证的过滤器:AuthInterceptor
packagecom.kenan.cxf.auth; importjava.util.List; importorg.apache.cxf.binding.soap.SoapMessage; importorg.apache.cxf.headers.Header; importorg.apache.cxf.interceptor.Fault; importorg.apache.cxf.phase.AbstractPhaseInterceptor; importorg.apache.cxf.phase.Phase; importorg.w3c.dom.Element; importorg.w3c.dom.NodeList; publicclassAuthInterceptorextendsAbstractPhaseInterceptor<SoapMessage>{ publicAuthInterceptor(){ super(Phase.PRE_INVOKE);//在调用方法之前拦截 //TODOAuto-generatedconstructorstub } @Override publicvoidhandleMessage(SoapMessagemsg)throwsFault{ List<Header>headers=msg.getHeaders(); if(headers!=null&&headers.size()>0){ Headerheader=headers.get(0); Elementele=(Element)header.getObject(); NodeListuserIdList=ele.getElementsByTagName("userId"); NodeListuserPassList=ele.getElementsByTagName("userPass"); if(userIdList!=null&&userIdList.getLength()==1&&userPassList!=null&&userPassList.getLength()==1){ StringuserId=userIdList.item(0).getTextContent(); StringuserPass=userPassList.item(0).getTextContent(); if(userId.equals("admin")&&userPass.equals("admin")){ return; } } } thrownewFault(newException("验证不通过")); } }
model层也就是数据库实体层,这里仅用于测试,并没有链接数据库
类cat
packagecom.kenan.cxf.domain; publicclassCat{ privateIntegerid; privateStringname; privateStringcolor; publicCat(){ super(); } publicCat(Integerid,Stringname,Stringcolor){ super(); this.id=id; this.name=name; this.color=color; } publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicStringgetColor(){ returncolor; } publicvoidsetColor(Stringcolor){ this.color=color; } @Override publicStringtoString(){ return"Cat[id="+id+",name="+name+",color="+color+"]"; } }
类User
packagecom.kenan.cxf.domain; publicclassUser{ privateIntegerid; privateStringname; privateStringpass; privateStringaddress; publicUser(){ super(); } publicUser(Integerid,Stringname,Stringpass,Stringaddress){ super(); this.id=id; this.name=name; this.pass=pass; this.address=address; } publicIntegergetId(){ returnid; } publicvoidsetId(Integerid){ this.id=id; } publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicStringgetPass(){ returnpass; } publicvoidsetPass(Stringpass){ this.pass=pass; } publicStringgetAddress(){ returnaddress; } publicvoidsetAddress(Stringaddress){ this.address=address; } @Override publicinthashCode(){ finalintprime=31; intresult=1; result=prime*result+((name==null)?0:name.hashCode()); result=prime*result+((pass==null)?0:pass.hashCode()); returnresult; } @Override publicbooleanequals(Objectobj){ if(this==obj) returntrue; if(obj==null) returnfalse; if(getClass()!=obj.getClass()) returnfalse; Userother=(User)obj; if(name==null){ if(other.name!=null) returnfalse; }elseif(!name.equals(other.name)) returnfalse; if(pass==null){ if(other.pass!=null) returnfalse; }elseif(!pass.equals(other.pass)) returnfalse; returntrue; } }
websercie接口
packagecom.kenan.cxf.ws; importjava.util.List; importjava.util.Map; importjavax.jws.WebService; importjavax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; importcom.kenan.cxf.adapter.MapXmlAdapter; importcom.kenan.cxf.domain.Cat; importcom.kenan.cxf.domain.User; @WebService publicinterfaceHelloWorld{ Stringhello(Stringname); List<Cat>getCatsByUser(Useruser); @XmlJavaTypeAdapter(value=MapXmlAdapter.class)Map<String,Cat>getAllCats(); }
webservice接口实现类
packagecom.kenan.cxf.ws.impl; importjava.util.ArrayList; importjava.util.HashMap; importjava.util.List; importjava.util.Map; importjavax.jws.WebService; importcom.kenan.cxf.domain.Cat; importcom.kenan.cxf.domain.User; importcom.kenan.cxf.ws.HelloWorld; @WebService(endpointInterface="com.kenan.cxf.ws.HelloWorld", serviceName="HelloWorldImpl") publicclassHelloWorldImplimplementsHelloWorld{ @Override publicStringhello(Stringname){ System.out.println("hello:"+name); returnname; } @Override publicList<Cat>getCatsByUser(Useruser){ System.out.println(user.getName()); Listlist=newArrayList(); list.add(newCat(12,"sdf","sdf")); list.add(newCat(12,"2df","sdf")); list.add(newCat(12,"3df","sdf")); list.add(newCat(12,"4df","sdf")); list.add(newCat(12,"sdf","sdf")); returnlist; } @Override publicMap<String,Cat>getAllCats(){ Map<String,Cat>m=newHashMap<String,Cat>(); m.put("1",newCat(12,"2","34")); m.put("2",newCat(12,"2","34")); m.put("3",newCat(12,"2","34")); m.put("4",newCat(12,"2","34")); returnm; } }
发布webservice
packagecom.kenan.cxf.ws.impl; importstaticorg.junit.Assert.*; importjavax.xml.ws.Endpoint; importorg.apache.cxf.interceptor.LoggingInInterceptor; importorg.apache.cxf.interceptor.LoggingOutInterceptor; importorg.apache.cxf.jaxws.EndpointImpl; importcom.kenan.cxf.auth.AuthInterceptor; importcom.kenan.cxf.ws.HelloWorld; publicclassWSTest{ publicstaticvoidmain(String[]args){ HelloWorldhello=newHelloWorldImpl(); EndpointImplep=(EndpointImpl)(Endpoint.publish("http://localhost:9999/hello",hello)); //ep.getInInterceptors().add(newLoggingInInterceptor()); //ep.getOutInterceptors().add(newLoggingOutInterceptor()); ep.getInInterceptors().add(newAuthInterceptor()); } }
客户端
1,首先用sadl2java命令倒入需要的java代码,然后开发自己的拦截器
2,拦截器,用户实现权限认证
packagecom.kenan.cxf.auth; importjava.util.List; importjavax.xml.namespace.QName; importorg.apache.cxf.binding.soap.SoapMessage; importorg.apache.cxf.headers.Header; importorg.apache.cxf.helpers.DOMUtils; importorg.apache.cxf.interceptor.Fault; importorg.apache.cxf.phase.AbstractPhaseInterceptor; importorg.apache.cxf.phase.Phase; importorg.w3c.dom.Document; importorg.w3c.dom.Element; publicclassAuthOutInterceptorextendsAbstractPhaseInterceptor<SoapMessage>{ privateStringuserId; privateStringuserPass; publicAuthOutInterceptor(StringuserId,StringuserPass){ super(Phase.PREPARE_SEND);//在消息发送前调用 this.userId=userId; this.userPass=userPass; } @Override publicvoidhandleMessage(SoapMessagemsg)throwsFault{ List<Header>headers=msg.getHeaders(); Documentdoc=DOMUtils.createDocument(); Elementele=doc.createElement("authHeader"); ElementidEle=doc.createElement("userId"); idEle.setTextContent(userId); ElementpassEle=doc.createElement("userPass"); passEle.setTextContent(userPass); ele.appendChild(idEle); ele.appendChild(passEle); headers.add(newHeader(newQName("kenan"),ele)); } }
客户端webservcie调用
packagetest; importjava.util.List; importorg.apache.cxf.endpoint.Client; importorg.apache.cxf.frontend.ClientProxy; importorg.apache.cxf.interceptor.LoggingOutInterceptor; importcom.kenan.cxf.auth.AuthOutInterceptor; importcom.kenan.cxf.ws.Cat; importcom.kenan.cxf.ws.Entry; importcom.kenan.cxf.ws.StringCat; importcom.kenan.cxf.ws.User; importcom.kenan.cxf.ws.impl.HelloWorldImpl; publicclassTest{ /** *@paramargs */ publicstaticvoidmain(String[]args){ HelloWorldImplhelloWorld=newHelloWorldImpl(); com.kenan.cxf.ws.HelloWorldhello=helloWorld.getHelloWorldImplPort(); //拦截器 Clientclient=ClientProxy.getClient(hello); client.getOutInterceptors().add(newAuthOutInterceptor("admin","admin")); client.getOutInterceptors().add(newLoggingOutInterceptor()); hello.hello("你好"); //Useruser=newUser(); //user.setName("柯南"); //List<Cat>l=hello.getCatsByUser(user); //for(Catcat:l){ //System.out.println(cat.getName()); //} //StringCats=hello.getAllCats(); //for(Entryentry:s.getEntries()){ //System.out.println(entry.getKey()+":"+entry.getValue()); //} } }