SpringBoot | 第三十四章:CXF构建WebService服务

前言

上一章节,讲解了如何使用Spring-WS构建WebService服务。其实,创建WebService的方式有很多的,今天来看看如何使用apache cxf来构建及调用WebService服务。

  • 前言
  • 一点知识
    • 何为Apache-CXF
    • 关于JAX-WS规范
      • 常用注解介绍
  • SpringBoot整合CXF实例
    • 服务端构建
    • 客户端调用
      • 异常捕获
    • 自定义拦截器
      • 服务端拦截器
      • 客户端拦截器
  • 参考资料
  • 总结
  • 最后
  • 老生常谈

一点知识

何为Apache-CXF

Apache CXF是一个开源的Services框架,CXF帮助您利用Frontend编程 API 来构建和开发Services,像JAX-WSJAX-RS。这些Services可以支持多种协议,比如:SOAPXML/HTTPRESTful HTTP或者CORBA,并且可以在多种传输协议上运行,比如:HTTPJMS 或者JBICXF大大简化了 Services 的创建,同时它可以天然地和Spring进行无缝集成

以下是官网给出的介绍:https://github.com/apache/cxf

最常用的是使用cxf开发web-service。本身是基于JAX-WS规范来实现的。当然,本身CXF也实现了JAX-RS规范来实现RESTFul Service

关于JAX-WS规范

JAX-WS全称:Java API for XML-Based Web ServicesJAX-WS是一种编程模型,它通过支持将基于注释的标准模型用于开发Web Service应用程序和客户机来简化应用程序开发。

JAX-WS是Java程序设计语言一个用来创建Web服务的API。

  • 在服务器端,用户只需要通过Java语言定义远程调用所需要实现的接口SEI(service endpoint interface),并提供相关的实现,通过调用JAX-WS的服务发布接口就可以将其发布为WebService接口。
  • 在客户端,用户可以通过JAX-WS的API创建一个代理(用本地对象来替代远程的服务)来实现对于远程服务器端的调用。当然JAX-WS也提供了一组针对底层消息进行操作的API调用,你可以通过Dispatch直接使用SOAP消息或XML消息发送请求或者使用Provider处理SOAP或XML消息。

常用注解介绍

JAX-WS提供了一系列的注解,可以对WebService的接口规范化。以下介绍下最常用的几个注解。

  • @WebService:用于将Java类标记为实现Web Service或者将服务端点接口 (SEI) 标记为实现Web Service接口。

    其包含的属性有:

    • name:此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称,wsdl:portType 的名称。缺省值为 Java 类的简单名称 + Service。(字符串)
    • targetNamespace:默认的值为 "http://包名/" ,可以通过此变量指定一个自定义的targetNamespace值。
    • serviceName:对外发布的服务名,指定Web Service的服务名称:wsdl:service。缺省值为 Java 类的简单名称 + Service。(字符串)
    • endpointInterface:
    • portName:wsdl:portName的值。缺省值为WebService.name+Port
    • wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址
  • @WebMethod:表示作为一项Web Service操作的方法。仅支持在使用@WebService注解的类上使用@WebMethod注解。
    • operationName:指定与此方法相匹配的wsdl:operation 的名称。缺省值为 Java 方法的名称。(字符串)
    • action:定义此操作的行为。对于 SOAP 绑定,此值将确定 SOAPAction 头的值。缺省值为 Java 方法的名称。(字符串)
    • exclude:指定是否从 Web Service 中排除某一方法。缺省值为 false。(布尔值)
  • @WebParam:用于定制从单个参数至Web Service消息部件和XML元素的映射。

其他注解,可以查看:WebService注解总结

为了有个直观感受,大家可以看看以下这个wsdl文件,对应以上各注解属性的值(加了前缀oKong)。

