• 使用maven构建scala项目

    构建及编译scala工程我使用过三种工具:sbt、gradle和maven。 感谢GFW,sbt的速度慢得让人心碎。即使使用诸如广谈公服等加速方案,也还是会遇到各种各样的问题(比如时不时的lock住)。所以pass。 gradle是最让人惊喜的一个方案,简洁便利上超过sbt,更远胜maven,执行速度也没短板。只有下载源码这问题让我头疼,但也是小问题。然而,关键是公司发布环境不支持gradle,所以一切休谈。 最后是maven。有人说比起gradle来,maven是老人脸上的皱纹。意思是maven的配置非常繁琐,显得过时了。比喻非常形象。但是老人的好处就是经验丰富,很多问题都能很快找到解决方案,虽然略嫌啰嗦但却让人放心。所以最终还是使用maven管理和构建生产环境中的scala项目。 介绍下如何使用maven构建scala工程。 编译 下面是一个示例pom文件: scala环境最重要的三个依赖:scala-compiler、scala-reflect和scala-library都可以通过scala-compiler及其间接依赖获取到。 一些依赖可能会间接引用不同版本的scala,所以在<dependencyManagement>中做了下scala版本的统一。 在<build>中使用了maven-scala-plugin插件来执行scala的编译,不然在测试执行时会提示“错误: 找不到或无法加载主类 chobit.MyDriver”这样的错误。 打包 如果有打包需求的话建议使用assembly插件执行打包。assembly支持的打包方案非常丰富,算是同类产品里最好的。 看个打包的示例: 首先添加assembly插件 这里的配置设置了两个重要信息: 打包配置文件的具体位置:src/main/assembly.xml; 在package阶段自动执行assembly插件;如果没有指明,就得执行mvn assembly:assembly命令来进行打包。 打包配置文件的详情: 配置文件中的注释应该已经足够说明用法了。 这里的代码是从一个spark工程上copy来的,所以会有将工程打包成zip文件,又包含shell脚本这样的内容。 在windows主机上执行mvn clean package命令时,可能会遇到这样的错误: 解决方案有两个: 如果要把文件放在zip包下的根目录,可以直接将<outputDirectory>标签闭合,如: <outputDirectory /> 如果要把文件放在zip包下的其他目录,可以使用相对路径,如:<outputDirectory>./shell</outputDirectory> 示例代码见: GitHub / Zhyea 。 参考文档 Scala With Maven

    [阅读更多...]
  • springboot入门08 – 创建非web项目

    概述 从开始使用SpringBoot到现在,一直都是在用SpringBoot开发web服务(API服务)。直到前段时间,需要帮其他组的同事写一个非web的简单服务时,才想到Springboot是不是也支持非web项目。 答案是肯定的:spring诞生之初就不是为web项目定制的,springBoot无非是在spring核心项目的基础上添加了一些方便开发者使用的组件,所以使用springboot开发非web项目也是可行的。 首先我们要弄明白常用的web项目和非web项目的区别在哪儿?私以为是服务启动和执行逻辑触发的方式: web项目需要依赖web容器来启动,通过http请求来触发相关的服务; 非web项目则不需要依赖web容器来启动,它可以是自启动的; 非web项的服务通常是主动触发的或者通过非http的方式被动触发的。 接下来详细介绍下如何使用springboot构建非web项目。 依赖 创建web项目通常需要使用的依赖是spring-boot-starter-web: 这个依赖间接引用了tomcat,spring-webmvc,spring-context,json-starter等依赖。这些在非web项目里基本上都用不到,非web项目可以直接使用spring-boot-starter: spring-boot-starter间接引用了spring-core、yaml、auto-configure等依赖,已经足够用来创建一个普通的spring项目了。 如果有特殊情况(说实话,我强烈怀疑)非要使用spring-boot-starter-web依赖来构建非web项目也不是不行,只需要加一些配置来避免启动web容器就行: 或者在启动类中添加web配置并设置为NONE: 启动 springboot非web项目的启动类定义和web项目并无不同。如下: 但是执行启动类中的main方法以后呢?如果是web项目,在启动后等待HTTP请求调用就行了。不过这里是非web项目,我们得想办法执行我们定义好的业务逻辑。接下来按常见的业务逻辑特征分别介绍下。 1. 任务需要定时执行 这种情况需要配置定时任务。SpringBoot对定时任务的支持还算可以。前段时间我写过关于《SpringBoot定时任务配置》这样的文章,有需要可以参考下,这里就不重复介绍了。 2. 在某个类的实例注入后就立即执行 这种场景说实话不多见。通常是需要该类实现InitializingBean接口,并在afterPropertiesSet方法中实现相关的逻辑。如下: 还有一种方式是使用java提供的@PostConstruct注解: 建议最好使用前者。 3. 使用SpringBoot的提供的主动执行接口 springboot提供了两个接口ApplicationRunner和CommandLineRunner来支持主动执行业务逻辑。 比如我们可以直接实现ApplicationRunner接口,并在run方法中添加业务逻辑: CommandLineRunner接口和ApplicationRunner接口的差别不大。从执行时机还是调用过程上来着,这两者几乎都是一样的。这唯一的区别在于他们提供的run方法的args参数类型上: ApplicationRunner的args参数是ApplicationArguments类型,对原始参数做了一层封装; CommandLineRunner的args参数是字符串类型,取的是启动类收到的原始参数。 这种差别源于SpringBoot对二者的定位上: ApplicationRunner适用于启动即执行的场景,只需要读取一次参数信息即可。它的参数通常是“option=value”这种结构的,如:“–foo=bar –foo=baz” 。ApplicationArguments中封装了一些对这种参数进行处理的方法,以便开发使用。 CommandLineRunner从名字上看就是用来做命令行交互用的,所以它这里直接取了原始参数,看一个使用示例: 如果在执行命令行交互之前也需要读取解析传入的参数,那么这里的MyRunner3类完全也可以实现ApplicationRunner接口。二者的差别几乎可以忽略。 在一个应用里面可以有多个ApplicationRunner或CommandLineRunner的实现。要调整两者的实现类之间的执行顺序可以使用@Order注解。 就这样。这一节虽然介绍的是如何创建启动非web项目,但是在web项目中也会有主动触发一些执行逻辑的需要,上面介绍的这些方案也是完全可用的。 示例代码已上传到了 GitHub/Zhyea ,有需要可自行查阅。 End!

    [阅读更多...]
  • spring feign https配置

    前两天需要通过springboot-feign来调用一个https的外部服务接口,因此要实现feign-client的SSL设置。 feign执行http请求通常会调用feign.Client接口的实现。这个接口的默认实现类Default提供了添加SSL配置的构造器: 简单介绍下构造器参数中用到的SSL相关的接口: SSLSocketFactory:SSLSocket工厂;SSLSocket扩展了Socket并提供使用SSL或TLS协议的安全套接字。这种套接字是正常的流套接字,但是它们在基础网络传输协议(如TCP)上添加了安全保护层。 HostnameVerifier:实现主机名验证功能;在握手期间,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口实现程序来确定是否应该允许此连接,如果回调内实现不恰当,默认接受所有域名,则有安全风险。 默认使用的Client实例的两个构造参数的值都是null。我们需要在配置类中创建使用SSL的Client实例,这里参考了默认的配置类FeignClientsConfiguration: 代码中使用了SSLContext的getSocketFactory方法来创建 SSLSocketFactory对象。在创建SSLContext对象时,通过(chain, authType) -> true指定了SSL信任策略,即:不做任何处理,信任全部请求。 另一个HostnameVerifier类型的参数使用了一个实现类NoopHostnameVerifier的实例,作用是关闭主机名验证功能,并且永远不会抛出SSLException。 最后将配置添加到FeignClient注解上即可: 如代码中所示可以为configuration项一次指定多个配置类。这里的第一个配置类是spring默认提供的配置类。 另外,如需要创建Client的Bean实例,在使用Eureka的情况下,应选择LoadBalancerFeignClient的对象,不然会产生诸如无法解析host之类的问题。 就这样了。

    [阅读更多...]
  • springboot入门07 – 配置文件详解

    概述 这一篇主要介绍下SpringBoot配置相关的内容。 通过配置文件,我们可以做到如下事情: 修改springboot的默认配置项 添加自定义配置项 SpringBoot使用全局的配置文件,主配置文件名是固定的:application.xxx springboot的配置文件有多种格式可选,如:.properties,.xml,.yml。 我个人比较喜欢yaml的简洁,所以推荐使用yaml。 yaml的基本语法 下面介绍些yaml的语法 使用空格缩进来表示结构,相同层级的数据其缩进的长度必定相同: 支持使用注解,但只支持使用单行注解,注解以“#”号开头: 支持直接类型或字符串的列表或数组结构,有两种表达形式: 每个列表成员占一行,以横杠和空格(- )开头 所有列表成员在同一行,被方括号([])包围,列表成员间以逗号和空格分隔 也支持Map结构,同样也有两种表达形式: 每个KV对占一行,键值用冒号分隔 所有KV对在同一行,被大括号({})包围, KV对之间用逗号和空格分隔,键值用冒号分隔 通常字符串不需要使用引号,但必要时可以使用单引号(’)或双引号(”),比如需要使用转义字符时 可以使用两个叹号(!!)来强制指定格式(这个感觉用处不大,但是万一用到了呢) 内容可以重复使用,原始位置的内容用(&标记名)来标记,在引用位置用(*标记名)来引用 如果想在一个文件里面配置不同的profile,可以使用三个横杠“—”将yaml文件分割成多个独立区域,每个区域被视为一个独立的文件。三个点号“…”用来标识文件结尾。 如示例代码,在一个yaml文件中存在多个独立区域时,使用spring.profiles属性来标识配置区域的profile信息。 多环境配置 在主配置文件application.yml之外,还可以有多个外部配置文件,其名称格式固定为application-{profile}.yml。 SpringBoot会根据主配置文件中的spring.profiles.active参数来决定加载哪个配置文件。 通过这种方式SpringBoot实现了支持多环境配置,从而能够在不同环境下(开发、测试、生产)进行配置参数的切换。 主配置文件中的spring.profiles.active属性还可以有多个值,每个值以逗号进行分割: 这种方式允许我们为不同需求创建不同的配置文件。SpringBoot会依序加载每个值指向的配置文件。 配置文件的加载顺序是主配置文件application.yml,spring.profiles.active指定的外部配置文件。如有冲突项,后面加载的配置会覆盖前面的。 如果多个外部配置文件有相同的配置项,建议在主配置文件中进行配置。 如spring.profiles.active指定的值无法与任何外部文件匹配,SpringBoot会尝试加载application-default.yml文件。 结合@Profile实现内部实例的动态选择 spring.profiles.active参数不仅可以指明要使用哪个外部配置文件,也可以控制内部实例的选择。这需要通过配合@Profile注解实现。 如下例: 如果没有@Profile注解,在启动的时候SpringBoot就会报错,因为为一个Worker类提供了两个实例。但是现在,SpringBoot可以根据spring.profiles.active指明的profile和@Profile注解来选择注入哪个实例。 @Profile注解可以用于含有@Component、@Configuration、@Bean等注解的类及其方法。 @Profile注解也可以有多个值,如下: 这样,在profile是dev或test时,这个Bean都可以被成功注入。 配置项获取 使用SpringBoot的配置项有两种方式:通过@Value注解或通过@ConfigurationProperties注解。这两种方式各有长处,可根据进行选择。 通过@Value注解获取 @Value注解适用于单个配置项的获取,比如我们可以获取当前的外部配置文件的profile: @Value注解的一个优势是可以使用SPEL表达式(关于SPEL表达式,我有整理过:《SpringBoot SpEL表达式》),如下是一个非常粗略的SPEL表达式: @Value注解的不足之处在于只能获取直接类型的值或字符串,不能映射为复杂类型。 通过@PropertiesConfiguration注解获取 @PropertiesConfiguration注解的主要特点是可以将配置映射为对象。因此平时还是通过这种方式获取SpringBoot配置为主。 @PropertiesConfiguration注解可应用于类或成员方法。 如下是将应用于类的示例: 从示例代码中可以看到配置类需要有@Component注解或@Configuration注解,以便完成注入。 此外,在使用@ConfigurationProperties注解的时候需要传入注入配置属性的前缀,以便获得配置项的范围。 在成员方法上使用@PropertiesConfiguration注解则是一种更为灵活的方式,能够充分弥补@Value注解的不足,示例如下: 注意这里还需要用到@Bean注解,以便将读取到的配置信息注入到实例上。不过不能通过getWorker()方法调用,否则获取的还是一个只有默认值的对象。要使用该实例还是需要通过@Autowired注解来注入。 最后,可以看下我在GITHUB上的示例应用,在里面我尝试了SpringBoot配置的各种应用实践。 就这样。

    [阅读更多...]
  • springboot入门06 – 接口单元测试方案

    以前写过关于springboot Controller层单元测试的系列文章(Spring Controller层测试)。但是那几篇文章还是更偏方法论一些,不能直接拿来使用。所以有了这偏内容,目的主要是记录下平时使用的Controller层单元测试方案。 在这里先定义一个普通的api接口类WorkerApi: 这个接口里的4个方法覆盖了平时常用的四种Http请求方案,并将请求结果用一个统一的ResultWrapper类进行了封装(关于如何封装请求结果请参考上一篇文章SpringBoot Controller返回值封装)。 然后是单元测试方案。这里有两个超类:TestBase和ApiTestBase。前者用来对普通的注入实例进行测试,后者主要用来对Api接口进行测试。 TestCase类内容如下: 通过类内容可以看到,在测试中会启动一个内嵌的WebServer来加载Spring Context。这样的测试比较重一些,不过也和实际使用场景更一致一些。 ApiTestBase类继承了TestBase类。在这个类里引用了TestRestTemplate的实例执行具体的接口调用,此外还定义了一些公用方法来减少使用时的代码量: 这里为每种请求都定义了两个方法,以根据需要返回不同形式的返回值。 看下是怎样使用的: 有时候还会有在执行测试前添加虚拟机环境参数的需要,此时可以视情况在TaseBase或ApiTestBase或测试类中添加静态代码块并设置虚拟机参数: 就这样。代码已经传到了GitHub上,有需要请自行查阅: GitHub / zhyea

    [阅读更多...]
  • springboot入门05 – 包装SpringBoot Controller返回值

    一个项目使用了SpringBoot,需要对Controller的返回值进行二次包装。包装类结构大致如下: 通过查找资料,找到了两种封装方式。 方法一 第一种方式是替换掉RequestResponseBodyMethodProcessor,这需要使用一个MethodReturnValueHandler的装饰类: 在装饰类中使用一个Result类的实例替换了returnValue。而后在InitializingBean中基于原来的RequestResponseBodyMethodProcessor的实例创建一个ResponseBodyWrapHandler的实例来完成替换: 使用ResponseBodyWrapFactoryBean,完成afterProperties方法的调用,只需要创建一个ResponseBodyWrapFactoryBean的实例即可: 这行代码可以放在启动类中。 方法二 第二种方式基于ControllerAdvice和HttpMessageConverter实现。 首先用一个ResponseBodyAdvice类的实现包装Controller的返回值: 如果Controller类的返回值没有String类型的,仅有上面这个类就够了。如果有String类型的返回值,就有可能遇到类型不匹配的问题。HttpMessageConverter是根据Controller的原始返回值类型进行处理的,而我们在ResponseAdvisor中改变了返回值的类型。如果HttpMessageConverter处理的目标类型是Object还好说,如果是其它类型就会出现问题,其中最容易出现问题的就是String类型,因为在所有的HttpMessageConverter实例集合中,StringHttpMessageConverter要比其它的Converter排得靠前一些。我们需要尝试将处理Object类型的HttpMessageConverter放得靠前一些,这可以在一个Configuration类中完成: 在这个方案中,如需要对异常做些特别处理,还可以创建一个ExceptionAdvisor类来完成: 这样还可以根据异常类型来设置返回时的HttpStatus。 就这样。 有朋友在评论区指出问题了,做了些调整,也写了一份示例程序上传到CSDN。有兴趣的可以下载来看看。下载地址如下:点击此处下载。 这是许久之前刚用springboot时写的,现在适应springboot的最新版本存在一些问题。所以稍稍重新调整了下,并将之加入到了最近正在尝试进行的一个系列里面。 新版本的代码可以在GitHub / zhyea上看到。 ##########

    [阅读更多...]