2022年3月

当Service方法被内部调用时,Spring注解会失效。就是Spring的Service类,如果public方法加上注解,类内部的其它方法使用this调用该方法,会导致注解失效。

例如Spring的Service实现类如下:

@Service("userService")
class UserServiceImpl implements UserService{
    
    @Override
    @Cacheable(CacheNames="USER_CACHE", key="#userId")
    public User getUser(String userId){
        // do something
    }

    @Override
    public String getUserName(String userId) {
        User u = this.getUser(userId); // getUser方法的@Cacheable注解失效了
        return u == null ? "" : u.getName();
    }
}

原因是this引用的对象没有被Spring代理,调用该对象的public方法时,Spring不能处理相关注解。

解决方法很简单,就是使用Spring代理过的对象,代替this。然后只需解决如果获取该Service类被Spring代理过的对象。


1 循环依赖

就是自己注入自己。在Service类定义一个自身对象的属性,让Spring装配时把自己注入到自己。虽然Spring 5(具体版本待查证)声称解决了循环依赖的问题,但是Spring Boot 2.6.0开始默认设置不允许循环依赖。循环依赖是一个古老的问题,一样认为要避免。所以此方法不推荐

1)先设置

# Spring Boot 2.6.0之后,允许循环依赖
spring.main.allow-circular-references = true

2)上面的例子改为

@Service("userService")
class UserServiceImpl implements UserService{
    
    @Autowired
    private UserService self; // 自己注入自己
    
    @Override
    @Cacheable(CacheNames="USER_CACHE", key="#userId")
    public User getUser(String userId){
        // do something
    }

    @Override
    public String getUserName(String userId) {
        User u = self.getUser(userId); // 用self代替this,注解生效
        return u == null ? "" : u.getName();
    }
}

2 获取装配后的自己

避免Bean的循环依赖,主要思路是,在Bean装配完成后,再获取被Spring代理的自己。至于怎样获取,实现方法是多种多样的。

方法1,从Bean容器中获取自己。即:

UserService self = applicationContext.getBean("userService");

至于怎么获取Bean容器applicationContext,方法也是多样的。

方法2,开启AspectJ自动代理来获取自己。

详细参考:一个Spring AOP的坑!很多人都犯过!

要注意,AspectJ自动代理不只是解决本文档的问题,需考虑是否会带来未知的问题。

开启AspectJ自动代理的方法有多种,这里列出三种:

1)在启动类添加注解:

@EnableAspectJAutoProxy(proxyTargetClass=true, exposeProxy=true)

2)Spring增加配置:

<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true" />

3)Spring Boot的配置文件增加配置:

# 开启AspectJ自动代理
spring.aop.auto=true
# 开启CGLIB代理
spring.aop.proxy-target-class=true

然后就可以在当前Service类的方法中,通过类似的代码调用自身的方法,且能保证该方法的注解正常执行:

User u = ((UserService) AopContext.currentProxy()).getUser(userId);

方法3,延迟执行自己注入自己。

很简单,就是使用@Lazy注解,达到Bean初始化不执行自己注入自己,避免循环依赖的错误。我记得解决Spring 2.x循环依赖的问题时,也是采用延迟注入的配置。此方法写的代码最少,目前倾向采用这种方法。于是,上面的代码改为:

@Service("userService")
class UserServiceImpl implements UserService{
    
    @Lazy
    @Autowired
    private UserService self; // 延迟自己注入自己
    
    @Override
    @Cacheable(CacheNames="USER_CACHE", key="#userId")
    public User getUser(String userId){
        // do something
    }

    @Override
    public String getUserName(String userId) {
        User u = self.getUser(userId); // 用self代替this,注解生效
        return u == null ? "" : u.getName();
    }
}

注意:本文涉及电工改造,需要拆解爆米花机和接电线。如果对电工操作不熟悉,或有任何疑问,请勿胡乱操作。

一 背景

为了节省喝咖啡的成本,入坑了手冲咖啡(毕竟新鲜咖啡豆比店里一壶咖啡便宜)。同样也是贪便宜,入坑了烘培咖啡豆,毕竟咖啡生豆的价格更便宜。后来发现钱能省,但成本(时间、器材、技术)不能省。

但是玩过了,收获还是满满的。就像为什么要看程序源码一样。

二 烘豆的总结

优点:

  1. 咖啡生豆的存储期限比烘好的咖啡豆要长。生豆保存得好,一年是没问题的。烘好的咖啡豆,几个星期到一个月就能感受到香味、风味的流失。
  2. 咖啡生豆更便宜。入手了云南的生豆为为烘豆练习(仅推荐作为练习不建议作为“口粮”),34rmb/kg,平摊到每杯手冲咖啡,跟滤纸成本一个档次。进口的咖啡生豆,也只是烘好的咖啡豆的1/4,或者更低。
  3. 能更多地感受一杯咖啡的整个过程。非常适合对咖啡充满喜爱和热情的人。

缺点:

  1. 麻烦。包括,需要学习和掌握技巧、需要花时间去烘豆、需要购买设备且要考虑存放的空间……
  2. 出品可能不够稳定。咖啡店都是商用机器,玩曲线(烘豆过程的温度与咖啡豆变化过程记录),出品比较稳定。
  3. 不是每个喝咖啡的人都喜欢折腾。

三 烘豆的方式