//@WebService 属性示例
@WebService(targetNamespace = ‘http://www.lqdev.cn/webservice‘ ,name = "oKongName", serviceName="oKongServiceName", portName = "oKongPortName",endpointInterface="cn.lqdev.learning.springboot.cxf.service.AuthorService")

//@webMethod @WebParam 常用属性示例
@WebMethod(operationName="oKongOperationName",action="oKongAction")
String getAuthorName(@WebParam(name = "paramName") String name);

标记的有点花,⊙﹏⊙‖∣。大家可以自己对照下。

SpringBoot整合CXF实例

接下来,我们以一个简单的示例来演示下,如何发布服务及如何进行服务调用。

服务端构建

创建一个工程:spring-boot-cxf-service.

0.引入CXF的POM文件

        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.2.5</version>
        </dependency>

1.创建实体,按JAX-WS规范,创建接口及其实现类。

AuthorDto.java

/**
 * 作者信息实体
 * @author oKong
 *
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AuthorDto {

    String name;

    List<String> hobby; 

    String birthday;

    String description;

    Sex sex;
}

Sex.java性别枚举类

/**
 * 性别枚举类
 * @author oKong
 *
 */
public enum Sex {

    MALE("male"),
    FEMALE("female");

    String value;

    Sex(String value) {
        this.value = value;
    }

    public String value() {
        return value;
    }

    public static Sex fromValue(String v) {
        for (Sex c : Sex.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
}

AuthorService.java接口类

/**
 * 创建服务接口
 * @author oKong
 *
 */
@WebService(targetNamespace = WsConst.NAMESPACE_URI ,name = "authorPortType")
public interface AuthorService {

    /**
     * 根据名称获取作者信息
     * @author 作者:oKong
     */
    @WebMethod(operationName="getAuthorByName")
    AuthorDto getAuthor(@WebParam(name = "authorName") String name);

    /**
     * 获取作者列表信息
     * @author oKong
     */
    @WebMethod
    List<AuthorDto> getAuthorList();

    /**
     * 返回字符串测试
     * @author oKong
     */
    String getAuthorString(@WebParam(name = "authorName")String name);
}

AuthorServiceImpl.java接口实现类

@WebService(
         targetNamespace = WsConst.NAMESPACE_URI, //wsdl命名空间
         name = "authorPortType",                 //portType名称 客户端生成代码时 为接口名称
         serviceName = "authorService",           //服务name名称
         portName = "authorPortName",             //port名称
         endpointInterface = "cn.lqdev.learning.springboot.cxf.service.AuthorService")//指定发布webservcie的接口类,此类也需要接入@WebService注解
public class AuthorServiceImpl implements AuthorService{

    @Override
    public AuthorDto getAuthor(String name) {
        AuthorDto author = new AuthorDto();
        author.setBirthday("1990-01-23");
        author.setName("姓名:" + name);
        author.setSex(Sex.MALE);
        author.setHobby(Arrays.asList("电影","旅游"));
        author.setDescription("描述:一枚趔趄的猿。现在时间:" + new Date().getTime());
        return author;
    }

    @Override
    public List<AuthorDto> getAuthorList() {
        List<AuthorDto> resultList = new ArrayList<>();
        AuthorDto author = new AuthorDto();
        author.setBirthday("1990-01-23");
        author.setName("姓名:oKong");
        author.setSex(Sex.MALE);
        author.setHobby(Arrays.asList("电影","旅游"));
        author.setDescription("描述:一枚趔趄的猿。现在时间:" + new Date().getTime());
        resultList.add(author);
        resultList.add(author);
        return resultList;
    }

    @Override
    public String getAuthorString(String name) {
        AuthorDto author = getAuthor(name);
        return author.toString();
    }
}

注意:相关注解可以查看章节:常用注解介绍

主要是接口实现类的@WebService对应属性值都要wsdl文件的映射关系。

@WebService(
         targetNamespace = WsConst.NAMESPACE_URI, //wsdl命名空间
         name = "authorPortType",                 //portType名称 客户端生成代码时 为接口名称
         serviceName = "authorService",           //服务name名称
         portName = "authorPortName",             //port名称
         endpointInterface = "cn.lqdev.learning.springboot.cxf.service.AuthorService")//指定发布webservcie的接口类,此类也需要接入@WebService注解

2.创建常量类,配置类,设置访问uri路径等。

WsConst.java

/**
 * 常量类
 * @author oKong
 *
 */
public class WsConst {
    public static final String NAMESPACE_URI = "http://www.lqdev.cn/webservice";
}

CxfWebServiceConfig.java

/**
 * cxf配置类
 * @author oKong
 *
 */
@Configuration
public class CxfWebServiceConfig {

    //这里需要注意  由于springmvc 的核心类 为DispatcherServlet
    //此处若不重命名此bean的话 原本的mvc就被覆盖了。可查看配置类:DispatcherServletAutoConfiguration
    //一种方法是修改方法名称 或者指定bean名称
    //这里需要注意 若beanName命名不是 cxfServletRegistration 时,会创建两个CXFServlet的。
    //具体可查看下自动配置类:Declaration org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration
    //也可以不设置此bean 直接通过配置项 cxf.path 来修改访问路径的
    @Bean("cxfServletRegistration")
    public ServletRegistrationBean dispatcherServlet() {
        //注册servlet 拦截/ws 开头的请求 不设置 默认为:/services/*
        return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
    }

    /**
     * 申明业务处理类 当然也可以直接 在实现类上标注 @Service
     * @author oKong
     */
    @Bean
    public AuthorService authorService() {
        return new AuthorServiceImpl();
    }

    /*
     * 非必要项
     */
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        SpringBus springBus = new SpringBus();
        return springBus;
    }

    /*
     * 发布endpoint
     */
    @Bean
    public Endpoint endpoint(AuthorService authorService) {
        EndpointImpl endpoint = new EndpointImpl(springBus(), authorService);
        endpoint.publish("/author");//发布地址
        return endpoint;
    }
}

注意事项:

  • 配置ServletRegistrationBean时,需要注意设置方法的名称或者bean的名称时,不要和默认的DispatcherServlet类重名了,会导致原先的mvc接口无法使用,因为被覆盖了。
  • 修改访问的路径可以通过设置ServletRegistrationBean来修改,但同时,要注意需要设置bean的名称为cxfServletRegistration,不然会造成注册多个CXFServlet的。具体原因可查看自动配置类:org.apache.cxf.spring.boot.autoconfigure.CxfAutoConfiguration

所以,修改访问路径还可以通过配置项:cxf.path来设置。其默认的访问url为:/services

3.创建启动类,同时启动应用。

/**
 * cxf服务发布示例
 * @author oKong
 *
 */
@SpringBootApplication
@Slf4j
public class CxfServiceApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(CxfServiceApplication.class, args);
        log.info("spirng-boot-cxf-service-chapter34启动!");
    }
}

