Python 笔记之赋值语句和表达式

赋值语句比较简单,Learning Python 这本书中对赋值语句介绍比较详细,分类也讲述的比较细,这篇文章就只简单的记录一些容易混乱的知识点,并不记录所有赋值语句需要注意的点。

Augmented assignment and shared references

在之前的文章中就交代过共享引用是需要特别注意的,在 augmented assignment 中也是

>>> L = [1, 2]
>>> M = L                           # L M 共享同一个对象
>>> L = L + [3, 4]                  # Concatenation 会创建新的对象
>>> L, M                            # 所以他们的值不相同
([1, 2, 3, 4], [1, 2])

>>> L = [1, 2]
>>> M = L                           # Share
>>> L += [3, 4]                     # 但是 `+=` 其实用的是 extend 方法,所以是原地修改
>>> L, M
([1, 2, 3, 4], [1, 2, 3, 4])

Expression Statements and In-Place Changes

表达式同样也分为很多种

>>> L = [1,2]
>>> L.append(3)
>>> L
[1, 2, 3]

不过需要注意的是,比如 append() 方法并没有返回值,所有返回的 L 会是 None

>>> L = L.append(4)
>>> L
None

2017-09-20 python , notes , assignment , expression

Maven 介绍

Maven 是一个项目管理工具,主要用于项目构建,依赖管理,项目信息管理。自动化构建过程,从清理、编译、测试和生成报告、再到打包和部署。Maven 通过一小段描述信息来管理项目。

Maven 安装

安装之前先把 JDK 环境配置好。

Debian/Ubuntu/Linux Mint 下

sudo apt install maven

如果要手动安装则按照下面步骤

  • 下载 Maven 最新安装包,地址 http://Maven.apache.org/download.cgi 比如 apache-Maven-3.3.9-bin.tar.gz
  • tar -zxvf apache-Maven-3.3.9-bin.tar.gz
  • 将 apache-Maven-3.3.9 目录移动到 /usr/local 目录 命令: sudo mv apache-maven-3.3.9/ /usr/local/
  • root 身份修改配置命令 sudo vi ~/.bashrc 在文件最后添加:

      #set Maven environment
      M2_HOME=/usr/local/apache-maven-3.3.9
      export Maven_OPTS="-Xms256m -Xmx512m"
      export PATH=$M2_HOME/bin:$PATH
    

    保存并关闭。

  • 使配置生效必须重启机器或者在命令行输入: source ~/.bashrc
  • 查看 Maven 是否安装成功: mvn -version

如果进行了上面步骤在任意目录中 mvn 命令不能使用,可以在 /etc/profile 文件后面加入下面三行 sudo vim ~/.bashrc

然后输入以下内容

Maven_HOME=/usr/local/apache-maven-3.3.9
export Maven_HOME
export PATH=${PATH}:${Maven_HOME}/bin

设置好 Maven 的路径之后,需要运行下面的命令 source ~/.bashrc 使刚刚的配置生效

Maven 作用

Maven 最熟悉的一个概念就是 POM,Maven 项目会有一个 pom.xml 文件, 在这个文件里面添加相应配置,Maven 就会自动帮你下载相应 jar 包

<dependency>
  <groupId>com.google.firebase</groupId>    项目名
  <artifactId>firebase-admin</artifactId>   项目模块
  <version>5.3.1</version>                  项目版本
</dependency>

项目名 - 项目模块 - 项目版本 三个坐标定义了项目在 Maven 世界中的基本坐标,任何 jar,pom, war 都是基于这些坐标进行区分的。

  • groupId 定义了项目组,组和项目所在组织或公司,或者开源项目名称,一般为公司域名反写,比如 com.google.firebase 等等。Maven 项目和实际项目并不一定是一对一关系,比如 SpringFramework 实际项目,对应的 Maven 项目会有很多,spring-core, spring-context 等等,更进一步推荐 groupId 应当定义项目隶属的实际项目,如果定义到组织或者公司,那么一个组织下可能会有很多实际项目,造成混乱
  • artifactId 定义了 Maven 项目的名称,在组中的唯一 ID,在同一个项目中可能有不同的子项目,可以定义不同的 artifactId,可以理解为 Maven 项目的模块。artifactId 也是构建完成项目后生成的 jar 包或者 war 包的文件名的一部分。
  • version 顾名思义,就是项目的版本号,如果项目维发布,一般在开发中的版本号习惯性加上 SNAPSHOT, 比如 1.0-SNAPSHOT

