Decorative image frame

Mean Space

平均数空间

Mean Space

科技之巅一&&二书摘

总的来说,这两本书还是比较鸡肋的,还好是趁打折,总价应该不到80拿下,不然一本近100的价格实在是不划算。算是列举了从2012年起,由《麻省理工科技评论》评出的年度10大黑科技,但也仅仅是比较粗浅地列举,内容深度可能还没有“科学声音”的好。

一个有趣的点是,有些技术在这五六年间被多次提到,如CRISPR;但另一些,在今天已经被证明是一个笑话了,如MagicLeap。再想到之前听吴晓波2015年的节目,其中有提到他对当时房价的预测,在今天看妥妥被打脸。时间过了,再回头看看之前的豪言壮语,倒也是可以找点乐子的,当然对于个人成功来说,也并没有什么用。另外,很令我不爽的是,每篇后面,还会有个专家点评,有些是院士什么的也就算了,但一直有个阿里的人要来说一通有的没的,也不是什么高阶的、科学圈的,让我觉得是白瞎花钱买了这几页。

以下是2016年到2012年的部分:

  1. 当今科学界,比较认同的第一个科学家是希腊的泰勒斯,前625-前545;用三角测量海上船只的距离,使用了科学的思维,全部采用更改思考的方式,而不是涉及神或者超自然的东西

  2. 世界科学的发展也不是一路前进的,在476年,罗马帝国时期已经发明了水泥技术;但罗马帝国灭亡后,长达1200年间,欧洲人只能依赖落后的沙土黏合材料,直到1568年水泥被再次发明

  3. 全球化对人们生活的影响早在中世纪已经出现,由中国传入欧洲的胸带挽具,使得马可以取代牛,进行重犁深耕;同时期,欧洲对农业的改进也从二田轮作改进为三田轮作。(这其实也是本质推动世界发展的,但在之前不大被提到的农业技术)

  4. 牛顿确实是个奇葩,有大量的科学成果,如:微积分,色彩理论,运动定律,万有引力等,但又不喜欢撰文公开;在其友劝说下,才硬是整理了一本《自然哲学的数学原理》;(以上几条其实都是序中,科学史的东西,加上之前卓老板那儿听的,这科学圈一点不比江湖少事儿啊)

  5. 现在的免疫学研究,终于已经推进到,开始研究了解肿瘤细胞和免疫系统对话的本质,并逐步绘制出控制免疫系统与肿瘤互动方式的分子网络,已经到分子层面了,Orz。

  6. 热闹的生物公司,基因编辑这一块不是很熟,但是现实表明,测序一般一次会产生上百GB的数据,如何快速处理这些数据,也已经是一门大问题了。有一家波士顿的公司,AbVitro,其立身之本就是将原来7个月的测序时间缩短到7天,主要就是依靠了新的计算方法。(跨界融合,如果无法真正革新)

  7. CRISPR全称为Clustered regularly interspaced short palindromic repeats,是一套完整的基因编辑系统,是细菌在和病毒斗争中演化出来的免疫武器,本质还是来源于大自然,所以这一成果也可以说是发现;与一般的基因工程不同的是,CRISPR不会引入外源基因,这在舆论宣传上比较站得稳;这个在2014年和2016年两次被提到。

  8. Slack进了2016年的名单,这倒是比较意外,功能不多说了,要不是有墙,早就全用上了;其背后也找了点理论支撑,由于其信息形式更像Gossip,会让你感觉周围发生的一切,“环境知觉”,这已经被证明是有一些商业价值的;当然,Slack也由于信息过多,也会影响正常的工作;(个人觉得,有一个Gitlab机器人,次次来通知你代码更新,确实是有一点环境知觉的feel,在偶然catch到一个点时,也能切入判断对其它系统的影响,也能感受整个系统的繁忙程度)

  9. Sroek是全球最大的海水淡化工厂,超大规模海水淡化技术;主要技术为蒸馏和反渗透,成本为能量消耗,两种方式都不低,一般在3~10kWh/m3,Sroek还是传统的RO技术,不过利益于工艺和材料上的改进,成本达到了新低,58美分/m3

  10. 光合作用不止一种!!!常见的是C3形光合作用,在1960年左右,澳大利亚科学家使用C18同伴素,发现了玉米、甘蔗中还有另一种C4光合作用;C4中,CO2先被固定,再进行光合作用,被固定于叶肉细胞的花环状结构中,这样的存储结构无形中增加了CO2的浓度,提高了光合作用的水平;主要在热带植物中,原因是,过热天气时,植物也要关闭气孔,就无法呼吸CO2了,所以进化出了先固化,再按需使用的结构。

  11. Quip一家没怎么听过的,Salesforce下的公司,做在线协作的,关注下,不过在国内确实不热,反正被墙。

  12. 早在2014年,农用无人机,就已经被提出了,主要当然是用于如咖啡、葡萄这类高产值经济作物;能够提供不同的作物观察视角:一是,从空中观察作物形态,如灌溉问题、土壤变化、真菌感染等,二是,可以进行多谱段拍摄,同时从红外和可见光中捕捉信息,三是,可以定期巡航,自动比对;(可以上一个农业AI的思路了,不过大概真的只能处理这类高附加值的经济作物,常规的农产品,成本算不来啊)

  13. 2013年,已经在提DL了,当时,游离在圈外确实有点没feel了;这个点,也大概是这10年的开端了;不过对于大多数人来说,也还是在2015年9月tf发布后,才陆续有了这些高级的学习接口,才能上手实验的。

  14. 之前一直无法使用直流高压输电,主要原因是,断路器,由于基础原理的不同,直流电网的断路器在2013年前一直没法做好,ABB在2013年搞出的,混合直流断路器,可以将开头频率做到了200kHz,解决了直流输电断路器灭弧因难的问题,才让真正的大规模直流电网成为可能。另外直流输电好处多,制造低,损耗小,稳定。(不过从发展来看,这样精巧的设计,可能在一些历史变故下会导致整个系统无法使用,感觉是一个delicated系统)

  15. 2012年的时候,有人搞了一个Faster Fourier Transform,可以在原来FFT的基础上,再提速10到100倍,基础的想法是,在处理输入信号时,先选择性地过滤了一部分输入信号,实际上是滤波了,信息熵减少,要处理的信息量减少,这不仅在传统信号处理中,在图像中也可以考虑适用这类思路;当然,比较不好的是,由于这一方法有一定的信号损失和对旧有系统的不兼容性,不过总的来说还是一个直观且有效的思路。

