Spring 项目中把属性或 SQL 语句写在 .xml 文件中

Spring 项目中把大量的 SQL 分散在 Java 代码中,无 Here Doc 的情况下用加号来连接写着实在是不爽,于是之前思考这个 Spring 项目中把 SQL 语句写在 .sql 文件中 --  把它们写在 *.sql 文件中,但是这个 *.sql 需要特定的格式来标识属性 Key

--!select.user
select id, firstname, lastname, address

--!update.user
update ........

而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值,用注解引用它时还有点额外的 factory 属性来配置,如

@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)

所以一直在思考是否能够再简单些,是否能用一个自定义的注解,如

@SqlPropertySource("classpath:sql/queries.sql")

捉摸了很久,似乎有点难度,不过再不断发掘的过程中找到了这个类 org.springframework.core.io.support.PropertiesLoaderUtils, 有下面的代码片断 阅读全文 >>

类别: Spring. 标签: . 阅读(40). 评论(0) »

JMockit 中捕获 mock 对象方法的调用参数

三个月前写过一篇 Mockito 中捕获 mock 对象方法的调用参数,一般项目中 Mockito 不决求助于 JMockit, 同样的在 JMockit 也需对捕获被 Mock 的方法调用参数。当我们用 new Expectations(){{}} 打桩并在后面断言了返回值,那就无需捕获参数来断言,匹配到了方法调用即证明传入的参数也是对的,如下面的代码所示

public class UserServiceTest {

    @Mocked
    private UserDao userDao;

    @Test
    public void couldCallUserDaoToAddUser() {
        new Expectations(){{
           userDao.findById(123);
           result = "Yanbin";
        }};

        UserService userService = new UserService(userDao);
        String user = userService.findBy(123);

        assertThat(user).isEqualTo("Yanbin");  //这里断言成功也就证明了 userDao.findById(123) 方法被调用,参数必须是 123
    }
}

但如果是未打桩的方法,或打桩是用的模糊参数(withInstanceOf(String.class)), 或是无返回值的方法就要事后对是否调用了某个方法以及传入什么参数的情况进行断言。 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(26). 评论(0) »

使用 Awaitility 测试异步代码

对于同步方法的测试很简单,调用完后可立马检查执行状态; 而异步方法,由于我们无法确切的知道何时结束,因此以往的办法是用 Thread.sleep(500) 来预估一个执行时间。然后通常我们估计的要长于实际的时间,这就很浪费,况且偶然的超过预估的等待时间也并不意味着代码有问题。还有 sleep 方法还抛出一个检测异常 InterruptedException, 一般会要对 Thread.sleep(500) 作下简单包装。

于是今天要介绍的 Awaitility 就应运而生了,专门针对异步方法的测试。它的官方文档在 https://github.com/awaitility/awaitility/wiki/Usage。本文主要关注在 Java 8 环境下用 Lambda 的代码书写方式。Awaitlity 实际运行是以某种轮询的方式来检查是否达到某个运行状态,可设定最多,最少等待时间,或永久等待,或自定义轮询策略,之后就开始进行需要的断言,所以它可以尽可能的节省测试异步方法所需的时长。而不像 Thread.sleep(500) 一路等到黑,并且没有回头路。

通常我会在项目中给 JUnit 配上三个最佳伴侣,它们是(按 mvn dependency:tree 中的显示方式):

  1. org.awaitility:awaitility:2.0.0:test
  2. org.easytesting:fest-assert:1.4:test
  3. org.mockito:mockito-core:2.7.22:test

当然如果项目中没有异步调用自然是不需要 Awaitility, 在我的项目中是基本不可能的。以上三种都追求 DSL,以流畅的方式进行愉快的测试。

现在来尝试下 Awaitility 的几种基本的用法,先假定有下面的代码 UserService 阅读全文 >>

类别: Java/JEE. 标签: . 阅读(32). 评论(0) »

使用 Mockito 修改私有属性

修改私有属性来 Mock 可能不是一种很好的测试方式, 因为属性名是动态的,但有时不得已而为了,例如下面的代码:

public class UserService {
    private ExternalApi external = ExternalApi.default();
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public User findUserById(int id) {
        return userDao.findById(external.convertId(id));
}

测试时欲隔离对 ExternalApi 的外部依赖, 当然可以把它也作为构造函数的一个参数,这样创建 UserService 实例时就可以 Mock external 属性。不过 external 经常是不变的,所以作为方法参数的必要性也不大。这就希望能在构造出 UserService 之后对 external 私有属性进行 Mock 处理。

在 Mockito 1.x 和 2.x 下要使用不同的方式,分别使用到 Whitebox 和 FieldSetter 类,它们都来自于  mockito.internal.util.reflection 包,可见 Mockito 打心底不推荐直接使用它们,但谁叫它们是 public 的呢。还有一种方式是使用 PowerMock + Mockito, 这是后话。 阅读全文 >>

类别: Java/JEE. 标签: . 阅读(48). 评论(0) »

使用 JMockit 来 mock 构造函数

Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式,经常这也 Mock 不了,那也 Mock 不了,需要迁就于测试来调整实现代码,使得实现极不优雅。比如 Mockito 在 私有方法,final 方法,静态方法,final 类,构造方法面前统统的缴械了。powermock 虽然可作 Mockito 的伴侣来突破 Mockito 本身的一些局限,但是我一用它来 Mock 一个构造方法就出错

Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter

原因是 Mockito 变化太快,powermock 跟不上它的步伐 -- https://github.com/powermock/powermock/issues/684,于是我只能止步。

不得已再祭出 JMockit 这号称(也确实是)一无所不能的大杀器,在此见识一下它怎么 Mock 构造函数的

本篇实例所使用的 JMockit 版本是 1.30, 当前最新版 1.31, 由于尚未被 Maven 中央仓库收录,所以暂用 1.30。在 pom.xml 中如下方式引入 阅读全文 >>

类别: Java/JEE. 标签: , . 阅读(121). 评论(0) »

JdbcTemplate 易被 Java 8 Lambda 带入的坑

Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利,不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后,JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化,而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。

下面就是我们的一个样板代码,在我们的 UserRespository 中有一个方法 findAll() 用于获得所有用户:

public List<User> findAll() {
    List<User> users = new ArrayList<>();
    jdbcTemplate.query("select id, name from user", rs -> {
        while (rs.next()) {
            users.add(new User(rs.getInt("id"), rs.getString("name")));
        }
    });
    return users;
}

阅读全文 >>

类别: Java8, Spring. 标签: . 阅读(99). 评论(1) »

Java 的参数检查与断言 - Spring Assert

之前有一篇 Java 的参数检查与断言 介绍了在 Java 中如何应用 Guava 的 Preconditions 来进行参数检查与状态断言,原本是可以心无旁骛,专心的用它就行了,可是刚刚因琢磨我们使用 JdbcTemplate 存在的一大 Bug,阅读 Spring 的源代码时发现 spring-util 也提供了一个类似于 Gruva Preconditions 的工具类 -- Assert,它是自 Spring 1.1.2 开始就静静的躺在那儿了。

所以现在要检查参数或状态断言时反而犯上了选择综合证,虽然内心还是偏向于 Guava Preconditions,但总之不那么坚决了,也不知到底是谁在重新发明着轮子。可以说 Spring Assert 与 Guava Preconditions 的功能基本一致,也是针对入口参数或中间运行结果的检查分别抛出 IllegalArgumentException 和 IllegalStateException . 下面一张图来了解它的所有方法。 阅读全文 >>

类别: Java/JEE. 标签: . 阅读(57). 评论(0) »

Java 可变参 Object...objects 方法的陷进

Java 的可变参数(Varargs) 方法让我们调用起来很方便,不需要总是去构造一个数组来传递不定数量的参数,而且还可以作为方法的一个可选参数,如

void foo(int id, String...name) {
    String yourName = name.length == 0 ? "Anonymous" : name[0];
        ......
}

想要告诉名字就调用 foo(1, "Yanbin"), 不想的话就用 foo(1).

但我们在使用 Java Varargs 时,当变参类型定义为 Object...objects 时就要当心了,因为 Object 类型的包容性原因一不小心就可以掉到坑里去了,例如下面的方法

void foo(Object...objects) {
     Arrays.stream(objects).forEach(System.out::println());
}

当引用类型是 Object[] 时调用没问题,下面代码调用可以得到预期的结果 阅读全文 >>

类别: Java/JEE. 标签: . 阅读(44). 评论(0) »

Java 与 Python 通过 Apache Avro 交换数据

最近转战到 Amazon 的云服务 AWS 上,考虑到在使用它的 Lambda 服务时 Python 应用有比较可观的启动速度,与之相比而言,Java 总是慢热型,还是一个内存大户。所以有想法 Lambda 函数用 Python 来写,来增强响应速度,而内部的应用仍然采用 Java, 于是就有了 Java 与 Python 的数据交换格式。使用 Kafka 的时候是用的 Apache Avro, 因此继续考察它。

注意,本文的内容会有很大部份与前一篇 Apache Avro 序列化与反序列化 (Java 实现) 雷同,不过再经一次的了应用,了解更深了。

在不同类型语言间进行数据交换,很容易会想到用 JSON 格式,那我们为什么还要用 Apache Avro 呢?通过接下来的内容,我们可以看到以下几点:

  1. Apache Avro 序列化的格式也是 JSON 的,Java 的 Avro 库依赖于 Jackson 库
  2. 序列化数据库本身带有 Schema 定义的,方便于反序列化,特别是对于 Java 代码; 而 JavaScript 会表示多此一举
  3. 自动支持序列化数据的压缩,在官方提供的库中,Java 可支持 deflate, snappy, bzip2, 和 xz. 其他语言中可能少些,如 Python 只支持 deflate, 和 snappy, 应该可扩充。序列化数据中 Schema 部分不被压缩
  4. 天然支持序列化对象列表,这样在序列化数据中只需要一份 Schema,类似于数据库表 Schema 加上多记录行的表示方式。只用 Apache Avro 传输小对象的话,数据量比 JSON 事 JDK 序列化的数据要大。

Apache Avro 官方提供有 C, C++, C#, Java, PHP, Python 和 Ruby 的支持库,可在网上找到其他语种的类库,如 NodeJS, Go 的,等等。 阅读全文 >>

类别: Java/JEE, Python. 标签: . 阅读(203). 评论(0) »

一个简单的 Java 自动批处理队列

实际中可能有这样的应用场景,得到一个记录不需要立即去处理它,而是等累积到一定数量时再批量处理它们。我们可以用一个计数器,来一个加一个,量大时一块处理,然后又重零开始计数。如果记录的来源单一还好办,要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。

这里我编写了一个最简单的任务批处理的队列,构造了告诉它批处理数量,消费者,然后就只管往队列里添加记录,队列在满足条件时自动进行批处理。因为内部使用的是 BlockingQuque 来存储记录,所以多线程往里同时添加记录也没关系,最后的未达到 batchSize, 的那些记录可主动调用 completeAll() 函数或在达到 timeout 后来触发批处理,并且结束队列内的循环线程。

注意: 多线程环境下往一个无线程保护的集合或结构中,如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失,而往有线程保护的目的地写东西就安全了,如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价,不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。

下面是 BatchQueue 的简单实现 阅读全文 >>

类别: Java/JEE. 标签: . 阅读(90). 评论(0) »
Page 1 of 3912345...102030...Last »