启动后,可以从控制台看见可以访问的url路径信息。

2018-11-10 22:06:40.898  INFO 46364 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: ‘CXFServlet‘ to [/ws/*]
2018-11-10 22:06:40.899  INFO 46364 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: ‘dispatcherServlet‘ to [/]

访问:http://127.0.0.1:8080/ws/author?wsdl ,验证是否发布成功。

自此,webService发布成功了。

客户端调用

创建一个客户端工程:spring-boot-cxf-client

0.引入cxf依赖。

        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.2.5</version>
        </dependency> 

1.创建wsdl文件,同时利用插件:cxf-codegen-plugin创建相关类。

<!-- cxf-codegen-plugin -->
      <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>3.2.5</version>
        <executions>
          <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
              <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
              <wsdlOptions>
                <wsdlOption>
                  <wsdl>src/main/resources/wsdl/author.wsdl</wsdl>
                  <wsdlLocation>classpath:wsdl/author.wsdl</wsdlLocation>
                </wsdlOption>
              </wsdlOptions>
            </configuration>
            <goals>
              <goal>wsdl2java</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

wsdl文件,放入main/resources/wsdl目录下。之后执行:mvn generate-sources命令,就会自动创建相应的类文件了。拷贝相应的类文件至src/java目录下即可。或者直接指定sourceRoot也是可以的。

2.创建调用的配置类,这里演示两种方式。

WsConst.java

/**
 * 常量类
 * @author oKong
 *
 */
public class WsConst {
    public static final String NAMESPACE_URI = "http://www.lqdev.cn/webservice";
    public static final String SERVICE_ADDRESS= "http://127.0.0.1:8080/ws/author?wsdl";
}

CxfClinetConfig.java

/**
 * 配置类
 *
 * @author oKong
 *
 */
@Configuration
public class CxfClientConfig {

    /**
     *  以接口代理方式进行调用 AuthorPortType接口
     */
    @Bean("cxfProxy")
    public AuthorPortType createAuthorPortTypeProxy() {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setServiceClass(AuthorPortType.class);
        jaxWsProxyFactoryBean.setAddress(WsConst.SERVICE_ADDRESS);//服务地址:http://127.0.0.1:8080/ws/autho

        return (AuthorPortType) jaxWsProxyFactoryBean.create();
    }

    /*
     * 采用动态工厂方式 不需要指定服务接口
     */
    @Bean
    public Client createDynamicClient() {
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(WsConst.SERVICE_ADDRESS);
        return client;
    }
}

注意:除了使用JaxWsProxyFactoryBeanJaxWsDynamicClientFactory调用外,还可以直接使用自动生成的AuthorService类直接调用的,此类继承至javax.xml.ws.Service

如:

    /*
     * 直接调用
     */
    @Bean("jdkProxy")
    public AuthorPortType createJdkService() {
        AuthorService authorService = new AuthorService();
        return authorService.getAuthorPortName();
    }

其实,最后都是使用AuthorPortType进行调用的。

3.创建控制层,进行调用示例。

/**
 * 调用示例
 * @author oKong
 *
 */
@RestController
@RequestMapping("/cxf")
public class DemoController {

    @Autowired
    Client client;

    @Autowired
    @Qualifier("cxfProxy")
    AuthorPortType authorPort;

    @GetMapping("/getauthorstring")
    public String getAuthorString(String authorName) {
        return authorPort.getAuthorString(authorName);
    }

    @GetMapping("/getauthor")
    public AuthorDto getAuthor(String authorName) {
        return authorPort.getAuthorByName(authorName);
    }

    @GetMapping("/getauthorlist")
    public List<AuthorDto> getAuthorList() {
        return authorPort.getAuthorList();
    }

    @GetMapping("/dynamic/{operation}")
    public Object getAuthorStringByDynamic(@PathVariable("operation")String operationName, String authorName) throws Exception {
        //这里就简单的判断了
        Object[] objects = null;
//        client.getEndpoint().getBinding().getBindingInfo().getOperations()
        if ("getAuthorList".equalsIgnoreCase(operationName)) {
            objects = client.invoke(operationName);
        } else if ("getAuthorString".equalsIgnoreCase(operationName)) {
            objects = client.invoke(operationName, authorName);
        } else if ("getAuthorByName".equalsIgnoreCase(operationName)) {
            objects = client.invoke(operationName, authorName);
        } else {
            throw new RuntimeException("无效的调用方法");
        }
        return objects != null && objects.length > 0 ? objects[0] : "返回异常";
    }
}

4.编写启动类,同时制定应用端口为:8090。

/**
 * cxf-客户端调用示例
 *
 * @author oKong
 *
 */
@SpringBootApplication
@Slf4j
public class CxfClientApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(CxfClientApplication.class, args);
        log.info("spring-boot-cxf-client-chapter34启动!");
    }
}