根据上面的例子,比如上面定义的 Maven 坐标,可以在对应的中央仓库中 https://repo1.maven.org/maven2/com/google/firebase/firebase-admin/5.3.1/ 目录下找到对应的文件。

  • scope 定义了依赖范围,如果依赖范围为 test ,那么该依赖只对测试有效,也就是说在测试代码中引入 junit 有效,在主代码中用 junit 会造成编译错误。如果不声明依赖范围则默认为 compile ,表示该依赖对主代码和测试代码都有效。

Maven 有以下几种依赖范围:

  • compile 编译依赖,在编译、测试、运行时都有效
  • test 测试依赖,只对于测试 classpath 有效, JUnit 典型
  • provided 已提供依赖,只在编译和测试有效,运行时无效, servlet-api 编译和测试项目时需要该依赖,但是在运行项目时,由于容器已经提供,不需要 Maven 重复引入
  • runtime 运行时依赖,对于测试和运行有效,编译主代码无效, JDBC 驱动实现,项目主代码编译只需要 JDK 提供的 JDBC 接口,只有执行测试或者运行项目才需要实现上述接口的具体 JDBC 驱动
  • system 系统依赖范围,和 provided 范围依赖一致,但是使用 system 范围的依赖时必须通过 systemPath 元素显示地指定依赖文件的路径。
  • import 导入依赖,一般不用

依赖范围和 classpath 的关系

依赖范围 Scope 编译 classpath 有效 测试 classpath 有效 运行 classpath 有效 例子
compile Y Y Y spring-core
test - Y - junit
provided Y Y - servlet-api
runtime - - Y JDBC 驱动
system Y Y - 本地的, Maven 仓库之外的类库

Maven 依赖调解 Dependency Mediation ,第一原则:路径最近者优先;第二原则:第一声明者优先

SNAPSHOT 快照版本只应该在组织内部的项目或者模块之间的依赖使用,组织对于这些快照版本的依赖具有完全的理解和控制权。项目不应该依赖于任何组织外部的快照版本,由于快照的不稳定性,依赖会产生潜在的危险,即使项目构建今天是成功的,由于外部快照版本可能变化,而导致未来构建失败。

Maven 核心概念 仓库

在上面介绍 Maven 的作用的时候提到了 Maven 的两个核心概念:坐标和依赖,这也是 Maven 首要解决的问题。这里要引入 Maven 另外一个核心概念:仓库。 Maven 时间中通过坐标来定位依赖,Maven 通过仓库来统一管理这些依赖。

Maven 项目的每一个构件对应着仓库的路径为: groupId/artifactId/version/artifactId-version.packageing

Maven 的仓库分为远程仓库和本地仓库,当第一次运行 Maven 命令时,需要网络连接,从远程仓库下载可用的依赖和插件。而当以后在运行 Maven 命令的时候,Maven 会自动先检查本地 ~/.m2/repository 目录下的依赖,如果本地有缓存优先从本地获取,在找不到的情况下去远程仓库寻找。

常用的在线 maven 依赖查看网站

Maven 核心概念 生命周期和插件

Maven 的生命周期是抽象的,实际行为都有插件完成。

clean 生命周期

清理项目,包含三个阶段

  • pre-clean 执行清理前需要完成的工作
  • clean 清理上一次构建生成的文件
  • post-clean 执行一次清理后需要完成的工作

default 生命周期