以下是2017年的部分:

  1. AlphaGo的DeepMind将蒙特卡洛树搜索与两个尝试神经网络–价值网络和策略网络结合,通过人类职业棋手的比赛数据对网络进行了监督学习训练。通俗来说,先让其学会评价棋路优劣,再通过不断与自己对弈强化学习;强化学习是众多学习方法中可能最为特殊的一个,其本质是需要通过对“环境”的交互,来逐步学习进化。同环境的交互过程,就类同与人类成长,人类成长也是一种无监督强化学习;Google使用DeepMind做了一个我很喜欢的事情,使用机器学习帮助其数据中心冷却账单下降40%,这是AI真正与世界的交互,而不是下棋之类的玩玩游戏;(今后,全面无人车时代,道路行驶优化也就靠这个了)

  2. 直接走不通,就换一个方式,传统的太阳能电池面临的一个大问题是,能量转化率不高,主要是由于单个类型的光伏电池,只能吸收特定波长的光;一个变通的思路,通过半透明,叠加多层,来增加吸收率;另一个思路是,将光吸收成热(使用类黑体),再通过中间介质,输出特定波长的辐射,这样吸收端就可以单独针对一个波长的光吸收做优化了。

  3. 2013年Stanford的人,在《自然》上发表了Clarity技术,一种可以使小鼠组织透明化的的技术;通过Clarity,人们能够在器官中定位目标细胞的三维位置;Clarity的诞生使人们走进了器官图像分析的新纪元,从2D到了3D,最终给人体细胞图谱的构建起了很大的帮助;(所以无论在哪个方面,科学技术是第一生产力)

  4. Self-driving Truck可能会比家用车更早形成市场;好处太多,并且都是直接影响经济效益的,如:多车形成车列,风阻减少,不分时段,没有疲劳驾驶;otto是其中一家比较大的公司;矿业方面,英国的RioTinto已经在澳大利亚的矿场部署了73辆日本小松产的自动驾驶货车,可以24小时运行

  5. 听起来很好的量子计算,在有现成可用的计算机之前,已经有科学家研发相关的量子算法了;(其实之前就有个疑惑,量子计算机,要用好,肯定是无法使用旧有的算法的,旧有的高效算法,可能正是量子计算不擅长的;之后一长段时间内,应当是传统计算机+量子计算核心的方式);Shor算法,用于有效分解大数因子,Grover算法,量子搜寻算法;量子计算机,另一个大挑战是,如何读出计算后的结果,这可能比计算过程还要难一点,暂时有离子阱、量子点之类的实现方法,但显然还没有成为定式。