端口号配置:

server.port=8090

5.启动应用,依次访问。查看是否调用成功。

http://127.0.0.1:8090/cxf/getauthorstring?authorName=oKong

http://127.0.0.1:8090/cxf//getauthorlist?authorName=oKong

动态工厂方式调用:

http://127.0.0.1:8090/cxf/dynamic/getAuthorList?authorName=oKong

其他的就不一一贴图了,可自行访问下。

异常捕获

Cxf发生异常时,会统一抛出:org.apache.cxf.interceptor.Fault类的,所以想要捕获异常,可以在统一异常里面进行捕获,关于统一异常处理,可以查看文章:第八章:统一异常、数据校验处理

自定义拦截器

CXF的拦截器分为两种:InInterceptorOutInterceptor。显而易见,InInterceptor可以处理soap请求消息OutInterceptor可以处理soap响应消息。其拦截器都继承至AbstractPhaseInterceptor<Message>接口类,而且,本身也自带了很多的拦截器,可以自行添加看看,比如日志拦截器之类的:LoggingInInterceptorLoggingOutInterceptor

请求流程图:

拦截器链的阶段:

输入拦截器链有如下几个阶段,这些阶段按照在拦截器链中的先后顺序排列。

输出拦截器链有如下几个阶段,这些阶段按照在拦截器链中的先后顺序排列。

