Spring 与 Auto-Configuration

1. 简述

最近在捣鼓一个类似 Spring Boot starter 的小项目:把常见的 Web 开发所使用的组件或者技术方案封装在一起,通过一个 Maven 引用快速地将项目搭建起来。

2. 遇到的问题

遇到了一个问题:我在被引用项目中用 @Configuration 注解标注了一个类,在开发项目中这个类却没有被实例化。

该类代码如下所示:

  package com.avaloninc.web.aop.config;
  
  import com.avaloninc.web.aop.filter.RequestBodyWrapperFilter;
  import com.avaloninc.web.aop.interceptor.RequestAuditInterceptor;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  
  /**
   * @Author: wuzhiyu.
   * @Date: 2019-02-22 15:15:18.
   * @Description:
   */
  @Configuration
  @ConditionalOnProperty(value = "log.request.audit.enable", havingValue = "true")
  public class RequestAuditConfiguration extends WebMvcConfigurerAdapter {
  
    private final String   logPartSeparator;
    private final String[] uriWhiteList;
  
    @Autowired
    public RequestAuditConfiguration(@Value("${log.request.audit.separator:'||'}") String logPartSeparator,
                                     @Value("${log.request.audit.whitelist:''}") String[] uriWhiteList) {
      this.logPartSeparator = logPartSeparator;
      this.uriWhiteList = uriWhiteList;
    }
  
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(new RequestAuditInterceptor(logPartSeparator, uriWhiteList));
    }
  
    @Bean
    public RequestBodyWrapperFilter getRequestBodyWrapperFilter() {
      return new RequestBodyWrapperFilter();
    }
  }

3. 解决方案

定下心来想了一下,被引用项目的配置类全名为:com.avaloninc.web.aop.config.RequestAuditConfiguration ,而开发项目中以 @SpringBootApplication 修饰的启动类全名为 com.avaloninc.web.demo.Main

第一个想法是:会不会是因为包名的问题导致 Spring 没有去扫描 com.avaloninc.web.aop 包下面的 Bean 呢?

尝试了解决办法:在启动类上加上注解 @ComponentScan("com.avaloninc"),果然好使!

但是转念一想,Spring Boot 的各种 starter 中定义的 Bean 也不会和我们自己的项目同属于一个包下面啊,为什么它们没有这个问题呢?

于是找了一篇自定义 starter 的文章1看了看了一下。大部分没什么特别,但是其中提到了一个 spring.factories 文件唤醒了我记忆中关于 Spring Boot 初始化过程的一点记忆。所以果断在被引用项目中也加了这个文件 src/main/resources/META-INF/spring.factories,内容为:

  org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.avaloninc.web.aop.config.RequestAuditConfiguration

果然好使!

当然还是参考官方文档2来的更加详细准确。

4. 参考文献

土豆炖牛肉

  • 配料表:

    1. 牛肉一斤

    2. 西红柿 4 个

    3. 土豆 2 个

    4. 白洋葱 1 个

    5. 白胡椒粉

    6. 现磨黑胡椒

    7. 冰糖

    8. 香叶若干

  • 注意点:

    1. 牛肉飞水,加入姜片

    2. 捞干净血沫,热水清洗牛肉,保留牛肉汤

    3. 西红柿切块,洋葱切块

    4. 油锅下姜片,加入牛肉和白洋葱翻炒

    5. 加入切块的西红柿(最好事先去皮)

    6. 加入牛肉高汤没过食材,放入适量冰糖

    7. 大火煮开,撒入少量白胡椒粉、现磨黑胡椒、香叶若干片

    8. 加入土豆小火炖煮

    9. 起锅前洒入适量食盐再稍加闷煮

    10. 大火收汁

  • 经验总结:

    1. 牛肉最好选用有嚼劲的部位

    2. 炖肉的过程中要时不时搅拌一下,小心收汁过头锅底糊了

  • 参考资料:

    1. 土豆炖牛肉

Java 8 DateTimeFormatter 踩坑

1. 问题描述

本月是 2019 年的第一个月,除了迎接新年之外同样也迎来了跨年带来的关于时间参数的 bug。

废话不多说,进入正题。之前写了一个任务调度系统,支持以周为粒度进行任务的调度。针对任务的每次运行都会产生一个批次号,一般以 任务名_<年份><周数> 的格式编号(比如 job_201850 ),但是在 2018 年 12 月 31 日却发现了自然周的序号发生了跳跃。

2. 分析出问题的代码

查看了一下代码,问题出在 DateTime 格式化上,原来的 DateTimeFormatter 的定义如下:

  DateTimeFormatter.ofPattern("yyyyww")

预想的是 yyyy 代表年,ww 代表自然周,对于 2018 年 12 月 31 日原本预计得到的结果是 201853,但是此处 ww 得到的结果竟然是 01。

百思不得其解之下上网查了一下,发现原来错的不是 ww,而是 yyyy