Modern PHP简记

  1. PHP闭包对象可以使用$this,其默认实现仅有一个invoke和bindTo(),但通过bindTo()可以改变绑定闭包的上下文,如:`$routeCallback->bindTo($this, __CLASS)`

  2. PHP从5.5.0开始,就内置了OPCache功能,在生产环境中,应当设置opcache.validate_timestamps=0

  3. PSR标准:单独开发的框架没有考虑到和其他框架的通信,这样的开发效率很低,无论是对开发者还是框架本身,所以搞了几个PSR(PHP Standards Recommendation)

    • PSR-1,基本的代码风格

    • PSR-2,严格的代码风格,laravel并没有使用PSR-2

    • PSR-3,日志记录器接口,每个日志方法的接口,第一个参数$message应当是一个(等效)字符串,第二个参数应当是一个$context(kv array)用于替换第一个$message里面的点位标记

    • PSR-4,自动加载

  4. 为什么composer?或者说,为什么那么多包管理工具,感觉这个和当前盛行的微服务也很象,社区本身不再产出大而全的框架,反而是通过几个不同的组件、模块沉淀出一套标准,再套用这一标准实现了一大堆有共同沟通基础的库,在代码、库的组织管理层面,微框架化应该是已经完成的趋势

  5. 有个Aura的php框架之前没怎么听闻过,看了下,还是比较老的思想Page->index这样的,不深入了,有laravel和swoole的坑应该是够了

  6. composer仓库可以私有建一个,对应的composer登录凭据也会保存在auth.json中,这个和之前的gradle和maven的配置有点像

  7. PHP作为一个给网络用的框架,确实也有不少这方面的记计,被用的比较少的一块,但确是PHP网站中相当重要的一部分,参数过滤,filter_xxxx这类函数,很多validation都可以这么搞,laravel的validation底层估计也是用的这个吧

    • 更多的HTML过滤,还有个库,HTML Purifier,可以防止XSS、注入

    • 同样,对于数据库,最好用PDO预处理语句,也可以防止注入

  8. 密码最佳实践,尽量用新的password_hash,并用password_need_rehash来定期更新hash值

  9. 对于Date相关的,也已经引入了不少新的类,如DateTime,DatePeriod,DateInterval之类的,用于计算周期可选时间列表还是比较表意的

  10. PDO的fetchColumn可以只拿一列,和Eloquent::list有点像,但laravel的底层好像是自己用内存做的,并不会优化到list,确认了下,为了统一化sql的生成,全部是通过抽象类生成sql后,直接pdo查询的,全文搜索了下,也确实没有用到相关的函数

  11. PHP的错误报告有时候设置会比较混乱,建议是生产环境全部off并log errors,并报告E_ALL & ~E_NOTICE,这块感觉还是PHP7处理的比较好了,不可fatal的都可以catch了

  12. Apache运行的模式:安装apache的mod_php模块,apache web服务器会给每个PHP请求派生一个专门的子进程,并在子进程中嵌入专门的PHP 解释器,即使是静态资源的访问也会有同样的处理,所以资源消耗大。

    • nginx+php-fpm方式,就用nginx代理掉了很大部分的静态资源请求,可以有效降低资源消耗

    • PHP-FPM(PHP FastCGI Process Manage),是用来管理php进程的,主要管理要留存多少个进程,及接受请求数,时长等行为

  13. 可以使用PHP Iniscan工具来检查php.ini的文件配置是否安全

  14. PHP配置中有一项realpath cache用于缓存path,默认是16k,真实值需要测定,可以先设置一个大的,再在最后打一个realpath_cache_size()来看看,用了多少

  15. 作者在写的时候,还流行用Capistrano之类的发布管理,用于维持版本、回滚,唉,变化快啊,现在已经全面docker了

  16. 关于调试,可以使用xdebug生成CacheGrind的文件,使用WinCacheGrind,KCacheGrind,WebGrind等都可以来看这个结果

    • 在生产环境的话,最好是不要开xdebug,可以用xhprof,这个也是facebook搞的,也算是APM的一种吧

    • 当然现在也有很多侵入式的在线分析的:New Relic,Blackfire,可惜价格挺贵

  17. 提升PHP性能上,除了HHVM还有Hack(可惜没能打开官网),不过感觉上将被PHP7取代

    • 尤其是Hack,不少语言级别的特性,在PHP7上已经全部有体现了

微服务 Design Pattern