具体名称,可查看:org.apache.cxf.phase.Phase

现在,我们自定义个实现拦截器,实现请求时header需要带上特定参数,或者大家可不写一些安全校验的自定义拦截器,本例只是简单的示例。

服务端拦截器

1.检验拦截器:CheckAuthInterceptor.java

/**
 * 简易-安全校验拦截器
 *
 * @author oKong
 *
 */
@Slf4j
public class CheckAuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    public CheckAuthInterceptor() {
        super(Phase.PRE_INVOKE);// 拦截节点:调用之前
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        log.info("检验拦截器开始检验:{}", message);
        // 处理方法
        List<Header> headers = message.getHeaders();

        // 判断是否存header
        // 检查headers是否存在
        if (headers == null | headers.size() < 1) {
            throw new Fault(new IllegalArgumentException("验证失败,请传入正确参数(40001)"));//可自定义编码规范
        }
        //取出header
        Header header = headers.get(0);
        //获取对象
        Element element = (Element) header.getObject();//这里获取的就时 auth对象了
        NodeList tokenNode = element.getElementsByTagName("token");
        if(tokenNode == null || tokenNode.getLength() < 1) {
            //无token节点
            throw new Fault(new IllegalArgumentException("验证失败,请传入正确参数(40002)"));//自定义编码规范
        }
        //获取token
        String token = tokenNode.item(0).getTextContent();
        log.info("请求的token为:{}", token);
        //这里可以对token 有效性进行判断
    }
}

2.Endpoint中加入拦截器配置。

    /*
     * 发布endpoint
     */
    @Bean
    public Endpoint endpoint(AuthorService authorService) {
        EndpointImpl endpoint = new EndpointImpl(springBus(), authorService);
        endpoint.publish("/author");//发布地址
        endpoint.getInInterceptors().add(createCheckAuthInterceptor());//加入拦截器
//        endpoint.getOutInterceptors().add()//响应拦截器
        return endpoint;
    }

    @Bean
    public Interceptor<SoapMessage> createCheckAuthInterceptor(){
        return new CheckAuthInterceptor();
    }

客户端拦截器

1.编写拦截器。

/**
 * 简易-安全校验拦截器
 * @author oKong
 *
 */