一般来说 yyyy 是和自然年的月份、日期搭配的,对于和自然周的搭配是要用 YYYY 的。代码如下:

  @Test
  public void testForWeek() {
      DateTimeFormatter rightFormatter = DateTimeFormatter.ofPattern("YYYYww");
      DateTimeFormatter wrongFormatter = DateTimeFormatter.ofPattern("yyyyww");
  
      LocalDateTime now = LocalDateTime.of(2019, 1, 7, 0, 30, 0);
      System.out.println(now);
      String right = rightFormatter.format(now);
      String wrong = wrongFormatter.format(now);
      System.out.println("right = " + right);
      System.out.println("wrong = " + wrong);
  
      now = LocalDateTime.of(2018, 12, 31, 0, 30, 0);
      System.out.println(now);
      right = rightFormatter.format(now);
      wrong = wrongFormatter.format(now);
      System.out.println("right = " + right);
      System.out.println("wrong = " + wrong);
  }

输出结果:

  2019-01-07T00:30
  right = 201902
  wrong = 201902
  2018-12-31T00:30
  right = 201901
  wrong = 201801

也就是说 2018 年 12 月 31 日实际上算是 2019 年的第一周,这也算刷新了我的认知!

虎皮尖椒酿肉

  • 配料表:

    1. 尖椒 500 g(大约四个比较大的尖椒)

    2. 绞肉(七分瘦,三分肥)350 g

    3. 姜片 2 片 切末

    4. 生抽 15 mL

    5. 老抽 7 mL

    6. 鸡蛋 1 个

    7. 马铃薯淀粉 10 g

    8. 芝麻油 5 mL

    9. 葱花 一小把(缺失)

    10. 蒜 2 瓣 切碎

    11. 生抽 10 mL

    12. 醋 20 mL

    13. 糖 10 g

    14. 水 50 mL

    15. 盐 2 g

    16. 芡汁(5 g 水 + 5 g 淀粉)

  • 注意点:

    1. 材料 2~ 9 搅拌均匀拌成肉馅。

    2. 尖椒切段去籽填充肉馅。

    3. 小火将尖椒一面煎至虎皮状,翻面盖上锅盖继续煎至虎皮状。

    4. 放入蒜蓉炒香,加入 11 ~ 14,盖上锅盖中火煮开大约再煮 5 分钟。

    5. 加入芡汁收汁。

  • 经验总结:

    1. 肉馅放入的酱油量基本可以,有足够的咸味。

    2. 调味汁还是有淀粉的腥味,这个要想想办法。

    3. 调味汁有醋的味道不是很喜欢,可以尝试一下其他配方。

  • 参考资料:

    1. 虎皮尖椒酿肉

卤金钱腱

  • 配料表

    1. 7 fresh 金钱腱一个

    2. 台湾信牌卤料包一个

    3. 红茶包 1 个

    4. 黄色多晶冰糖 50 g

    5. 生抽 100 mL

    6. 老抽 50 mL

    7. 葱姜

    8. 料酒

  • 注意点

    1. 金钱腱抹上盐,放入冰箱腌制 1~2 天。

    2. 金钱腱、葱姜、料酒加入冷水飞水,大约一个小时,中间不断捞出血沫做牛肉高汤。

    3. 捞出金钱腱用沸水再次冲洗。

    4. 在铸铁锅(22cm)中放入金钱腱,加入牛肉高汤,如果水不够一次加足开水。

    5. 加入冰糖、生抽、老抽、卤料表和茶包。

    6. 大火烧开,小火炖两个小时。

    7. 将金钱腱放入密封袋,冷藏一晚。

  • 经验总结

    1. 注意红茶的茶叶包一定要结实的否则卤水只能用一次

  • 参考资料

    1. 五香卤牛肉简易做法,附一般卤水保养方法

    2. 真羡慕你们,年纪轻轻就养了一锅老卤

啤酒炖鸭

  • 配料表:

    1. 半片鸭

    2. 鸭腿 2 只

    3. 青椒 2 只

    4. 八角 1 个

    5. 桂皮 1 块

    6. 大葱两段

    7. 生姜 8 片

    8. 小米椒 5 个

    9. 生抽

    10. 老抽

    11. 青岛啤酒 500 mL 一罐

    12. 白胡椒

  • 注意点:

    1. 鸭子切块洗净。

    2. 鸭肉冷水入锅加入料酒葱姜飞水。

    3. 炒锅中加入少量油,加入葱姜、香料。

    4. 加入鸭肉上糖色并炒至鸭肉出油,加入生抽、老抽和糖。

    5. 将炒锅中的鸭肉转移至铸铁锅内,加入葱姜和啤酒炖煮,先大火煮开然后小火慢炖。

    6. 炖煮 30 分钟以上,加入青椒和小米椒。

    7. 出国前撒入适量白胡椒。

  • 经验总结:

    1. 鸭肉尽量还是选用冰鲜鸭肉,冷冻的解冻比较花时间,而且味道无法保证。

    2. 香料不能多加,加了一片桂皮和大约 6 个八角结果香料味道太重盖住了鸭肉的香味。

    3. 鸭肉入锅前没有先炒制香料和葱姜蒜。

    4. 生抽和老抽没有在炒鸭肉的时候加,在炖煮的时候再加晚了。

  • 参考资料:

    啤酒烧鸭

