SpringBoot2.3整合WebService实现远程调用

1. 概述

最近接手了一个老项目,项目中用到了WebService技术,WebService试一种传统的SOA技术架构,它不依赖于任何编程语言,也不依赖于任何技术平台,可以直接基于HTTP协议实现网络应用之间的数据交互。WebService组成架构采用传统的"C/S"模型,如果某个平台需要对外暴露操作接口,这个时候就可以直接通过WSDL(Web Services Description Language)Web服务描述语言对要公布的接口进行描述。
WebService服务端是以远程接口为主的,在Java实现的WebService技术里主要依靠CXF开发框架,而这个CXF开发框架可以直接将接口发布成WebService。
CXF又分为JAX-WS和JAX-RS,JAX-WS是基于xml协议,而JAX-RS是基于Restful风格,两者的区别如下:

  • RS基于Restful风格,WS基于SOAP的XML协议
  • RS比WS传输的数据更少,效率更高
  • WS只能传输XML数据,RS可以传输XML,也可以传输JSON

2. JAX-WS开发

2.1. 接口开发

新建一个工程,引入核心依赖

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

新建实体类

@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement
public class UserInfo implements Serializable {

    private static final long serialVersionUID = -5352429333001227976L;
    private Long id;
    private String username;
    private String password;
}

新建接口

@WebService(name = "userInfoService", targetNamespace = "http://service.api.ws.webservice.boot.xlhj.com/")
public interface UserInfoService {

    @WebMethod(operationName = "saveUserInfo")
    void saveUserInfo(@WebParam(name = "userInfo") UserInfo userInfo);

    @WebMethod
    UserInfo getUserInfoById(@WebParam(name = "id") Long id);
}

2.2. 服务端开发

新建接口实现类

@Service
@WebService(serviceName = "userInfoService", targetNamespace = "http://service.api.ws.webservice.boot.xlhj.com/", endpointInterface = "com.xlhj.boot.webservice.ws.api.service.UserInfoService")
public class UserInfoServiceImpl implements UserInfoService {
    @Override
    public void saveUserInfo(UserInfo userInfo) {
        System.out.println("保存用户信息成功" + userInfo.toString());
    }

    @Override
    public UserInfo getUserInfoById(Long id) {
        return UserInfo.builder().id(1L).username("zhangsan").password("123456").build();
    }
}

新建配置类

@Configuration
public class CXFConfig {

    @Autowired
    private Bus bus;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private WebServiceAuthInterceptor interceptor;

    /**
     * 设置WebService访问父路径
     * @return
     */
    @Bean
    public ServletRegistrationBean getRegistrationBean() {
        return new ServletRegistrationBean(new CXFServlet(), "/services/*");
    }

    @Bean
    public Endpoint messageEndPoint() {
        EndpointImpl endpoint = new EndpointImpl(this.bus, this.userInfoService);
        endpoint.publish("/userInfoService");
        endpoint.getInInterceptors().add(this.interceptor);
        return endpoint;
    }
}

新建拦截器类