public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage>{

    public AuthInterceptor() {
        super(Phase.PREPARE_SEND);//准备请求时进行拦截
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        //处理方法
        List<Header> headers = message.getHeaders();

        Document doc = DOMUtils.createDocument();
        Element element = doc.createElement("auth");
        Element tokenEle = doc.createElement("token");
        tokenEle.setTextContent(UUID.randomUUID().toString());
        element.appendChild(tokenEle);
        //这里需要注意 默认情况下 是使用 org.w3c.dom.Element对象设置对象值的。
        //也可以指定 DataBinding 设置对象的。可继承抽象类: org.apache.cxf.databinding.AbstractDataBinding
        //具体源码可查看:org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor
        Header tokenHeader = new SoapHeader(new QName(""), element);
//        tokenHeader.setDataBinding()
        headers.add(tokenHeader);
    }

}

这里需要注意:

  • 设置header时,默认是org.w3c.dom.Element对象。
  • 自定义对象时,可设置DataBinding类来解析(未尝试,只是看了一眼源码,里面有此逻辑,有兴趣的同学可以自行试试)。

2.请求类中加入拦截器。

CxfClientConfig.java

    /**
     *  以接口代理方式进行调用 AuthorPortType接口
     */
    @Bean("cxfProxy")
    public AuthorPortType createAuthorPortTypeProxy() {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setServiceClass(AuthorPortType.class);
        jaxWsProxyFactoryBean.setAddress(WsConst.SERVICE_ADDRESS);//服务地址:http://127.0.0.1:8080/ws/autho
        jaxWsProxyFactoryBean.getOutInterceptors().add(createInterceptor());//加入自定义拦截器
        return (AuthorPortType) jaxWsProxyFactoryBean.create();
    }

    /*
     * 采用动态工厂方式 不需要指定服务接口
     */
    @Bean
    public Client createDynamicClient() {
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(WsConst.SERVICE_ADDRESS);
        client.getOutInterceptors().add(createInterceptor());
        return client;
    }

    @Bean
    public Interceptor<SoapMessage> createInterceptor() {
        return new AuthInterceptor();
    }

重新启动后,再次请求就可以看见相关日志输出了,可以试着不设置token,看看有拦截。

异常请求:

参考资料

  1. http://cxf.apache.org/docs/springboot.html
  2. https://www.code996.cn/post/2017/cxf-interceptor/

    总结

    本章节主要简单介绍了apache-cxf的使用。这文章示例写下来,我发现比spring-ws更简单呀,也更让人容易理解、逻辑比较清晰,而且也能设置一些差异化的东西。不知道是不是真的对spring-ws了解的不够呀,没有发现spring-ws的优点呀。自此,关于WebService的文章就暂时告一段落了。

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ:499452441
  • 微信公众号:lqdevOps

个人博客:http://blog.lqdev.cn

完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-34

原文地址:http://blog.lqdev.cn/2018/11/12/springboot/chapter-thirty-four/

原文地址:https://www.cnblogs.com/okong/p/springboot-thirty-four.html

时间: 2024-10-28 10:00:33

SpringBoot | 第三十四章:CXF构建WebService服务的相关文章

Gradle 1.12用户指南翻译——第三十四章. JaCoCo 插件

本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://github.com/msdx/gradledoc/tree/1.12. 直接浏览双语版的文档请访问: http://gradledoc.qiniudn.com/1.12/userguide/userguide.html. 另外,Android 手机用户可通过我写的一个

第三十四章

11 道渢(fēng)呵,其可左右也.万物恃之以生而不辞,成功遂事而弗名有也.万物归焉而弗为主.则恒无欲也,可名于小.万物归焉而弗为主,可名于大.是以圣人之能成大也,以其不为大也,故能成大. 第三十四章1 相当比例的人,因占有心而生病 道渢呵,其可左右也.万物恃之以生而不辞(帛书未见此句),成功遂事而弗名有也.万物归焉而弗为主.则恒无欲也,可名于小. 道广泛盛大,充满左右无处不在.它生养万物而不推辞这个庞大的工作,完成了这些事而不说是自己所拥有的.万物源于它但它不说自己是主人.道没有欲望,所以可

Python 3标准库 第十四章 应用构建模块