定义了真正构建时所需要执行的步骤

  • validate 验证工程是否正确,所有需要的资源是否可用。
  • initialize
  • generate-sources
  • process-sources 主要资源文件
  • generate-resources
  • process-resources
  • compile 编译主源码
  • process-classed
  • generate-test-sources
  • process-test-sources 测试资源
  • generate-test-resources
  • process-test-resources
  • test-compile 编译项目测试代码
  • process-test-classes 已发布的格式,如 jar,将已编译的源代码打包
  • prepare-package
  • pre-integration-test 在集成测试可以运行的环境中处理和发布包
  • post-integration-test
  • verify 运行任何检查,验证包是否有效且达到质量标准。
  • install 将包安装到 Maven 本地仓库,供本地 Maven 项目使用
  • deploy 复制到远程仓库,供其他人使用

site 生命周期

建立和发布项目站点

  • pre-site 生成项目站点之前需要完成的工作
  • site 生成站点文档
  • post-site 生成之后
  • site-deploy 生成的站点发布到服务器上

具体的流程可以参考下图

Maven 生命周期

Maven 项目文件结构

下面是一个标准的 Maven 工程

src/main/java - 存放项目.java 文件;
src/main/resources - 存放项目资源文件;
src/test/java - 存放测试类.java 文件;
src/test/resources - 存放测试资源文件;
target - 项目输出目录;
pom.xml - Maven 核心文件(Project Object Model);

Maven 常用命令

mvn archetype:create 创建 Maven 项目
mvn compile 编译源代码
mvn deploy 发布项目
mvn test-compile 编译测试源代码
mvn test 运行应用程序中的单元测试
mvn site 生成项目相关信息的网站
mvn clean 清除项目目录中的生成结果
mvn package 根据项目生成的 jar
mvn install 在本地 Repository 中安装 jar
mvn eclipse:eclipse 生成 eclipse 项目文件
mvn jetty:run 启动 jetty 服务
mvn tomcat:run 启动 tomcat 服务
mvn clean package -DMaven.test.skip=true 清除以前的包后重新打包,跳过测试类
mvn clean package 清除以前的包后重新打包

简单例子

创建一个 Maven 项目

mvn

reference


2017-09-20 maven , java , build , management

IntelliJ IDEA 中使用 Resin 调试

平时开发环境使用的是 jetty,而 Java Web 有一个更好更快的服务器 Resin,这篇文章就来说一下什么是 Resin,以及在 Debug 中如何使用。

什么是 Resin

Resin是一个提供高性能的,支持 Java/PHP 的应用服务器。目前有两个版本:一个是GPL下的开源版本,提供给一些爱好者、开发人员和低流量网站使用;一种是收费的专业版本,增加了一些更加适用于生产环境的特性。

Resin也可以和许多其他的web服务器一起工作,比如Apache Server和IIS等。Resin支持Servlets 2.3标准和JSP 1.2标准。熟悉ASP和PHP的用户可以发现用Resin来进行JSP编程是件很容易的事情。Resin支持负载平衡,可以增加WEB站点的可靠性。方法是增加服务器的数量。比如一台Server的错误率是1%的话,那么支持负载平衡的两个Resin服务器就可以使错误率降到0.01%。到目前为止,Resin对WEB应用的支持已经远远超过Tomcat等各种大型的Server。

Resin 安装

在 Resin 的官方 quick start 教程中有各大平台详细的安装指导。我在使用 apt 安装时没有成功,这里就记录下手工安装的过程。

http://caucho.com/download 网址下载, Resin 有两个版本, Pro 版和GPL开源版,个人使用开源基础版本已经足够。安装过程如下:

  1. 安装JDK 6 或者以上版本,将 java 可执行程序配置到环境变量 JAVA_HOME
  2. tar -vzxf resin-4.0.x.tar.gz 根据下载的最新版本解压
  3. cd resin-
  4. ./configure 更多参数参考 configure options
  5. make
  6. sudo make install
  7. 执行 sudo resinctl start, 或者运行 java -jar lib/resin.jar start
  8. 浏览 http://localhost:8080

可以使用 sudo resinctl stop 来停止运行,更多内容可以参考官方指南

IntelliJ IEDA 中使用 Resin 调试

第一步,添加 Resin Local 选项,在 IDEA 中 Run/Debug Configuration 中添加 Resin Local 选项

resin-local