泡椒凤爪

  • 配料表:

    1. 凤爪 400g

    2. 大葱两段

    3. 白酒若干

    4. 八角两颗

    5. 桂皮两片

    6. 柠檬两片

    7. 干辣椒 15g

    8. 花椒 10g + 若干散装

    9. 洋葱 100g

    10. 胡萝卜半根 切条状

    11. 尖椒一根切环

    12. 小米辣若干

    13. 白醋 50mL

    14. 冰糖 30g

    15. 盐 30g

    16. 姜片 50g + 飞水用若干

    17. 大蒜 50g

    18. 野山椒 180g

  • 注意点:

    1. 鸡爪洗净,剪掉指甲。

    2. 鸡爪冷水入锅加料酒、葱姜、一小把花椒。

    3. 大火煮沸,然后保持沸腾状态 15 分钟,期间不断捞除血沫。

    4. 捞出鸡爪,倒入白酒搅拌。搅拌均匀并且鸡爪开始降温后再用冷开水或者矿泉水(最好是冰水)清洗一遍。

    5. 依次放入野山椒 180g、大蒜 50g、姜片 50g、冰糖 30g、盐 30g、花椒 10g、白醋 50 mL、干辣椒 15g、八角、桂皮、鸡爪、胡萝卜、洋葱、尖椒、小米辣、、柠檬。

    6. 注入适量的矿泉水淹没鸡爪。

    7. 腌制 24 小时。

  • 经验总结:

    1. 使用白酒的目的有三点:(1)可以洗去鸡爪表面剩余的油腻;(2)有助于降温;(3)为鸡爪增加一些白酒的风味。使用白酒和清水清洗两次可以使得鸡爪的异味清除的更加彻底。

    2. 鸡爪不宜放的太多,鸡爪摆置后空隙较大,如果鸡爪太多需要注入大量水,反而会稀释泡椒的味道。

  • 使用器具:

    1. 2000 mL 密封瓶

  • 参考资料:

    1. 如何制作泡椒凤爪——灰子美食实验室

    2. 怎么做泡椒凤爪

耙牛肉

  • 配料表:

    1. 牛腹肉 500g

    2. 牛蹄筋 500g

    3. 火锅底料 250g

    4. 梅花花椒若干

    5. 八角

    6. 桂皮

    7. 干辣椒

    8. 生抽、蚝油

    9. 葱姜蒜若干

  • 注意点:

    1. 牛腹肉和牛筋切大块,在水中浸泡泡出血水。

    2. 加入葱姜和料酒飞水,捞出血沫。

    3. 飞水完成捞出葱姜和牛肉,尽量保持肉汤清澈作为牛肉高汤。

    4. 牛肉捞出后温水洗净,沥干水分。

    5. 起油锅放入八角、桂皮、花椒、大蒜、姜片爆香,放入火锅底料炒出红油。

    6. 放入牛肉进行翻炒均匀上色。

    7. 将高汤移到珐琅锅内,将炒锅中的肉放入珐琅锅。

    8. 加入几片生姜、两段大葱、适量生抽和蚝油炖煮 2 小时。

  • 经验总结:

    1. 锅底太辣的话放的量需要斟酌,如果牛油太多的话可以放凉冷却以后去掉表面的牛油和辣油。

    2. 火锅锅底的香料如果太浓烈会掩盖牛肉本身的原味,如何平衡锅底的香料和牛肉的原味是个问题。

  • 参考资料:

    耙牛肉火锅

虫草枸杞乌鸡汤

  • 配料表

    1. 湘佳 丝乌鸡(900g)一只

    2. 红枣 10 颗

    3. 枸杞一小把

    4. 虫草花 75g

    5. 葱姜若干

  • 注意点:

    1. 鸡肉斩件。

    2. 鸡肉飞水可以温水下锅,加入姜蒜和料酒。

    3. 尽量捞干净血沫,飞水后捞出洗净。

    4. 将鸡肉、姜片(3片)和泡好的红枣、枸杞、虫草一起下炖锅,炖锅里注入适量冷水(冷水慢慢升温炖出鸡肉内部的风味,中途不可加水)。

    5. 大火煮开后加入适量冰糖,小火炖煮两个小时。

    6. 起锅之前加入适量的盐调味。

  • 经验总结:

    1. 汤锅里的水不宜太满,否则沸腾之后容易溢出。

    2. 冰糖不宜加太多,冰糖可以赋予汤头更深的层次感,但是太多了会偏甜。

    3. 枸杞、红枣和虫草会赋予鸡汤琥珀色的颜色,如果鸡汤可以过一遍滤筛的话效果会更好。

  • 参考资料

    香菇鸡汤