Python 3标准库 The Python3 Standard Library by  Example -----------------------------------------第十四章     应用构建模块-----------------------------14.1  argparse:命令行选项和参数解析----------------------------- argparse模块 14.1.1  建立解析器 14.1.2  定义参数 argparse模块 14.1.3 

第三十四章 metrics(2)- 搭建metrics平台

一.基本架构图 1.整个架构组件: java客户端 dropwizard-metrics:springboot.hystrix的metric体层使用了这个,所以我们需要自己封装向statsd发包的方法,来发送这些metrics给statsd java-statsd-client:直接进行计数并且非常方便的向statsd发送这些metrics(向statsd发包的方法已经封装好了) statsd graphite carbon carbon-relay carbon-cache whisper g

SpringBoot | 第三十二章:事件的发布和监听

前言 今天去官网查看spring boot资料时,在特性中看见了系统的事件及监听章节.想想,spring的事件应该是在3.x版本就发布的功能了,并越来越完善,其为bean和bean之间的消息通信提供了支持.比如,我们可以在用户注册成功后,发送一份注册成功的邮件至用户邮箱或者发送短信.使用事件其实最大作用,应该还是为了业务解耦,毕竟用户注册成功后,注册服务的事情就做完了,只需要发布一个用户注册成功的事件,让其他监听了此事件的业务系统去做剩下的事件就好了.对于事件发布者而言,不需要关心谁监听了该事件

第十四章:构建自定义的同步工具——Java并发编程实战

一.状态依赖性管理 对于单线程程序,某个条件为假,那么这个条件将永远无法成真 在并发程序中,基于状态的条件可能会由于其他线程的操作而改变 1 acquire lock on object state 2 while (precondition does not hold) 3 { 4 release lock 5 wait until precondition might hold 6 optionally fail if interrupted or timeout expires 7 rea

奋斗吧,程序员——第三十四章 挥泪别痴心,只怨情长天未怜【上半部完】

那天后来发生的事让我下巴差点掉在地上. 我虽然成功帮他引开了恐龙,但其实也没指望他能搭上美女. 没想到老四超常发挥,跟美女逛了一天街后,居然去开了房. 那美女是音乐系的,怪不得长得漂亮. 至此以后老四一直对那美女念念不忘,倒是人美女貌似只是想做一回露水夫妻而已,表示不必再联系. 不是我不明白,这世界变化快. 蒋小涵的邀请我最后还是拒绝了,没有说为什么. 彪悍的人生不需要解释. 她又提出晚上一起去唱歌,和她在一起,似乎每次的活动都是唱歌. 其实我不是很喜欢KTV的环境,情愿安安静静地煮一壶茶,聊聊

第三十四章 软件工艺的话题

征服复杂性 降低复杂度是软件开发的核心. 在架构层将系统划分为多个子系统,以便让思绪在某段时间内能专注于系统的一小部分: 仔细定义类接口,从而可以忽略类内部的工作机理: 保持类接口的抽象性,从而不必记住不必要的细节: 避免全局变量,因为他会大大增加总是需要兼顾的代码比例: 避免深层次的继承,因为这样会耗费很大精力: 避免深度嵌套的循环或条件判断,因为他们能用简单的控制结构取代,后者占用较少的大脑资源: 别用goto,因为它们引入了非顺序执行,多数人都不容易弄懂: 小心定义错误处理的方法,不要滥用

Eclipse+Maven+Spring+CXF 构建webservice 服务

一.   软件准备 Eclipse 4.2.1 Maven 2.2.1 Spring 3.2.6 CXF 3.0.2 软件下载和Eclipse 安装 maven插件等请參考其它文章. 二.   步骤 1.        新建webproject,利用maven管理.例如以下: project名为test,完毕以后.项目结构例如以下图: src/main/java 准备放 java 程序. src/main/resources准备放各类资源文件. 2.        加入代码 1)