2016 年终总结

一、工作

离开顺丰科技

在顺丰待满了刚好两年,选择离开。很感谢顺丰科技,在那里获得一段不错的经历。

加入友金所

离开顺丰后加入了友金所的资产端团队,即友金普惠。友金普惠的贷前贷后的管理系统是从一个厂商购买的,受限很大,进去后主要做一些新系统搭建、业务逻辑重写。

刚进去不久,因为一些定时任务出问题,其实是由于 Quartz 使用不当导致,基本上把 Quartz 关于并发控制的源码都看了遍。

做了个规则引擎,这个引擎是由规则+数据组成,规则由业务人员在网页上自己配置,数据主要是对接的合作方的数据、去第三方网站搜索的数据。

为了第三方网站搜索统一起来,对搜索的流程进行了封装,只要通过配置就可以直接把搜索结果转化为特定的 POJO。为了把网页信息提取为 POJO,基于 WebMagic 做了个组件来基于 XML 配置信息完成这个提取。

其他的主要是一些业务逻辑开发、重写,比如记账、扣款的逻辑等。

二、学习

虽然 RSS 订阅、微信公众号每天看,但这些都不是系统性的、零散,收获不多。

继续阅读

发布组件到 Maven 中央仓库

折腾了几天,终于把一个小组件MyBatis-batch 发布了中央仓库,做个笔记记录下。

  1. 注册 sonatype JIRA 帐号并配置 settings.xml
    我是先在 sonatype 上发布,然后由 sonatype 自动同步到中央仓库的。首先要在 sonatype 注册一个 JIRA 帐号,

$M2_HOME/conf/settings.xmlservers 标签下添加如下配置:

<server>
    <id>sonatype-nexus</id>
    <username>sonatype 登录名</username>
    <password>sonatype 密码</password>
</server>
  1. 在 github 添加 ssh key
    Maven 构建的时候,会自动操作 github,比如创建 tag 。
    用 ssh-keygen 生成一对秘钥,在 https://github.com/settings/keys 页面可以添加 SSH key。

继续阅读

Java finalize 方法与垃圾回收

JVM 对于覆写了 Object.finalize() 方法的类是实例,在垃圾回收时,先放进一个队列里,
然后用一个线程执行这些实例的 finalize 方法,最后才做内存回收。

这个机制的问题是:它会影响垃圾回收,如果实现了 finalize 的实例很多,那么回收队列就会很长,而只有一个线程
在执行这些方法,这些对象就会回收得很慢。

最近碰到的一个问题跟 finalize 方法有关。
厂商提供的代码把 javax.sql.Connection 做了层封装,覆写了 finalize 方法,在里面把 Connection 关闭了。

一个同事调用的方法定义是用封装类作为入参,他调用的时候是直接 new 了一个封装类,把 connection 传进去,后续数据库操作就不定位置出现连接关闭的异常。原因就是这个临时 new 出来的封装类被垃圾回收,把 connection 关闭了。由于垃圾回收的时间是不确定的,所以报异常的时间点也不确定。

接口与Spring自动注入

接口与自动注入

有业务接口 IService 和两种业务逻辑的实现 Aservice, Bservice 如下:

public interface IService {}

@Component
public class Aservice implements IService {}

@Component
public class Bservice implements IService {}

采用下面的方式自动注入时,会报错:

public class ManageService {
    @Autowired
    private Aservice aservice;

    @Autowired
    private Bservice bservice;

    // ...
}

异常信息类似为:nested exception is java.lang.IllegalArgumentException: Can not set xxx.ManageService field xxx.ManageService.aservice to com.sun.proxy.$Proxy48

继续阅读

SonarQube 配置

1. 安装 SonarQube 和数据库

下载最新的 sonarqube-6.1、MySQL 5.7.X。

创建数据库:

CREATE DATABASE `SonarQube` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

CREATE USER SonarQube IDENTIFIED BY 'SonarQube#123';

GRANT ALL PRIVILEGES ON SonarQube.* TO SonarQube;
flush privileges;

配置 sonarqube 服务器, $sonarqube_home/conf/sonar.properties 文件:

sonar.jdbc.username=SonarQube
sonar.jdbc.password=SonarQube#123

sonar.jdbc.url=jdbc:mysql://localhost:3306/SonarQube?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance

sonar.web.host=0.0.0.0
sonar.web.port=9000

继续阅读

《程序员必读之软件架构》–笔记

架构是什么

架构作为名词解释时,概括起来都与结构有关:将产品分解为一序列组件、模块和交互。

架构作为动词来解释时,包括了理解你需要构建什么、设定愿景以便进行构建和做出恰当的设计决策。所有这些都以需求为基础,因为需求驱动架构。关键在于,架构是关于交流愿景以及引入技术领导力的,这样参与构建产品的每个人都能理解这个愿景,并为产品的成功做出积极贡献。

不论何种领域的架构,其实主要就是结构和愿景。

架构分类

应用程序架构的关注点是应用程序,通常包括将应用程序结构为类和组件,确保设计模式正确应用,构建或使用框架,等等。本质上,应用程序架构谈论的是软件设计的低级别切面,通常只考虑单一的技术栈。应用程序架构着重考虑软件和代码组织。

系统架构是更大规模的应用程序架构。大多数软件系统实际上是由横跨不同层次和技术的多个应用程序组成。系统架构还关注互操作性和与环境中其他系统的集成。系统架构描述为从组件和服务到子系统等高层次的抽象。系统架构的定义大多数还包括了软件和硬件。

软件架构就是应用程序和系统架构的结合。从代码结构和基础到将代码成功部署到生产环境,与一个软件系统重要元素相关的所有东西就是软件架构。

企业架构一般是指整个组织的中心工作,着眼于如何组织和利用人员、流程和技术来使企业有效和高效地工作。它是关于企业如何分成组或部门,业务流程如何在这上层运作,以及技术如何支撑这一切。

好的架构带来敏捷。

软件架构角色

架构 VS. 设计

作为名词,设计是指一个系统内命名的结构或行为,解决或有助于月解决该系统的一个或多个问题。因而,设计代表了潜在的决策空间中的一个点。

所有架构都是设计,但并非所有设计都是架构。

架构反映了使一个系统成型的重要设计决策,而重要性则通过改变的成本来衡量。

尽管“重要决策”没法彻底消失,但能通过架构分层等多种策略来改变。软件系统架构流程的一部分就是搞清楚哪些是重要的及为什么。

思考软件架构的好处

  • 让团队跟随一个清晰的愿景和路线图,无论这个愿景是一个人所有还是整个团队共有;
  • 技术领导力和更好的协调;
  • 与人交流的刺激因素,以便回答与重要决策、非功能需求、限制和其他横切关注点相关的问题;
  • 识别或减轻风险的框架;
  • 方法和标准的一致性,随之而来的结构良好的代码库;
  • 正在构建的产品的坚实基础;
  • 对不同的听众,以不同层次的抽象来交流解决方案的结构。

C4:语境、容器、组件和类

c4: 语境 容器 组件 类
软件的静态视图:

  • 语境:设定场景的高层次图,包括关键的系统依赖和参与者。

    语境图帮助回答下面的问题:

    1. 我们构建的(或已经构建的)软件系统是什么?
    2. 谁会用它?
    3. 如何融入已有的 IT 系统?
  • 容器:荣企图显示了高层次的技术选择,容器如何分担职责、如何通信。

    1. 软件系统的整体形态是什么样的?
    2. 高层次技术决策有哪些?
    3. 职责在系统中如何分布?
    4. 容器之间如何相互交流?
    5. 为了实现特性,作为一个开发者,我需要在哪里写代码?
  • 组件:组件图可以让你看到每个容器的关键逻辑组件及之间的关系。

    1. 系统由哪些组件/服务组成?
    2. 在高层次上,系统如何工作是否清晰?
    3. 所有组件/服务都有一个家吗(即驻留在一个容器中)?
  • 类:可选的细节层次。

原则

约束通常是强加于你的,而原则是你为了将一致性和清晰度引入最终代码库而想采用的原则(如编码规范、自动化测试等)或架构的原则(如分层策略,架构模式等)。