主要是热源与器具。

热源一般是明火和电热两种。明火主要选择瓦斯炉。电热一般是配合器具的加热方式,反正有电就能烘。城里人嘛,选择电热相对安全和方便。

器具有太多的选择。比如:手网(比较费手)、陶锅(新手劝退)、手摇烘豆机(一般配合明火)、热风式爆米花机(需要改装)、空气炸锅(看不见咖啡豆的变化)、带旋转烤笼和内置风扇的烤箱(需要解决银皮和排烟)、小奶锅(需要技巧)等等。还有专门的机器,一步到位解决所有问题,只是价格相对比较贵(入门级的烘豆机,1000rmb左右吧)。

油管上很多是手摇烘豆+明火,而且主要是宝岛的up主。B站则有较多改装爆米花机的up主。近期,九阳的旋转小烤箱成为改造热门。

最后,我选择的是热风式爆米花机(也是下文要介绍的),并进行简单改装。主要是B站有改装教程,改装过程也简单,成本相对较低。虽然一锅只能烘50g生豆,能满足自身需求。

四 烘豆过程的问题

  1. 加热温度要可调。
  2. 烘豆机的风力要可调。
  3. 测量烘豆机能承载的生豆量。
  4. 测量烘豆过程的温度。
  5. 烘豆过程要可以观察,以便根据豆子的颜色进行烘培参数(温度、时间等)的调整。
  6. 收集银皮,避免银皮乱飞。银皮是指残留在咖啡生豆外表的一层薄薄的皮。烘豆过程中,热风会把银皮吹出。
  7. 烘豆结束要立刻给咖啡豆降温。

五 参考视频

  1. 爆米花机本体改造,参考这个:
    爆米花机改热风烘豆机--下篇
  2. 玻璃管和银皮收集方案,参考这个视频
    《 爆米花机&咖啡豆烘焙机》改装实用思路(超长步骤版)

六 设备清单

  1. 热风式爆米花机,功率1100W。关键词参考:B301、winghang、永恒。参考价:50rmb。
    用于烘豆。
  2. 可控硅电子调压器,220V,3000W,带电源插座。参考价:25rmb。
    插上爆米花机电源,即可实现调温,不用改装。
  3. 可调直流稳压电源,3-24V,2A,送母头。参考价:15rmb。
    需要改装,连接到风扇电机,实现风力调节。
  4. 电子数显温度计,[902推拉开关]主机 + 1米线。参考价:15rmb。
    这个建议选择探针式,并且耐高温300度。爆米花机的热风,能达250摄氏度。
  5. 304不锈钢排烟管变径接头,8变7。就是直径8cm转直径7cm,需要两个。参考价:8rmb/个。
    用于套在玻璃管两头,一头插在爆米花机,另一头接上茶漏,用于收集银皮。
  6. 高硼硅玻璃管,80mm100mm5mm。即外径80mm,长度100mm,管壁厚度5mm。参考价:15rmb。
    用于观察生豆的烘焙变化。
  7. 卤料过滤网,304不锈钢,直径8cm。参考价:12rmb。
    顶部排风,并实现银皮收集。
  8. 304不锈钢卡箍,直径60~89cm,两个。参考价:1.5rmb/个。
    连接爆米花机、玻璃管和卤料过滤网。需要使用铁皮剪刀修改。
  9. 其它工具:电烙铁、焊锡、十字螺丝刀、Y型螺丝刀、铁皮剪刀等。

七 烘焙技巧

这个方案受限于爆米花机,最多能吹动60g生豆。一般称60g生豆,挑出瑕疵豆,烘完后,大概50g熟豆。

最大风速时,温度最高170°C左右。想要温度升高,需要在温控最高的基础上,降低风速,能上去250°C。

烘豆时,最好先参考别人关于相同咖啡豆的烘焙曲线。一般下豆后,从温度100°C左右,逐渐上升到200°C左右,然后一爆、一爆密集、二爆等过程。一般手冲的话,一爆到一爆完结之间是浅烘到中烘,确定选择哪个烘焙程度来决定下豆时间。整个过程控制在10分钟左右。烘焙过程,我还是新手,B站有很多大佬的视频可以参考。有疑问的话,大佬们也会回复。反正就是要多试、多交流。

烘豆结束,我直接关闭加热,直接最大风速给豆子降温。目前冬天的效果还可以,但夏天的话就不知道怎样。

另外,试过失败的案例:

1)第一次没有控制好温度,最高上去250°C,还弄得冒烟才下豆,总体时间15分钟,然后豆子都是乌黑油亮,就是烤糊了。

2)试过参考“三豆客 Q5”的 3 分钟快速烘焙,但是爆米花机的温度没那么高,所以整个过程 200°C 左右。然后为了一爆而拖到在 10 分钟下豆。虽然达到了中烘的外表,但是豆子出现点状出油,后面还出现油臭味,也算是失败。

目前烘了 4 、5 次,虽然达到能喝的程度,但是离好喝还有一段距离。

八 后续升级

B站的大佬会改造得很疯狂,但是我觉得最重要还是关注烘焙过程。如果要升级,实现更大的烘焙量,可以考虑换个爆米花机。例如:北欧欧慕NBM001,采用底部直吹的方式,发热功率是1400W,据说可以吹起100g生豆。但是相关的两个调压器和风扇调压改造都得重新研究。