@Component
public class WebServiceAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    /**用户名*/
    private static final String USER_NAME = "xxxx";
    /**密码*/
    private static final String USER_PASSWORD = "xlhj.com";
    private static final String NAME_SPACE_URI = "http://service.api.ws.webservice.boot.xlhj.com/";
    /**创建拦截器*/
    private SAAJInInterceptor interceptor = new SAAJInInterceptor();
    private static final Logger logger = LoggerFactory.getLogger(WebServiceAuthInterceptor.class);

    public WebServiceAuthInterceptor() {
        super(Phase.PRE_PROTOCOL);
        //添加拦截
        super.getAfter().add(SAAJInInterceptor.class.getName());
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        //获取指定消息
        SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
        if (null == soapMessage) {
            this.interceptor.handleMessage(message);
            soapMessage = message.getContent(SOAPMessage.class);
        }
        //SOAP头信息
        SOAPHeader header = null;
        try {
            header = soapMessage.getSOAPHeader();
        } catch (SOAPException e) {
            e.printStackTrace();
        }
        if (null == header) {
            throw new Fault(new IllegalAccessException("没有Header信息,无法实现用户认证处理!"));
        }
        //SOAP是基于XML文件结构进行传输的,所以如果要想获取认证信息就必须进行相关的结构约定
        NodeList usernameNodeList = header.getElementsByTagNameNS(NAME_SPACE_URI, "username");
        NodeList passwordNodeList = header.getElementsByTagNameNS(NAME_SPACE_URI, "password");
        if (usernameNodeList.getLength() < 1) {
            throw new Fault(new IllegalAccessException("没有用户信息,无法实现用户认证处理!"));
        }
        if (passwordNodeList.getLength() < 1) {
            throw new Fault(new IllegalAccessException("没有密码信息,无法实现用户认证处理!"));
        }
        String username = usernameNodeList.item(0).getTextContent().trim();
        String password = passwordNodeList.item(0).getTextContent().trim();
        if (USER_NAME.equals(username) && USER_PASSWORD.equals(password)) {
            logger.debug("用户访问认证成功!");
        } else {
            SOAPException soapException = new SOAPException("用户认证失败!");
            logger.debug("用户认证失败!");
            throw new Fault(soapException);
        }
    }
}

新建主启动类后启动项目,在浏览器输入地址http://localhost:8235/services/
webservice接口
点击WSDL链接可以查看到具体的接口信息
webservice接口信息

2.3. 客户端开发

新建客户端拦截器类

public class ClientLoginInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    private String username;
    private String password;
    private static final String NAME_SPACE_URI = "http://service.api.ws.webservice.boot.xlhj.com/";

    public ClientLoginInterceptor(String username, String password) {
        super(Phase.PREPARE_SEND);
        this.username = username;
        this.password = password;
    }

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        List<Header> headers = soapMessage.getHeaders();
        Document document = DOMUtils.createDocument();
        Element authority = document.createElementNS(NAME_SPACE_URI, "authority");
        Element username = document.createElementNS(NAME_SPACE_URI, "username");
        Element password = document.createElementNS(NAME_SPACE_URI, "password");
        username.setTextContent(this.username);
        password.setTextContent(this.password);
        authority.appendChild(username);
        authority.appendChild(password);
        headers.add(0, new Header(new QName("authority"), authority));
    }
}

新建客户端接口

@Component
public class UserInfoApiClient {

    private static final String USERNAME = "xxxx";
    private static final String PASSWORD = "xlhj.com";
    private static final String ADDRESS = "http://localhost:8235/services/userInfoService?wsdl";

    /**
     * 使用代理方法
     * @param userInfo
     */
    public void saveUserInfoWithProxy(UserInfo userInfo) {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setAddress(ADDRESS);
        jaxWsProxyFactoryBean.setServiceClass(UserInfoService.class);
        jaxWsProxyFactoryBean.getOutInterceptors().add(
                new ClientLoginInterceptor(USERNAME, PASSWORD)
        );
        UserInfoService userInfoService = (UserInfoService) jaxWsProxyFactoryBean.create();
        userInfoService.saveUserInfo(userInfo);
    }

   /**
     * 使用动态代理
     * @param id
     * @throws Exception
     */
    public void getUserInfoByIdWithDynamic(Long id) throws Exception {
        JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
        Client client = clientFactory.createClient(ADDRESS);
        client.getOutInterceptors().add(new ClientLoginInterceptor(USERNAME, PASSWORD));
        Object[] userInfos = client.invoke("getUserInfoById", id);
        String userInfo = userInfos[0].toString();
        System.out.println(userInfo);
    }
}

2.4. 测试验证

@SpringBootTest
class WebServiceWSClientApplicationTest {

    @Autowired
    private UserInfoApiClient userInfoApiClient;