%e5%85%b3%e6%b3%a8%e7%82%b9

《代码简洁之道–Clean Code》 摘记

第 2 章 有意义的命名

  • 名副其实: 变量、函数或类的名称应该告诉你,它为什么会存在,它做什么事,应该怎么用。如果名称需要注释来补充,那就不算名副其实。
    代码的模糊度:即上下文在代码中未被明确体现的程度。

  • 避免误导: 提防使用不同之处较小的名称。

  • 做有意义的区分: 要区分名称,就要以读者能鉴别不同之处的方式来区分。

  • 使用读得出来的名字。

  • 使用可搜索的名称 : 长名称胜于短名称,搜得到的名称胜于自造编码代写就的名称。单字母名称仅用于端方法中的本地变量。名称长短应与其作用域大小相对应。

  • 避免使用编码 : 不要用类型前缀、特定前缀来标记成员。

  • 避免思维映射 : 聪明程序员和专业程序员之间的区别在于,专业程序员了解,明确是王道

  • 类名 : 类名和对象名应该是名词或名词短语。

  • 方法名 : 方法名应当是动词或动词短语。重载构造器时,使用描述了参数的静态工厂方法名。

  • 每个概念对应一个词 : 给每个抽象概念选一个词,并且一以贯之。

  • 别使用双关语;

  • 使用解决方案领域名称 :

  • 使用源自所涉问题领域的名称;

  • 添加有意义的语境

  • 不要添加没用的语境 : 精确是命名的要点。

取好名字最难的地方在于需要良好的描述技巧和共有文化背景。

继续阅读

《 Reactive Microservices Architecture 》 响应式微服务架构 摘记

第三部分未完结,挖坑先。。。

第一章 介绍

我们只在我们没有其他选择时改变单体 (monolithic) 系统。与其说是敏捷地抓住机会,我们考虑它是否确实值得倾覆我们称为纸牌屋的企业系统的微妙平衡。通常,机会很快就消失了,被快速的公司抓住。

Flaus Schwab: 在新世界,不是大鱼吃小鱼,是快鱼吃慢鱼。

基于微服务的架构是个简单的概念:它提倡用一组小的、隔离的服务来创建系统。这些服务拥有它自己的数据,各自是独立的、可扩展且对失败有弹性。服务集成其他服务来构建一个内聚的系统,它比我们当前构建的典型企业系统更灵活。

传统企业系统设计为单体应用,难以扩展,难以理解且难以维护。单体应用可能很快就变成噩梦,扼杀创新、进度和乐趣。单体应用导致的负面作用对一个公司可能是灾难性的,从底层的士气到高层雇员的周转,从阻止公司雇佣顶尖工程天才到失去市场机会,在极端情况下,甚至是公司的失败。

战争故事通常听起来是这样的:“我们终于做了决定来改变我们的 Java EE 应用,在得到管理层批准后。然后我们经过长达数月的大设计,在我们最终开始构建点东西之前。但我们构建期间的大多数时间都花在尝试弄清楚单体应用真正做了什么。我们因害怕而瘫痪,担心一个小的错误可能导致非预期的、未知的副作用。最终,在数月的担忧、害怕和艰难工作后,改变实现了,但地狱也打开了。”

这样的经历会加强害怕,使我们瘫痪更严重。这就是系统、公司如何停滞的。如果有更好的方式呢?

Steve Jobs:You’ve got to start with the customer experience and work back towards the technology 。

微服务的客户是投资于系统的组织,所以让我们从客户开始:开发人员、架构师和关键利益相关者。

需要拯救的服务

Helen Keller:Although the world is full of suffering, it is also full of the overcomming of it .

因为技术原因,微服务是软件里不纯粹的下一代设计演进。微服务这个词汇呈现的思想在我们进入面向服务的架构(SOA, Service Oriented Architecture)之前已经遍布各处。阻止我们采用微服务内嵌的概念的特定技术约束已经进入下一水平:单台机器运行着单个内核处理器,慢速的网络,昂贵的磁盘,昂贵的 RAM,组织结构是个单体。像把系统组织成一个定义良好的、有单一职责的系统的想法不是新的。