个人对微服务的感性理解

感觉 DP 的出现、兴盛还是在单体应用时代,为了更好地解耦模块。那么到了微服务时代,单个服务已经实现高内聚了,服务间的解耦又通常会是一种什么样的模式?

个人认为,DP 是对于常见的一些 OOP 技法的总结和梳理,粗糙地说,一切 DP都可以用,类、继承、接口等方式来模拟,不存在语言级别的特性。以上一名是废话,明明这个过程应该是倒过来的。

想当然的,在微服务模式下,是否在微服务中,也会被抽象出一些通行的 Pattern?而这一抽象的主体思想,也是解耦,那 DP 和微服务的常见模式之间是否存在可类比性?

  1. 网关 vs Facade
    • DP 中 Facade,将一堆操作,封装到 Facade 中,对外暴露一个较为简单的接口
  2. 代理 vs Proxy
  3. Adapter
    • DP 中 Adapter 用户协调多继承导致的接口不一致性,主要用于解决接口一致性问题
  4. Decorator vs sidebar 的 service-mesh
  5. Single vs 注册中心、服务发现
  6. 策略 vs 注册中心、服务发现
Read More...

锁事一二

文章内容主要来源是一些公众号文章

一些分类方式

这部分的分类主要来源于一个关于面试问题的分类,so,确实很面试

乐观和悲观

乐观和悲观并不是一种锁的状态 or 行为,而是一个并发编程的 Pattern。早期是在 DB 上引入的说法,现在在 java.util.concurrent 包中也引入了。

DB 中常见的做法,对于悲观来说就是无脑上锁;对于乐观来说,随便读(除了值,再加个版本号),写时对比版本号,如果无变化,直接写入,如果变化了,失败重试。
* 提一句,正好看了阿里最近的 Tair 的文章,里面关于 RCU无锁设计,其实也是个乐观锁
* 再跟一句,是否乐观一定好?不一定,如果在冲突严重的情况,不断要退回重试,代价也不低

Read More...

一个概率问题的贝叶斯例子

问题:

如果拿一枚硬币,抛五次,如果都是正面,为什么你会感觉这个硬币有问题

问题解析:

对于独立事件来说,出现 PPPPP 和 PNPNP 和 PNNNP 的概念是一样的,都是 1/32,那为什么我们会觉得这个 case 下硬币有问题,而 PNNPN 下不会觉得有什么问题?

问题转化:

如果从一袋硬币中取一枚,抛出了五次连续正面,认为硬币是问题币的概率较大

符号定义:

  • UC = UnFair Coin
  • P = Positive
  • N = Negtive

符号化问题:

已知 $P(P|UC) > P(N|UC) $, 需要证明:$$ P(UC | PPPPP) > P(UC | PNNPN) $$

  1. 用正常贝叶斯来看看
    先做贝叶斯展开,需要证明:$ \dfrac{P(PPPPP | UC)*P(UC)}{P(PPPPP)} > \dfrac{P(PNNPN | UC) *P(UC)}{P(PNNPN)} $
    单纯考虑一枚硬币的概率,$ P(PPPPP) = P(PNNPN) $
    所以只需要证明:$ P(PPPPP | UC) > P(PNNPN | UC) $
    这个比较显然易得

  2. 再用朴素贝叶斯来看看:
    还是做贝叶斯展开:$ \dfrac{P(P,P,P,P,P|UC)P(UC)}{P(P,P,P,P,P)} $
    将五次当成独立事件来考查,也是易得 $ P(P,P,P,P,P|UC) > P(P,N,N,P,N|UC) $
    $$ \Leftrightarrow P(P|UC)
    P(P|UC)P(P|UC) > P(N|UC)P(N|UC)*P(N|UC) $$
    $$ \Leftrightarrow P(P|UC) > P(N|UC) $$

TCP BBR 是个什么玩意儿

一、BBR是个什么东西:

TCP BBR(Bottleneck Bandwidth and Round-trip propagation time)是由Google设计,于2016年发布的拥塞算法。以往大部分拥塞算法是基于丢包来作为降低传输速率的信号,而BBR则基于模型主动探测。该算法使用网络最近出站数据分组当时的最大带宽和往返时间来创建网络的显式模型。数据包传输的每个累积或选择性确认用于生成记录在数据包传输过程和确认返回期间的时间内所传送数据量的采样率。

Google在YouTube上应用该算法,将全球平均的YouTube网络吞吐量提高了4%,在一些国家超过了14%。根据实地测试,在部署了最新版内核并开启了 TCP BBR 的机器上,网速甚至可以提升好几个数量级。