点击configure按钮,在弹出窗Application Servers中选择部分一中安装的Resin目录路径和目录下Resin的配置文件路径。

resin-configure

Run/Debug Configurations 中Server页面配置,基本都是默认。

Run/Debug Configurations 中Deployment页面配置,注意红色方框部分选择。选择resin.xml而不是JMX否则项目的index路径是localhost:8080/appname/ 而不是localhost:8080/

resin-deplyment

在Resin的服务器配置下 Depolyment中 Depolyment method:有 JMX 和resin.xml 两种选择, JMX 是把项目打包的文件放在resin 服务器下webapp下 只有在服务器启动时 才把项目给拷贝过去 无法在intellij中实时更新; resin.xml 是在C盘Temp 目录下 copy了一份resin.xml的配置文件 然后把服务器目录空间指向了你的项目工作空间 可以实现IntelliJ 修改实时更新。IntelliJ 默认的选择是JMX 所以我们要选中resin.xml模式。同时当项目 Artifacts 指向的目录是 ROOT 时 上图中的Use default context name(always true if depolyment method is JMX)取消勾选

执行build,得到war文件。执行resin run/debug,会自动在你选择的浏览器中打开项目index页面。也可以在IDEA下方的Application Servers面板中进行Resin的启动,停止等操作。Resin启动的打印信息也在此窗口显示。

run-resin


2017-09-19 Resin , IntelliJ , Java

Python 中 subprocess.call() vs os.system() 区别

在 Python 中调用系统命令可以使用两种方法,一种是 os 模块中的 system() 方法,一种是 subprocess 模块中的 call() 方法。

os.system()

这个方法会接受一个字符串命令,然后在 subshell 中执行,通常是 linux/OSX 下的 bash ,或者 Windows 下面的 cmd.exe。根据官方的文档,os.system() 方法时使用标准 C 方法 system() 来调用实现的,所以存在和 C 方法中一样的限制。

os.system("python –version")

举例

import os
cmd = "git --version"
returned_value = os.system(cmd)  # returns the exit code in unix
print('returned value:', returned_value)

subprocess.call()

默认情况下不使用 shell,他只是简单的执行传入的字符串

运行带参数的命令需要传 list

subprocess.call(["python", "–version"])

subprocess.call() 当使用 shell=True 时和 os.system() 一样使用 shell

subprocess.call(["python", "–version"], shell=True)

举例

import subprocess

cmd = "date"
# returns output as byte string
returned_output = subprocess.check_output(cmd)
# using decode() function to convert byte string to string
print('Current date is:', returned_output.decode("utf-8"))

subproecess.Popen()

subproecess 模块中如果想要实现更加复杂的命令,可以使用 Popen()。popen() 会创建一个管道,fork 一个子进程,然后该子进程执行命令。返回值在标准 IO 流中,该管道用于父子进程间通信。父进程要么从管道读信息,要么向管道写信息,至于是读还是写取决于父进程调用 popen 时传递的参数(w 或 r)。

import subprocess
child = subprocess.Popen(['ping','-c','4','douban.com'])
child.wait()
print 'main process'

默认情况下,开启子进程之后不会等待 child 执行完成,需要使用 wait() 来等待子进程完成。

父进程对子进程还有其他的操作

child.poll()           # 检查子进程状态
child.kill()           # 终止子进程
child.send_signal()    # 向子进程发送信号
child.terminate()      # 终止子进程

子进程的标准输入、标准输出和标准错误

child.stdin
child.stdout
child.stderr

举例

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
print child1.stdout.read()

使用 subprocess 创建子进程能够重定向 stdin stdout,总体来说要比 os.system() 更加强大。

reference


2017-09-18 python , subprocess , system , subshell

写了一个推送图书到 Kindle 的 bot

最近因为好奇所以大致的看了一下 Telegram 的 bot,很早 就开始用 Telegram 但事实上,我大部分情况下就是将 Telegram 作为一个跨平台同步工具和备份工具来使用,直到最近因为 Ingress 的一些朋友和一个图书群我对 Telegram 才有了更多的使用,也在 Telegram 发现了每周会推送一本书的 Channel。

