WebService-CXF学习使用_长篇

1 篇文章 0 订阅
0 篇文章 0 订阅

以下是废话:

写文章的原因是要给医院方面写一个考核相关的接口,让护士等人员的手持设备可以实时访问,反馈结果数据。

对于比较成熟(老)的框架,我没兴趣,于是锁定了相对流行和新的框架,RESTEasy和Apache CXF。一个是jboss的一个是Apache的,因为习惯使用spring,个人也觉得apache的东西一直都比较牛叉,所以决定用CXF。

这里我会说明服务器方面接口怎么写,也会说明客户端(调用者)方面怎么调用。

1:新建maven项目

我使用的是idea,步骤为:file-new-project-选择maven-选择maven-archetype-webapp-根据向导一步步完成。

2:导入相关包

在pom.xml文件内加入如下包,然后刷新maven项目。第三个是一个类似tomcat的服务器,方便测试。

<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-frontend-jaxws -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>3.2.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-core -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-core</artifactId>
      <version>3.2.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-transports-http-jetty -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http-jetty</artifactId>
      <version>3.2.1</version>
    </dependency>

3:编写接口

注意需要加webservice注解来标识,这里是要实现的三个学习使用的接口。

分为简单类型返回,较复杂类型返回,cxf不支持的类型怎么写适配器然后返回。

/**
 * Created by gcc on 2018/2/6.
 */
@WebService
public interface HelloWord {

    public String say(String something);

    public List<Roles> getRoleByUsers(Users users);

    /**
     * 获取所有用户以及对应的角色,返回类型map需要适配转化。需要加注解
     */
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String,List<Roles>> getRoles();

}

4:实现接口

say方法实现:获取数据并打印,然后返回修改后的字符串

getRoleByUsers实现:通过用户实体返回对应权限集合

getRoles实现:返回复杂类型,这里实现代码简单,但因为cxf不支持这种返回类型,需要写适配类。

这里省略Roles和Users实体类,实体有id,名字和密码属性即可

/**
 * Created by Administrator on 2018/2/6.
 */
@WebService
public class HelloWordImpl implements HelloWord {


    public String say(String something) {
        System.out.println("get:"+something);
        return "you post"+something;
    }

    //开放一个,提供用户获取权限的接口
    public List<Roles> getRoleByUsers(Users users) {
        List<Roles> rolesList = new ArrayList<Roles>();
        //用户测试,模拟数据
        if("gcc".equals(users.getUsername()) & "123".equals(users.getPassword())){
            rolesList.add(new Roles(1,"super manger"));
        }else if("admin".equals(users.getUsername()) & "123".equals(users.getPassword())){
            rolesList.add(new Roles(2,"system manger"));
        }else{
            rolesList.add(new Roles(3,"normal user"));
        }
        return rolesList;
    }

    public Map<String, List<Roles>> getRoles() {
        Map<String,List<Roles>> map=new HashMap<String,List<Roles>>();
        List<Roles> roleList1=new ArrayList<Roles>();
        roleList1.add(new Roles(1,"super manger"));
        roleList1.add(new Roles(2,"system manger"));
        map.put("gcc", roleList1);

        List<Roles> roleList2=new ArrayList<Roles>();
        roleList2.add(new Roles(3,"normal user"));
        map.put("jack", roleList2);

        return map;
    }

}

实现map类型的适配转化,这里通过数组进行转化。

public class MapAdapter extends XmlAdapter<MyRole[], Map<String,List<Roles>>>{

	/**
	 *  适配器转换,MyRole[] -> Map<String, List<Role>>
	 */
	@Override
	public Map<String, List<Roles>> unmarshal(MyRole[] v) throws Exception {
		Map<String, List<Roles>> map=new HashMap<String,List<Roles>>();
		for(int i=0;i<v.length;i++){
			MyRole r=v[i];
			map.put(r.getKey(), r.getValue());
		}
		return map;
	}