    @Test
    void saveUserInfoWithProxy() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(1L);
        userInfo.setUsername("张三");
        userInfo.setPassword("123456");
        userInfoApiClient.saveUserInfoWithProxy(userInfo);
    }

    @Test
    void getUserInfoByIdWithDynamic() throws Exception {
        userInfoApiClient.getUserInfoByIdWithDynamic(1L);
    }
}

注意:客户端使用动态代理访问时,参数中有bean对象时会提示类型转换错误异常,有解决办法的小伙伴欢迎在评论区留言

3. JAX-RS开发

3.1. 接口开发

新建一个项目,引入核心依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
    <version>3.4.5</version>
</dependency>
<!--json转换-->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-extension-providers</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jettison</groupId>
    <artifactId>jettison</artifactId>
    <version>1.4.1</version>
</dependency>

新建实体类和接口
实体类同ws中实体类

public interface UserInfoService {

    @POST
    void saveUserInfo(UserInfo userInfo);

    @PUT
    void updateUserInfo(UserInfo userInfo);

    @DELETE
    @Path("/{id}")
    void deleteUserById(@PathParam("id") Long id);

    @GET
    List<UserInfo> findAllUser();

    @GET
    @Path("/{id}")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    UserInfo findUserInfoById(@PathParam("id") Long id);
}

3.2. 服务端开发

新建配置类

@Configuration
public class CXFConfig {

    @Autowired
    private Bus bus;
    @Autowired
    private UserInfoService userInfoService;

    /**
     * 设置WebService访问父路径
     * @return
     */
    @Bean
    public Server createServer() {
        JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean();
        factoryBean.setAddress("/userInfoService");
        factoryBean.setBus(bus);
        factoryBean.setServiceBean(userInfoService);
        return factoryBean.create();
    }
}

新建业务接口实现类

@Service
public class UserInfoServiceImpl implements UserInfoService {
    @Override
    public void saveUserInfo(UserInfo userInfo) {
        System.out.println("保存用户信息成功!" + userInfo.toString());
    }

    @Override
    public void updateUserInfo(UserInfo userInfo) {
        System.out.println("更新用户信息成功!" + userInfo.toString());
    }

    @Override
    public void deleteUserById(Long id) {
        System.out.println("删除用户信息成功!" + id);
    }

    @Override
    public List<UserInfo> findAllUser() {
        return null;
    }

    @Override
    public UserInfo findUserInfoById(Long id) {
        return null;
    }
}

3.3. 客户端开发

开发客户端接口类

@Component
public class UserInfoApiClient {

    private static final String ADDRESS = "http://localhost:8335/services/userInfoService";

    public Response saveUserInfo(UserInfo userinfo) {
        return WebClient.create(ADDRESS).post(userinfo);
    }

    public Response updateUserInfo(UserInfo userInfo) {
        return WebClient.create(ADDRESS).put(userInfo);
    }

    public Response deleteUserById(Long id) {
        return WebClient.create(ADDRESS + "/" + id).delete();
    }

    public Response findAllUser() {
        return WebClient.create(ADDRESS).get();
    }

    public Response findUserInfoById(Long id) {
        return WebClient.create(ADDRESS + "/" + id).get();
    }
}

3.4. 测试

@SpringBootTest
public class WebServiceRSClientApplicationTest {

    @Autowired
    private UserInfoApiClient userInfoApiClient;

    @Test
    void saveUserInfoTest() {
        UserInfo userInfo = UserInfo.builder()
                .id(1L)
                .username("zhangsan")
                .password("123456")
                .build();
        userInfoApiClient.saveUserInfo(userInfo);
    }

    @Test
    void findUserInfoByIdTest() {
        Response response = userInfoApiClient.findUserInfoById(1L);
        System.out.println(response);
    }

    @Test
    void deleteUserByIdTest() {
        userInfoApiClient.deleteUserById(1L);
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值