所以我想照着有一个叫做 to_kindle_bot 的号自己写了一个将图书发送到 kindle 邮箱的机器人,bot 要实现的功能很简单,就是将图书资源发送给 bot 之后将图书发送邮件推送到关联的 kindle 邮箱。将任务拆解开,主要的步骤也比较简单

  • 首先要了解的就是 Telegram bot 的 API
  • 再次存储用户的 kindle 邮箱
  • 再次就是将文件获取之后发送到对应的邮箱中

再做的过程中,我又增加了一些功能

  • 比如,在 bot 获取到图书之后会自动解析这本图书的 meta 信息和封面信息
  • 比如,kindle 有些格式不支持,所以要提前进行转码

所以一一解决这些问题即可。

Telegram Bot API

这一部分其实也没有什么要多说的,botFather 会创建好 bot,然后我用 Python 的 python-telegram-api。基本上也都是非常成熟的文档。

存储邮箱

因为早之前就已经做了一个图书相关的网站,所以理所当然的将用 telegram uid 和用户的 kindle 邮箱做了以下映射。

发送邮件

这部分其实之前也做过,所以直接使用 Flask mail 或者标准库里面的 smtp 去发就行了,邮件服务早之前 也做过调查,也是现成的用 mailgun 了。

获取图书信息

这一部分确实花了我一些精力,使用 python 的 ebooklib 库来解析了 epub 格式的电子书,也因为 epub 格式鱼龙混杂,所以修了几个 bug,但是 epub 格式的图书解析确实解决了,但是 mobi 格式我是没有找到任何方式来解析,不过幸好,调研的过程中发现了 Calibre 自带的 ebook-meta 命令行工具,在测试之后发现非常好用,支持的格式也非常多,几乎能够支持 99% 的情况,那这样一来获取图书 meta 和封面的工作也 OK。

图书格式转码

这部分一开始准备想要用 Amazon 提供的 kindlegen 命令行工具来实现的,但是发现他只能够将 其他的格式转为 mobi 格式,那这样局限就比较大了,虽然能够 cover 我现在的需求,但是如果以后有 mobi 格式转 epub 格式的需求,其实就有了问题,不够幸好还是 Calibre 这个强大的工具,提供了 ebook-convert 命令行工具,能够转化非常多的格式,所以理所当然的就采用了它。

至此就完成了 bot 90% 的工作,剩下就是组织一些文案和 bot 的零星的 bug 问题,所以可以试用下 @kindlepush_bot

bot 主要命令功能

bot 现在为止一共有 start help email settings 这几个功能,除开 settings 功能还未完成, email 命令用来设置 kindle 邮箱,start 和 help 命令用来显示帮助,settings 功能我设想可以让用户选择一个频率,bot 会自动在这个频率下发送一本书到 kindle 邮箱中。

另外如果大家喜欢电子书,我用 Flask 练手的电子书网站 http://book.einverne.info 也欢迎使用。


2017-09-17 kindle , telegram , bot , python-telegram-api

每天学习一个命令:jstack 打印Java进程堆栈信息

Jstack 用于打印出给定的 java 进程 ID 或 core file 或远程调试服务的 Java 堆栈信息.

Prints Java thread stack traces for a Java process, core file, or remote debug server. This command is experimental and unsupported.

如果 java 程序崩溃生成 core 文件,jstack 工具可以用来获得 core 文件的 java stack 和 native stack 的信息,从而可以轻松地知道 java 程序是如何崩溃和在程序何处发生问题。另外,jstack 工具还可以附属到正在运行的 java 程序中,看到当时运行的 java 程序的 java stack 和native stack 的信息, 如果运行的 java 程序呈现 hung 的状态,jstack 是非常有用的。

thread dump 就是将当前时刻正在运行的 JVM 的线程拷贝一份,可以用来分析程序执行情况。

用法

打印某个进程的堆栈信息

jstack [PID]

jstack -l [PID]
jstack -m [PID]

关于如何找到PID,有很多方法,使用 jps -v 或者 ps -aux 或者 htop 等等方法都可以。

