SpringBoot + CXF

服务端

pom.xml

<dependencies>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
      <version>3.1.11</version>
   </dependency>

   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
   </dependency>
</dependencies>

服务端接口

@WebService(name = "helloWordService",//暴露服务名称
        targetNamespace = "http://webservice.server.cxf.springboot.com")//命名空间一般是接口的包名倒序
public interface HelloWord {

    public String say(String str);

    public List<Role> getRoleByUser(@WebParam(name = "user1") User user);
    
    @XmlJavaTypeAdapter(MyAdapter.class)
    public Map<String, List<Role>> getAllRole();

@XmlJavaTypeAdapter常用在转换比较复杂的对象时,如map类型或者格式化日期等。使用此注解时,需要自己写一个adapter类继承XmlAdapter抽象类,并实现里面的方法。

  @XmlJavaTypeAdapter(value=xxx.class),value为自己定义的adapter

因为CXF不识别Map集合

这里我们自定义了一个map和array相互转换的类

public class MyAdapter extends XmlAdapter<MyRoleArray[], Map<String, List<Role>>> {

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

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

这时我们也需要一个数组类MyRoleArray 与Role.class 和User.class 放在entity包下

/**
 * 自定义实体 CXF可以接受
 * 
 * @author zkr-lsy
 *
 */

public class MyRoleArray {

   private String key;
   
   private List<Role> value;

   public String getKey() {
      return key;
   }

   public void setKey(String key) {
      this.key = key;
   }

   public List<Role> getValue() {
      return value;
   }

   public void setValue(List<Role> value) {
      this.value = value;
   }
   
   
}

接口实现类

@WebService(serviceName = "helloWordService",//与接口中指定的name一致
      targetNamespace = "http://webservice.server.cxf.springboot.com",//与接口中命名空间一致,一般是接口的包名倒叙
      endpointInterface = "com.springboot.cxf.server.webservice.HelloWord"//接口地址
     )
@Component
public class HelloWordImpl implements HelloWord {

   public String say(String str) {
      System.out.println("执行Say");
      return "Hello" + str;
   }

   public List<Role> getRoleByUser(User user) {
      List<Role> roleList = new ArrayList<Role>();
      if (user != null) {
         if (user.getUsername().equals("lili") && user.getPassword().equals("123")) {
            roleList.add(new Role(1, "管理员"));
         } else if (user.getUsername().equals("qqq") && user.getPassword().equals("123")) {
            roleList.add(new Role(2, "程序员"));
         }
         return roleList;
      } else {
         return null;
      }
   }

   public Map<String, List<Role>> getAllRole() {
      Map<String, List<Role>> map = new HashMap<String, List<Role>>();
      List<Role> list = new ArrayList<Role>();
      list.add(new Role(1, "管理员"));
      list.add(new Role(2, "genene"));
      map.put("abc", list);

      List<Role> list2 = new ArrayList<Role>();
      list2.add(new Role(1, "管3"));
      list2.add(new Role(2, "gen4"));
      map.put("liuyl", list2);
      return map;
   }

}

下面我们需要写一个拦截器,这个拦截器的作用是进行客户端的判断

@Component
public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

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

    /**
     * 在调用时会有Soap消息,传入解析 权限信息都被封装到Saop消息的Header中
     */

    public void handleMessage(SoapMessage message) throws Fault {
        List<Header> headersList = message.getHeaders();
        if (headersList == null || headersList.size() == 0) {
            throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截"));
        }
        Header firstheader = headersList.get(0);
        Element element = (Element) firstheader.getObject();// 得到元素
        NodeList userNameList = element.getElementsByTagName("userName"); // 获取元素节点
        NodeList passwordList = element.getElementsByTagName("password"); // 获取元素节点
        if (userNameList.getLength() != 1) {
            throw new Fault(new IllegalArgumentException("用户名格式不正确"));
        }
        if (passwordList.getLength() != 1) {
            throw new Fault(new IllegalArgumentException("密码格式不正确"));
        }
        String userName = userNameList.item(0).getTextContent();//获得用户第一个节点的值
        String password = passwordList.item(0).getTextContent();//获得用户第一个节点的值

        if (!userName.equals("lili") || !password.equals("123456")) {
            throw new Fault(new IllegalArgumentException("用户名或密码不正确"));
        }
   }

需要注意的是这个拦截器继承 AbstractPhaseInterceptor<SoapMessage> 不能像添加正常拦截器那样使用

public class MyWebConfig extends WebMvcConfigurerAdapter{
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration ir =registry.addInterceptor(new XXX);
    }
}

下面我们开始填加拦截器,

添加一个Configuration

/**
 * 配置cxf服务发布,默认服务在Host:port/services/***路径下
 * 这里相当于把Helloword接口发布在了路径/services/helloSay下,helloSay是endpoint.publish("/helloSay");添加的
 * wsdl文档路径为http://localhost:8080/services/helloSay?wsdl
 */
@Configuration
public class CxfConfig {
    @Autowired
    private Bus bus;
    @Autowired
    private HelloWord helloWord;
    @Autowired
    private MyInterceptor myInterceptor;

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, helloWord);
        endpoint.publish("/helloSay");
        endpoint.getInInterceptors().add(new LoggingInInterceptor());
        endpoint.getInInterceptors().add(myInterceptor);
        endpoint.getOutInterceptors().add(new LoggingOutInterceptor());
        return endpoint;
    }
}