官方 Link:https://cloud.google.com/blog/products/gcp/tcp-bbr-congestion-control-comes-to-gcp-your-internet-just-got-faster

设计思想:

主要是针对大带宽,浅 buffer(我的理解是带宽大了深不起来)的现代互联网场景设计

  • 理解提升:

    之前的基于 loss 的 CC 也是合理的,处理了当时 99%的情况;而今,基础不同后,新的 99%情况也不同,所以需要另外一种方法来处理;可能之后,99%的情况又不同了,还会有新的方法的;所以持续地统计、观察这个拥塞情况,才能做高层设计

  • 理解提升:

    之前的基于 loss 的 CC 也是合理的,处理了当时 99%的情况;而今,基础不同后,新的 99%情况也不同,所以需要另外一种方法来处理;可能之后,99%的情况又不同了,还会有新的方法的;所以持续地统计、观察这个拥塞情况,才能做高层设计

以下原文

What is BBR?

BBR (“Bottleneck Bandwidth and Round-trip propagation time”) is a new congestion control algorithm developed at Google. Congestion control algorithms — running inside every computer, phone or tablet connected to a network — that decide how fast to send data.
How does a congestion control algorithm make this decision? The internet has largely used loss-based congestion control since the late 1980s, relying only on indications of lost packets as the signal to slow down. This worked well for many years, because internet switches’ and routers’ small buffers were well-matched to the low bandwidth of internet links. As a result, buffers tended to fill up and drop excess packets right at the moment when senders had really begun sending data too fast.

But loss-based congestion control is problematic in today’s diverse networks:

In shallow buffers, packet loss happens before congestion. With today’s high-speed, long-haul links that use commodity switches with shallow buffers, loss-based congestion control can result in abysmal throughput because it overreacts, halving the sending rate upon packet loss, even if the packet loss comes from transient traffic bursts (this kind of packet loss can be quite frequent even when the link is mostly idle).
In deep buffers, congestion happens before packet loss. At the edge of today’s internet, loss-based congestion control causes the infamous “bufferbloat” problem, by repeatedly filling the deep buffers in many last-mile links and causing seconds of needless queuing delay.

Mybatis

使用

直接引入就可以用了,starter,约定大于“一切”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.28</version>
<scope>runtime</scope>
</dependency>

