使用CXF调用WSDL

简介

时隔多年,再次遇到需要调用WebService的业务,对方给予的wsdl说明文档还是内网的链接,并且设有基础访问权限,即在浏览器打开wsdl链接时需要输入【用户名+密码】登录后方可查看wsdl文档,这需要设置代理(我使用putty完成了代理),本文只记录使用org.apache.cxf调用wsdl的过程

附一张putty的下载链接:

https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

步骤一 :添加org.apache.cxf的maven引用

        
        
            org.apache.cxf
            cxf-rt-frontend-jaxws
            3.5.2
        
        
            org.apache.cxf
            cxf-rt-transports-http
            3.5.2
        
        

步骤二:访问WSDL

/**
     * 打开WSDL文件
     * @return
     */
    private static Bus openWSDL(){
        Bus bus = BusFactory.getThreadDefaultBus();
        bus.setExtension((name, address, httpConduit) -> {
            //设置访问wsdl所需的用户名和密码
            final AuthorizationPolicy authorization = new AuthorizationPolicy();
            authorization.setUserName("username");
            authorization.setPassword("password");
            httpConduit.setAuthorization(authorization);
            final HttpAuthSupplier supplier = new DefaultBasicAuthSupplier();
            httpConduit.setAuthSupplier(supplier);
            //设置service中location的映射代理IP和端口 
            final HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
            httpClientPolicy.setProxyServer("127.0.0.1");
            httpClientPolicy.setProxyServerPort(50000);
            httpClientPolicy.setAllowChunking(false);
            httpClientPolicy.setConnectionTimeout(50000);
            httpClientPolicy.setReceiveTimeout(50000);
            httpConduit.setClient(httpClientPolicy);
        }, HTTPConduitConfigurer.class);
        return bus;
    }

步骤三:获取WSDL文档内容

/**
     * 获取WSDL内容
     * @return
     */
    private static Map getWSDLContent() {
        Map wsdl = new HashMap<>();
        try {
            Bus bus = openWSDL();
            ClassLoader loader = Gmm1020Server.class.getClassLoader();
            // 创建动态客户端
            JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(bus);
            Client client = dcf.createClient("你的wsdl地址",
                    new QName("wsdl中的targetNamespace", "wsdl:service的name"),
                    loader,
                    new QName("wsdl中的targetNamespace", "HTTP_Port"));
            QName qName = new QName("wsdl中的targetNamespace", "wsdl:operation的name");
            List partInfos = client.getEndpoint()
                    .getService().getServiceInfos().get(0)
                    .getBinding(new QName("wsdl中的targetNamespace", "wsdl:binding的name"))
                    .getOperation(qName)
                    .getInput().getMessageParts();
            wsdl.put("client",client);
            wsdl.put("qname",qName);
            wsdl.put("messagePartInfo",partInfos);
        } catch (Exception e) {
            throw new WebServiceException(e);
        }
        return wsdl;
    }

注意:Bus引用的是上一个方法

Bus bus = openWSDL();

步骤四:调用远程接口(远程过程调用)

/**
     * 调用远程过程
     */
    public static void call(Map map) {
        Map wsdl = getWSDLContent();
        Client client = (Client) wsdl.get("client");
        List partInfos = (List) wsdl.get("messagePartInfo");
        QName qName = (QName) wsdl.get("qname");

        String clazzName = partInfos.get(0).getTypeClass().getName();
        try {
            Object requestParamObject = Thread.currentThread().getContextClassLoader().loadClass(clazzName).newInstance();
            Field[] fields = requestParamObject.getClass().getDeclaredFields();
            for (Field field : fields) {
                //如果是泛型
                boolean b = field.getGenericType() instanceof ParameterizedType;
                if(b && field.getType() == List.class){
                    Type[] types = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();
                    for (Type type : types) {
                        Class aClass = (Class) type;
                        Object obj = aClass.newInstance();
                        List curElementList = new ArrayList<>();
                        List params = (List) map.get(field.getName().toLowerCase());
                        if(!CollectionUtils.isEmpty(params)){
                            for (Map param : params) {
                                writeFiledVal(obj,param);
                                curElementList.add(obj);
                            }
                        }
                        //writeCustomValue(obj);
                        field.setAccessible(true);
                        field.set(requestParamObject,curElementList);
                    }
                }else if(field.getType().getName().equals("java.lang.String")){
                    //字符串则直接赋值
                    Field f = requestParamObject.getClass().getDeclaredField(field.getName());
                    f.setAccessible(true);
                    f.set(requestParamObject,map.get(f.getName()));
                }else {
                    //对象类型
                    Class clazz = field.getType();
                    Object obj = clazz.newInstance();
                    Map param = (Map) map.get(field.getName().toLowerCase());
                    writeFiledVal(obj,param);
                    //writeCustomValue(obj);
                    Field f = requestParamObject.getClass().getDeclaredField(field.getName());
                    f.setAccessible(true);
                    f.set(requestParamObject,obj);
                }
            }
            log.info("请求参数:{}",JSON.toJSON(requestParamObject));
            Object result = client.invoke(qName, requestParamObject);
            log.info("响应结果:{}",JSON.toJSONString(result,true));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    } 
  

注意:该代码的入参是一个Map对象,该对象中包含了三种类型的属性和一层属性对象的嵌套(java.util.List、java.lang.String、POJO对象)因此该代码只针对三种类型做了解析处理,并不具备所有类型的通用性。

比如:你的入参对象中有java.lang.Integer类型,或者Set ...,那就需要编写相应的解析方式,这是类型解析。

属性对象的嵌套的意思是:如果你的入参对象Map中有个List或者直接就是POJO,这就是一层属性嵌套,而如果这个List中的POJO对象又嵌套了其他的POJO对象,这就属于二层解析,即:

List userList = new ArrayLIst();

class User {

private String userName;

private UserDetails userDetails;

}

而或许UserDetails对象中还存着Enterprise对象... 以此嵌套,想要全面解析你就需要使用递归算法。(我想我应该把问题说清楚了)

代码层面:ParameterizedType

Type[] types = ((ParameterizedType)field.getGenericType()).getActualTypeArguments();

这个对象是一个接口,表示参数化类型,这句代码意思是返回这个field的实际类型参数

步骤五:字段赋值

    /**
     * 字段写值
     * @param obj
     */
    private static void writeFiledVal(Object obj,Map param) {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                field.set(obj,param.get(field.getName()));
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }


    /**
     * 测试使用
     * 给字段写入自定义值
     * @param obj
     */
    private void writeCustomValue(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if(!field.getType().isPrimitive()){
                field.getType().getConstructors();
            }
            try {
                field.set(obj,"1");
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

说明:writeCustomValue是测试方式,而writeFiledVal多了一个入参Map,主要用来赋值的。

使用的时候,只需要讲你的入参对象转换成map,放入 'call()' 方法即可完成调用

使用CXF调用WSDL_第1张图片

 

响应结果虽然是服务器告诉我有问题(因为我的数据是乱写的),但是可以看得出来,调用是通了 

你可能感兴趣的:(CXF,org.apache.cxf,WebService,CXF调用WebService,CXF解析WSDL)