分析

在执行 jstack -l [PID] > /tmp/output.txt 之后可以对 /tmp/output.txt 进行分析

开头交代当前 dump 的时间和 JVM 基本信息

2018-05-24 14:41:06
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode):

接下来就是程序的线程信息

"resin-8015" #8015 daemon prio=5 os_prio=0 tid=0x00007f991820a800 nid=0x25e65 waiting on condition [0x00007f96b1b39000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
        at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196)
        at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147)
        at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)

   Locked ownable synchronizers:
        - None

线程的状态 Thread Life States

  • alive 通常运行时状态
  • not started 线程已经被 java.lang.Thread.start() 请求运行,但是实际上操作系统还没有开始
  • terminated 线程已经结束 run() 并且通知其他线程 joining

线程运行状态 Thread Run States

  • blocked 该线程尝试进入一个被其他线程占用的 synchronized 块,当前线程直到锁被释放之前一直都是 blocked 状态
  • blocked (on thin lock) 和 blocked 相同的状态,但是锁需要是 thin lock
  • waiting 当前线程调用了对象的 Object.wait() ,当前线程会保持该状态直到其他线程发送通知到该对象
  • sleeping 当前线程调用了 java.lang.Thread.sleep()
  • parked 当前线程调用了 java.util.concurrent.locks.LockSupport.park()
  • suspended 当前线程的执行被 java.lang.Thread.suspend() 暂停,或者有 JVMTI/JVMPI agent 调用

以上内容来自 Oracle

通过 jstack 信息可以分析线程死锁,或者系统瓶颈,但是这篇文章比较粗浅,只介绍了大概,等以后熟悉了补上。

reference


2017-09-14 jstack , java , debug , linux , thread-dump

Spring 中 HandlerMethodArgumentResolver 使用

HandlerMethodArgumentResolver 是一个接口:

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    @Nullable
    Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}

HandlerMethodArgumentResolver 的接口定义如下:

  • supportsParameter() 用于判断是否支持对某种参数的解析
  • resolveArgument() 当判断支持后将请求中的参数值进行相应的转换

关于 HandlerMethodArgumentResolver 执行流程,大部分可以在类 InvocableHandlerMethod 中看到

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // 获取 Controller 中函数的参数对象
    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(this.getMethod(), this.getBeanType()) + "' with arguments " + Arrays.toString(args));
    }

    Object returnValue = this.doInvoke(args);
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Method [" + ClassUtils.getQualifiedMethodName(this.getMethod(), this.getBeanType()) + "] returned [" + returnValue + "]");
    }

    return returnValue;
}

private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // 获取执行的具体函数的参数
    MethodParameter[] parameters = this.getMethodParameters();
    Object[] args = new Object[parameters.length];

    for(int i = 0; i < parameters.length; ++i) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = this.resolveProvidedArgument(parameter, providedArgs);
        if (args[i] == null) {
            // 首先判断是否有参数解析器支持参数 parameter,采用职责链的设计模式
            if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    // 如果参数解析器支持解析参数 parameter,那么解析参数成 Controller 的函数需要的格式
                    args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                } catch (Exception var9) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug(this.getArgumentResolutionErrorMessage("Failed to resolve", i), var9);
                    }

                    throw var9;
                }
            } else if (args[i] == null) {
                throw new IllegalStateException("Could not resolve method parameter at index " + parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() + ": " + this.getArgumentResolutionErrorMessage("No suitable resolver for", i));
            }
        }
    }

    return args;
}

外延

解析请求头及参数 AbstractNamedValueMethodArgumentResolver

AbstractNamedValueMethodArgumentResolver 抽象类主要用来解析方法参数 (resolving method arguments from a named value),请求参数,请求头,path 变量都是 named value 的例子。他下面有很多的子类

AbstractCookieValueMethodArgumentResolver
ExpressionValueMethodArgumentResolver
MatrixVariableMethodArgumentResolver
PathVariableMethodArgumentResolver
RequestAttributeMethodArgumentResolver
RequestHeaderMethodArgumentResolver
RequestParamMethodArgumentResolver
ServletCookieValueMethodArgumentResolver
SessionAttributeMethodArgumentResolver