配置相关

  1. 常规的几个配置项是给 Datasource 的:
    1
    spring.datasource.url=jdbc:mysql:///test01?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

    一般看到要声明 driver-class-name 的,应该是用了 jdbc,druid 不用

  2. 可以在 properties 配置中指定相关 mybatis 的配置
    1
    2
    3
    mybatis.config-location=classpath:mybatis-config.xml
    mybatis.mapper-locations=classpath:mapper/*.xml
    mybatis.type-aliases-package=space.mean.learn.dataobject

    在声明mybatis.type-aliases-package后,resultType 这类可以不用写全类 Referrence
    几个 xml 在写的时候要当心<!DOCTYPE 这一行不可省,反正少了也启动不了

  3. 以上配置也可以自行写@Configuration实现,但比较繁琐,常规来说 mybatis 的 config 还是会写成 xml

自动配置的实现

在 SSM 整合中,开发者需要自己提供两个 Bean,一个SqlSessionFactoryBean ,还有一个是 MapperScannerConfigurer,在 Spring Boot 中,这两个东西虽然不用开发者自己提供了,但是并不意味着这两个 Bean 不需要了,在 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 类中,我们可以看到 Spring Boot 提供了这两个 Bean

注意配置 @MapperScan

MyBatis-Plus

为了简化开发而生,提供了常用的查询包装及基本的 CRUD 操作,不用生成 xml 了,同时也支持扩展 xml

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.3.1</version>
</dependency>

但分页接口风格不同于 PageHelper,使用上要注意

JDBC

  1. 当 JDBC 类型是 Date 且数据为’0000-00-00’的错误数据时,DO 中对应字段如果设置为 Date,会有异常,Zero Date value prohibited
  2. 简单处理
    • 把 DO 中的字段类型设置为 String
    • 在 JDBC 的 connection String 中加一个参数:zeroDateTimeBehavior=convertToNull,就会把全 0 的当成 null 处理
  3. 2B 处理
    • 在 Mybatis 的结果集映射的 Result 中,设置一 property,typeHandler,自己去继承下对应的 TypeHandler(此处为 DateOnlyTypeHandler),override 下 getNullableResult
  4. 上述过程的机制
    1. 默认行为中,会使用 com.mysql.cj.jdbc.io.JdbcDateValueFactory::createFromXXXX 来创建对应 DO 的 Date,此时全 0 的时间,会抛个 DataReadException
    2. 当使用 zeroDateTimeBehavior=convertToNull 时,在 com.mysql.cj.jdbc.result.ResultSetImpl::ResultSetImpl 构造时,读取这一参数,并使用 decorate factory模式,按不同behavior 生成不同的 valueFactory;此处,如果是 convertToNull 的话,会生成 ZeroDateTimeToNullValueFactory,该类的 createFromXXX 在碰到全 0 时,不会抛错,直接return null
    3. Mybatis 中,会使用 TypeHandler 处理返回值,也可以方便自己扩展,当然底下是调用了 JDBC;是在 getPropertyMappingValue 中间接调用到的
    4. 上述中 TypeHandler 在处理,类似 terra_entry.attributes的反序列化场景时,还比较有用

Spring

Spring

Spring MVC

  • 关于 Interceptor 和 Filter,引用自StackOverflow:
    fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
    所以来说,做验证码校验的逻辑,应该是用 Interceptor 更为合适。But 由于 Filter 在 Interceptor 之前执行(优先级高,更底层),用 Interceptor 无法拦截到非法登录请求,还是要用 Filter
    • Filters are called by your Server(tomcat). while Interceptors are called by Spring.
    • filter is always executed BEFORE the preHandle interceptor method.
      • interceptors are part of Spring MVC which begins at DispatcherServlet. Filters are called before servlets.

upload successful

  • 最后用了 httpSecurity 的 addFilterBefore 在用户名密码验证前加了个 GeetestFilter

Spring Boot

环境变量获取的 hack 逻辑(SpringBoot 2 中变化了很多,但还是支持的)

  • 在 properties文件中一般是 a.b.c,但在环境变量中,A_B_C 也是可以被 DataBinder 到的,代码见:RelaxedDataBinder::getPropertyValuesForNamePrefix,会处理_ 和 .的分隔符
    • 大小写变换的逻辑,在这个类:RelaxedNames

运行时配置

  1. 准备多个 application-dev.properties, application-prod.properties,可以用 spring.profiles.active=xxx 来激活
  2. 部署时,根据优先级,在 java -jar -Dspring.profiles.active=prod 来激活
  3. 有 Apollo,Spring Cloud Bus,应该是用不到这个了

@RestController 对于 response 的 POJO 会自动用 Jackson 搞成json,这时时间类型的序列化会是个问题

  • Java8 之前,Date 类型,直接在注解上 @JsonFormat(pattern = 'yyyy-MM-dd')
  • Java8 之后,LocalDate 相关类型,只上面不够,需要引入新的依赖
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.8.8</version>
    </dependency>
  • 实现机制:
    1. 对于不指定的 POJO 返回,会默认调用到 Jackson 的 ObjectWriter
    2. 会 resolve 到相应的 ValueSerializer
    3. 第一次访问到这个时,BeanSerializerBase这个会构造(Lazy 的)
    4. 里面的 BeanPropertyWriter 里面有个_member 就是对应的类反射数据
    5. 可以根据_member 上的 Annotation 来构造相应的 Formatter

PropertySources 相关

  • 在 populate Bean 时,对于@Value的 field,会尝试各种 resolver,到最后,会落到一堆 propertySouces 上,Genrally,越后被定义到的@PropertySources,会越先win
  • 一个小问题,使用 app.setDefaultProperties 得到的 propertySource 是最低(最先定义)Priority 的 PropertySource,会被从 config-bus 那边的 CompositePropertySource 覆盖掉
  • 转而设置 property 在 env 中,会用到 EnvironmentPropertySource 就可以了

如何优雅地用 factory strategy模式

  1. 自己定义一个 annotation on runtime
  2. 给各个 handler/strategy/impl 上加这个 annotation,并指定好各自不同的type/value/sort 等
  3. 加一个 BeanFactoryPostProcessor 的实现,在beanFactory 初始化完成后,扫描指定包下含有此 annotation 的 class
  4. 提取此 annotation 中的 value,和对应的 class 的 impl做成一个 map,可以在 factory 中使用
  • 如果使用 @Autowired SomeInterface handlers[] 这样的方式,一定要给所有的 impl 弄成bean,会需要多考虑一些线程问题

Java日志

LogBack 是 Slf4j 的一个实现

  • Slf4j 中的 f 是 Facade
  • 在配置 logback.xml 时,注意完整,如果没有合适的 logger or root section,就相当于没配置
  • 新版本中已经不用 layout 了,而是用 encoder
  • 涉及到 SpringProperty 的使用,需要用 logback-spring.xml,会有 spring 加载后加载,可以使用 application.properties 中定义的变量
  • 阿里云的SLS appender倒是一用就好

日志级别的几个建议

  1. ERROR
    • 影响到程序正常运行、当前请求正常运行的异常情况
    • 如果进行了抛出异常操作,请不要记录error日志,由最终处理方进行处理
  2. WARN
    • 不应该出现但是不影响程序、当前请求正常运行的异常情况
    • 即将接近临界值的时候,例如:缓存池占用达到警告线
  3. DEBUG
    • 需要先进行 log.isDebugEnabled 判断,再记录
    • 推荐大家用 SLF4J 的门面接口,可以用参数化形式输出日志,debug 级别也不必用 if 判断,简化代码

日志应当包括内容

  • 日志时间
  • 日志级别主要使用
  • 调用链标识(可选):TraceID 和 SpanID
  • 线程名称
  • 日志记录器名称
  • 日志内容
  • 异常堆栈(不一定有)

日志框架

log4j、Logging、commons-logging、slf4j、logback,开发的同学对这几个日志相关的技术不陌生吧,为什么有这么多日志技术,它们都是什么区别和联系呢?

  1. Logging
    这是 Java 自带的日志工具类,在 JDK 1.5 开始就已经有了,在 java.util.logging 包下。通常情况下,这个基本没什么人用了
  2. commons-logging
    commons-logging 是日志的门面接口,它也是Apache 最早提供的日志门面接口,用户可以根据喜好选择不同的日志实现框架,而不必改动日志定义,这就是日志门面的好处,符合面对接口抽象编程。现在已经不太流行了(不过 SpringBoot 内好像还是用了这个)
  3. Slf4j
    slf4j,英文全称为“Simple Logging Facade for Java”,为java提供的简单日志Facade。Facade门面,更底层一点说就是接口。它允许用户以自己的喜好,在工程中通过slf4j接入不同的日志系统。
    它不负责具体的日志实现,只在编译时负责寻找合适的日志系统进行绑定。具体有哪些接口,全部都定义在slf4j-api中
  4. Log4j
    Log4j 是 Apache 的一个开源日志框架,也是市场占有率最多的一个框架
    log4j 在 2015.08.05 这一天被 Apache 宣布停止维护了,用户需要切换到 Log4j2上面去
  5. Log4J2
    Log4j2与Log4j1发生了很大的变化,log4j2不兼容log4j1。
  6. Logback
    Logback 是 Slf4j 的原生实现框架,同样也是出自 Log4j 一个人之手,但拥有比 log4j 更多的优点、特性和更做强的性能,现在基本都用来代替 log4j 成为主流

阿里云 SLS

有一个github 开源的 appender,能用,但不完全方便

  • 不需要 layout,因为不设置 encoder 时,就没有 log,按 logContent 类似 key-value 推送数据上去就 ok
  • 但需要管理好 MDC,不自己实现的话,只能从 MDC 上取extra 字段
  • 建议可以继承 slsAppender,扩展下 logContent 的字段注入
  • 或者参考并自行实现,据说依赖的 Producer 0.3 版本已经改了接口,此处依赖的 0.2已经不再维护了
  • 再或者维持当前 java–>logstash–>sls的路数,改进下 log-starter 就好,善用 logback

Spring IoC

比较散,还不能理解成一个整体

几个关键点:

beanFactory

  • 用不同的 IoC 容器(Context)可以用不同方式创建:Xml、Annotation 等
  • 不同的 Bean 类型会有不同生成方式,主要有:
    1. singleton,常用的方式
    2. prototype,每次生成一个
    3. 和 springmvc 相关的,跟request, session 等周期相关
  • Bean 的获取(无论第一次还是后续),本质上最后都是去了 doGetBean,brief 流程:
    1. 尝试从 singleton 缓存中取,是否已有实例:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)
    2. 已经有,则实例化之,并在convertIfNecessary中进行类型转换
    3. 如无singleton 缓存,且是 prototype,则创建Bean 并类型转换
    4. 根据 RootBeanDefinition 创建 Bean
    5. 以上涉及到创建时,均需要判断是否是 FactoryBean,如是 FactoryBean 还需要使用 FactoryBean 进行创建,类似SalukiReferenceBean
      • 而是否 FactoryBean 的认定是使用 BeanName 的前缀进行的,一般为&
    6. 实例化过程中,会检查 mbd 的 dependsOn,这个是Bean 创建依赖树上的依赖,而不是常说的依赖注入的依赖(确实正确的实例化顺序)
    7. 最后对于 Singleton 的创建会走到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[]),其中的 doCreateBean中的 populateBean 就是依赖注入的核心了
    8. Spring 的 AOP 就是在上述 7 的过程中,使用 InstantiationAwareBeanPostProcessor来进行处理的
    9. 最终的 BeanCreation 是出来一个 BeanWrapper,用于注入不同的属性
  • Autowired 和 Resource 区别在于,前者用AutowiredAnnotationBeanPostProcessor 处理,后者用 CommonAnnotationBeanPostProcessor 处理,区别在postProcessPropertyValues`上,两个的处理注入的函数不一样
    • 明面上区别在于,Autowired 先类型后名字(隐性);Resource 先 name,后类似 Autowired

      BeanDefinition 如何来

  • mbd 会贯穿 spring 启动的整个流程,是创建用的基础数据
  • 核心容器 ApplicationContext 启动时,会扫描所有需要的bean 并注册到容器中;启动时,会把 xml 的描述或者 Annotation 转化成 BeanDefinition
  • 一般来说一个 Spring 容器(如 ApplicationContext中),会有两个Map,一个beanName–>Bean,另一个 beanName–>BeanDefinition
  • xml 中声明的 property 注入,实为 set 注入;注意:
    • ref 是注入依赖,这个注入是一个递归过程
    • value 是属性
  • 可以用 CustomAutowireConfigurer 来添加自己的 AnnotationType 来调整执行注入过程
  • 此处org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata是进行 Annotation Autowire 的 Metadata 生成的启点
  • TODO: 看下 mbd 的分析代码

BeanPostProcessor

  • 六大知名:
  1. ConfigurationClassPostProcessor
  2. AutowiredAnnotationBeanPostProcessor
  3. CommonAnnotationBeanPostProcessor
  4. Jpa的 PersistenceAnnotationProcessor
  5. EventListenerMethodProcessor
  6. DefaultEventListenerFactory
  • 可自己继承实现 BeanPostProcessor 接口,来对 Bean 的实例化过程进行干预
    • 但如果在自己实现的 bpp 中引用了业务bean,会导致业务 bean 过早初始化,如果此业务 bean 上有类似@Async的注解,则 Async 功能会失效(原因是,Async 是通过 AsyncAnnotationBeanPostProcessor 实现的,给他创建一个代理,但此时此 async bpp 还没注册上,自然没法创建合适的代理对象)
  • 动态 Aop 使用了:AnnotationAwareAspectJAutoProxyCreator

代理

Cglib JDK 代理
改写字节码 Proxy
动态生成子类 包一个 Proxy
创建性能低,但调用性能好 反之
无法对 private 和 final 方法代理(因其为动态子类) -

其它

  • @Lazy的话,会先有 beanDefinition 信息,会注册上,但不会实例化,即不会提前调用创建方法
    • Lazy 注解会在注入时生成一个代理对象,只有在真正执行目标方法时才会去容器中拿真正的bean 实例
  • 依赖注入的几种方法的对比
    方式|优点|缺点

–|–|–
Set 注入|单例下可解决循环依赖|额外配置 set 方法,不能保证注入完整性
构造器注入|可以保证注入的完整性|需要相应的构造器,无法解决循环依赖
Field 注解|简洁,单例下可解决循环依赖|依赖关系不明显,可能会注入一个 null(可以用@Required显示说明)

  • TODO: 有没有办法选定一批 Bean 指定 Lazy 初始化
  • TODO: Bean 的循环依赖 Spring 是怎么解决的
    • 只有 Singleton 的才能解决循环依赖,Prototype 的Bean 无法解决循环依赖,因为 Prototype 下的 Bean 不是共享的,如果循环,则会不断创建新的 prototype
    • singleton 时,spring 通过提前暴露beanFactory 来解决循环依赖
      • 如果 A 中有 B,B 中有 A,当 Inject 时,A 还未创建完成时,因为实例化 A 依赖了 B 的 bean,进而需要再实例化 A,此时调用的提前暴露的单列工厂的实例化方法,实际上获得的是同一个引用
      • doCreateBean中,如果可以循环引用,则会将一个beanName–>创建闭包加入到 singletonFactory 中,以供引用
    • 个人理解,将对象的创建、初始化分离,类似 C++中的 new 和 构造