	/**
	 * 适配器转换 Map<String, List<Role>> -> MyRole[]
	 */
	@Override
	public MyRole[] marshal(Map<String, List<Roles>> v) throws Exception {
		MyRole[] roles=new MyRole[v.size()];
		int i=0;
		for(String key:v.keySet()){
			roles[i]=new MyRole();
			roles[i].setKey(key);
			roles[i].setValue(v.get(key));
			i++;
		}
		return roles;
	}

}

下面是转化时需要使用的entity类

/**
 * cxf不能接受map,需要使用适配器自己定义
 */
public class MyRole {

	private String key;
	private List<Roles> value;
	
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public List<Roles> getValue() {
		return value;
	}
	public void setValue(List<Roles> value) {
		this.value = value;
	}
	
	
}

5:编写测试代码

这里先忽略拦截器代码,稍后解释。

第一步获得接口对象,第二步设置访问地址(本机地址),之后见代码注释,在所有设置完成后暴露接口。若在暴露接口后写设置编译没问题,但设置内容是无效的。

public class ServiceCxf {

    public static void main(String[] args) {
        System.out.println("webService start");
        HelloWord helloWordImpl = new HelloWordImpl();
        String addr = "http://192.168.0.122/helloWordGcc";
//        Endpoint.publish(addr,helloWordImpl);
        JaxWsServerFactoryBean serverFactoryBean = new JaxWsServerFactoryBean();//cxf初始化服务工厂
        serverFactoryBean.setAddress(addr);//设置地址
        serverFactoryBean.setServiceClass(HelloWord.class);//设置接口类
        serverFactoryBean.setServiceBean(helloWordImpl);//设置实现类

        serverFactoryBean.getInInterceptors().add(new LoggingInInterceptor()); // 添加in拦截器 日志拦截器
        serverFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor()); // 添加out拦截器 日志拦截器
        serverFactoryBean.getInInterceptors().add(new MyInterceptor());//自定义拦截器,用于接口调用的权限验证

        serverFactoryBean.create();//暴露接口
        System.out.println("webService started");
    }

}

6:浏览器查看结果

http://192.168.0.122/helloWordGcc?wsdl

浏览器地址输入设置的访问地址,追加?wsdl,表示显示wsdl格式内容。wsdl就是web service description language

到此客户端接口完成,但我们不能就这样使用,因为鬼也不知道会不会有人写个死循环一直调用你的接口。所以需要写一个拦截器,在调用之前进行权限拦截。

代码如下:

public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

	public MyInterceptor() {
		super(Phase.PRE_INVOKE);  // 在调用方法之前调用自定拦截器

	}

	@SuppressWarnings("null")
	public void handleMessage(SoapMessage message) throws Fault {
		List<Header> headers=message.getHeaders();
		if(headers==null && headers.size()==0){
			throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截"));
		}
		Header firstHeader=headers.get(0);
		Element ele=(Element) firstHeader.getObject();
		NodeList uList=ele.getElementsByTagName("userName");
		NodeList pList=ele.getElementsByTagName("password");
		if(uList.getLength()!=1){
			throw new Fault(new IllegalArgumentException("用户名格式不对"));
		}
		if(pList.getLength()!=1){
			throw new Fault(new IllegalArgumentException("密码格式不对"));
		}
		String userName=uList.item(0).getTextContent();
		String password=pList.item(0).getTextContent();

		if(!userName.equals("gcc")||!password.equals("123")){
			throw new Fault(new IllegalArgumentException("用户名或者密码错误!"));
		}
	}

}

到此服务器端基本任务已经完成,下面介绍客户端如何调用接口。

1:新建maven项目

另起一个项目作为客户端调用使用。

2:生成代码

这里介绍一个神奇的工具,apache-cxf-3.2.1:http://cxf.apache.org/download.html去cxf官网下载即可,这文件夹下面的bin目录下有一个wsdl2java.bat文件,将到bin为止的路径配置到环境变量,方便cmd使用。简单使用为:cd到文件生成路径,然后写wsdl2java,然后粘贴访问路径(例如:http://192.168.0.122/helloWordGcc?wsdl),具体使用方法各位百度即可。