快进到 2016 年,阻止我们采用微服务的技术限制已经消失了。网络是快速的,磁盘是便宜的(且更快速),RAM 是便宜的,多核处理器是便宜的,云架构正在彻底改革我们如何设计、部署系统。现在,我们终于可以考虑着客户来构建我们的系统。

设计和编写软件是有趣的,这是大多数的我们进入软件行业的初衷。微服务不仅仅是一序列的原则和技术。它们以一种更专业的方式来达成复杂问题的系统设计。

微服务允许我们以组织我们团队的方式来构建系统,在团队成员之间划分职责,保证他们自由处理自己的工作。当我们理顺了我们的系统,我们把权利从中央治理主体移交到小团队里,小团队可以快速抓住机会并保持敏捷,因为他们明白 他们控制的、有定义良好的边界的软件。

继续阅读

Spring AOP 与 事务实现

1. AOP 与方法调用

对于 接口 interface,可以通过 java.lang.reflect.Proxy 来生成动态代理对象;
对于 类 class,可以通过字节码技术,如 CBLIB 来生成一个子类作为代理对象,此时类不能声明为 final 。

当 Spring 把 Bean 注入到另一个 Bean 时,其实注入的是它生成的代理对象,进行方法调用时,首先调用的是代理对象上的方法,代理对象的方法最终再调目标对象的方法,也就是开发人员编写的方法。Spring 通过在调用目标方法前后做处理来实现一些特性,例如事务管理、缓存等。

Spring 的 AOP 是基于对代理对象的方法调用的拦截的,只能拦截外部对象 对 某个对象的方法的调用,对象的方法调用同一个类的方法是不会被拦截的。

@Service
public class SpringTxService {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(SpringTxService.class);

    @Transactional(propagation = Propagation.REQUIRED)
    public void add() {
        LOGGER.info("do add");
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void requireNew() {
        LOGGER.info("do requireNew");
    }

    public void composite() {
        add();
        requireNew();
    }
}

上面的代码是基于注解进行事务控制的,composite 方法没有声明事务属性,它会调用 add、 requireNew
方法。当把 SpringTxService 注入到另一个 BeanB:
1. 在 BeanB 的方法里调用代理对象 composite 时,最终执行 add、 requireNew 是没有在事务里执行的,只是普通的方法调用。
2. 在 BeanB 的方法里调用代理对象的 add、 requireNew 方法时,这两个方法都会分别在相应的事务里执行。

当发现基于 AOP 实现的特性没有预期的效果时,一定要看看是不是在代理对象上调用还是目标类内部的方法调用。

继续阅读

Object Extract Mapping 与 网页 API

最近的项目需要访问一些外部的网页,比如百度、必应搜索等,有的只是要求把响应内容的文本提取出来,有的是要求做结构化解析的,比如百度的搜索结果,解析为一个结构化的列表,每个列表项有(标题、概要信息、详细信息的链接)。

开始时感觉是用爬虫去爬网页,然后做解析,但有个问题困扰着:比如要保存百度、必应的搜索结果到数据库,肯定只希望定义一个类来对应搜索结果的一条记录,然后写一套保存到数据库的代码。看了一些爬虫框架对于内容解析部分其实都只能自己写代码来解析网页,这样的话,如果要接搜狗就写一套解析网页的逻辑、接 360 也得写一套。。。。每接一个就得写一套。。。这不是我需要的生活。。。

怎么减少这些匹配代码?

后面看到 webmagic 的文档提到其基于注解实现的 Object/Extraction Mapping 功能,在类的字段上定义一个注解,表示这个字段用什么样的方式抽取,这样在爬虫有关的代码里就不会出现这些解析网页的逻辑。

Object Extract Mapping

如果我只需要解析百度的搜索结果,这是很好的方式,但我还要解析必应、搜狗等更多的搜索结果时就不行了。因此需要换一种方式来实现:把抽取方式的定义放到代码外面来

我就实现了一个基于 XML 的 Object-Extrac-Mapping (OEM) 小工具。

继续阅读