客户端

抽取了获取客户端的方式ClientUtil

public class ClientUtil {

    public static Client getClient(String wsdlUrl,String userName,String password) {
        JaxWsDynamicClientFactory dynamicClientFactory = JaxWsDynamicClientFactory.newInstance();
        Client client = dynamicClientFactory.createClient("http://localhost:8080/services/helloSay?wsdl");

        client.getOutInterceptors().add(new AddHeaderInterceptor("lili", "123456"));

        client.getInInterceptors().add(new LoggingInInterceptor());//添加In拦截器,日志拦截器
        client.getOutInterceptors().add(new LoggingOutInterceptor());//添加拦截器,日志拦截器
       return client;
    }
}

通过添加HeaderInterceptor拦截器,实现在发送SOAP信息之前 添加响应的账号密码到headers中供服务端验证

需要在客户端出的时候拦截所以通过getOutInterceptor拦截

public class AddHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

   private String userName;
   private String passWord;

   /**
    * 设置拦截器的阶段
    * 
    * @param phase
    *            Phase.XXX 拦截器的阶段
    */
   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> headersList = message.getHeaders();
      Document doc = DOMUtils.createDocument();
      Element element = doc.createElement("authHeader");
      Element uElement = doc.createElement("userName");
      uElement.setTextContent(userName);
      Element pElement = doc.createElement("password");
      pElement.setTextContent(passWord);
      element.appendChild(uElement);
      element.appendChild(pElement);

      headersList.add(new Header(new QName("cxf"), element));
   }

}

客户端启动类

在其中分别实现了3个client

分别返回字符串,集合,数组(在服务端接口实现类中getAllRole返回类型为Map<String,List<Role>>,在调用服务的过程中通过我们的MyAdapter.class转换成了数组返回到客户端)

@SpringBootApplication
public class SpringbootCxfClientApplication {
    private static Client client = ClientUtil.getClient(
            "http://localhost:8080/services/helloSay?wsdl",
            "lili",
            "123456");
   private static Object[] objects = new Object[0];
    public static void main(String[] args) {
        SpringApplication.run(SpringbootCxfClientApplication.class, args);
        client2();
        client3();
        client4();
    }

    public static void client2() {
        String date = new Date().toString();
        try {
            objects = client.invoke("say", date);
            System.out.println("返回数据:" + objects[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void client3() {
        User user1 = new User();
        user1.setId(1);
        user1.setUsername("lili");
        user1.setPassword("123");

        try {
            objects = client.invoke("getRoleByUser", user1);
            System.out.println("返回数据:" + objects[0]);
            List<Role> roleList = (List<Role>) objects[0];
            for (Role role : roleList) {
                System.out.println("id : " + role.getId() + " -- RoleName : " +
                        role.getRoleName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void client4() {
        try {
            objects = client.invoke("getAllRole");
            System.out.println("返回数据:" + objects[0]);
            MyRoleArrayArray allRole = (MyRoleArrayArray)objects[0];
            List<MyRoleArray> myRoleArrayList = allRole.item;
            for (MyRoleArray myRoleArray : myRoleArrayList) {
                List<Role> roleList = myRoleArray.getValue();
                System.out.println(myRoleArray.getKey() + " : ");
                for (Role role : roleList) {
                    System.out.println(role.getId() + "---" + role.getRoleName());
                }
                System.out.println();
                System.out.println("------------------");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这时我们需要服务端中的entity类,并且需要添加一个MyRoleArrayArray.class

如果没有MyRoleArrayArray这个类会报错



public class MyRoleArrayArray {
    public List<MyRoleArray> item;
}

只需通过此方法将它转换成一个权限数组的集合,之后通过遍历获取

值得注意的是,和Server有关的这几个类一定要放在以客户端接口的命名空间为包名的包下面,

否则还是会报cannot be cast to 这样的错误


暂时先理解成这样,总感觉不会这么麻烦 ,不过现在是可以运行的

个人观点仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值