3:编写客户端测试代码

将文件生成在main的java目录下,生成代码很容易理解,这里不再介绍。

获取实现对象就可以调用接口方法。当然也需要编写通过拦截器验证的代码。

代码如下:

public class Client {

    public static void main(String[] args) throws Exception {
        //基本使用
        HelloWordService helloWordService = new HelloWordService();
        HelloWord helloWord = helloWordService.getHelloWordPort();
//        System.out.println(helloWord.say("你好"));

        org.apache.cxf.endpoint.Client client= ClientProxy.getClient(helloWord);
        client.getOutInterceptors().add(new AddHeaderInterceptor("gcc","123")); // 添加自定义拦截器,用于获得调用接口的权限

        //返回类型相对复杂的使用
        Users users = new Users();
        users.setUsername("gcc");
        users.setPassword("123");
        List<Roles> rolesList = helloWord.getRoleByUsers(users);
        for (Roles roles:rolesList){
            System.out.println(roles.getId()+":"+ roles.getRolename());
        }

        //需要适配的返回类型使用
        MyRoleArray arrys = helloWord.getRoles();
        List<MyRole> myRoles = arrys.getItem();
        for (int i = 0; i <myRoles.size() ; i++) {
            System.out.println(myRoles.get(i).getKey());
            for(Roles list:myRoles.get(i).getValue()){
                System.out.println(list.getId()+":"+list.getRolename());
            }
        }

    }

}

拦截器代码如下:

public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

	private String userName;
	private String password;

	public AddHeaderInterceptor(String userName,String password) {
		super(Phase.PREPARE_SEND); // 准备发送SOAP消息的时候调用拦截器
		this.userName=userName;
		this.password=password;
	}

	public void handleMessage(SoapMessage message) throws Fault {
		List<Header> headerList=message.getHeaders();

		Document doc=DOMUtils.createDocument();
		Element ele=doc.createElement("authHeader");//节点名字自定义
		Element uElement=doc.createElement("userName");//对应验证的拦截器
		uElement.setTextContent(userName);
		Element pElement=doc.createElement("password");
		pElement.setTextContent(password);

		ele.appendChild(uElement);
		ele.appendChild(pElement);

		headerList.add(new Header(new QName("gcc"),ele));//key-valuex形式,key的name自定义
	}
}

至此,非spring的服务器端和客户端的使用方法多已经阐述完毕。

敬礼!

咳咳,差点忘了还要整合spring

在spring项目中修改三处即可:

1:web.xml中添加:

    <servlet>  
	 <servlet-name>CXFServlet</servlet-name>  
	 <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>  
	</servlet>  
	 
	<servlet-mapping>  
	   <servlet-name>CXFServlet</servlet-name>  
	   <url-pattern>/webservice/*</url-pattern>  
	</servlet-mapping>

2:applicationContext.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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        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  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">    
        
	<import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
    
    <!-- 自动扫描 -->
	<context:component-scan base-package="com.gcc.webservice" />
	
	<!-- 定义服务提供者,在指定非拦截的路径下,追加HelloWorld路径,即执行名字为helloWorld的bean类 -->
	<jaxws:endpoint
	    implementor="#helloWorld"
	    address="/HelloWorld">

		<!-- 添加in拦截器 -->
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
			<bean class="com.gcc.interceptor.MyInterceptor"/>
		</jaxws:inInterceptors>
		<!-- 添加out拦截器 -->
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
		</jaxws:outInterceptors>

	</jaxws:endpoint>
</beans>

3:给需要暴露的接口实现添加为bean,让spring扫描到。

@Component("helloWorld")
@WebService
public class HelloWordImpl implements HelloWord {


    public String say(String something) {
        System.out.println("get:"+something);
        return "you post"+something;
    }
…………

暂时说明到此,若有追加,后续补充……

感谢各位顾客观赏






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值