PathVariableMethodArgumentResolver

PathVariableMethodArgumentResolver 用来解析注解 @PathVariable 的参数。

@PathVariable 用来解析 URI 中的 Path 变量,Path 变量是必须的,并且没有默认值。

RequestParamMethodArgumentResolver

用来将请求参数解析到注解 @RequestParam 修饰的方法参数中。

比如请求 https://localhost:8080/hello?uid=1234

@RequestMapping(value="/hello",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> test(@RequestParam(value = "uid", required = true, defaultValue = "1") String uid){
    return m;
}

其中将请求的 uid 值解析到方法参数 uid 就是通过 RequestParamMethodArgumentResolver 来实现的。

解析请求体 AbstractMessageConverterMethodArgumentResolver

AbstractMessageConverterMethodArgumentResolver 是一个抽象类,主要用来将请求 body 中的内容解析到方法参数中,其子类:

  • AbstractMessageConverterMethodProcessor
  • HttpEntityMethodProcessor
  • RequestPartMethodArgumentResolver
  • RequestResponseBodyMethodProcessor

代码片段

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    protected final List<HttpMessageConverter<?>> messageConverters;
    protected final List<MediaType> allSupportedMediaTypes;
}

AbstractMessageConverterMethodProcessor 的子类 RequestResponseBodyMethodProcessor:支持 @RequestBody@ResponseBody,使用举例:

@RequestMapping(value="/hello/test",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> test(@RequestBody Map<String,Object> m){
    return m;
}

reference

  • Spring doc

2017-09-10 spring , resolver , spring-boot

使用 antigen 来管理 zsh 插件

antigen 是 zsh 的插件管理工具,在他 GitHub 主页上的一句话非常形象的解释了他的功能。

Antigen is to zsh, what Vundle is to vim.

安装

curl -L git.io/antigen > antigen.zsh

或者

apt-get install zsh-antigen

配置

如果使用过 Vim 的 Vundle 对 antigen 的配置应该不陌生。

    source /path-to-antigen-clone/antigen.zsh

    # Load the oh-my-zsh's library.
    antigen use oh-my-zsh

    # Bundles from the default repo (robbyrussell's oh-my-zsh).
    antigen bundle git
    antigen bundle heroku
    antigen bundle pip
    antigen bundle lein
    antigen bundle command-not-found

    # Syntax highlighting bundle.
    antigen bundle zsh-users/zsh-syntax-highlighting

    # Load the theme.
    antigen theme robbyrussell

    # Tell antigen that you're done.
    antigen apply

使配置生效 source ~/.zshrc

可以从这个页面 查看更多的插件。

reference


2017-09-07 antigen , zsh , bash , linux , vim , tmux

爬虫相关技术整理

部分内容从 Python 3 网络爬虫开发实战 中整理。

Python 模块

主要依赖 Python 模块

数据库

  • MySQL
  • Redis

抓包

工具依赖

  • Selenium 自动化测试框架
  • Appium 移动端自动化测试框架

爬虫框架


2017-09-05 crawler , spider , python , mitm , linux

使用Shell命令来对 Unix 时间戳和日期进行转换 date 命令

在程序中经常要使用到 Unix timestamp 和日期的转换,通常情况下都是Google一个时间戳转换的网页在进行转换,其实 Linux 命令中就有能够快速实现转换的命令。主要都是集中在 date 这个命令。date 命令主要用于显示或设定系统时间和日期。

修改系统的时区

Linux 用来修正系统的时区

sudo dpkg-reconfigure tzdata

选择 Asia > Shanghai

date 常用命令

获取当前的 unix timestamp

date +%s    # 返回 11 位时间戳,%s 表示从 1970-01-01 0点(epoch开始的秒数
date +%s%3N # 返回 14 位时间戳,毫秒
date +%s%N  # 返回 11 + 9 位纳秒

将时间戳转换成日期

$ date +%s
1504516338
$ date -d @1504516338
Mon Sep  4 17:12:18 CST 2017

将 string 日期转成日期

使用 -d 参数可以用来将输入 String 转成特定格式日期,如果不指定具体时间,date会使用 00:00:00

$ date -d "06/04/1989"
Sun Jun  4 00:00:00 CDT 1989
$ date -d "04 June 1989"
Sun Jun  4 00:00:00 CDT 1989
$ date -d "June 04 1989"
Sun Jun  4 00:00:00 CDT 1989
$ date -d "June 04 1989 12:01:01"
Sun Jun  4 12:01:01 CDT 1989

-d 选项也有一些其他很强大的功能,比如

$ date -d '5 minutes ago' # 5 分钟前的时间
Mon Sep  4 17:22:58 CST 2017
$ date -d '100 days'      # 100 天以后的日期
Wed Dec 13 17:29:14 CST 2017
$ date -d '-100 days'     # 100 天以前的日子
Sat May 27 17:30:01 CST 2017
$ date -d '100 days ago'  # 同上
Sat May 27 17:31:10 CST 2017
$ date -d 'next monday'    
Mon Sep 11 00:00:00 CST 2017

格式化参数

可以使用+来输出不同格式

date +%<format options>

比如

$ date '+%Y-%m-%d %H:%M:%S'
2017-09-04 17:38:46
Format options Purpose of Option Output
date +%a 缩略方式显示星期 (like Mon, Tue, Wed) Thu
date +%A 全称显示星期(like Monday, Tuesday) Thursday
date +%b Displays Month name in short (like Jan, Feb, Mar ) Feb
date +%B Displays Month name in full short (like January, February) February
date +%d Displays Day of month (e.g., 01) 07
date +%D Displays Current Date; shown in MM/DD/YY 02/07/13
date +%F Displays Date; shown in YYYY-MM-DD 2013-02-07
date +%H Displays hour in (00..23) format 23
date +%I Displays hour (01..12) format 11
date +%j Displays day of year (001..366) 038
date +%m Displays month (01..12) 02
date +%M Displays minute (00..59) 44
date +%S Displays second (00..60) 17
date +%N Displays nanoseconds (000000000..999999999) 573587606
date +%T Displays time; shown as HH:MM:SS Note: Hours in 24 Format 23:44:17
date +%u Displays day of week (1..7); 1 is Monday 4
date +%U Displays week number of year, with Sunday as first day of week (00..53) 05
date +%Y Displays full year i.e. YYYY 2013
date +%Z alphabetic time zone abbreviation (e.g., EDT) IS

reference


2017-09-04 shell , linux

电子书

最近文章

  • 使用 Huginn 搭建自己的 IFTTT IFTTT, Zapier
  • notion 使用记录 很早就有人推荐 Notion,但是注册了用了一下,本以为就是一个在线的 Google Docs,可昨天在豆瓣看到一篇文章介绍比 Trello 更加智能的代替品,然后一看就是 Notion,于是就再来研究下 Notion。然后发现原来 Notion 可以是 Google Calendar, 可以是 Trello,可以是 Google Docs,可以是 todo list,可以是 Google Excel。甚至可以导入 word,markdown,html,csv。
  • Google Cloud Platform 使用向导 Google Cloud 提供 300 刀的初始优惠,而最近我的 Linode 节点越来越不稳定,时常撞墙,所以不得不再别人强烈推荐下注册了一下 GCP。这里就记录一下遇到的问题,其他具体的细节网上已经够多,就不再赘述。
  • html 转 pdf 命令行工具 wkhtmltopdf 最近因为用 HTML 写了一个文档,当想要输出时保存为 PDF,而 Chrome 自带的打印功能,本来就能够快速的保存为 PDF,但是却保留不了页面中的链接,所以找到了这个 wkhtmltopdf.
  • freemarker Java 模板引擎 FreeMarker is a free Java-based template engine, originally focusing on dynamic web page generation with MVC software architecture. However, it is a general purpose template engine, with no dependency on servlets or HTTP or HTML, and is thus often used for generating source code, configuration files or e-mails. by wikiPedia