<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>隔叶黄莺 Yanbin's Blog - 软件编程实践</title><link>https://yanbin.blog/</link><description>Recent content on 隔叶黄莺 Yanbin's Blog - 软件编程实践</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>yabqiu@gmail.com (Yanbin Qiu)</managingEditor><webMaster>yabqiu@gmail.com (Yanbin Qiu)</webMaster><copyright>Yanbin 隔叶黄莺</copyright><atom:link href="https://yanbin.blog/index.xml" rel="self" type="application/rss+xml"/><item><title>《从零构建大模型》阅读笔记(三) - 因果和多头注意力</title><link>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-3/</link><pubDate>Mon, 08 Jun 2026 15:18:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-3/</guid><description>
&lt;p&gt;接下来，我们将改进自注意力机制，引入因果机制和多头机制。因果机制是让模型预测只访问前序的 token，多头机制是将注意力机制分成多个 &amp;quot;头&amp;quot;,
每个头关注数据的不同特征，以提升模型在复杂任务中的性能.&lt;/p&gt;
&lt;h4 id="使用因果注意力causal-attention-隐藏未来词汇"&gt;使用因果注意力(causal attention) 隐藏未来词汇&lt;/h4&gt;
&lt;p&gt;前一文学过的自注意力，对于当前 token 会计算它与所有 token 的注意力分数(相似度)，而因果注意力(又称掩码注意力: masked attention)在预测时只需关注当前和前序
token. 这是符合前因后果自然逻辑的，还是拿读书作类比，想要弄清楚当前在讲什么，我们只用去翻看前面有过什么说明与铺叙.&lt;/p&gt;
&lt;p&gt;现在将通过标准自注意力机制来创建因果注意力机制，对于每个处理的 token, 需要遮盖住当前 token 之后的 token，参考以下两个图：&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-3/causal-attention-tokens.png"
width="480px"
/&gt;
&lt;p&gt;左图是标准注意力机制，在按序处理每一个输入 token 时会计算它与所有 token 的注意力分数，而右图的因果注意力机制，只关注当前 token 之前的 token，
比如处理第一个 token &amp;quot;Your&amp;quot; 时只关注它自己，处理第二个 token &amp;quot;journey&amp;quot; 时只关注 &amp;quot;Your&amp;quot; 和 &amp;quot;journey&amp;quot; 两个 token, 以此类推.&lt;/p&gt;</description></item><item><title>《从零构建大模型》阅读笔记(二) - 自注意力</title><link>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-2/</link><pubDate>Sun, 07 Jun 2026 12:28:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-2/</guid><description>
&lt;p&gt;来到第三章，关于注意力机制(attention mechanism), Transformer 可以说是由《Attention Is All You Need》这篇论文起的火，那 Attention
几乎就是 Transformer, 也就是现代 LLM 的核心。在学习 Attention 之前先通过类比的方式理解一下什么是注意力机制，以及为什么取名为 Attention
这个词.&lt;/p&gt;
&lt;p&gt;当你读到一句话里某个词时，你的大脑不会孤立地看这个词，而是会&amp;quot;环顾四周&amp;quot;——看看其他词，决定哪些词对理解当前这个词最有帮助，然后把注意力集中在那些词上。
LLM 的 Attention 机制，做的正是同一件事。而且通常看书时，多是翻看前面的部分，帮助当前的理解，找到故事的因果关系, 很少查阅后面的内容, 这就是因果注意力.&lt;/p&gt;
&lt;p&gt;本章将由浅入深的介绍以下 Attention 各种变体的原理和实现细节。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简化的自注意力(Simplified self-attention)&lt;/li&gt;
&lt;li&gt;自注意力(Self-attention): 带有可训练权重的自注意力机制&lt;/li&gt;
&lt;li&gt;因果注意力(Causal attention): 只关注序列中先前和当前的输入，从而保持文本生成的时间顺序&lt;/li&gt;
&lt;li&gt;多头注意力(Multi-head attention): 自注意力和因果注意力的组合，可同时关注来自不同表示子空间的信息&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>《从零构建大模型》阅读笔记(一) - 分词和嵌入</title><link>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-1/</link><pubDate>Thu, 04 Jun 2026 10:51:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/build-a-large-language-model-from-scratch-reading-notes-1/</guid><description>
&lt;p&gt;读完《Hands-On Large Language Models》又回头重新看《Build a Large Language Model from Scratch》一书，阅读时零乱记点什么。&lt;/p&gt;
&lt;p&gt;大语言模型构建通常包括两个阶段: 预训练(pre-training)和微调(fine-tuning), 有时也把偏好调优当作一个单独阶段，其实也是一种形式的微调。
预训练使用未标注的大量文本数据理解语言结构，即自监督学习，预训练生成的模型为基础模型(base/foundation model), 这能够完成文本预测任务。
再经过指令微调或分类任务微调后就能回答问题或进行分类了。&lt;/p&gt;
&lt;p&gt;Transformer 架构最初由 Google 于 2017 年的论文 &amp;quot;Attention is All You Need&amp;quot; 提出，它已成为了现代大语言模型的基础架构，基本上 LLM 指的就是这种架构。
Transformer 的两个子模块：编码器(encoder)和解码器(decoder), 一大关键组件是自注意力机制(self-attention), 使模型能够捕捉输入序列中不同位置之间的关系和依赖性。
Transformer 的并行处理能力和长距离依赖建模能力使其成为了训练大规模语言模型的理想选择。Transformer 的变体有 BERT(Bidirectional Encoder
Representations from Transformers)、GPT(Generative Pre-trained Transformer)等。&lt;/p&gt;
&lt;p&gt;GPT-3 基础模型预训练数据集为过滤后的 Common Crawl, WebText2, Book1, Books2, 和 Wikipedia, 在共 3000 亿 Token 上进行训练，
文本压缩后大小为 700G 左右。Llama 扩展了它的训练数据范围，包括 Arxiv 论文(92G)和 StackExchange 上的代码问答(78G)。训练基础模型成本极高，
GPT-3 的预训练成本高达 460 万美元。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(十三)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-13/</link><pubDate>Mon, 01 Jun 2026 21:55:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-13/</guid><description>
&lt;p&gt;这应该是阅读本书的最后一篇笔记了，下一本书的目标是《Build a Large Language Model (From Scratch)》。大模型的三步走，预训练、微调、和偏好调优。
本篇的来了解最后一步偏好调优, 对齐(Preference-Tuning / Alignment RLHF), RLHF 是一个像那些 RoPE, LoRA, MoE, SFT 等经常见得着但不知道意义的高级词。
RLHF(Reinforcement Learning from Human Feedback): 用人类的偏好判断来训练一个&amp;quot;打分模型&amp;quot;，再用强化学习让语言模型朝高分方向优化。&lt;/p&gt;
&lt;p&gt;模型经过指令微调后更听话了，我们还要最后的训练来进一步改进其行为，使用与我们期望它在不同场景中的表现保持一致。做法就是让一个偏好评估者(人或其他方式)
评估模型生成内容的质量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果分数较高，更新模型，以鼓励它产生更多这类生成内容&lt;/li&gt;
&lt;li&gt;如果分数较低，更新模型，以抑制它产生这类内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以训练一个叫作奖励模型(reward model) 的模型来实现偏好评估自动化，是要在偏好调优步骤之前增加一个步骤，即训练一个奖励模型&lt;/p&gt;
&lt;div style="display: flex;"&gt;
&lt;div style="flex: 1;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/hands-on-large-language-models-reading-notes-13/preference-tuning-1.png"
width="412px"
/&gt;
&lt;/div&gt;
&lt;div style="flex: 1;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/hands-on-large-language-models-reading-notes-13/preference-tuning-2.png"
width="412px"
/&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;经过指令微调的模型，去掉建模头(LM Head), 加上质量分类头(Quality classification head), 让该分类头只输入单一的分数。如果观察前一篇微调出来的模型
&lt;code&gt;TinyLlama-1.1B-sft&lt;/code&gt; 它最后有一个 &lt;code&gt;lm_head&lt;/code&gt; 层。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(十二)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-12/</link><pubDate>Mon, 01 Jun 2026 15:45:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-12/</guid><description>
&lt;p&gt;终于来到最后一章了，要学习是如何微调生成模型，生成模型才是天天所面对的模型，嵌入模型只在需要 RAG 或者模型记忆的时候才会用到，而用表示模型进行分类的场景还是极少。
对于私有数据资料应该用 RAG 还是微调生成模型呢？到目前为止自己也拿不准，因为还只会 RAG, 学完了本章会有一个更感性的认识。 相较于微调表示模型，
微调生成模型后可以进行更直接的交互用以验证微调的效果。在本章会接触到不少炫目的名词，如 SFT, RLHF, PEFT, LoRA, QLoRA, DPO 等。&lt;/p&gt;
&lt;p&gt;微调生成模型有两种最常见的方法: 监督微调(Supervised fine-tuning) 和偏好调优(preference tuning)&lt;/p&gt;
&lt;h4 id="llm-训练三阶段-预训练监督微调偏好调优"&gt;LLM 训练三阶段: 预训练，监督微调，偏好调优&lt;/h4&gt;
&lt;h5 id="语言建模language-modeling"&gt;语言建模(Language Modeling)&lt;/h5&gt;
&lt;p&gt;或称预训练(Pre-training)阶段，简称 PT, 使用海量无标注文本数据进行预训练，这一阶段是最贵的，一般公司是玩不起的。它的目标是学习语言的统计规律，
预测下一个词是什么，或者给定上下文预测缺失的词是什么。预训练阶段的模型被称为基座(Base)模型，通常也称为预训练(Pretrained)模型或基础(Foundation)模型。&lt;/p&gt;
&lt;p&gt;比如模型在输入 &amp;quot;法国的首都是&amp;quot; 后，预测下一个词是 &amp;quot;巴黎&amp;quot;。它还听不懂指令，不回答问题，比如对于输入 &amp;quot;解释黑洞是什么？&amp;quot;, 基础模型只会续写，比如&lt;/p&gt;
&lt;p&gt;输入: &lt;code&gt;把'苹果'翻译成英文&lt;/code&gt;
基础模型续写: &lt;code&gt;把&amp;quot;苹果&amp;quot;翻译成英文：苹果的英文是apple，另外橙子是orange，香蕉是banana……&lt;/code&gt;&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(十一)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-11/</link><pubDate>Sat, 30 May 2026 00:13:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-11/</guid><description>
&lt;p&gt;一鼓作气，继续阅读本书，来到了第十一章，本章的标题是 “为分类任务微调表示模型”，而嵌入模型也是表示模型的一种，前一章有学过如何训练和微调嵌入模型。
第 4 章中学习了使用预训练模型进行文本分类，本章将要学习对 BERT 模型的多种微调方法，以适应分类任务。还是文本分类，下一章也就是原书的最后一章才会讲到微调生成模型，
这才是本人更加期待的内容，还是按顺序来消化吧。&lt;/p&gt;
&lt;p&gt;在第 4 章处理监督分类时采用过两类模型：特定任务模型(如情感分析)和用于生成文本嵌入表示的嵌入模型。在微调时是否要冻结一部分参数可分为参数高效微调与全量微调&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/hands-on-large-language-models-reading-notes-11/fine-tune-architecture.png"
width="686px"
/&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(十)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-10/</link><pubDate>Wed, 27 May 2026 23:41:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-10/</guid><description>
&lt;h4 id="第三部分-训练和微调语言模型---微调嵌入模型"&gt;第三部分: 训练和微调语言模型 - 微调嵌入模型&lt;/h4&gt;
&lt;p&gt;仍然是第 10 章的部分，会学习到微调嵌入模型，监督学习(Supervised Learning)和无监督学习(Unsupervised Learning)的内容。
前面学习的其实也是在一个基础模型的基础上创建一个嵌入模型。&lt;/p&gt;
&lt;p&gt;本书把以 &lt;code&gt;bert-base-uncased&lt;/code&gt; 作为基础模型创建嵌入模型叫做从头开始训练嵌入模型，有点不理解。&lt;code&gt;bert-base-uncased&lt;/code&gt; 本身就是一个预训练的基础模型，
它的训练语料是 BookCorpus + English Wikipedia, 共约 33 亿词。而所谓的微调嵌入模型就是把 &lt;code&gt;bert-base-uncased&lt;/code&gt; 换为另一个预训练的
sentence-transformers 模型。这与前面的训练过程又有何区别呢？&lt;/p&gt;
&lt;p&gt;因为 &lt;code&gt;bert-base-uncased&lt;/code&gt; 就可以直接用来对句子生成嵌入向量，比如下面的代码可得到句子的嵌入向量&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bert-base-uncased&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cuda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;The weather is nice today&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以本章的微调嵌入模型时所谓的监督学习不过是把前面的基本模型换成了另一个专门用对比学习预训练过的 &lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt; 模型，其他代码完全就没变。
选基础模型的时候还得了解它是怎么训练的，训练语料是什么样的，训练目标是什么样的，这样才能知道它适不适合用来微调嵌入模型。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(九)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-9/</link><pubDate>Tue, 26 May 2026 11:08:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-9/</guid><description>
&lt;h4 id="第三部分-训练和微调语言模型---构建文本嵌入模型"&gt;第三部分: 训练和微调语言模型 - 构建文本嵌入模型&lt;/h4&gt;
&lt;p&gt;终于来到了可以真正实战的部分，前面的章节都是关于理解和使用大语言模型的知识，现在可以开始动手实践了。涉及到训练和微调模型，
不过本书只讲了如何训练一个嵌入模型，要学习训练一个生成模型的知识还得看
&lt;a href="https://www.manning.com/books/build-a-large-language-model-from-scratch"&gt;Build a Large Language Model (From Scratch)&lt;/a&gt;
这本书，或者参考 Karpathy 的 &lt;a href="https://github.com/karpathy/nanogpt"&gt;nanoGPT&lt;/a&gt; 项目。&lt;/p&gt;
&lt;p&gt;先从构建一个文本嵌入模型开始吧。嵌入模型是 NLP 的基础，它可应用于多种场景, 如监督分类(supervised classification),
无监督分类(unsupervised classification), 语义搜索等, 甚至为 ChatGPT 赋予记忆功能。&lt;/p&gt;
&lt;p&gt;嵌入模型的功能就是把非结构化的文本转换为数值表示的向量，这样才可计算，这一转换过程称为嵌入(embedding)。对输入进行嵌入通常由 LLM 执行，
这样的模型就是嵌入模型。嵌入模型可针对多种目的进行训练，如基于语义，或情感的分类等，比如通过微调使嵌入模型关注情感倾向，通过向模型展示足够多的语义相似文档，
引导模型向语义分析的方向发展，使用情感数据则向情感分析的方向发展。&lt;/p&gt;
&lt;p&gt;训练，微调和引导嵌入模型的方法很多，其中最强大的且应用最广泛的技术是对比学习。&lt;/p&gt;
&lt;h4 id="对比学习"&gt;对比学习&lt;/h4&gt;
&lt;p&gt;对比学习是训练和微调文本嵌入模型的一种主要技术。对比学习的目标是训练嵌入模型，使相似文档在向量空间中距离更近，而不相似文档相距更远。
对比学习的基本理念是，向模型输入相似的和不相似的文档对作为示例，这是学习文档之间的相似性或差异性并构建相关模型的最佳方式。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(八)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-8/</link><pubDate>Wed, 20 May 2026 13:08:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-8/</guid><description>
&lt;h4 id="第九章多模态-llm"&gt;第九章：多模态 LLM&lt;/h4&gt;
&lt;p&gt;本书第二部分(使用预训练模型)除了 &amp;quot;文本分类&amp;quot;，&amp;quot;文本聚类和主题建模&amp;quot; 两章外，其他的 &amp;quot;提示词工程&amp;quot;, &amp;quot;高级文本生成技术与工具&amp;quot;，&amp;quot;语义搜索与 RAG&amp;quot;
这三章都只是对原有知识的巩固. 现在学习本部分的最后一章 &amp;quot;多模态 LLM&amp;quot; 应该能了解到一些模型处理图片的相关知识, 这对我来说是新的知识。&lt;/p&gt;
&lt;p&gt;多模态就是指模型能够处理多种类型的数据，比如文本、图像、音频等，而不仅仅是文本，如果你的模型只能处理图像和音频也是多模态。视觉 Transformer
(Vision Transformer, ViT) 在图像识别任务中超越了传统的卷积神经网络(CNN)。ViT 的核心功能是将非结构化的图像数据转换为可用于分类等任务的数值表示。
像处理自然语言一样来处理图像。&lt;/p&gt;
&lt;p&gt;对于文本，Transformer 编码器对文本拆分再编码成数值表示，对文本的切割看成是一维的，ViT 对图像按水平方向和垂直方向进行网格化切割成小块，这和 CNN
用卷积核来提取图像特征的方式类似。&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/hands-on-large-language-models-reading-notes-8/vit-split-image.png"
width="792px"
/&gt;
&lt;p&gt;文本的词汇量是有限的，所以可以把文本分割的 Token 映射为一个数值，切成的图片块太过多样性，无法为每一种可能的图片块分配一个 Token.
Transformer 的做法是把图片块平铺后对所有图像块实施线性嵌入操作，像文本嵌入一样把一组图片块转换为嵌入向量。这些蕴含语义信息的向量便可作为
Transformer 模型的标准化输入。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(七)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-7/</link><pubDate>Tue, 19 May 2026 11:54:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-7/</guid><description>
&lt;h4 id="第八章语义搜索与-rag"&gt;第八章：语义搜索与 RAG&lt;/h4&gt;
&lt;p&gt;在 BERT 之前 Google 只是关键词搜索，当你把程序的出错信息(特别是带有本地文件路径)往 Google 一贴，什么也搜索不出来，此时就能体现低级和高级程序员的不同水平。
现在形势不同，自从一篇 BERT 的论文(Pre-Training of Deep Bidirectional Transformers for Language Understanding) 发表(2018年) 数月后,
Google 把&lt;code&gt;BERT&lt;/code&gt; 整合进它的搜索引擎中，特别是加上各种 AI 工具，从此程序员不用再为出错信息而交流了。这就是语义搜索(semantic search)的威力.&lt;/p&gt;
&lt;p&gt;很早就听说过 &lt;code&gt;RAG&lt;/code&gt;(Retrieval-Augmented Generation) 已过时的论调，然而实际学习 AI 时总也避不开 &lt;code&gt;RAG&lt;/code&gt;. 因为微调模型的少， 再加上模型的上下文窗口太小，
所以有了 &lt;code&gt;RAG&lt;/code&gt;, 持 &lt;code&gt;RAG&lt;/code&gt; 过时之说大概是因为模型支持的上下文不断增大，原本以 &lt;code&gt;RAG&lt;/code&gt; 片段作为提示词一部分的内容可以全部塞进上下文窗口中，
但对于长短期记忆和大量的资料库的检索，&lt;code&gt;RAG&lt;/code&gt; 仍然有其存在的价值。&lt;/p&gt;
&lt;p&gt;语义搜索和 RAG 是否也可以集成到 Lucene, Solr, Elasticsearch 等搜索引擎中，以提升搜索的准确性和效率呢？&lt;/p&gt;
&lt;p&gt;语义搜索的几个概念和通常做法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;稠密搜索(dense retrieval): 从向量数据库中搜索与当前文本向量相似的一些文档，比如 10 个文档. 稠密搜索就是粗泛的大面积快速搜索。&lt;/li&gt;
&lt;li&gt;重排序(reranking): 对稠密搜索结果重排序(更精细的比较相似度)，比如取前 3 个文档. 稠密搜索筛选，重排序进行精选，重排序也可让 LLM 介入.
有一个相关的论文 &lt;a href="https://arxiv.org/pdf/1910.14424"&gt;Multi-Stage Document Ranking with BERT&lt;/a&gt;, 常称作 monoBERT 方法&lt;/li&gt;
&lt;li&gt;RAG：通过包括资料分片，向量化存储，对输入向量化后检索相似文档，作为输入提供给 AI 模型，让 AI 基于它们生成结果的全过程&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(六)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-6/</link><pubDate>Mon, 18 May 2026 23:59:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-6/</guid><description>
&lt;h4 id="第七章高级文本生成技术与工具"&gt;第七章：高级文本生成技术与工具&lt;/h4&gt;
&lt;p&gt;在上一章中已经从 &lt;code&gt;AutoModelForCausalLM&lt;/code&gt;, &lt;code&gt;AutoTokenizer&lt;/code&gt;, &lt;code&gt;pipeline&lt;/code&gt; 过渡到了稍微高那么一层的 &lt;code&gt;llama-cpp-python&lt;/code&gt; 的使用，
这一章将继续学习 &lt;code&gt;LLM&lt;/code&gt; 的使用, 到真正能训练，微调模型还远着呢。 其中大部分的内容都在学习 &lt;code&gt;LangChain&lt;/code&gt; 的过程中有所掌握，包括记忆机制，
智能体工具调用等，所以这方面的内容没有具体展开。&lt;/p&gt;
&lt;p&gt;本章所覆盖的在不对模型作微调的情况下提升文本生成质量的方法论与技术:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;模型输入/输出：模型加载与调用, 用 llama-cpp-python 演示&lt;/li&gt;
&lt;li&gt;记忆机制：增强模型的上下文记忆能力，查看 &lt;code&gt;LangChain&lt;/code&gt; 短期记忆相关日志 &lt;a href="https://yanbin.blog/langchain-core-component-short-term-memory/"&gt;LangChain 核心组件之短期记忆&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;智能体系统：整合外部工具实现复杂行为，用 &lt;code&gt;LangChain&lt;/code&gt; 1.0 后的 &lt;code&gt;create_agent()&lt;/code&gt; 将会非常简单&lt;/li&gt;
&lt;li&gt;链式架构：模块化方法与组件的衔接组合, 这是 &lt;code&gt;LangChain&lt;/code&gt; 0.x 的架构，1.0 后不再使用链式架构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本章进到 &lt;code&gt;LangChain&lt;/code&gt; 的学习当中，本人对 &lt;code&gt;LangChain&lt;/code&gt; 已经有了一定程度的了解，由于 &lt;code&gt;LangChain&lt;/code&gt; 1.0 于 2025 年 10 月份才正式发布，
显然写作本书的时候用的还是 &lt;code&gt;LangChain 0.x&lt;/code&gt; 的版本，而 &lt;code&gt;LangChain&lt;/code&gt; 1.0 带来了巨大的变化，所以学习当中会把书中的例子改写为 &lt;code&gt;LangChain 1.x&lt;/code&gt; 的版本。&lt;/p&gt;
&lt;p&gt;下载 llama-cpp 的 GGUF 单文件模型: &lt;a href="https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf/resolve/main/Phi-3-mini-4k-instruct-q4.gguf?download=true"&gt;Phi-3-mini-4k-instruct-q4.gguf&lt;/a&gt;&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(五)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-5/</link><pubDate>Mon, 18 May 2026 15:45:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-5/</guid><description>
&lt;h4 id="第六章提示词工程prompt-engineering"&gt;第六章：提示词工程(Prompt Engineering)&lt;/h4&gt;
&lt;p&gt;还是继续学习怎么使用生成模型, 这本书也只能涉及到 Prompt Engineering 了, 另外两个高层次的 Content Engineering 和 Harness Engineering
必须找别的资料.&lt;/p&gt;
&lt;p&gt;主要为文本生成而训练的模型通常被称为生成式预训练模型(Generative Pre-trained Transformers-GPT), 将要学习如何设计高效的提示词,
了解生成模型, 研究它们的推理, 输出的验证和评估.&lt;/p&gt;
&lt;p&gt;由于使用了新的 &lt;code&gt;transformers&lt;/code&gt; 版本(5.8.0), 以下的代码与书中有所不同&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;text-generation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;microsoft/Phi-3-mini-4k-instruct&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Create a funny joke about chickens.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出的信息包含 &lt;code&gt;user&lt;/code&gt; 和 &lt;code&gt;assistant&lt;/code&gt; 两部分, 因为 &lt;code&gt;pipeline()&lt;/code&gt; 有一个默认的参数 &lt;code&gt;return_full_text=True&lt;/code&gt;(同时返回用户和 LLM 的消息)&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(四)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-4/</link><pubDate>Fri, 15 May 2026 22:00:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-4/</guid><description>
&lt;h4 id="第五章文本聚类与主题建模text-clustering-and-topic-modeling"&gt;第五章：文本聚类与主题建模(Text Clustering and Topic Modeling)&lt;/h4&gt;
&lt;p&gt;继续巩固 LLM 的基础知识，文本聚类与主题建模是 NLP 中的两个概念，文本聚类就是文本按语义分类，如猫狗一块，苹果西瓜在一起，足球篮球放一堆，
而它们的分类名就是主题了，如动物，水果，体育。主题范围有大有小，水果可以延展到食物，体育可以缩小到球类，语义越相近，就离得越近，这其实就是物以类聚。&lt;/p&gt;
&lt;p&gt;可用嵌入模型进行聚类，与文本聚类相关的主题建模方法是 BERTopic. 下面是用 Hugging Face 上的 &lt;code&gt;arxiv_nlp&lt;/code&gt; 数据集来进行本章的学习，Hugging Face
还真是一个宝藏啊，估计一些禁书都能在里面找到。&lt;code&gt;arxiv_nlp&lt;/code&gt; 包含了 1991 年到 2024 年来自 ArXiv cs.CL(计算与语言)板块 44949 篇摘要。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;maartengr/arxiv_nlp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;得到的 dataset 是&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(三)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-3/</link><pubDate>Thu, 14 May 2026 14:00:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-3/</guid><description>
&lt;h4 id="第四章文本分类"&gt;第四章：文本分类&lt;/h4&gt;
&lt;p&gt;文本分类(Text Classification) 是 NLP 中常见的任务之一，如不同语义的分类，动物，植物; 肉类，还是水果，蔬菜，开心还是悲伤，或业务分类等。
表示模型和生成模型在文本分类中的作用不容忽视。&lt;/p&gt;
&lt;p&gt;下面是用预训练模型对 Hugging Face 上的电影评论数据集 &lt;code&gt;rotten_tomatoes&lt;/code&gt; 进行文本分类的测试。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rotten_tomatoes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;里面包含三部分的数据 &lt;code&gt;train&lt;/code&gt; 训练集，&lt;code&gt;validation&lt;/code&gt; 验证集，&lt;code&gt;test&lt;/code&gt; 测试集。我们将用训练集训练模型，使用测试集来验证结果，
附加的验证集用来验证模型的泛化能力。&lt;code&gt;rotten_tomatoes&lt;/code&gt; 中记录有两个字段，分别为&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text&lt;/code&gt;: 电影评论文本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;label&lt;/code&gt;: 电影评论的标签，0 代表负面评论，1 代表正面评论。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>LangChain Deep Agent 快速入门</title><link>https://yanbin.blog/langchain-deep-agent-quick-start/</link><pubDate>Wed, 13 May 2026 20:58:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-deep-agent-quick-start/</guid><description>
&lt;p&gt;前面差不多历经了 LangChain 学习和组件的以下底，中, 高三个阶段&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;底层的 LangGraph 的 GraphState, 是 LangChain 的基石，做一个 AI Agent 的内部表现形式就是一个状态图&lt;/li&gt;
&lt;li&gt;LangChain 的 &lt;code&gt;init_chat_model()&lt;/code&gt;, Prompt Engineering, 用于一次性的与模型的对话，想要实现短长期记忆，工具调用，实现起来很麻烦，就得前进到下一层&lt;/li&gt;
&lt;li&gt;LangChain 的 &lt;code&gt;create_agent()&lt;/code&gt;， Context Engineering, 有了开箱即用的短长期记忆，自动的工具调用，Human-in-the-loop, 并且也可以实现多 Agent&lt;/li&gt;
&lt;li&gt;DeepAgents 的 &lt;code&gt;create_deep_agent()&lt;/code&gt;, Harness Engineering, 要做一个完备功能的 Agent 就得靠它了，它自动提供了像计划，自动管理上下文
支持 Skills, 内置工具，需要处理复杂任务时自动启动子 Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面就来探索一下它的内置的功能, 先分析一个最简单的例子看看背后发生了什么&lt;/p&gt;
&lt;p&gt;依赖安装: &lt;code&gt;uv add deepagents langchain-ollama&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;deepagents&lt;/code&gt; 会连带安装 &lt;code&gt;langchain&lt;/code&gt;, &lt;code&gt;langchain-anthropic&lt;/code&gt;, &lt;code&gt;langchain-google-genai&lt;/code&gt; 和 &lt;code&gt;wcmatch&lt;/code&gt;。&lt;code&gt;langchain&lt;/code&gt;
会安装它的传递依赖，像 &lt;code&gt;langchain-core&lt;/code&gt;, &lt;code&gt;langgraph&lt;/code&gt; 之类。&lt;code&gt;langchain-ollama&lt;/code&gt; 用来使用本地 &lt;code&gt;Ollama&lt;/code&gt; 模型的。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(二)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-2/</link><pubDate>Wed, 13 May 2026 14:00:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-2/</guid><description>
&lt;h4 id="第三章llm-的内部机制"&gt;第三章：LLM 的内部机制&lt;/h4&gt;
&lt;p&gt;这一章才是本书的最重要的部分，一个支撑现在大语言模型的基石，那就是 Transformer 模型, 占据了本书约 12% 的篇幅。学完分词与词嵌入后，现在开始探究
Transformer 模型的工作原理。我们把 &lt;code&gt;Transformer LLM&lt;/code&gt; 看成是一个接收文本输入并生成响应的系统， 模型总是在预测下一个 Token, 每个 Token
的生成都是模型的一次前向传播(forward pass). 在生成当前 Token 后，会将该生成的 Token 追加到输入序列中，作为下一次预测的输入(
或者说调整下次生成的提示词), 如此循环往复，逐个生成 Token，直到完成整段响应——这种机制称为自回归(autoregressive) 生成。&lt;/p&gt;
&lt;p&gt;这种使用生成的前一个 Token 又作为输入来生成下一个 Token 的模型被称为自回归模型(autoregressive model)。而 BERT 是双向(Bidirectional)编码模型。&lt;/p&gt;
&lt;p&gt;前向传播的组成, 分词器后就是由 &lt;code&gt;Transformer&lt;/code&gt; 堆叠而成的神经网络，最后是一个 LM head, 它负责将 Transformer 块的输出转换为 Token 的概率分数。&lt;/p&gt;</description></item><item><title>《Hands-On Large Language Models》阅读笔记(一)</title><link>https://yanbin.blog/hands-on-large-language-models-reading-notes-1/</link><pubDate>Tue, 12 May 2026 18:49:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hands-on-large-language-models-reading-notes-1/</guid><description>
&lt;h4 id="第一章大语言模型简介"&gt;第一章：大语言模型简介&lt;/h4&gt;
&lt;p&gt;应该还是在前年读了 《Build a Large Language Model (From Scratch)》前面一小部分，就中断了, 如今又找来一本相关的书籍
《Hands-On Large Language Models》试着啃一啃，能明白多少是多少。拿着中英文的两个版本对照着看，有些中文翻译最好还是不译的好。
这里只会一些杂乱的笔记，算不得什么记要，重要的信息仍然在书上。&lt;/p&gt;
&lt;p&gt;学习中相关的测试代码放在 GitHub 仓库 &lt;a href="https://github.com/yabqiu/Hands-On-Large-Language-Models"&gt;yabqiu/Hands-On-Large-Language-Models&lt;/a&gt;,
主要是在 macOS 苹果芯片上进行的测试。虽然官方有一个相应的仓库 &lt;a href="https://github.com/HandsOnLLM/Hands-On-Large-Language-Models"&gt;HandsOnLLM/Hands-On-Large-Language-Models&lt;/a&gt;,
但作为沉浸式学习，亲自根据自己的口味撸一遍代码是非常必要的，所以也会发现我学习时的代码与书中不完全相同。&lt;/p&gt;
&lt;p&gt;文字的计算机语义处理有过 &lt;code&gt;Bag-of-words&lt;/code&gt;, &lt;code&gt;word2vec&lt;/code&gt;, &lt;code&gt;sequence-to-sequence&lt;/code&gt;, 再就是 &lt;code&gt;BERT&lt;/code&gt;, &lt;code&gt;GPT&lt;/code&gt; 这些概念了。
写作 &amp;quot;Attention Is All You Need&amp;quot; 这篇论文的作者们真是太伟大了，拿不到诺贝尔奖，也应该给他们个图灵奖，虽然其中的技术并非很高级，
但这篇论文在 &lt;code&gt;AI&lt;/code&gt; 发展史中绝对有着里程碑式的意义。&lt;/p&gt;
&lt;p&gt;模型从大的功能分为&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;仅编码的表示模型(representation model)，如种种嵌入模型，都比较小的，约几百兆大小, 像 BERT(bidirectional encoder representations from Transformers)&lt;/li&gt;
&lt;li&gt;生成模型(generative model), 关注生成文本, 通常不会被训练用于生成嵌入, 像 GPT(Generative Pre-trained Transformer)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>LangChain 高级用法之多 Agent 协作</title><link>https://yanbin.blog/langchain-advanced-multi-agent/</link><pubDate>Tue, 05 May 2026 17:16:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-advanced-multi-agent/</guid><description>
&lt;p&gt;在往 &lt;code&gt;Deep Agents&lt;/code&gt; 的路径还要继续啃下一些基础知识，其中之一就是多 Agent 协作(Multi-agent)。复杂的系统需要多个 &lt;code&gt;Agent&lt;/code&gt;(智能体)同共完成,
不那么复杂的系统可以用动态的 &lt;code&gt;Prompt&lt;/code&gt;, &lt;code&gt;Tool&lt;/code&gt;, 或 &lt;code&gt;Model&lt;/code&gt; 来切换。多 Agent 的好处是每个 Agent 有自己的上下文，工具，专业领域的知识，
可以独立开发，分布式部署，多个 Agent 能并行执行，加速工作与烧钱的速度。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LangChain&lt;/code&gt; 在构建多 Agent 系统时，有以下几种常见模式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Subagents(子 Agent): 主从 Agent 方式，子 Agent 作为主 Agent 的工具使用，由主 Agent 指挥子 Agent 干活&lt;/li&gt;
&lt;li&gt;Handoffs(任务交接): 任务可转交给(以 Tool 方式)其他 Agent，其他 Agent 的结果可直接返回给用户, &lt;code&gt;hand off&lt;/code&gt; 就是传球的意思&lt;/li&gt;
&lt;li&gt;Skills(技能): 动态加载特定的 Prompt 和知识，用单一 &lt;code&gt;Agent&lt;/code&gt; 控制按需加载技能，本质上它是单 Agent&lt;/li&gt;
&lt;li&gt;Router(路由)：由一个路径控制任务如何分配给不同的 Agent，结果汇集成一个组合的响应&lt;/li&gt;
&lt;li&gt;Custom workflow(自定义工作流)：用更底层的 &lt;code&gt;LangGraph&lt;/code&gt; 定制工作流程，以上模型均可嵌入到工作流中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面贴上每种类型的组件和时序图(顺便给自己的 Hugo 加上 &lt;code&gt;tabs&lt;/code&gt; 标签页功能，以方便对比切换和节约滚动条)&lt;/p&gt;</description></item><item><title>LangChain 核心组件之 Streaming</title><link>https://yanbin.blog/langchain-core-component-streaming/</link><pubDate>Mon, 04 May 2026 10:18:01 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-core-component-streaming/</guid><description>
&lt;p&gt;一接触 &lt;code&gt;LangChain&lt;/code&gt; 就有种停不下来的感觉，在 &lt;code&gt;Deep Agents&lt;/code&gt; 与底层的 &lt;code&gt;LangGraph&lt;/code&gt; 之间，还是想把 &lt;code&gt;LangChain&lt;/code&gt; 的核心组件搞明白一些，
本文是学习 &lt;code&gt;Streaming&lt;/code&gt; 的笔记。&lt;code&gt;Streaming&lt;/code&gt; 给人的第一印象就是用 &lt;code&gt;ChatGPT&lt;/code&gt; 聊天时机器回复时是一个字一个字蹦出来的，因为这就是 &lt;code&gt;LLM&lt;/code&gt;
的思维方式，不断的预测下一个 Token.&lt;/p&gt;
&lt;p&gt;有个疑问: 这种预测下一个 Token 方式, 中国产的 &lt;code&gt;LLM&lt;/code&gt; 放到 &lt;code&gt;Ollama&lt;/code&gt; 中运行是怎么实现的过滤敏感词的呢？&lt;/p&gt;
&lt;p&gt;前端一个字一个字蹦出来的效果反应到后台 API 响应(以 Ollama 为例)格式是 &lt;code&gt;Content-Type&lt;/code&gt; 为 &lt;code&gt;application/x-ndjson&lt;/code&gt;，这种格式是 &lt;code&gt;Newline Delimited JSON&lt;/code&gt;，即每行是一个 JSON 对象，以 &lt;code&gt;\n&lt;/code&gt; 分隔, &lt;code&gt;Transfer-Encoding&lt;/code&gt; 为 &lt;code&gt;chunked&lt;/code&gt;，表示数据是以分块传输的，所以客户端只要一个个
&lt;code&gt;Chunk&lt;/code&gt; 的输出就是那种效果了。&lt;/p&gt;
&lt;p&gt;从 &lt;code&gt;LLM&lt;/code&gt; 的那种流式(Chunk)输出也能体会出 &lt;code&gt;Markdown&lt;/code&gt; 在逐字显示内容时的优势，自上而下，自然而然的清晰结构。&lt;/p&gt;
&lt;p&gt;下面是 &lt;code&gt;LangChain&lt;/code&gt; 中使用 &lt;code&gt;ChatOllama&lt;/code&gt; 模型时，服务端响应数据的片断：&lt;/p&gt;</description></item><item><title>LangChain 实战 - 使用 On-demand Skills</title><link>https://yanbin.blog/langchain-practice-with-on-demand-skills/</link><pubDate>Sun, 03 May 2026 15:40:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-practice-with-on-demand-skills/</guid><description>
&lt;p&gt;Prompt, Context, Function Calling (Tools), MCP, Agent Skills, Harness, Vibe Coding, 随着 AI 新名词不断的涌现, 对 Tools, MCP
应该比较熟悉了，为了避免自己更早的变成 &lt;code&gt;同事.skill&lt;/code&gt;，将参考 LangChain 官方的实战 &lt;a href="https://docs.langchain.com/oss/python/langchain/multi-agent/skills-sql-assistant"&gt;Build a SQL assistant with on-demand skills&lt;/a&gt;
来学习来理解什么是 &lt;code&gt;Agent Skills&lt;/code&gt; 以及它的工作原理。&lt;/p&gt;
&lt;p&gt;在刚听到 &lt;code&gt;Agent Skills&lt;/code&gt; 这个概念的时候，对它的模糊理解是参考 Tools 是本地工具，MCP 是远程工具，它们都是在 &lt;code&gt;Prompt&lt;/code&gt; 中把工具的名称，描述，
以及参数列表发给了模型，模型会按需通知 &lt;code&gt;Agent&lt;/code&gt; 调用相应的工具(本地 Tool 或远程 MCP Tool); &lt;code&gt;Agent Skills&lt;/code&gt; 大约是在 &lt;code&gt;Prompt&lt;/code&gt; 只包含每个
&lt;code&gt;Skill&lt;/code&gt; 的名称与描述，模型会进一步按需加载相应 &lt;code&gt;Skill&lt;/code&gt; 中的工具，然后指导 &lt;code&gt;Agent&lt;/code&gt; 调用这些工具。&lt;code&gt;Agent Skills&lt;/code&gt; 即按需加载的工具，无需在
一个 &lt;code&gt;Prompt&lt;/code&gt; 中包含所有工具的 &lt;code&gt;Schema&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;下面通过参考 'Build a SQL assistant with on-demand skills' 并实践来验证上面的初步猜想。&lt;/p&gt;</description></item><item><title>LangChain 与 Mem0 集成长期记忆</title><link>https://yanbin.blog/langchain-mem0-integration/</link><pubDate>Sun, 03 May 2026 00:29:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-mem0-integration/</guid><description>
&lt;p&gt;&lt;code&gt;LangChain&lt;/code&gt; 的长期记忆可以在创建 &lt;code&gt;Agent&lt;/code&gt; 时指定 &lt;code&gt;store&lt;/code&gt; 参数，如 &lt;code&gt;create_agent(store=InMemoryStore())&lt;/code&gt;, 但它只是把 &lt;code&gt;Agent&lt;/code&gt; 与
&lt;code&gt;store&lt;/code&gt; 关联了起来，仅此而已，要让长期记忆生效的话必须选择适当的时机，用 &lt;code&gt;Middleware&lt;/code&gt; 或 &lt;code&gt;Tool&lt;/code&gt; 手动的对 &lt;code&gt;store&lt;/code&gt; 进行 &lt;code&gt;get()&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt;,
&lt;code&gt;search()&lt;/code&gt; 等操作。而短期记忆则不同，只要 &lt;code&gt;create_agent(checkpointer=InMemorySaver())&lt;/code&gt; 就让 &lt;code&gt;Agent&lt;/code&gt; 具有了短期记忆能力。&lt;/p&gt;
&lt;p&gt;在使用 &lt;code&gt;store&lt;/code&gt; 的时候，无论是使用 &lt;code&gt;InMemoryStore&lt;/code&gt; 还是 &lt;code&gt;PostgreSQLStore&lt;/code&gt; 等，历史会话的保存与召回还有很多讲究的地方，例如哪些消息需要保存，
消息如何保存(是否要向量化)，新旧消息如何处理等。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mem0.ai/"&gt;Mem0&lt;/a&gt; 是一个为 AI Agent 提供长期记忆能力的开源框架，它的核心思路是利用大语言模型(LLM)把对话内容转化为结构化的 &lt;code&gt;事实&lt;/code&gt;
存入向量数据库，并通过 &lt;code&gt;LLM&lt;/code&gt; 动态维护这些事实的增删改。&lt;code&gt;Mem0&lt;/code&gt; 在存入时会不存入原文，而是用 &lt;code&gt;LLM&lt;/code&gt; 抽取事实，更新时能与旧记忆合并，删除矛盾记忆，
记忆查询也是把文本转换成向量后进行相似度匹配。向量检索擅长语义模糊匹配，关系推理时 Mem0 1.1 之后引入了图记忆(如用 Neo4j 图数据库)作为补充。
提供 MCP 协议支持不同 AI 应用间的记忆共享与互通。&lt;/p&gt;</description></item><item><title>LangChain 核心组件之 Messages</title><link>https://yanbin.blog/langchain-core-component-messages/</link><pubDate>Wed, 29 Apr 2026 16:26:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-core-component-messages/</guid><description>
&lt;p&gt;经历了一番 &lt;code&gt;LangChain&lt;/code&gt; 的学习之后，现在能更好的理解 &lt;code&gt;LangGraph&lt;/code&gt;, &lt;code&gt;LangChain&lt;/code&gt; 以及 &lt;code&gt;Deep Agents&lt;/code&gt; 之间的关系了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LangGraph&lt;/code&gt;: 是该家族的底层实现,代表是 &lt;code&gt;StateGraph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LangChain&lt;/code&gt;: 1.0 后不再是 &lt;code&gt;Chain&lt;/code&gt;，而是 &lt;code&gt;Graph&lt;/code&gt;, 代表是 &lt;code&gt;init_chat_model()&lt;/code&gt; 和 &lt;code&gt;create_agent()&lt;/code&gt;, 前者与模型完成单次请求/响应,&lt;br&gt;
后者能与模型进行完整会话，包括工具的自动调用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Deep Agents&lt;/code&gt;: 是 &lt;code&gt;LangChain&lt;/code&gt; 的高级应用，代表是 &lt;code&gt;create_deep_agent()&lt;/code&gt;, 在 &lt;code&gt;create_agent()&lt;/code&gt; 之上还能进行自动的规划&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;学习过一些基本的和高级概念, 如 &lt;code&gt;Agents&lt;/code&gt;, &lt;code&gt;Models&lt;/code&gt;, &lt;code&gt;Shot-term memory&lt;/code&gt;, &lt;code&gt;MCP&lt;/code&gt;, &lt;code&gt;Human-in-the-loop&lt;/code&gt;, 以及 &lt;code&gt;Long-term memory&lt;/code&gt; 之后,&lt;br&gt;
再一次回到 &lt;code&gt;Agent&lt;/code&gt; 与模型交互的原点上来，即 &lt;code&gt;Messages&lt;/code&gt;. 我们将学习两个层面的消息表示：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Agent&lt;/code&gt; 与模型交互时 &lt;code&gt;Python&lt;/code&gt; 层面的消息表示，&lt;code&gt;LangChain&lt;/code&gt; 与模型无关的消息格式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Agent&lt;/code&gt; 与模型交互时 &lt;code&gt;HTTP&lt;/code&gt; 协议交互层面的请求/响应消息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文依然以本地 &lt;code&gt;Ollama&lt;/code&gt; 模型/服务为例，&lt;code&gt;HTTP&lt;/code&gt; 响应数据都是一个个 &lt;code&gt;Chunk&lt;/code&gt;, 每个 &lt;code&gt;Chunk&lt;/code&gt; 是一个 &lt;code&gt;JSON&lt;/code&gt; 对象，包含一两个 &lt;code&gt;Token&lt;/code&gt;,
我们会直接提取整个响应的 &lt;code&gt;Token&lt;/code&gt; 数据，同时会关掉 &lt;code&gt;Reasoning&lt;/code&gt;(不希望产生 &lt;code&gt;Thinking&lt;/code&gt; 的响应)。&lt;/p&gt;</description></item><item><title>LangChain 高级用法之长期记忆</title><link>https://yanbin.blog/langchain-advanced-usage-long-term-memory/</link><pubDate>Tue, 28 Apr 2026 22:40:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-advanced-usage-long-term-memory/</guid><description>
&lt;p&gt;关于短期记忆已写过两篇 &lt;a href="https://yanbin.blog/langchain-about-session-memory/"&gt;LangChain - 关于会话记忆&lt;/a&gt; 和 &lt;a href="https://yanbin.blog/langchain-core-component-short-term-memory/"&gt;LangChain 核心组件之短期记忆&lt;/a&gt;.
有短期记忆就有长期，记忆的短与长的区分标准是看记忆是否能跨越会话，与选择的存储介质, 时效性，中途模型切换都无关。知期记忆限定在同一个会话当中，
只要没跨会话，即使是一年前聊过的天，重新拣起来继续聊也是短期记忆; 而长期记忆是专指跨越会话的，在一个会话中聊过的，重开一个新的会话，&lt;code&gt;Agent&lt;/code&gt;
还能知道你在别的会话中聊过的内容, 这就是长期记忆。即便这种记忆用内存保存数据，&lt;code&gt;Agent&lt;/code&gt; 重启数据会丢失，但只要能跨会话就是长期记忆。&lt;/p&gt;
&lt;p&gt;所谓的会话就是像 &lt;code&gt;ChatGPT&lt;/code&gt;, &lt;code&gt;Claude&lt;/code&gt; 桌面应用对应的 &lt;code&gt;Chat&lt;/code&gt;, &lt;code&gt;New chat&lt;/code&gt; 就创建了一个新的会话，短期记忆局限于同一个 &lt;code&gt;Chat&lt;/code&gt;, 长期记忆则跨越 &lt;code&gt;Chat&lt;/code&gt;。
同一个会话中聊天，&lt;code&gt;Agent&lt;/code&gt; 的回答一直有当前会话上下文中，是好理解的。长期记忆则是无论你 &lt;code&gt;New chat&lt;/code&gt; 重开了一个新的 &lt;code&gt;Chat&lt;/code&gt;，&lt;code&gt;Agent&lt;/code&gt; 都知道你在其他会话中聊过什么。&lt;/p&gt;
&lt;p&gt;现在的 &lt;code&gt;ChatGPT&lt;/code&gt; 和 &lt;code&gt;Claude&lt;/code&gt; 都具有了长期记忆，这带来一个恐怖的事情，随着你使用它们的时间越来越长，&lt;code&gt;AI&lt;/code&gt; 可能比你还更了解你，它有了你的隐私，
能描绘你的性格，甚至能预测你的下一步行动。看来不想 &lt;code&gt;AI&lt;/code&gt; 介入的太多，难道要经常切换着帐号来使用某个 &lt;code&gt;AI&lt;/code&gt; 工具？还得探索能不能要求删除长期记忆。
如果它们像广告那样粘住你的设备与 IP 就更可怕了。&lt;/p&gt;</description></item><item><title>LangChain 高级用法之人在回路</title><link>https://yanbin.blog/langchain-advanced-usage-human-in-the-loop/</link><pubDate>Mon, 27 Apr 2026 16:06:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-advanced-usage-human-in-the-loop/</guid><description>
&lt;p&gt;&lt;code&gt;Human-in-the-loop&lt;/code&gt; 按字面意思译为 &lt;code&gt;人在回路&lt;/code&gt;, 或 &lt;code&gt;人在循环中&lt;/code&gt;, 还是第一种译法更雅一些。从行为上来说 &lt;code&gt;人工介入&lt;/code&gt; 更能表达它的意图，而 &lt;code&gt;人在回路&lt;/code&gt;
听来让我回想起了 80 年代的一部电视连续剧《人在旅途》。&lt;code&gt;人在回路&lt;/code&gt; 在 &lt;code&gt;Agent&lt;/code&gt; 中很常见, 对于用户安全是必须的，只要是像 &lt;code&gt;Claude&lt;/code&gt;, &lt;code&gt;Copilot&lt;/code&gt;
那些编程 &lt;code&gt;Agent&lt;/code&gt; 想要调用 bash, 或创建文件时都会有提示让用户介入, 比如在 &lt;code&gt;Claude&lt;/code&gt; 控制台中让它创建一个 &lt;code&gt;hello.txt&lt;/code&gt; 文件，立即就是下面熟悉的提示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; Do you want to create hello.txt?
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; ❯ 1. Yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; 2. Yes, allow all edits during this session (shift+tab)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; 3. No
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; Esc to cancel · Tab to amend
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是 &lt;code&gt;Agent&lt;/code&gt; 在碰到某些敏感性的工具调用之前会停下来让人 Review 作出决策，是准许，拒绝，还是进一步编辑工具调用(更改工具名称或参数)。
&lt;code&gt;LangChain&lt;/code&gt; 通过中间件和 &lt;code&gt;interrupt&lt;/code&gt; 把控制权从 &lt;code&gt;agent.invoke()&lt;/code&gt; 中让出来，人工决策后，再用 &lt;code&gt;agent.invoke()&lt;/code&gt; 向模型发送 &lt;code&gt;resume&lt;/code&gt;
的消息，多个 &lt;code&gt;agent.invoke()&lt;/code&gt; 都处在同一个会话当中，所以我们需要用到短期记忆。&lt;/p&gt;</description></item><item><title>Java 25 新特性学习 - Scoped Values</title><link>https://yanbin.blog/java-25-new-features-scoped-values/</link><pubDate>Fri, 24 Apr 2026 23:34:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-25-new-features-scoped-values/</guid><description>
&lt;p&gt;Java 25 是一个 LTS 版本，它的众多新特性中就数 &lt;a href="https://openjdk.org/jeps/506"&gt;&lt;code&gt;JEP 506: Scoped Values&lt;/code&gt;&lt;/a&gt; 能改变我们从前使用
&lt;code&gt;ThreadLocal&lt;/code&gt; 时的编程范式。因其重要性，所以单列一篇来专门学习它。&lt;code&gt;Scoped Values&lt;/code&gt;, 可用来替代 &lt;code&gt;ThreadLocal&lt;/code&gt; 的使用，特别是在虚拟线程当中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Scoped Values&lt;/code&gt; 用于在当前线程或子线程中共享不可变的数据，说到不可变的数据就会想到 &lt;code&gt;JEP 502: Stable Values (Preview)&lt;/code&gt;.
&lt;code&gt;ThreadLocal&lt;/code&gt;， 甚至 &lt;code&gt;InheritableThreadLocal&lt;/code&gt; 用于子线程中共享数据还是有些挑战性，特别碰到线程池的情况, 而且 &lt;code&gt;ThreadLocal&lt;/code&gt;
不在乎数据可变还是不可变的，执行当中谁改了数据，不知道。&lt;code&gt;Scoped Values&lt;/code&gt; 的 JEP 说其目的不是用来完全替代 &lt;code&gt;ThreadLocal&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;下面来看看 &lt;code&gt;Scoped Values&lt;/code&gt; 如何在子线程中共享数据，它还能与虚拟线程的结构化并发配合使用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ThreadLocal&lt;/code&gt;/&lt;code&gt;InteritableThreadLocal&lt;/code&gt; 仍然是很多框架用来在线程中(间)共享数据的办法，在 &lt;code&gt;Spring&lt;/code&gt; 框架中有大量的使用， 如
&lt;code&gt;XxxContextHolder&lt;/code&gt; 之类的。目前的 &lt;code&gt;ThreadLocal&lt;/code&gt; 存在一些问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ThreadLocal 中的数据是可变的，多数应用场景只要不可变的数据&lt;/li&gt;
&lt;li&gt;难以管理共享数据的生命周期，特别是在线程池中，线程被重用时，ThreadLocal 中的数据可能会被意外共享或泄露。由使用方主动清除数据，更是会造成潜在的 Bug&lt;/li&gt;
&lt;li&gt;成本高昂，创建子线程，或线程切换时要对共享数据进行复制，&lt;code&gt;VirtualThread&lt;/code&gt; 也是继承自 &lt;code&gt;Thread&lt;/code&gt;, 所以虚拟线程也能用 &lt;code&gt;ThreadLocal&lt;/code&gt;。
一旦虚拟线程的数量达到成千上万，十百万的级别时，&lt;code&gt;ThreadLocal&lt;/code&gt; 数据不停复制的代价就很高了&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>LangChain 高级用法之 MCP</title><link>https://yanbin.blog/langchain-advanced-usage-mcp/</link><pubDate>Thu, 23 Apr 2026 22:16:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-advanced-usage-mcp/</guid><description>
&lt;p&gt;&lt;code&gt;LangChain&lt;/code&gt; 1.0 于 2025 年 10 月 22 日发布，这是一个里程碑式的版本，听说在 0.x 要创建一个 &lt;code&gt;agent&lt;/code&gt; 很麻烦， 那时候内部是真正的 &lt;code&gt;链&lt;/code&gt;，
1.0 后虽然还叫 &lt;code&gt;LangChain&lt;/code&gt;, 实际上内部实现是图(&lt;code&gt;LangGraph&lt;/code&gt;), 用 &lt;code&gt;create_agent()&lt;/code&gt; 创建 &lt;code&gt;agent&lt;/code&gt;. 从数据结构来看，&lt;code&gt;图&lt;/code&gt; 比 &lt;code&gt;链表&lt;/code&gt;
更能直观的表达 &lt;code&gt;Agent&lt;/code&gt; 与模型及工具的交互场景。很庆幸在 &lt;code&gt;LangChain&lt;/code&gt; 1.0 之后才开始学习这个框架，不用体验 &lt;code&gt;LangChain&lt;/code&gt; 0.x 的痛苦。&lt;/p&gt;
&lt;p&gt;大概对 &lt;code&gt;LangChain&lt;/code&gt; 的 tools 有些许了解之后，现在跳到 &lt;code&gt;Model Context Protocol(MCP)&lt;/code&gt; 协议这一章，本人对 &lt;code&gt;MCP&lt;/code&gt; 的初步理解是相对于工具，
&lt;code&gt;MCP&lt;/code&gt; 是一个远程(跨进程)的工具。为了方便的使用互联网上的各种资源，&lt;code&gt;MCP&lt;/code&gt; 在实现一个完备的 &lt;code&gt;Agent&lt;/code&gt; 也是一个非常重要的工具。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/introduction"&gt;Model Context Protocol (MCP)&lt;/a&gt; 是 &lt;code&gt;Anthropic&lt;/code&gt; 推出并开放的协议，用于构建
&lt;code&gt;Agent&lt;/code&gt; 与外部资源的交互,下面会与工具对照着学习它. 以前也写过一篇关于 &lt;code&gt;MCP&lt;/code&gt; 的文章，今天从不同的角度再次强化对 &lt;code&gt;MCP&lt;/code&gt; 的理解。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;LangChain&lt;/code&gt; 中要使用 &lt;code&gt;MCP&lt;/code&gt; 需安装 &lt;code&gt;langchain-mcp-adapters&lt;/code&gt; 依赖，然后使用它的 &lt;code&gt;MultiServerMCPClient&lt;/code&gt;, 它是无状态的。要创建自己的
&lt;code&gt;MCP&lt;/code&gt; 服务，使用 &lt;a href="https://gofastmcp.com/getting-started/welcome"&gt;&lt;code&gt;FastMCP&lt;/code&gt;&lt;/a&gt; 库。&lt;/p&gt;</description></item><item><title>LangChain 核心组件之短期记忆</title><link>https://yanbin.blog/langchain-core-component-short-term-memory/</link><pubDate>Tue, 21 Apr 2026 19:11:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-core-component-short-term-memory/</guid><description>
&lt;p&gt;学习了 &lt;code&gt;Agent&lt;/code&gt;, &lt;code&gt;Models&lt;/code&gt; 之后直接跳到 &lt;code&gt;Short Term Memory&lt;/code&gt; 节，短期记忆能让我们更好的理解与模型的交互, 为什么与机器人越聊到最后它可能就
偏离主题，智商降低了。前面所用的手动拼接整个会话历史和使用 &lt;code&gt;InMemorySaver&lt;/code&gt; 就是模型的短期记忆，如果会话历史过长，超过上下文件大小，
导致上下文丢失或错误，这就需要对会话进行压缩，压缩做了些什么事情呢，这就是本文要学习的内容。&lt;/p&gt;
&lt;p&gt;之前写过一篇 &lt;a href="https://yanbin.blog/langchain-about-session-memory/"&gt;LangChain - 关于会话记忆&lt;/a&gt;, 也是关于短期记忆的，这里重新阅读官方的文档，也是加以巩固。&lt;/p&gt;
&lt;p&gt;模型是没有记忆的，所有你和模型说过的话你都必须记住，记忆就是让 Agent 记住你与模型的会话历史，短期记忆特指单一会话的历史。短期记忆与长期记忆这两
个概念总会让人有所迷惑的，简单的可以这么理解，比如使用 ChatGPT 时&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在同一个 &lt;code&gt;Chat&lt;/code&gt; 中的对话就是短期记忆，即便你一年后再回到那个没有删的 &lt;code&gt;Chat&lt;/code&gt; 中接着聊，也是短期记忆&lt;/li&gt;
&lt;li&gt;而长期记忆是跨会话的，比如在某个 &lt;code&gt;Chat&lt;/code&gt; 中说过喜欢猪头饭，再点 &lt;code&gt;New chat&lt;/code&gt; 打开一个新的 &lt;code&gt;Chat&lt;/code&gt;，问今天吃什么，模型直接建议吃猪头饭，这就是长期记忆&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;还是那句话，大语言模型是没有记忆的, 和大语言模型对话就像和一个失忆症的人对话, 每次问话都必需把之前的对话复述一遍, 过程相当于:&lt;/p&gt;</description></item><item><title>我的第一个 LangChain AI Agent</title><link>https://yanbin.blog/my-first-langchain-ai-agent/</link><pubDate>Sat, 18 Apr 2026 15:35:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/my-first-langchain-ai-agent/</guid><description>
&lt;p&gt;学习了一段时间 LangChain，了解到用 &lt;code&gt;create_agent()&lt;/code&gt; 创建的 Agent 的用法，以及底层 &lt;code&gt;init_chat_model()&lt;/code&gt; 与模型的交互，决定以古法的方式
亲自创建一个 AI Agent, 要实现的功能是把原来用 Apache &lt;code&gt;Airflow&lt;/code&gt; 做的一个从猫收留网站上查看有没有新进入收容所的猫，有则发邮件通知。换成 AI
Agent 的话，功能列表是&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;使用 AWS Bedrock 上的一个 Claude 模型&lt;/li&gt;
&lt;li&gt;用 Telegram 创建一个 Bot, 配置好 Bot Token 与 Chat ID&lt;/li&gt;
&lt;li&gt;工具方面，提供读文件，写文件，和更新文件的函数，web_fetch, 还有向 Telegram 发送消息的函数&lt;/li&gt;
&lt;li&gt;AI Agent 从一个特定的网页上收集猫的信息，借助文件判定是否是新的&lt;/li&gt;
&lt;li&gt;发现新的猫，向 Telegram Bot 发送通知, 每只新猫一个消息，消息包含猫的基本信息，图片与链接&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;</description></item><item><title>LangChain 核心组件之 Models</title><link>https://yanbin.blog/langchain-core-component-models/</link><pubDate>Thu, 16 Apr 2026 14:39:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-core-component-models/</guid><description>
&lt;p&gt;在上一篇 &lt;a href="https://yanbin.blog/langchain-core-component-agents"&gt;LangChain 核心组件之 Agent&lt;/a&gt; 有介绍到模型。模型其实是一个很粗泛的概念，放到任何领域都有立身之地，
比如各种建模，经济增长模型，Covid 感染模型等。但来到 AI 时代，会不会只要有人一开口说模型便默认为大语言模型(LLM)呢？而如今的 LLM 模型又基本就是
Transformer 模型，所谓的模型开源只是开放了一堆的 Token 的权重值，不同源软件开源，拿过来能随意定制使用。&lt;/p&gt;
&lt;p&gt;大语言模型更像是人类知识的压缩包，可用它生成多种形式的内容，比如文本、图像、音频等。模型在生成内容的过程中，还支持下面几种形式的交互&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;工具调用：可以引导模型通知 Agent 调用工具，如计算，互联网搜索，API 调用等&lt;/li&gt;
&lt;li&gt;结构化输出：模型本身就支持按照约定的规则格式化输出内容&lt;/li&gt;
&lt;li&gt;多模态：不仅能生成文本，还能生成图像、音视频等&lt;/li&gt;
&lt;li&gt;推理：模型可以进行推理，如数学计算、逻辑推理等，就是经常看到的 Thinking 的过程&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最初在 Llama3 刚发布的年代，在使用 MCP 时还要查哪个模型支不支持工具使用，现在基本上以上特性是模型的标配了。现在几大商业模型就是 OpenAI 的 GPT，
Anthropic 的 Claude，Google 的 Gemini, Musk 的 Grok 在下一梯队。国外的模型选择很清晰，反而中国的大语言模型众多，
仿佛不出个自己的大语言模型就像个互联网公司。&lt;/p&gt;</description></item><item><title>LangChain 核心组件之 Agent</title><link>https://yanbin.blog/langchain-core-component-agents/</link><pubDate>Tue, 14 Apr 2026 15:14:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-core-component-agents/</guid><description>
&lt;p&gt;经过了一番 &lt;code&gt;LangChain&lt;/code&gt; 的学习之后，我们开始跟随 &lt;code&gt;LangChain&lt;/code&gt; 的官方文档系统性的学习。首先是它的核心组件，包括 &lt;code&gt;Agents&lt;/code&gt;, &lt;code&gt;Models&lt;/code&gt;,
&lt;code&gt;Messages&lt;/code&gt;, &lt;code&gt;Tools&lt;/code&gt;, &lt;code&gt;Short-term memory&lt;/code&gt;, &lt;code&gt;Streaming&lt;/code&gt;, 和 &lt;code&gt;Structured output&lt;/code&gt;. 现在从 &lt;a href="https://docs.langchain.com/oss/python/langchain/agents"&gt;Agents&lt;/a&gt;
开始。&lt;/p&gt;
&lt;h3 id="创建-agent"&gt;创建 Agent&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;create_agent&lt;/code&gt; 是可用于正式产品中创建 Agent 的函数， 它返回的是一个 &lt;code&gt;langgraph.graph.state.CompiledStateGraph&lt;/code&gt; 对象，而非一个 &lt;code&gt;XxxAgent&lt;/code&gt;
样的东西。所以它创建的是一个状态图，图吗，就有顶点和边，Agent 就是这个图中移动，比如在 &lt;code&gt;Model&lt;/code&gt; 和 &lt;code&gt;Tools&lt;/code&gt; 之间往复, 或在中间加入的互动的(Human-in-the-loop).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;LangGraph&lt;/code&gt; 把与模型，工具，以及人的交互做成一个图还是很表意的，下面是来源于官方文档的表示 Agent 的状态图。&lt;/p&gt;</description></item><item><title>Java 24 新特性学习</title><link>https://yanbin.blog/java-24-new-features/</link><pubDate>Sun, 12 Apr 2026 13:30:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-24-new-features/</guid><description>
&lt;p&gt;Java 24 也是一个过渡版本, 还是到下面两个链接中找相应的更新&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/javase/24all-relnotes.html#JSERN24"&gt;JDK 24 Release Notes - Major New Functionality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/projects/jdk/24/"&gt;OpenJDK JDK 24 Features&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;IntelliJ IDEA 对 Java 24 Language level 描述是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;24 - No new language features&lt;/li&gt;
&lt;li&gt;24 (Preview) - Flexible constructor bodies, simple source files, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把上面第二个链接中的特性列出来&lt;/p&gt;
&lt;div style="display: flex;font-size: 14px;"&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;404: &lt;a href="https://openjdk.org/jeps/404"&gt;Generational Shenandoah (Experimental)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;450: &lt;a href="https://openjdk.org/jeps/450"&gt;Compact Object Headers (Experimental)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;472: &lt;a href="https://openjdk.org/jeps/472"&gt;Prepare to Restrict the Use of JNI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;475: &lt;a href="https://openjdk.org/jeps/475"&gt;Late Barrier Expansion for G1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;478: &lt;a href="https://openjdk.org/jeps/478"&gt;Key Derivation Function API (Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;479: &lt;a href="https://openjdk.org/jeps/479"&gt;Remove the Windows 32-bit x86 Port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;483: &lt;a href="https://openjdk.org/jeps/483"&gt;Ahead-of-Time Class Loading &amp; Linking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;484: &lt;a href="https://openjdk.org/jeps/484"&gt;Class-File API&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;485: &lt;a href="https://openjdk.org/jeps/485"&gt;Stream Gatherers&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;486: &lt;a href="https://openjdk.org/jeps/486"&gt;Permanently Disable the Security Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;487: &lt;a href="https://openjdk.org/jeps/487"&gt;Scoped Values (Fourth Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;488: &lt;a href="https://openjdk.org/jeps/488"&gt;Primitive Types in Patterns, instanceof, and switch (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;489: &lt;a href="https://openjdk.org/jeps/489"&gt;Vector API (Ninth Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;490: &lt;a href="https://openjdk.org/jeps/490"&gt;ZGC: Remove the Non-Generational Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;491: &lt;a href="https://openjdk.org/jeps/491"&gt;Synchronize Virtual Threads without Pinning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;492: &lt;a href="https://openjdk.org/jeps/492"&gt;Flexible Constructor Bodies (Third Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;493: &lt;a href="https://openjdk.org/jeps/493"&gt;Linking Run-Time Images without JMODs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;494: &lt;a href="https://openjdk.org/jeps/494"&gt;Module Import Declarations (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;495: &lt;a href="https://openjdk.org/jeps/495"&gt;Simple Source Files and Instance Main Methods (Fourth Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;496: &lt;a href="https://openjdk.org/jeps/496"&gt;Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;497: &lt;a href="https://openjdk.org/jeps/497"&gt;Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;498: &lt;a href="https://openjdk.org/jeps/498"&gt;Warn upon Use of Memory-Access Methods in sun.misc.Unsafe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;499: &lt;a href="https://openjdk.org/jeps/499"&gt;Structured Concurrency (Fourth Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;501: &lt;a href="https://openjdk.org/jeps/501"&gt;Deprecate the 32-bit x86 Port for Removal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Java 24 中列出的特性恰如其版本一样，有 24 条，因其是一个过渡版本，多数为非正式特性，也就为什么 IntelliJ IDEA 从语言特性上把 Java 标记为
No new language features. 其实也不然，个人觉得有两个正式特性值得关注，即 Class-File API 和 Stream Gathers。&lt;/p&gt;</description></item><item><title>LangChain - 关于会话记忆</title><link>https://yanbin.blog/langchain-about-session-memory/</link><pubDate>Fri, 10 Apr 2026 15:49:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-about-session-memory/</guid><description>
&lt;p&gt;所谓大语言模型的记忆功能，就是要在说当前这句话的时候让 LLM 知道整个对话的上下文。比如说与人对话&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A: 给我讲个笑话&lt;/li&gt;
&lt;li&gt;B: 有一天，有人问你：“你喜欢什么？”, 你回答：“我喜欢一切……&lt;strong&gt;只要不叫‘考试’&lt;/strong&gt;。” 😄 够快，够短，有没有让你会心一笑？&lt;/li&gt;
&lt;li&gt;A: 再来一个&lt;/li&gt;
&lt;li&gt;B: 问：&lt;strong&gt;海里面最怕什么？&lt;/strong&gt; 答：&lt;strong&gt;脱水。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;#3 当 A 问 &amp;quot;再来一个&amp;quot; 时候，B 因为有基于前面对话的上下文，所以能理解 &amp;quot;再来一个&amp;quot; 来一个笑话，而不是一个妞什么的，也记得曾经讲过一个笑话，而不
至于重复之前的笑话。如果突然有一天 A 对 B 突兀的说 &amp;quot;再来一个&amp;quot;，B 就会有些傻眼了，AI 在没有上下文的情况也是一样。&lt;/p&gt;
&lt;p&gt;不过人与人之间对话的上下文是互相的，两方的共同记忆，双方都是建立在你知道我问了什么，你自己回复了什么的基础之上的。而于大语言模型的对话时，
大语言模型是没有记忆的，就好像和一个失忆的人对话一样，记忆责任全在一方，你和 LLM 每一次对话时都要不停的唠叨：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我曾经问了这个，你回复过那个，问过这个，你回复过那个......，我现在问一个问题，请基于我们的会话历史作出回答&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;和机器对话就是这么傻傻的，当对话历史过大的时候因为上下文大小限制的原因，就必须压缩总结之前的对话内容，压缩后总会失真，这就是为什么大语言模型会
不断降智的一个原因。&lt;/p&gt;</description></item><item><title>为什么 OpenClaw 会狂烧你的 Token</title><link>https://yanbin.blog/why-openclaw-burn-your-token/</link><pubDate>Thu, 09 Apr 2026 10:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/why-openclaw-burn-your-token/</guid><description>
&lt;p&gt;前段时间 OpenClaw 确实很火，尤其是在中国，它在 2025 年 11 月上线后，最初名字是 Clawbot, 中间更名为 Molbot, 最后定名为 OpenClaw. 我在它
真正火热当中并没有产生多大兴趣去跟风，只最近一个月想看看它到底是个什么东西。弄了个虚拟机装上后，就是可以连接各个大模型， 打通与国外流行的即时通讯
软件，能自动化的就是它的 Cron Jobs.&lt;/p&gt;
&lt;p&gt;界面上功能不少，最后发现真正有用的就是那个 Cron Jobs, 比如自己设置了一个每日两次的定时任务，实现的是从监控一个猫收容网上的列表，当发现有新的猫
就发送到指定的 Telegram Bot 上，描述很简单，但 Agent 确实很聪明，不用告诉它细节，它自己知道怎么本地文件来辅助判断是否为新猫。聪明的另一个原因是
OpenClaw 蹭了我 $20 Claude 会员的光。但到 4 月 4 日，Claude 订阅会员不让绑定到 OpenClaw 之后，试用了 Google 免费的 API Key 不怎么理想。&lt;/p&gt;
&lt;p&gt;也不想再多折腾，如果只是要一个 &lt;code&gt;Cron Job&lt;/code&gt; 的话可以自己用 &lt;code&gt;LangChain&lt;/code&gt; 实现一个。听说 OpenClaw 十分耗 Token, 于是对它的提示词有点好奇。
本文将查看一下一个新鲜的 &lt;code&gt;OpenClaw&lt;/code&gt; 的提示词长什么样子。&lt;/p&gt;
&lt;p&gt;于是又新装了一个 &lt;code&gt;OpenClaw&lt;/code&gt; v2026.4.9 版本，使用本地的 &lt;code&gt;Ollama&lt;/code&gt; 模型，由于未能成功设置 &lt;code&gt;OpenClaw&lt;/code&gt; 使用代理来访问 &lt;code&gt;Ollama&lt;/code&gt; API， 于是在
&lt;code&gt;Ollama&lt;/code&gt; service 前端放了一个反向代理，该反向代理也是临时用 Vibe Coding 搓出来的。让 &lt;code&gt;OpenClaw&lt;/code&gt; 使用通过反向代理 &lt;code&gt;http://localhost:9091&lt;/code&gt;
来访问实际的 &lt;code&gt;Ollama&lt;/code&gt; &lt;code&gt;http://localhost:11434&lt;/code&gt;, 并在反向代理中记录下对 &lt;code&gt;Ollama&lt;/code&gt; API 完整的请求与响应数据，使用什么模型都无所谓。&lt;/p&gt;</description></item><item><title>LangChain Quick Start AI Agent 分析</title><link>https://yanbin.blog/langchain-quickstart-agent-analysis/</link><pubDate>Wed, 08 Apr 2026 20:06:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-quickstart-agent-analysis/</guid><description>
&lt;p&gt;上一篇 &lt;a href="https://yanbin.blog/langchain-1-2-get-started/"&gt;LangChain 1.2 入门学习&lt;/a&gt; 中使用了特定的 ChatOllama/ChatGoogleGenerativeAI, 或统一的
init_chat_model 方式创建一个与 LLM 的交互程序，并以此基础实现了一个简单的 AI Agent, 工具调用是根据响应中消息格式手工调用的，
会话处理是自己拼接全部对话历史。现在知名的 LLM 都支持推理，所以顺代也用灰色字体显示了 'thinking' 的文本内容。&lt;/p&gt;
&lt;p&gt;本文直接学习官方 &lt;a href="https://docs.langchain.com/oss/python/langchain/quickstart"&gt;Quickstart&lt;/a&gt; 中的一个 &lt;code&gt;AI Agent&lt;/code&gt;, 该 Agent
实现了以下功能&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;有了上下文，会记忆会话历史&lt;/li&gt;
&lt;li&gt;支持调用多个工具&lt;/li&gt;
&lt;li&gt;提供结构化一致性的响应数据格式&lt;/li&gt;
&lt;li&gt;能通过上下文处理用户特定的信息&lt;/li&gt;
&lt;li&gt;跨交互维护会话状态&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>LangChain 1.2 入门学习</title><link>https://yanbin.blog/langchain-1-2-get-started/</link><pubDate>Tue, 07 Apr 2026 21:25:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/langchain-1-2-get-started/</guid><description>
&lt;p&gt;AI 界日新月异，新名词层出不穷，像 Prompt Engineering, Context Engineering, 到如今又来了 Harness Engineering. 现在最怕两个人出来搞事情，
Dario Amodei 和 Andrej Karpathy, 前者宣称 AI 可替代一切，人变得一文不值，后者专造名词。在大语言模型时代，搞机器学习了，专注模型的不用多少人，
多数人还能在 AI 上蹭热点的话就剩下 AI Agent 的这个赛道了，其余就是应用了，例如 Vibe Coding，或更贴近具体业务的应用。&lt;/p&gt;
&lt;p&gt;像各大 AI 编程工具，如 Codex, Claude Code, OpenCode, GitHub Copilot， Gemini, Cursor, Antigravity, Trae 本质上都是在比拼各家
AI Agent 的能力。最近火的那个 OpenClaw 也是一个学习 AI Agent 的好范例。&lt;/p&gt;
&lt;p&gt;而 AI Agent 方面的框架首当其冲就是 LangChain, 它提供了 Python 和 TypeScript 两种语言的支持，网上有人做了个
&lt;a href="https://github.com/langchain4j/langchain4j"&gt;langchain4j&lt;/a&gt;, 这种跟随项目恐怕也坚持不了多久。其他 AI Agent 框架有 [Pydantic AI]，
微软的 AutoGen, Crew AI, AWS Strands Agents. 各大语言模型 OpenAI, Anthropic, Gemini 都有自己的 SDK, 但要开发一个 Multi Agent
的系统更需要一个第三方的 AI Agent 框架来整合各家模型的能力，提供统一的接口和工具，这个框架就是 LangChain.&lt;/p&gt;</description></item><item><title>一个简单的 C++ 实现的 gRPC 服务</title><link>https://yanbin.blog/cpp-grpc-server-example/</link><pubDate>Sun, 29 Mar 2026 22:32:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cpp-grpc-server-example/</guid><description>
&lt;p&gt;上一篇 &lt;a href="https://yanbin.blog/spring-grpc-service-demo/"&gt;使用 Spring 的 gRPC 服务&lt;/a&gt; 学习了如何用 Java SpringBoot 4 + Spring gRPC 实现服务端与客户端，
并且用 Postman 和 grpcurl 命令测试了如何访问 Java 实现的 gRPC 服务，其中还用 Wireshark 抓包工具进一步验证了 gRPC 的通信方式是 Protobuf
Over HTTP/2。&lt;/p&gt;
&lt;p&gt;完后又突然对 C++ 实现一个 gRPC 服务产生了兴趣，因为现实项目是想要把 C++ 动态库的调用代码独立为一个进程，这时候直接用 C++ 代码调用 C++ 动态库
就是最简单直接的方式，不存在任何数据结构的转换。把这部分实现为一个 C++ 的 gRPC 服务，然后由 Java 的 gRPC 客户端来调用。&lt;/p&gt;
&lt;p&gt;下面来看这样一个简单的例子，在 C++ 只实现一个只包含一个同步的 gRPC 的服务，测试将用 grpcurl 命令和 Postman，并且也用 Wireshark 抓包工具验证 gRPC
的通信方式是否依然是 HTTP/2, Protobuf 数据编码肯定没得说了。&lt;/p&gt;</description></item><item><title>使用 Spring 的 gRPC 服务</title><link>https://yanbin.blog/spring-grpc-service-demo/</link><pubDate>Sun, 29 Mar 2026 13:52:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-grpc-service-demo/</guid><description>
&lt;p&gt;人们在跨语言, 跨进程通信方面采用过不少的方案, 交换文件, CORBA, SOAP, 最后得到最广泛应用的是 RESTful API, 交换格式通常用文本格式的 JSON 和
XML. 但作为更高效的通信还是二进制格式, 在 Java 方面, 有过 Java 内置的 RMI, Spring 的 Hessian, Dubbo. 而发展到今天 gRPC 受到了更多的关注,
gRPC 的通信协议是 HTTP/2, 编码格式是 Google 高效的 Protobuf.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://grpc.io"&gt;gRPC&lt;/a&gt; 是由 Google 发起的一个远程调用框架, 是 &lt;strong&gt;g&lt;/strong&gt;RPC &lt;strong&gt;R&lt;/strong&gt;emote &lt;strong&gt;P&lt;/strong&gt;rocedure &lt;strong&gt;C&lt;/strong&gt;alls 的缩写, 各处的解释是
&lt;strong&gt;g&lt;/strong&gt; 最初是代表 Google, 现在只是像 &lt;strong&gt;Y&lt;/strong&gt;AML &lt;strong&gt;A&lt;/strong&gt;in't &lt;strong&gt;M&lt;/strong&gt;arkup &lt;strong&gt;L&lt;/strong&gt;anguage 类似的命名, 说 &lt;strong&gt;g&lt;/strong&gt; 不再代表 Google, 怎么听起来有点
既要又要的感觉, 怎么都会认为 gRPC 为 Google 的 RPC.&lt;/p&gt;
&lt;p&gt;总之, gRPC 是一个高性能的开源统一的 RPC 框架, 能叫做统一是因为它支持多种语方, 如 Go, C++, Java, Python, Node, C#, PHP 等, 多语言支持是
由 Protobuf 格式决定的, 总之它是 Protobuf over HTTP/2, 实现上是用 Protobuf 定义数据结构与服务方法, 再映射成不同语言的代码实现. gRPC
已然成为了 RPC 的标准, 真正意识到它的不一般是在 Postman 中发现了它, 可见其在业界受不到了应用的重视.&lt;/p&gt;</description></item><item><title>Java 22 - Foreign Function Memory API 与 JNA</title><link>https://yanbin.blog/java-22-foreign-function-memory-api-jna/</link><pubDate>Mon, 23 Mar 2026 14:26:35 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-22-foreign-function-memory-api-jna/</guid><description>
&lt;p&gt;Java 22 正式引入了 &lt;a href="https://openjdk.org/jeps/454"&gt;Foreign Function &amp;amp; Memory API&lt;/a&gt;, 简称 FFM API, 它源自于 &lt;a href="https://openjdk.org/projects/panama/"&gt;Project Panama&lt;/a&gt;,
旨在用来替代 JNI, 提供更安全，健壮的本地动态库调用方式。之前在一文 &lt;a href="https://yanbin.blog/java-22-new-features/"&gt; Java 22 新特性学习&lt;/a&gt; 中跳过了对它的体验，
因为直接用 FFM API 来调用本地代码的话，写起来比 JNI 还更复杂，更别提和 JNA 对比，性能上 FFM API 大致与 JNI 相当，但总比 JNA 要好些，
原因是 JNA 使用了一个与系统相关的中间层动态库。像这种调用本地代码的场景，性能的差异基本体现在本地代码上，调用过程的性能差异一般可以忽略不计.&lt;/p&gt;
&lt;p&gt;那时没深入 FFM API 的原因是它用起来过于复杂，这种复杂性应该由第三方库来屏蔽，可是自 Java 22 于 2024-03-19 发布以来目前仍未找到一款类似于
JNA 屏蔽了 JNI 那样的基于 FFM 的第三方库。所以还是事先来体验一下 FFM API 的开发过程，现在可以借助 AI 来引导学习, 并在后面与 JNA 作个性能对比，
只供参考。&lt;/p&gt;</description></item><item><title>简单例子用 Python + PostgreSQL 演示 RAG</title><link>https://yanbin.blog/rag-python-postgresql-pgvector/</link><pubDate>Thu, 19 Mar 2026 16:22:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rag-python-postgresql-pgvector/</guid><description>
&lt;p&gt;RAG(Retrieval-Augmented Generation) 中文名为检索增强生成, 在 LLM 更早期火过的概念，因为那时候上下文较小，所以要检索 LLM 中没有内容(私有数据)
须先在本地用相关性算法找到一些相关的片断，拼接到输入提示词中发送给 LLM。而目前上下文都达到 1M 以上的级别，一次会话甚至可以把私有的内容全部塞
提示词中而喂给 LLM, 就不必用 RAG, 而且内容更完整. 比如你可以把整部小说内容让 LLM 去阅读，然后根据输出总结，或讨论关于该小说的各种问题。
像现在的 Agent Skills 的 Reference 就会把一大段内容丢给 LLM.&lt;/p&gt;
&lt;p&gt;所谓的检索(Retrieval) 即在与 LLM 交互之前，从本地(如向量数据库)中找到一些相关的片断，拼接到提示词中，以此达到增强内容生成的效果.&lt;/p&gt;
&lt;p&gt;这里不去讨论 RAG 是否已死的问题，只想简单的用 Python, PostgreSQL 加 pgvector 扩展来体验一下什么是 RAG, 以及它的基本流程是什么样子的.
并且对向量数据库中是如何存储和检索的.&lt;/p&gt;</description></item><item><title>AWS ECS 使用 EC2 Capacity Provider (Managed Instances)</title><link>https://yanbin.blog/aws-ecs-capacity-provider-managed-instances/</link><pubDate>Tue, 10 Mar 2026 22:33:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-ecs-capacity-provider-managed-instances/</guid><description>
&lt;p&gt;上篇 &lt;a href="https://yanbin.blog/aws-ecs-capacity-provider-ec2-auto-scaling/"&gt;AWS ECS 使用 EC2 Capacity Provider (EC2 Auto Scaling)&lt;/a&gt; 学习了如何在 ECS
中使用 Capacity Provider + EC2 Auto Scaling 来部署一个简单的 Web 应用，以及了解 ECS 如何管理 EC2 的 Auto Scaling Group.&lt;/p&gt;
&lt;p&gt;ECS Capacity Providers 是于 2019 年 12 月发布的，随同的功能支持了 Fargate, Fargate Spot, 和 EC2 AutoScaling.
而 Managed Instances 在 2025-09-30 才加入的新特性，见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/09/amazon-ecs-managed-instances/"&gt;Announcing Amazon ECS Managed Instances&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;在近六年之间, AWS 大概也理解到了 Capacity Provider + EC2 Auto Scaling 的复杂性，虽说可由 ECS 来管理 EC2 的 ASG, 但毕竟有个 ASG 在那里。
ECS 与 EC2 ASG 之间联接由 CloudWatch Metrics, Alarms, EC2 ASG 的 Dynamic scaling policies 和 Lifecycle hooks 的一整套机制协作。
经常还不得不在 EC2 ASG 与 ECS 两个界面之间来回找问题。而 Managed Instances 的出现则将 EC2 实例的管理完全透明化了，在 EC2 端压根就不存在
一个相应的 ASG, 更不需要 CloudWatch Alarms 之类的关联组件。&lt;/p&gt;
&lt;p&gt;Managed Instances 与 EC2 Auto Scaling 之间就好比 Serverless 与 非 Serverless 的区别，用 Managed Instances 之后你只管控制好 ECS
Desired Count(ECS AutoScaling), 其余的都由 Managed Instances 来管理，在界面上只需要关注 ECS Infrastructure 中的 Container Instances.
由 Managed Instances 管理的 EC2 实例，你即使用有管理员权限都无法关闭它，只能全权由 Managed Instances 来控制。试图关闭这样的 EC2 实例时报错&lt;/p&gt;</description></item><item><title>AWS ECS 使用 EC2 Capacity Provider (EC2 Auto Scaling)</title><link>https://yanbin.blog/aws-ecs-capacity-provider-ec2-auto-scaling/</link><pubDate>Tue, 10 Mar 2026 15:40:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-ecs-capacity-provider-ec2-auto-scaling/</guid><description>
&lt;p&gt;在我 X 中 &lt;a href="https://x.com/YanbinQiu/status/2029611669068951703"&gt;AWS 上不同应用的部署策略&lt;/a&gt; 中提到过在 AWS 部署服务(特别是 Web 服务时)， 基于采用过以下演进的方式&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EC2(AutoScaling) + 直接 EC2 中部署本地应用, 靠 userdata 完成应用部署
2 EC2(AutoScaling) + ECS(AutoScaling), 运行容器，Replica 模式，有两个层次的 AutoScaling 要单独控制，比较麻烦&lt;/li&gt;
&lt;li&gt;ECS + EC2(AutoScaling), 运行容器，Daemon 模式。由 EC2 来驱动部署 ECS Task&lt;/li&gt;
&lt;li&gt;ECS(AutoScaling) + Fargate, 运行容器。这是最简单的，但硬件资源受限, 想要强大的 CPU, 没门&lt;/li&gt;
&lt;li&gt;ECS(AutoScaling) + Capacity Provider(EC2) + EC2(Managed AutoScaling), 运行容器。由 ECS Task 发起 EC2 实例需求&lt;/li&gt;
&lt;li&gt;ECS(AutoScaling) + Capacity Provider(Fargate)，运行容器，Serverless 就是简单，同样硬件资源受限&lt;/li&gt;
&lt;li&gt;EKS，开启 Auto mode，一切变得简单，像是 Capacity Provider(EC2). 但会涉及到复杂的 EKS 配置管理，集群内网络，如 ELB,&lt;br&gt;
基于服务的 WAF, IAM Role. 但基本是一次的工作，建立了健壮的 EKS, 以后部署任何应用都变得很轻松。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对于 #2 和 #3, 如果选择 Network 模式为 &lt;code&gt;vpc&lt;/code&gt; 的话，即每个容器都会有自己的 IP 地址，并且 Target Group 中注册的是 IP 地址，这时候 AutoScaling
在缩减实例时就要靠 AutoScaling 的 Lifecycle Hook 来处理对相应 ECS Task 的通知，因为 EC2 的 AutoScaling 只知在开始关闭实例停止向相应实例
转发请求. 如果 Network 模式为默认的 &lt;code&gt;Bridge&lt;/code&gt; 则没问题，因为请求是过通 NAT 方式(如 32768:80) 由 EC2 实例转发到其中的容器。&lt;/p&gt;</description></item><item><title>Docker 内进程如何响应 Linux 的信号</title><link>https://yanbin.blog/docker-process-linux-ipc-signal/</link><pubDate>Sat, 07 Mar 2026 23:49:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-process-linux-ipc-signal/</guid><description>
&lt;p&gt;本文主要专注在 Docker 内的应用进程如何与外部发过来的 Linux 信号进行响应。具体应用在当运行为一个 ECS 的 Docker 容器时，对 ECS 的 AutoScaling
以及部署时如何让应用能正确收到相应的信号。关于 Linux 的 Signal 请参考 Wikipedia 的 &lt;a href="https://en.wikipedia.org/wiki/Signal_%28IPC%29"&gt;Signal(IPC)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;其实相关的话题在以前三篇中都有涉及&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/pass-arguments-to-docker-container/"&gt;如何向 Docker 容器传递参数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/how-aws-batch-pass-parameters-to-docker-container/"&gt;AWS Batch 是如何向 Docker 容器传递参数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="dockerfile-difference-between-shell-exec-forms/"&gt;Dockerfile 中命令的两种书写方式的区别&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不同 Dockerfile 的 &lt;code&gt;ENTRYPOINT&lt;/code&gt; 或 &lt;code&gt;CMD&lt;/code&gt; 会影响到 Docker 内进程的启动方式，额外参数的接收方式，以及信号的传递。本篇只关注在信号这一议题。
先学习本地的 Docker 容器内与对 &lt;code&gt;docker stop&lt;/code&gt; 或 &lt;code&gt;docker kill&lt;/code&gt; 发出的信号的响应，进而讲述运行在 ECS 的容器与 ASG，部署行为的信号响应。&lt;/p&gt;</description></item><item><title>C 语言，Bash, Java 如何响应 Linux Signal</title><link>https://yanbin.blog/c-bash-java-react-to-ipc-signal/</link><pubDate>Fri, 06 Mar 2026 16:20:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/c-bash-java-react-to-ipc-signal/</guid><description>
&lt;p&gt;本文初衷是要专注在 Docker 内的应用进程如何与外部发过来的 Linux 信号进行响应。具体应用在当运行为一个 ECS 的 Docker 容器时，对 ECS 的 AutoScaling
以及部署时如何让应用能正确收到相应的信号。可是一提及这一话题，思维就产生了极大的发散，很想好好捋一捋进程与 Linux 信号之间关系和相关的概念。
关于 Linux 的 Signal 请参考 Wikipedia 的 &lt;a href="https://en.wikipedia.org/wiki/Signal_%28IPC%29"&gt;Signal(IPC)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;本文以渐进的方式，从信号的简介，本地 C, Bash, Java 程序与信号，然后将在下一篇学习本地 Docker 容器内进程与信号的处理，ECS 的容器与 ASG，部署行为的信号响应。&lt;/p&gt;</description></item><item><title>Docker 容器中 Java 调用 C++ 动态库问题诊断</title><link>https://yanbin.blog/docker-java-cpp-dll-core-dump/</link><pubDate>Tue, 24 Feb 2026 15:04:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-java-cpp-dll-core-dump/</guid><description>
&lt;p&gt;本想用一篇日志记录下在 Docker 容器中使用 Java 调用 C++ 动态库时，当 C++ Crash 时如何自动生成 core dump, 不想分成了至少三篇来完成这一研究。
可以回顾一下前两篇日志&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/cpp-shared-library-trouble-shooting/"&gt;C++ 调用 C++ 动态库时问题诊断&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/java-jna-cpp-trouble-shooting/"&gt;Java 使用 JNA 调用 C++ 动态库问题诊断&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文是基于第二篇进一步推进，继续探索如何在 Docker 容器中 Java 调用 C++ 动态库时的 core dump 如何生成。首先测试的平台依然是 AWS EC2 实例，
OS 为 Amazon Linux 2023, Docker 版本为 25.0.14。为叙事方便，本文所用代码与上篇一样，但还是重复一遍，省却了连接跳转。&lt;/p&gt;</description></item><item><title>Java 通过 JNA 调用 C++ 动态库定位错误</title><link>https://yanbin.blog/java-jna-cpp-trouble-shooting/</link><pubDate>Sun, 22 Feb 2026 22:29:37 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-jna-cpp-trouble-shooting/</guid><description>
&lt;p&gt;在 Java 应用程序通过 JNA 调用 C++ 动态库时，C++ 代码运行在与 Java 同一进程中，当 C++ 代码 Crash 的时候，将会导致整个 Java 应用程序崩溃。
对于一个 Web 应用，这不是我们期望的结果，由于某一个请求输入的数据导致 C++ 代码崩溃了当前 Java 进程，从而造成该 Web 服务已接受到的所有请求全部失败，
这是非常糟糕的用户体验。如果是 Java 代码本身的异常我们可用 try/catch 进行保护，影响只限制在当前请求。如果是 C++ 代码崩溃的话，Java
应用程序无法捕获到这个异常，以致于整个 Java 应用程序崩溃，甚至发生这种情况时连 hs_err.log 文件都来不及生成，更别说生成 HeapDump, 或 CoreDump 了。&lt;/p&gt;
&lt;p&gt;如果是用原始 JNI 的方式来调用动态库，我们还能在 JNI 相关的 C++ 代码中捕获到异常，并抛给 Java 去处理。而用 JNA, 我们贪图了它的方便，
比如一个 Java 进程中同时加载同一接口的不同动态库版本(JNI 要同样的实现必须用自定义的 ClassLoader)，但在 C++ 代码崩溃时， Java 就显得无能为力了，
只能跟随着立即死亡, 并且在控制台下找不到关于 C++ 因何失败的线索。比如 C++ 中内存被多次释放，或地址越界访问破坏了内存数据等。&lt;/p&gt;</description></item><item><title>C++ 调用 C++ 动态库出错时生成 core dump</title><link>https://yanbin.blog/cpp-shared-library-trouble-shooting/</link><pubDate>Sun, 22 Feb 2026 16:40:26 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cpp-shared-library-trouble-shooting/</guid><description>
&lt;p&gt;本文初衷是为了解决 Java 应用程序通过 JNA 调用 C++ 动态库时，C++ 代码运行崩溃导致整个 Java 应用程序崩溃而进行的研究。从一个 C++ 调用 C++
写的动态库起步，记录它在什么情况下产生 core dump 文件，如何分析 core dump 文件等过程。可惜篇幅无法控制，不足以再加入 Java-&amp;gt;JNA-&amp;gt;C++
动态库内容了，所以不得不单列此篇，并更名为 'C++ 调用 C++ 动态库时问题诊断'. 关于 Java JNA 到 C++ 的问题诊断只能另立一篇了。&lt;/p&gt;
&lt;p&gt;下面我们来用 JNA 的方式来调用 C++ 动态库，演示当 C++ 代码崩溃时会发生什么，并试图找到好的诊断办法。以下演示在 Linux 下进行, 并且 Linux
发行版是 Amazon Linux 2023.&lt;/p&gt;</description></item><item><title>创建基于 Amazon Linux 2023 的允许 SSH 登陆的 Docker 镜像</title><link>https://yanbin.blog/amazonlinux-2023-root-ssh-enabled-image/</link><pubDate>Tue, 20 Jan 2026 18:40:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/amazonlinux-2023-root-ssh-enabled-image/</guid><description>
&lt;p&gt;前年记录过一篇 &lt;a href="https://yanbin.blog/create-root-ssh-docker-image/"&gt;创建可直接用 root 用户 ssh 登陆的 Docker 镜像&lt;/a&gt;, 采用了基础镜像是 Ubuntu:20.04.
因为在 AWS 使用 AmazonLinux 2023 更为频繁，为贴近生产环境，本地开发也使用基本 AmazonLinux 2023 为基础镜像的容器，与 IDE 连接以 SSH 协议，
容器的操作由自己的控制，而非直接使用 devcontainer 的方式。比如可以在 Windows 或 macOS 进行 Linux 相关的开发。&lt;/p&gt;
&lt;p&gt;当今 Linux 主要还是两个发行版，一个是 RedHat 的家族，一个是 Debian 的家族，之前验证过 Debian 族的 Ubuntu, 这次要验证 RedHat 族的
AmazonLinux 2023, 也作为将来不时之需。&lt;/p&gt;
&lt;h3 id="创建允许-root--密码登陆的镜像"&gt;创建允许 root + 密码登陆的镜像&lt;/h3&gt;
&lt;p&gt;Dockerfile 内容为&lt;/p&gt;</description></item><item><title>如何定义 Copilot 斜线命令</title><link>https://yanbin.blog/how-to-customize-copilot-slash-command/</link><pubDate>Tue, 20 Jan 2026 13:15:55 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-to-customize-copilot-slash-command/</guid><description>
&lt;p&gt;各种 AI 编程工具，如 Codex, Claude Code, Gemini 等提供了一些类似的斜线命令，每个斜线命令大约也是对应着一段特定的提示词。由于工作中更方便的使用
Copilot, 所以本文来探讨如何定义自己的 Copilot 斜线命令。比如想要定义一个命令 &lt;code&gt;/c2py-dataclass&lt;/code&gt; 用于实现把 C/C++ 的类或结构转换成 Python
的 @dataclass 类，并遵循 Python 的命名规则和设置默认字段值, 也就是采用如下提示词&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Covert following C/C++ class/struct to Python dataclass, following Python naming convention, and set default field values.&lt;br/&gt;
&amp;lt;C/C++ source code goes here&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/how-to-customize-copilot-slash-command/copilot-custom-command2.png"
width="331px"
/&gt;
&lt;p&gt;有了自定义的 &lt;code&gt;/c2py-dataclass&lt;/code&gt; 命令的话，就不需要每次重复上面的描述，而只用输入 &lt;code&gt;/c2py-dataclass&lt;/code&gt; 然后指定某个 C++ 代码文件或粘贴 C/C++
代码就能实现转换需求。&lt;/p&gt;
&lt;p&gt;实现方式可以借鉴几天前写的一篇 &lt;a href="https://yanbin.blog/preparation-for-vibe-coding/"&gt;准备迎接 Vibe Coding - 相关工具与资源&lt;/a&gt; 中关于
&lt;a href="https://yanbin.blog/preparation-for-vibe-coding/#spec-kit"&gt;Spec Kit&lt;/a&gt; 一节。&lt;/p&gt;
&lt;h3 id="实现方法"&gt;实现方法&lt;/h3&gt;
&lt;p&gt;开门见山吧，想要添加一个自定义的命令，如 &lt;code&gt;/c2py-dataclass&lt;/code&gt;, 仅需在项目目录中添加 &lt;code&gt;.github/prompts/c2py-dataclass.prompt.md&lt;/code&gt; 文件,
立马就会在 Copilot 中出现一个 &lt;code&gt;/c2py-dataclass&lt;/code&gt; 命令，在 &lt;code&gt;.github/prompts/c2py-dataclass.prompt.md&lt;/code&gt; 添加所需的提示词即可。&lt;/p&gt;
&lt;p&gt;要像 &lt;code&gt;Spec Kit&lt;/code&gt; 那样的话可以使用两个文件 &lt;code&gt;.github/agents/c2py-dataclass.agent.md&lt;/code&gt; 和 &lt;code&gt;.github/prompts/c2py-dataclass.prompt.md&lt;/code&gt;
来配合。&lt;/p&gt;</description></item><item><title>Python 编程过程碰到的几个问题</title><link>https://yanbin.blog/python-notes-of-several-things/</link><pubDate>Sat, 10 Jan 2026 17:18:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-notes-of-several-things/</guid><description>
&lt;p&gt;最近在紧锣密鼓的用 Python 写代码，不是 Vibe Coding 那种，因为在真实的敲代码才能碰到一些与 Python 语言相关且容易踏入的坑，记录如下&lt;/p&gt;
&lt;h3 id="python-38-引入的-walrus-操作"&gt;Python 3.8 引入的 Walrus 操作&lt;/h3&gt;
&lt;p&gt;即赋值表达式，&lt;code&gt;:=&lt;/code&gt; 的使用形式，像海象的两个牙。&lt;code&gt;:=&lt;/code&gt; 让赋值有了返回值, 该赋值语句的返回值就是右边的值。C/C++ 的 &lt;code&gt;=&lt;/code&gt; 同时具有 Python 的
&lt;code&gt;=&lt;/code&gt; 和 &lt;code&gt;:=&lt;/code&gt; 的功能。&lt;/p&gt;
&lt;p&gt;我们在 Python 中的 &lt;code&gt;=&lt;/code&gt; 与 &lt;code&gt;:=&lt;/code&gt; 对比&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;python-input-0&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;~~~~~^^^^^&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;unexpected&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Python 的这种赋值又有返回值的 &lt;code&gt;:=&lt;/code&gt; 功能就可以应用到 &lt;code&gt;if&lt;/code&gt; 或 &lt;code&gt;while&lt;/code&gt; 语句中，例如&lt;/p&gt;</description></item><item><title>MacOS/Linux C++ GDB 远程调试动态库和静态库</title><link>https://yanbin.blog/macos-linux-cpp-remote-debug-library/</link><pubDate>Sun, 04 Jan 2026 17:03:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/macos-linux-cpp-remote-debug-library/</guid><description>
&lt;p&gt;前篇 &lt;a href="https://yanbin.blog/macos-linux-cpp-remote-debug-basic/"&gt;MacOS/Linux C++ GDB 远程调试基础&lt;/a&gt; 演示了如在 macOS 开发调试远程 Linux 下的 C++ 程序，
本篇将结合 &lt;a href="https://yanbin.blog/c-static-dynamic-library/"&gt;C 语言静态库与动态库的生成和使用&lt;/a&gt; 练习如何调试含动态库，静态库的 C++ 程序, 并了解如何指定符号文件。&lt;/p&gt;
&lt;h3 id="示例源码"&gt;示例源码&lt;/h3&gt;
&lt;p&gt;本文将用以下的目录结构, 分别验证主程序与动态库，静态库的源代码断点调试，以及把源文件放在不同目录中是否有特别之处。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;├── dynamic &lt;span class="c1"&gt;# 动态库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;│ └── add.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;├── static &lt;span class="c1"&gt;# 静态库&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;│ └── sub.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;└── main.cpp &lt;span class="c1"&gt;# 主程序&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>MacOS/Linux C++ GDB 远程调试基础</title><link>https://yanbin.blog/macos-linux-cpp-remote-debug-basic/</link><pubDate>Sat, 03 Jan 2026 22:05:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/macos-linux-cpp-remote-debug-basic/</guid><description>
&lt;p&gt;有那么一个古老的项目，编译用的是 g++, 目标平台的是 Linux x86_64， 构建工具是 make. 本地开发环境是 macOS, 硬件为 Arm64 的苹果芯片 M4，
IDE 尝试用 CLion, 如果喜欢的话，也可以选择 VSCode.&lt;/p&gt;
&lt;p&gt;想要单步调试的目的就是想让代码跑起来，然后逐步理解代码执行的逻辑。所以不想对现有项目构建过程进行改造，如换成 CMake, 使用 LLVM 编译之类的。
只想使用远程 Linux 机器, 仍然使用原来的 &lt;code&gt;make&lt;/code&gt; 命令编译，然后在 Linux 下启动程序，由 Clion 连到的 Linux 上进程， 关联源代码进行调试。&lt;/p&gt;
&lt;p&gt;因为编译用的 g++, 所以采用 gdbserver 和 gdb 进行远程调用，llvm 相应的解决方案是 lldb-server + lldb. 下面是关键的步骤&lt;/p&gt;
&lt;p&gt;. 本地 Clion 中编辑代码
. 代码传到远程 Linux 机器
. 远程 Linux 机器上编译，编译的二进制代码要保留符号信息
. 在 Linux 机器上的符号文件要下载给 Clion
. 在远程用 gdbserver 指定端口启动程序
. 本地 Clion 配置用 GDB 连接到远程 Linux 机器上的 gdbserver
. 在 Clion 中断点单步调试&lt;/p&gt;
&lt;p&gt;上面是基本的操作，当我们用某个客户端工具(比配置运行 Clion 的 Remote GDB Server) 就自动完成以上的某些操作，如自动双向同步源代码与二进制/符号文件,
自动驱动远程端的的编译，启动 gdbserver. 远程 Linux 还能进一步使用 Docker 容器替代。但是在苹果芯片的 macOS 中启动 &lt;code&gt;x86_64&lt;/code&gt; 的容器中运行的
&lt;code&gt;gdbserver&lt;/code&gt; 在 macOS 中用 gdb 是连不上的。&lt;/p&gt;</description></item><item><title>React.js 原始用法 - 掌握核心语法</title><link>https://yanbin.blog/react-js-bare-usage/</link><pubDate>Thu, 01 Jan 2026 19:40:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/react-js-bare-usage/</guid><description>
&lt;p&gt;前端框架目前基本还是 Vue.js 和 React.js 两大阵营为主流，尽管 Angular.js 版本升的飞快, 事实就是鲜有人问津, 想用 Angular.js
的人何不直接用 Vue.js 呢. 关于 Vue.js 和 React.js 的数据显示, Vue.js 延续了传统模板(如 JSP, Velocity, Freemarker, Thymeleaf)的用法,
通过自定义 Tag, 许多逻辑写到 HTML 里. 而 React.js 则另辟蹊径, 通过 JSX 语法将 HTML 直接写到 JavaScript 里, 页面显示时不夹杂逻辑.&lt;/p&gt;
&lt;p&gt;以前算是用 Vue.js 做过一些小项目, 对 React.js 未有多少了解, 现在也算是初学. 刚开始不想从一个 &lt;code&gt;npx create-react-app my-react-app&lt;/code&gt;
的脚手架开始, 而是想从最原始的 HTML 中引入 React.js 的方式开始学习, 这样可以更清晰地了解 React.js 的工作原理. 也是为使用 React.js
拥抱 Vibe Coding 做准备.&lt;/p&gt;
&lt;h3 id="基本-reactjs-virtual-dom-渲染"&gt;基本 React.js Virtual DOM 渲染&lt;/h3&gt;
&lt;p&gt;用 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 标签引入 react 和 react-dom 两个核心库的方式已经不推荐了, 官方的 &lt;a href="https://legacy.reactjs.org/docs/cdn-links.html"&gt;CDN Links&lt;/a&gt;
已经变成 Legacy 了, 也找不到引用 react@19 的相关链接了. 但本着要明就里的原则还是希望以这种方式演练一下.&lt;/p&gt;</description></item><item><title>准备迎接 Vibe Coding - 相关工具与资源</title><link>https://yanbin.blog/preparation-for-vibe-coding/</link><pubDate>Wed, 31 Dec 2025 23:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/preparation-for-vibe-coding/</guid><description>
&lt;p&gt;2022 年 11 月 ChatGPT 横空出世, 史称 ChatGPT 时刻, 从那一刻起, 不管你接不接受, 事情正在迅速起变化. 如果写代码从记事本, 一边查文档开始,
到 IDE 的智能提示, 再到 Google 搜索代码, 从 StackOverflow 拷贝代码, 甚至是用 ChatGPT 对话抄写代码这些阶段, 软件工程方面并没有发生太大的变化.&lt;/p&gt;
&lt;p&gt;在去年面对 AI 还犹豫做什么的时候, 今年毫无疑问就是 AI Agent. 就目前 AI 最大的成就莫过于解决掉了很多程序员的工作问题. 程序员们在面临 AI
应该作出什么变化的话, 那 Vibe Coding 就不得不认真去看待. Vibe Coding 给我们带来某种快感的同时, 也伴随着焦虑. 从 TDD, BDD, DDD, 到现在的
SDD, 大脑就外包给了 LLM.&lt;/p&gt;
&lt;p&gt;Vibe Coding 最早由 OpenAI 联合创始人, 前特斯拉 AI 负责人 Andrej Karpathy 于 2025 年 2 月提出的一个新型编码方式. Vibe Coding
给人一种最直白的感觉就是只与大语言模型对话的形式生成软件, 代码完全是个黑盒, 不直接修改代码, 基本都不看代码, 有编译等问题继续与 LLM 对话.
这种软件生产方式还要像传统方式来 Review 代码就很难了, AI 不辞辛苦生成的大量代码, 可能也不适于人类进行审核了.&lt;/p&gt;
&lt;p&gt;Vibe Coding 成就了不少一人一公司, 但也不必过于相信有些人在网络上过份吹嘘的那样--零编程经验, 不写一行代码. 小白确实能用 Vibe Coding
做出一个东西来, 但真零编程经验, 技术框架选型就描述不清, 例如用 Vue.js, React.js, Next.js, 或者什么编程语言适合做什么事情等.&lt;/p&gt;
&lt;p&gt;有编程经验搭配上了 Vibe Coding 一定能做的更好. 也别信什么 90% 代码是 AI 写的, Vibe Coding 就是一个黑盒. 那 Vibe Coding 试个多人协作的大型项目.
或者 Vibe Coding 做个银行, 政府, 航空航天项目? 这种关键领域的项目我想每一行代码都必须由人工审核. 所以远古的仍然稳定运行着的 COBOL
代码一直无法升级替换, 换成 AI 也别想简单的就能重写它们, 如果不需要重新测试的话, 那没问题.&lt;/p&gt;</description></item><item><title>Java 23 新特性学习</title><link>https://yanbin.blog/java-23-new-features/</link><pubDate>Sat, 27 Dec 2025 23:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-23-new-features/</guid><description>
&lt;p&gt;Java 23 也是一个过渡版本, 还是到下面两个链接中找相应的更新&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/javase/23all-relnotes.html#JSERN23"&gt;JDK 23 Release Notes - Major New Functionality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/projects/jdk/23/"&gt;OpenJDK JDK 23 Features&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;IntelliJ IDEA 对 Java 23 Language level 描述是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;23 - Markdown document comments&lt;/li&gt;
&lt;li&gt;23 (Preview) - Primitive types in patterns, implicitly declared classes, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把上面第二个链接中的特性列出来&lt;/p&gt;
&lt;div style="display: flex;"&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;455: &lt;a href="https://openjdk.org/jeps/455"&gt;Primitive Types in Patterns, instanceof, and switch (Preview)&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;466: &lt;a href="https://openjdk.org/jeps/466"&gt;Class-File API (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;467: &lt;a href="https://openjdk.org/jeps/467"&gt;Markdown Documentation Comments&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;469: &lt;a href="https://openjdk.org/jeps/469"&gt;Vector API (Eighth Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;473: &lt;a href="https://openjdk.org/jeps/473"&gt;Stream Gatherers (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;471: &lt;a href="https://openjdk.org/jeps/471"&gt;Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;474: &lt;a href="https://openjdk.org/jeps/474"&gt;ZGC: Generational Mode by Default&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;476: &lt;a href="https://openjdk.org/jeps/476"&gt;Module Import Declarations (Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;477: &lt;a href="https://openjdk.org/jeps/477"&gt;Implicitly Declared Classes and Instance Main Methods (Third Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;480: &lt;a href="https://openjdk.org/jeps/463"&gt;Structured Concurrency (Third Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;481: &lt;a href="https://openjdk.org/jeps/481"&gt;Scoped Values (Third Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;482: &lt;a href="https://openjdk.org/jeps/482"&gt;Flexible Constructor Bodies (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;本文对上面用红点标记的特性重点关注&lt;/p&gt;</description></item><item><title>Java 22 新特性学习</title><link>https://yanbin.blog/java-22-new-features/</link><pubDate>Sat, 27 Dec 2025 13:13:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-22-new-features/</guid><description>
&lt;p&gt;Java 22 是一个过渡版本, 还是到下面两个链接中找相应的更新&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/javase/22all-relnotes.html#JSERN22"&gt;JDK 22 Release Notes - Major New Functionality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/projects/jdk/22/"&gt;OpenJDK JDK 22 Features&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;IntelliJ IDEA 对 Java 22 Language level 描述是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;22 - Unnamed variables and patterns&lt;/li&gt;
&lt;li&gt;22(Preview) - Statements before super(), string templates (2nd preview), etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把上面第二个链接中的特性列出来&lt;/p&gt;
&lt;div style="display: flex;"&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;423: &lt;a href="https://openjdk.org/jeps/423"&gt;Region Pinning for G1&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;447: &lt;a href="https://openjdk.org/jeps/447"&gt;Statements before super(...) (Preview)&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;454: &lt;a href="https://openjdk.org/jeps/454"&gt;Foreign Function &amp; Memory API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;456: &lt;a href="https://openjdk.org/jeps/456"&gt;Unnamed Variables &amp; Patterns&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;457: &lt;a href="https://openjdk.org/jeps/457"&gt;Class-File API (Preview)&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;458: &lt;a href="https://openjdk.org/jeps/458"&gt;Launch Multi-File Source-Code Programs&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div style="flex: 1;"&gt;
&lt;ul&gt;
&lt;li&gt;459: &lt;a href="https://openjdk.org/jeps/459"&gt;String Templates (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;460: &lt;a href="https://openjdk.org/jeps/460"&gt;Vector API (Seventh Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;461: &lt;a href="https://openjdk.org/jeps/461"&gt;Stream Gatherers (Preview)&lt;/a&gt; &lt;strong style="color: red"&gt;*&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;462: &lt;a href="https://openjdk.org/jeps/462"&gt;Structured Concurrency (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;463: &lt;a href="https://openjdk.org/jeps/463"&gt;Implicitly Declared Classes and Instance Main Methods (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;464: &lt;a href="https://openjdk.org/jeps/464"&gt;Scoped Values (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;本文对上面用红点标记的特性重点关注&lt;/p&gt;</description></item><item><title>Java 21 虚拟线程外其他新特性</title><link>https://yanbin.blog/java-21-new-features-other-than-virtual-thread/</link><pubDate>Sat, 27 Dec 2025 01:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-21-new-features-other-than-virtual-thread/</guid><description>
&lt;p&gt;迁移完所有的 WordPress 日志到 Hugo 之后, 终于有时间真正继承学习相关的新技术. Java 21 是于 2023 年 9 月份释放出来的 LTS 版本, 目前主要在用该版.
Java 25 LTS 版本已发布, 按正常节奏应该要切换到该版本.&lt;/p&gt;
&lt;p&gt;随着 AI 在编程界的花式表演, 所宣传的似乎就是要扑灭他人的学习热情, 编程方面越小白越好, 只要能写好小作文就行了. AI 当然还是要用,
但我对以往多少年传统的学习方式并不感到白花了心血. 告诉 AI 的一个课题 AI 确实能写出一篇漂亮, 规整的博客文章, 但其中有没有胡说八道,
只有试了才知道, 即使生成的文章无误, 也必须实践一遍才有更多更深的斩获.&lt;/p&gt;
&lt;p&gt;如果没有相关的技术储备, 每次与 AI 互动的时候都要告诉它尽量用 Java 21 新特性, 因为新引入的特性基本能实现得更简洁, 高效,
估计 AI 才不那么在乎这些, 写出适于人阅读的代码恐怕不是 AI 的首要关注.&lt;/p&gt;
&lt;p&gt;还是老办法, 关于 Java 某一版本新特性从两个链接出发&lt;/p&gt;</description></item><item><title>Java 21 之虚拟线程深入学习及应用场景</title><link>https://yanbin.blog/java-21-virtual-thread-and-applications/</link><pubDate>Thu, 25 Dec 2025 15:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-21-virtual-thread-and-applications/</guid><description>
&lt;p&gt;篇首说明: 本文十分冗长, 语言组织混乱, 如果觉得 TLDR, 就直接跳到 &lt;a href="https://yanbin.blog/java-21-virtual-thread-and-applications/#conclusion"&gt;关于虚拟线程的总结&lt;/a&gt; 部分看要点,
若对总结上中的某些要素点仍有兴趣的话请倒查本文中其他部分的内容. 个人对 Java 虚拟线程的主动研究是为了在项目中更有效的使用它.&lt;/p&gt;
&lt;h3 id="关于线程的概要"&gt;关于线程的概要&lt;/h3&gt;
&lt;p&gt;Java 21 于两年前 2023 年 9 月份放出，它是一个 LTS(long term support) 版本，个人基本就是把 LTS 当作能在正式项目中使用的版本。 Java
21 有几个增进编程体验的特性，像 Sequenced Collections, Record Patterns, 和 Pattern Matching for switch, 而对于性能改进的，
也是 Java 21 最具代表的特性无疑就是 Virtual Threads -- 虚拟线程。本文单列出它来，着重感受一下虚拟线程是什么，以及我们应该如何使用它。&lt;/p&gt;
&lt;p&gt;其实在之前的 &lt;a href="https://yanbin.blog/java-19-20-new-features/"&gt;Java 19, 20 新特性学习&lt;/a&gt; 就有一定的笔墨介绍了于 Java 19 引入，
Java 20 中尚处于第二次预览的虚拟线程。于其中大致体验了在一台 36 G 物理内存，默认堆内存为 9 G 的情况下，
创建 9000 个线程没问题，但要创建 10000 个线程就 OutOfMemoryError 了。而相同的环境下创建一百万个虚拟线程都没问题，没在继续往下试探了。&lt;/p&gt;
&lt;p&gt;其实这种比较是没有意义的, Java 线程对应到平台线程的, 每个线程要至少实实在在的 2M 栈空间, 而一百万个虚拟线程相当于是创建了一百万个 Java
对象而已, 更像是相应数量的 Task, 实际运行时才由载体线程去调度执行 - (注: 后面所提到的载体线程和平台线程是同一个概念).&lt;/p&gt;
&lt;p&gt;重新回顾一下何谓虚拟线程，Java 的虚拟线程实现是来自于 Project Loom 项目。与此相关的概念有线程，协程，以及纤程(Fiber)，而虚拟线程对应的应该是纤程。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;线程是操作系统最小的调度单位，每个线程有独立较大的栈空间(比如 2M)，内核调度，切换开销大，可有效使用 CPU 多核&lt;/li&gt;
&lt;li&gt;协程在单个线程内执行，共享线程栈空间或独立小空间，用户态调度，切换开销极小，但无法使用多核&lt;/li&gt;
&lt;li&gt;纤程，介于线程与协程之间，很小的独立栈，用户态调度，切换开销较小。结合线程池，纤程可在线程间转移，这时岂不是要经内核态调度吗？&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>博客从 WordPress 迁移至 GitHub 手记</title><link>https://yanbin.blog/blog-from-wordpress-to-github-notes/</link><pubDate>Wed, 17 Dec 2025 21:28:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/blog-from-wordpress-to-github-notes/</guid><description>
&lt;p&gt;经过足足 3 周的时间终于把博客 &lt;a href="https://yanbin.blog"&gt;https://yanbin.blog&lt;/a&gt; 从 AWS Lightsail 主机的 WordPress 迁移到了 Hugo 搭建的 GitHub Pages 上。
从动态页面转换成纯静态页，访问时确实是飞快，至少是从北美访问每个半秒以下开。&lt;/p&gt;
&lt;p&gt;本来一个博客网站就没有必要搞成动态的，之前网站由于操作系统，WordPress, 和数据库的升级，一段时间以来常出现网站无法访问，1G 内存都顶不住，
后经 一番 Apache, MySQL, 系统调优才得以解决，不过 WordPress 再如何使用文件缓存性能都比静态页面差许多。&lt;/p&gt;
&lt;p&gt;快速回顾一下本博客的历史，2006 年前 QQ 空间，后来 blogcn.com, 再到 blogjava.net，2010 年始声请了 unmi.cc 域名, 租 VPS 自搭 WordPress
服务，后面就不断的换 VPS 提供商，也出现过数据少量丢失的现象，所以有些图片或附件不可考。由于 unmi.cc 无法顺利迁出才有了新的 yanbin.blog 域名,
所以博文中还有不少 unmi.cc 的影子，以至于 unmi.cc 被人注册了，并且还堂而皇之的建立了一个李鬼网站，其中很多标题是盗用我的，内容全是 AI 生成。&lt;/p&gt;</description></item><item><title>学习 Rust 的工作空间, 包, Crate 和模块管理</title><link>https://yanbin.blog/study-rust-workspace-package-crate-module/</link><pubDate>Fri, 28 Nov 2025 22:42:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/study-rust-workspace-package-crate-module/</guid><description>
Rust 项目一旦增大，用 &lt;code&gt;cargo new demo&lt;/code&gt; 创建的单一包，单个 src/main.rs 的项目组织方式不能满足需求了&lt;/p&gt;
&lt;blockquote&gt;
$ cargo new demo&lt;br /&gt;
$ tree demo&lt;br /&gt;
demo&lt;br /&gt;
├── Cargo.toml&lt;br /&gt;
└── src&lt;br /&gt;
          └── main.rs
&lt;/blockquote&gt;
比如至少要一个 src/lib.rs 文件吧，复杂些还需在  src 目录中创建模块层次的目录; 更大型项目还要在 Package 上边创建 Workspace。&lt;br/&gt;&lt;br/&gt;
这里就引出了 Rust 项目的几个概念，即 Package, Crate, 模块，以及 Workspace，再就是如何在代码中引用不同 Package, Crate, 模块中的资源要用到路径。</description></item><item><title>Rust 语言逆天的错误处理方式</title><link>https://yanbin.blog/rust-weird-error-handling-syntax/</link><pubDate>Fri, 21 Nov 2025 15:24:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-weird-error-handling-syntax/</guid><description>
写了几天 Rust 之后，不光被它的 Ownership, Lifetime 折磨的死去活来，还碰上个奇怪的错误处理方式。如果让程序员在 Java, C/C++, Python, Ruby, Scala, Go，甚至是 Lisp 语言之间换着干活，那还都不是难事，但是拉个人去弄 Rust 就会要人命了。&lt;br/&gt;&lt;br/&gt;
有垃圾回收的语言基本就是想怎么写都成，程序运行时也不会出太大的事，当然性能是个妥协; 像 C/C++  自己管理内存的语言写出来的程序通过编译也容易，只是执行时很可能会有内存泄漏或地址越界。而选择号称性能与安全兼备的 Rust 语言的话，按照正常思维逻辑写出来的代码能通过编译就是最幸福的事。碰到关于 Ownership, Lifetime 之类的编译问题很多时候当前的 AI 也无解。&lt;br/&gt;&lt;br/&gt;
所以现在还能用 Java, Python, C# 等写代码是个幸福的事，这种时光应该好好的珍惜。换个角度来想，如果能掌握 Rust 那还怕别的语言吗？&lt;br/&gt;&lt;br/&gt;
就算能侥幸的应付 Ownership, Lifetime 的问题，Rust 错误处理方式也会让人抓狂，起初大量的用 unwrap() 忽略错误，用多了也会觉得不是一回事。&lt;br/&gt;&lt;br/&gt;
主流的语言都是采纳 try/catch 方式处理异常方式，异常在栈中向上传播，想在哪一层捕获异常都行，所以才可能在某处集中的处理异常，也让程序结果返回与异常处理得已分离。&lt;br/&gt;&lt;br/&gt;
有一个视频 &lt;a href="https://www.youtube.com/watch?v=_p0SgfajQFo"&gt;什么是正确的错误处理方法【让编程再次伟大#21】&lt;/a&gt;介绍了各种编程语言错误处理方式的历史发展，视频博主十分推崇 Rust 的结果错误放 Result  的方式，觉得像  try/catch 那种能够实现关注点分离的方式是便宜了程序员，只有返回 Result&amp;lt;T, E&amp;gt; 的方式才能强制程序员步步小心。那何不让 try/catch 全部抛出 Checked 异常，然而事实是 Spring 框架把许多 Checked 异常转换成了 Unchecked 异常，能程序员处理更方便，且能集中处理异常。</description></item><item><title>用 Rust 写 AWS Lambda 的简单例子</title><link>https://yanbin.blog/rust-write-aws-lambda-simple-example/</link><pubDate>Tue, 18 Nov 2025 21:28:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-write-aws-lambda-simple-example/</guid><description>
AWS 在 2025-11-14 日发布了 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/11/aws-lambda-rust/"&gt;AWS Lambda adds support for Rust&lt;/a&gt;, 即 AWS 正式支持用 Rust 写 Lambda 了, 回顾到之前 AWS 为 Rust 提供 SDK 是两年前的 2023-11-27: &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/aws-sdk-rust/"&gt;AWS SDK for Rust is now generally available&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;
日常中总有几个编程语言是要去掌握的, 像基本的脚本 Bash, 网页用的 JavaScript, 大项目用的 Java, AI 时代的 Python, 再就是仿佛 Rust 也是绕不过的. 之前有学过 Rust, 到现在忘差不多了, 又看到 Tauri, AWS Lambda 对 Rust 的大力支持，有必要放 Rust 提到与 Python, Java 一样的地位来看待。尽管学习起来比 C/C++ 还陡峭，像 解引用, Either(Result, Error), Unwrap, Borrow, Move, Ownership 等概念及规则需慢慢去熟悉。&lt;br/&gt;&lt;br/&gt;
从 AWS 控制台看看 Lambda 是怎么对 Rust 提供支持的，创建函数时，Runtime 中可以选择&lt;br/&gt;&lt;br/&gt;
&lt;table style="border-collapse: collapse; width: 100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="width: 32.2526%;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2025/rust-write-aws-lambda-simple-example/aws-lambda-rust-1.png"
width="273px"
/&gt;
&lt;/td&gt;
&lt;td style="width: 28.2428%;"&gt;Amazon Linux 2023&lt;br /&gt;
&lt;span style="font-size: 10pt; color: #6b6666;"&gt;OS-only runtime for Go, Rust, C++, custom&lt;/span&gt;&lt;/td&gt;
&lt;td style="width: 28.3659%;"&gt;Amazon Linux 2&lt;br /&gt;
&lt;span style="color: #6b6666; font-size: 10pt;"&gt;OS-only runtime for Go, Rust, C++, custom&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
OS 方面当然是要选择 Amazon Linux 2023 优于 Amazon Linux 2。选择 Amazon Linux 2023 或 Amazon Linux 2 时最后还有一个 custom，也就是说可以自义的定义自己的 Lambda 运行时，可选择任何语言，只要符合 AWS Lambda 调用的接口规范 &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html"&gt;Using the Lambda runtime API for custom runtimes&lt;/a&gt; 即可。不过完全定义吗，还挺罗嗦的，用官方直接支持的语言就容易多了。</description></item><item><title>Terraform aws_iam_policy_attachment Policy 竞争问题</title><link>https://yanbin.blog/terraform-aws_iam_policy_attachment-policy-issue/</link><pubDate>Thu, 13 Nov 2025 22:40:29 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/terraform-aws_iam_policy_attachment-policy-issue/</guid><description>
自从  Terraform  resource "aws_iam_role" 不推荐使用 "inline_policy" 和  "managed_policy_arns" 以后，就尝试了用 "aws_iam_policy_attachment" 来为 iam role 指定 AWS  内置和自定义的 IAM policy。因为在官方文档 &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role"&gt;aws_iam_role&lt;/a&gt; 中最先看到的就是 aws_iam_policy_attachment, 其实仔细阅读该文档的话，建议是&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;用 "aws_iam_role_policy" 来代替 "inline_policy"&lt;/li&gt;
&lt;li&gt;用  "aws_iam_role_policy_attachment"  来代替 "managed_policy_arns"&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
然后在项目中为避免 inline_policy 和 managed_policy_arns 的警告而选择了通用的 aws_iam_policy_attachment, 同时原来也用的 "aws_iam_policy"，而非 "aws_iam_role_policy。 所以才撞入到以前一直用 aws_iam_role(inline_policy, managed_policy_arns) + aws_iam_policy 就没出过问题。</description></item><item><title>Python unittest.mock 的基本使用</title><link>https://yanbin.blog/python-unittest-mock-usages/</link><pubDate>Wed, 22 Oct 2025 23:27:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-unittest-mock-usages/</guid><description>
平时随意用 Python 写的用了就丢的小工具当然没必要写什么单元测试，如果是一个要反复打磨的工具，项目，单元测试就必须重视起来了。因为简单思路写出来的代码将要应对各种未知情形，加之想要大肆的重构，有了足够的测试用例才能安心。
&lt;br/&gt; &lt;br/&gt;
任何语言白盒的单元测试首先面对的就是 Mock, 不光是应对有副作用的操作，最好是隔离所有不在当前所见到的代码的任何调用，其实像 &lt;code&gt;print&lt;/code&gt; 这种有副作用的代码一般是能容忍的，可能也是为何很多测试框架默认会把控制台的输出关掉。&lt;br/&gt;&lt;br/&gt;
在 Python 中，一般基本的东西自己都有，Mock 就直接用 unittest.mock，可应用于所有的单元测试框架中，如 unittest, pytest。另有一块对 uniitest.mock 的简单封装，专为 pytest 提供方便的 pytest-mock。今天先了解 unittest.mock 的使用，它的官方文档是 &lt;a href="https://docs.python.org/3.12/library/unittest.mock.html"&gt;unittest.mock -- mock object library&lt;/a&gt;。</description></item><item><title>Python 3.14 新特性学习(第二部分)</title><link>https://yanbin.blog/python-3-14-new-features-2/</link><pubDate>Sun, 19 Oct 2025 00:43:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-14-new-features-2/</guid><description>
前一篇 &lt;a href="https://yanbin.blog/python-3-14-new-features-1/"&gt;Python 3.14 新特性学习(第一部分)&lt;/a&gt; 基本就是被 Python 3.14 标准库的多解释器霸屏，所以另起一篇继续 &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html"&gt;What's new in Python 3.14&lt;/a&gt; 中其他几个重要新特性。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;PEP 765: finally 代码块中的控制流&lt;/h3&gt;
编译器在检测到 finally 代码块存在 &lt;code&gt;return&lt;/code&gt;, &lt;code&gt;break&lt;/code&gt;, 或 &lt;code&gt;continue&lt;/code&gt; 语句, 会触发 SyntaxWarning. 原因也很简单, 可以反问自己一句, 在 finally 放上 return, break, 或 continue 语句想干什么, 还想跳出 finally 语句块?&lt;br/&gt;&lt;br/&gt;
用 Python 3.13 和 3.14 测试下面的代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Python 3.14 新特性学习(第一部分)</title><link>https://yanbin.blog/python-3-14-new-features-1/</link><pubDate>Sat, 18 Oct 2025 11:57:01 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-14-new-features-1/</guid><description>
在 AI FIRST 的年代到底还要不要对每个所用语言新特性有所了解呢？就像有了 AI 还需要升级  Python 吗？虽然如今在 ChatGPT 中问一句话就能出来一篇比我想要写的好的多的博客，但不多思考怕会退化。&lt;/p&gt;
&lt;br/&gt;
Python 3.14 于 2025 年 10 月 7 日发布，也就是前天，比起 Java 的发布节奏还是慢半拍，所以才能跟得上它的步伐。还是老方法，在官方的 &lt;a href="https://docs.python.org/3.14/whatsnew/3.14.html"&gt;What's new in Python 3.14&lt;/a&gt; 中吸收最原始的滋味，完后再去参考别人家的总结。&lt;br/&gt;&lt;br/&gt;
Python 官方说 Python 3.14 最大的变化包括 t-string(模板字符串)，注解的延迟求值，和子解释器的支持(用以使用自由线程)。再就是标准库的变化 asyncio 的内省功能，支持 Zstandard 压缩，以及 REPL 有了语法高亮了.&lt;br/&gt;&lt;br/&gt;
总体来说这个版本比 Python 3.13 新特性更有亮点，在 Python 3.13 中自由线程是实验性的，在 Python 3.14 可通过子解释器来使用，和自由线程一样，Python 3.13 中的 JIT 需以源代码通过编译选项获得，在 Python 3.14 中 JIT 仍为实验特性，但官方发布的 Python 3.14 二进制版已包含实验性的 JIT 编译器。&lt;br/&gt;&lt;br/&gt;</description></item><item><title>Java 19, 20 新特性学习</title><link>https://yanbin.blog/java-19-20-new-features/</link><pubDate>Tue, 30 Sep 2025 16:52:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-19-20-new-features/</guid><description>
之所以把 Java 19 与 20 放一块是因为这两个版本都没有一个算得上正式的特性。都是些预览的，孵化中的，唯有一个支持 Linux 下 RISC-V 指令集与我们基本无关。所以 Java 19 和  Java 20 纯粹的过度版本，根本不该被正式项目采用，在 IntelliJ IDEA 中也是标它们为 No new language features。在我们的实践中正式项目只用 LTS 版。&lt;/p&gt;
&lt;br/&gt;
还是分别从 &lt;a href="https://openjdk.org/projects/jdk/19/"&gt;https://openjdk.org/projects/jdk/19/&lt;/a&gt; 和 &lt;a href="https://openjdk.org/projects/jdk/20/"&gt;https://openjdk.org/projects/jdk/20/&lt;/a&gt; 抓关注点&lt;br/&gt;&lt;br/&gt;
&lt;table style="border-collapse: collapse; width: 100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="width: 50%;"&gt;
Java 19 新特性
&lt;ul&gt;
&lt;li&gt;405: &lt;a href="https://openjdk.org/jeps/405"&gt;Record Patterns (Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;422: &lt;a href="https://openjdk.org/jeps/422"&gt;Linux/RISC-V Port&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;424: &lt;a href="https://openjdk.org/jeps/424"&gt;Foreign Function &amp;amp; Memory API (Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;425: &lt;a href="https://openjdk.org/jeps/425"&gt;Virtual Threads (Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;426: &lt;a href="https://openjdk.org/jeps/426"&gt;Vector API (Fourth Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;427: &lt;a href="https://openjdk.org/jeps/427"&gt;Pattern Matching for switch (Third Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;428: &lt;a href="https://openjdk.org/jeps/428"&gt;Structured Concurrency (Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;td style="width: 50%;"&gt;
Java 20 新特性
&lt;ul&gt;
&lt;li&gt;429: &lt;a href="https://openjdk.org/jeps/429"&gt;Scoped Values (Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;432: &lt;a href="https://openjdk.org/jeps/432"&gt;Record Patterns (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;433: &lt;a href="https://openjdk.org/jeps/433"&gt;Pattern Matching for switch (Fourth Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;434: &lt;a href="https://openjdk.org/jeps/434"&gt;Foreign Function &amp;amp; Memory API (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;436: &lt;a href="https://openjdk.org/jeps/436"&gt;Virtual Threads (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;437: &lt;a href="https://openjdk.org/jeps/437"&gt;Structured Concurrency (Second Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;438: &lt;a href="https://openjdk.org/jeps/438"&gt;Vector API (Fifth Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
从上面可以挑几个稍加了解，详细的介绍应该在学习 Java 21 时。它们是</description></item><item><title>Python 3.13 新特性学习</title><link>https://yanbin.blog/python-3-13-new-features/</link><pubDate>Sun, 28 Sep 2025 18:48:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-13-new-features/</guid><description>
相比于 Java 的每半年一个版本, 跟踪学习 Python 每年一版本要轻松一些. 虽然实际上 Java 是每两年一个 LTS 版, 但它的新特性却是逐个版本释放出来. 也终于赶在 Python 3.14 将在预计的 2025-10-07 发布之前能够学习总结一下当前的 Python 3.13 的新特性.&lt;/p&gt;
&lt;br/&gt;
还是老办法, 从官方的 &lt;a href="https://docs.python.org/3/whatsnew/3.13.html"&gt;What's New In Python 3.13&lt;/a&gt; 中学习, 所以写作本文的目的就是阅读 What's New In Python 3.13 的学习笔记.&lt;br/&gt;&lt;br/&gt;
Python 3.13 最大的变化就是 REPL(Read-Evaluate-Print Loop) Python 控制台交互界面, 还有实验性的支持自由线程模型(free-threaded mode) - 即所谓的可禁用全局解释锁(Global Interpreter Lock), 和JIT(Just-In-Time) 编译器. 禁用 GLI 和使用 JIT 都可以让 Python 的执行性能得到提升.&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;更友好的 REPL 交互界面&lt;/h3&gt;
Python 3.13 的 python 控制台一下子把早先的 &lt;a href="https://bpython-interpreter.org/"&gt;bpython&lt;/a&gt; 和 &lt;a href="https://ipython.org/"&gt;ipython&lt;/a&gt; 的饭碗给抢了, 虽然 bpython 和 ipython 比 Python 3.13 的 REPL 要强很多, 但毕竟控制台下只是用来随手简单测试 Python 代码, 也就更不太可能单独安装第三方的 bpython 和 ipython 了.</description></item><item><title>学习 Java 18 的新特性</title><link>https://yanbin.blog/java-18-new-features/</link><pubDate>Sat, 27 Sep 2025 20:33:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-18-new-features/</guid><description>
有了 AI 是不是就用不着了解语言特性本身呢？用 Vibe Coding 难道就无所不能呢？如果是的话那些找工作的也就无需刷 LeetCode 了。试想 Vibe Coding 产生了成堆的代码，即使创建了 Pull Request, 也不是给人 Review 的，也只能由 AI 来 Review, 到头来就是 AI 与 AI 自己玩，有 Bug 也只有 AI 看得懂。以后的屎山代码是一车一车的来。&lt;br/&gt;&lt;br/&gt;
除了从 JDK 官方每个版本的 &lt;a href="https://www.oracle.com/java/technologies/javase/18all-relnotes.html#NewFeature"&gt;What's New in JDK 18 - New Features and Enhancements&lt;/a&gt;, 还可以看 &lt;a href="https://openjdk.org/projects/jdk/18/"&gt;OpenJDK JDK 18&lt;/a&gt; 列出的更简明的新特性。自 JDK 10 之后，每一版的新特性由链接 https://openjdk.org/projects/jdk/&amp;lt;version&amp;gt;/ 查看，如 JDK 10 新特性链接为 &lt;a href="https://openjdk.org/projects/jdk/10/"&gt;https://openjdk.org/projects/jdk/10/&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
在 &lt;a href="https://openjdk.org/projects/jdk/18/"&gt;https://openjdk.org/projects/jdk/18/&lt;/a&gt; 列出了 JDK 18 如下新特性&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;400: &lt;a href="https://openjdk.org/jeps/400"&gt;UTF-8 by Default&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;408: &lt;a href="https://openjdk.org/jeps/408"&gt;Simple Web Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;413: &lt;a href="https://openjdk.org/jeps/413"&gt;Code Snippets in Java API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;416: &lt;a href="https://openjdk.org/jeps/416"&gt;Reimplement Core Reflection with Method Handles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;417: &lt;a href="https://openjdk.org/jeps/417"&gt;Vector API (Third Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;418: &lt;a href="https://openjdk.org/jeps/418"&gt;Internet-Address Resolution SPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;419: &lt;a href="https://openjdk.org/jeps/419"&gt;Foreign Function &amp;amp; Memory API (Second Incubator)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;420: &lt;a href="https://openjdk.org/jeps/420"&gt;Pattern Matching for switch (Second Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;421: &lt;a href="Deprecate%20Finalization for Removal"&gt;Deprecate Finalization for Removal&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
找几个有代表性的着重加了学习</description></item><item><title>Python 3.12 新特性</title><link>https://yanbin.blog/python-3-12-new-features/</link><pubDate>Tue, 05 Aug 2025 17:26:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-12-new-features/</guid><description>
继续感受新特性系列，这次看看于 2023 年 10 月 2 日发布的 Python 3.12 给我们带来了什么新特性。Python 3.14 预计在今年 10 月份推出，一定要对每年一个正式版的新东西有所了解。依旧是由官方的 &lt;a href="https://docs.python.org/3/whatsnew/3.12.html"&gt;What's New In Python 3.12&lt;/a&gt; 阅读进行展开。&lt;/p&gt;
&lt;br/&gt;
Python 3.12 从编程上的观感变化不大，主要是移除了 &lt;code&gt;distutils&lt;/code&gt;, 增加了 &lt;code&gt;f-strings&lt;/code&gt; 和 &lt;code&gt;type&lt;/code&gt; 提示，其他对诸如迭代推导等的字节码优化，对性能的提示是不具有直接感受的。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Python 3.12 移除了 distutils(Distribution Utilities)&lt;/h3&gt;
distutils 是一个 Python 内建的构建，打包和分布 Python 模块/包的工具集，其实对于多数应用开发人员对此也是感的。能与此能顺利建立关联的就是有些 Python 项目中的那个 setup.py 文件，那也是史前的产物。随着 Python 的快速流行，Python 的包管与构建工具也多了起来， 像 &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt;, &lt;a href="https://pdm-project.org/"&gt;PDM&lt;/a&gt; 和 &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt;, 尤其是 uv(Rust 编写的) 的闪电速度及全能表现有望成为 Python 界的 Maven。</description></item><item><title>演示 MCP 服务与 Claude 桌面版或 LM Studio 的集成</title><link>https://yanbin.blog/demo-mcp-server-integrate-with-claude-desktop-or-lm-studio/</link><pubDate>Sun, 13 Jul 2025 23:50:23 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/demo-mcp-server-integrate-with-claude-desktop-or-lm-studio/</guid><description>
AI 领域真是风头正劲，各种概念扑面而来，像 AGI, RAG, AI Agent, Agentic AI 等，目前的 MCP(Model Context Protocol, 模型上下文协议) 又随处可见。MCP 是 Anthropic 于 2024 年 11 月底推出的一种开放标准，旨在统一大模型与外部数据源和工具之间的通信协议。MCP 使用 LLM 应用能安全的访问和操作外部资源，轻松的实现了 Function Calling 的功能。&lt;br/&gt;&lt;br/&gt;
试想一下，以前问大语言模型一个复杂一点的计算题&lt;br/&gt;
&lt;blockquote&gt;
12345 的 6.7 次方是多少？
&lt;/blockquote&gt;
光语言模型只会在自己的向量数据库里找简单的碰到过的计算题，如 100 的 2 次方，但看到偏门的计算就会出现幻觉了，因为它没有实际的计算引擎。下面是在 LM Studio 中使用 qwen2.5-7b-instruct-mlx 模型时的结果</description></item><item><title>Java 17 新特性</title><link>https://yanbin.blog/java-17-new-features/</link><pubDate>Sat, 12 Jul 2025 16:39:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-17-new-features/</guid><description>
昨天单列了 &lt;a href="https://yanbin.blog/java-17-new-features-sealed-classes"&gt;Java 17 新特性之密封类型&lt;/a&gt;, 继续刷 &lt;a href="https://www.oracle.com/java/technologies/javase/17-relnote-issues.html"&gt;What's New in JDK 17 - New Features and Enhancements&lt;/a&gt;.&lt;/p&gt;
&lt;br/&gt;
&lt;h3&gt;switch 模式匹配(预览)&lt;/h3&gt;
在 Java 21 才正式放出，主动就是原来的 switch...case 语句可以写成表达式的方式，有返回值, 无需每个分支的 break, 并增加模式匹配功能，比如匹配类型，带约束子条件，匹配 record 的字段值等，这里不展开说明，待到详细了解 Java 21 新特性时再深入研究。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;新的 macOS 渲染管道&lt;/h3&gt;
英文是 New macOS Rendering Pipeline, 主要是 Swing API 如果配置系统属性 &lt;code&gt;-Dsun.java2d.meta=true&lt;/code&gt; 就可以用 Apple Metal 替代 OpenGL 加速渲染界面，很少写 Java Swing 桌面应用的略过。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;新的 API 可访问大图标 &lt;/h3&gt;
同样是在对 Swing 的改进，可用 FileSystemView 加载解析度的图标</description></item><item><title>Java 17 新特性之密封类型</title><link>https://yanbin.blog/java-17-new-features-sealed-classes/</link><pubDate>Thu, 10 Jul 2025 00:35:12 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-17-new-features-sealed-classes/</guid><description>
工作中所有项目都已升级到了当前的 LTS 版 Java 21, 眼看 Java 快要来到了下一个 LTS 版本 - Java 25，将于今年 9 月份发布。四年前记录过一篇 &lt;a href="https://yanbin.blog/java-10-16-new-features/"&gt;Java 10 ~ 16 一路向前冲(新特性一箩筐)&lt;/a&gt;，其中夹杂着孵化，预览中的以及正式的特性。现在继续跟随着 Java 16 之后版本的特性，主要讲述正式的，可直观体验到的特性，孵化与预览中的特性只会简单提及。&lt;/p&gt;
&lt;br/&gt;
先还是看下 Java 的发布日期安排 &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html"&gt;Oracle Java SE Support Roadmap&lt;/a&gt;&lt;br/&gt;
&lt;blockquote&gt;
版本                     发布日                        原定支持至               延期支持至&lt;br /&gt;
Java 17(LTS)      2021/9                       2026/9            &lt;strong&gt;          2029/12&lt;/strong&gt;&lt;br /&gt;
Java 18-20         2022/3 - 2023/3      2022/9 - 2023/9     N/A&lt;br /&gt;
Java 21(LTS)      2023/9                       2028/9                     &lt;strong&gt;2031/9&lt;br /&gt;
&lt;/strong&gt;Java 22               2024/3                       2024/9                      N/A&lt;br /&gt;
Java 23               2024/9                       2025/3                      N/A&lt;br /&gt;
Java 24               2025/3                       2025/9                      N/A&lt;br /&gt;
Java 25(LTS)      2025/9                       2030/9                     &lt;strong&gt;2033/9&lt;/strong&gt;
&lt;/blockquote&gt;
也就是每两年(9月份)会有一个 LTS 版本，中间若干过度版本。在正式项目中尽可能只用 LTS 版本，因为 LTS 版更稳定，且有长期的补丁，不能项目进行中不得不在进行非 LTS 主版本升级。</description></item><item><title>SpringBoot 中自定义 Filter 支持 gzip 压缩的请求体</title><link>https://yanbin.blog/springboot-custom-request-filter-to-support-gzip-request-body/</link><pubDate>Mon, 07 Jul 2025 13:35:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot-custom-request-filter-to-support-gzip-request-body/</guid><description>
SpringBoot Web 项目要支持响应数据的自动压缩只需要在 &lt;code&gt;application.properties&lt;/code&gt; 中配置 &lt;code&gt;server.compression.enabled=true&lt;/code&gt; 即可，默认为 false. 这样对于默认 &lt;code&gt;server.compression.min-response-size=2KB&lt;/code&gt; 达到 2KB 大小的响应，并且请求头中有 &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; 或 &lt;code&gt;deflate&lt;/code&gt; 就会压缩响应数据。&lt;br/&gt;&lt;br/&gt;
相关的配置请参考：&lt;a href="https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties.server"&gt;SpringBoot Server Properties&lt;/a&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;server.compression.enabled: 默认为  false&lt;/li&gt;
&lt;li&gt;server.compression.excluded-user-agents: 针对哪些 user-agents(逗号分隔) 不压缩，默认为空&lt;/li&gt;
&lt;li&gt;server.compression.mime-types: 会对哪些响应的 content-type 进行压缩，默认为 text/html, text/xml, text/plain, text/css, text/javascript, application/javascript, application/json, application/xml&lt;/li&gt;
&lt;li&gt;server.compression.min-response-size: 默认为 2KB&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
文本数据压缩比可达到百分之七八十，对于节约网络消耗来说是非常可关的，不过要些许 CPU 资源。说完了响应的自动压缩，如果请求数据较大也应考虑对请求进行压缩。比如客户端发送请求时带上 &lt;code&gt;Content-Encoding:gzip&lt;/code&gt;, 并且请求内容是 gzip 压缩的。&lt;br/&gt;&lt;br/&gt;
提示：如果 SpringBoot Web 是放在 AWS API Gateway 后端，那么 AWS API Gateway 会在看到请求头 Content-Encoding 的值为 gzip, compress, deflate, 或 br, 会自动解压缩请求数据，然后转发解压缩后的数据到后端，这时候 SpringBoot Web 无需进行请求数据的解压处理。不过对响应数据的压缩是 SpringBoot Web 要做的，AWS API Gateway 并无该功能。&lt;br/&gt;&lt;br/&gt;
SpringBoot 默认不支持自动解压缩请求内容，如果手动在 Controller 方法中，可以接收字节数组，然后自行解压缩。比如定义如下的 Controller 方法</description></item><item><title>Python 3.11 关键新特性之其他</title><link>https://yanbin.blog/python-3-11-key-new-features-others/</link><pubDate>Sat, 21 Jun 2025 18:25:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-11-key-new-features-others/</guid><description>
前面整一篇只讲了 Python 3.11 的新语法特性，异常组与 &lt;code&gt;except*&lt;/code&gt;, 这篇说其他的。&lt;/p&gt;
&lt;h3&gt;可为异常添加备注&lt;/h3&gt;
在 BaseException 上新加了一个实例方法 add_note(self, note)，在捕获到异常后可进一步润色而无需创建一个新的异常再次抛出。&lt;br/&gt;
&lt;h3&gt;内置 &lt;a href="https://docs.python.org/3/library/tomllib.html#module-tomllib"&gt;tomllib&lt;/a&gt; 支持 &lt;a href="https://toml.io/"&gt;TOML&lt;/a&gt; 配置文件&lt;/h3&gt;
TOML 是 Tom's Obvious Minimal Language, 像 INI 文件，但表述性更强，支持丰富的数据类型。现代新型的 Python 项目依赖管理构建工具都使用了 pyproject.toml 文件，如 &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt;, &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt;, 以及 &lt;a href="https://pdm-project.org/"&gt;PDM&lt;/a&gt;。pyproject.toml 在 Python  3.6 就引入了，见 &lt;a href="https://peps.python.org/pep-0518/"&gt;PEF 518&lt;/a&gt;，但似乎一直被顽固的 Python -m venv 忽略。&lt;br/&gt;&lt;br/&gt;
从 Python 3.11 开始新加了对 TOML 配置文件的编程接口，像使用 JSON 和 Pickle 一样的 load() 和 loads() 反序列方法，没有序列化方法</description></item><item><title>Python 3.11 关键新特性之异常组与 except*</title><link>https://yanbin.blog/python-3-11-key-new-features-exception-group-except-star/</link><pubDate>Sat, 21 Jun 2025 11:00:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-11-key-new-features-exception-group-except-star/</guid><description>
还是在三年前记录的 &lt;a href="https://yanbin.blog/python-3-10-new-features/"&gt;Python 3.10 关键新特性&lt;/a&gt;，一晃 Python 3.13 都出来了。Python 遵循着主版本每 12 个一更新，并提供 5 年支持的节奏。所以自 Python 3.10 于 2021 年 10 月发布以来，又有了 3.11, 3.12, 3.13，还得紧紧跟一跟，踏实的了解每个版本新特性，总由着 AI 帮忙恐难得到自己想要的最优代码。学习必要实践，不然在 Google 或是 ChatGPT 中一问 "Python 3.11 key new features", 到的结果无疑是漂亮的，但只粗略一读，并无半点体会。&lt;/p&gt;
&lt;br/&gt;
学习 Python 3.11 新特性的入口是官方的 &lt;a href="https://docs.python.org/3/whatsnew/3.11.html"&gt;What's New In Python 3.11&lt;/a&gt;. 同时结合 Google 的相关资料，拣些确实对自己有大益处的学。&lt;br/&gt;&lt;br/&gt;
官方说的 Python 3.11 比 Python 3.10 要快个  10-60% 估计不是我们升级 Python 的主要动力，一般来说语言或组件能升级的话尽量跟着形势跑，不然越是滞后便越难升级，最终只能原地等死。</description></item><item><title>Nginx 反向代理 WebSocket 服务</title><link>https://yanbin.blog/nginx-reverse-proxy-websocket/</link><pubDate>Sat, 14 Jun 2025 10:52:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nginx-reverse-proxy-websocket/</guid><description>
前一篇 &lt;a href="https://yanbin.blog/experience-fastapi-websocket-simple-chat/"&gt;尝试 FastAPI WebSocket 写简单的聊天应用&lt;/a&gt; 刚体验了 WebSocket, HTTP 可以共用下层的 TCP 连接保持网页与服务器的双向连接。先简单作个回顾，也是为了解释后面关于 Nginx 反向代理 WebSocket 作准备，比如说下面的 WebSocket 服务&lt;/p&gt;
&lt;blockquote&gt;
ws://localhost:8000/ws
&lt;/blockquote&gt;
当在浏览器中用 JavaScript&lt;br/&gt;
&lt;blockquote&gt;
var ws = new WebSocket("ws://test.exmple.com:8080/ws");
&lt;/blockquote&gt;
后，浏览器首先会发送一个 GET /ws 的 HTTP 请求，如下(不相关的 HTTP 头省去)</description></item><item><title>尝试 FastAPI WebSocket 写简单的聊天应用</title><link>https://yanbin.blog/experience-fastapi-websocket-simple-chat/</link><pubDate>Wed, 11 Jun 2025 00:43:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/experience-fastapi-websocket-simple-chat/</guid><description>
听说 WebSocket 那么一个东西已经很久很久了，但始终未曾尝试，大概对它的了解就是它可以让服务与客户端之间保持一个通道，并实现 Server Push 的功能，这样客户端就无需使用长连接或轮询来与服务端交互，更重要是 WebSocket 能复用 Web 的 80 或 443 端口号，通常是防火墙友好的端口。最近在为 WebSocket 配置 Nginx 反射代理时意识到要加上 Upgrade, Connection 头，加之各种流行的 Web 框架都提供了 WebSocket 的支持，所以又不得不对它重视起来。&lt;/p&gt;
&lt;br/&gt;
下面以 Python 的 FastAPI 框架为例，实现一个简单的聊天站点，主要是参考自 FastAPI 的 &lt;a href="https://fastapi.tiangolo.com/advanced/websockets/"&gt;WebSockets&lt;/a&gt; 官方样例。 当然如果从一开始就询问 ChatGPT: Create a simple chat web app with Python FastAPI framework + WebSocket, 肯定能让你获得一个不错的实现。但本人仍然希望能自我深度思考，而不是让 AI 帮我深度思考，而不是深度思考如何向 ChatGPT 问问题。记得多年前看过一位台湾历史老师讲《史记》, 经常说当看到哪一段时，把课本扣过来，然后思考碰到书中的情形之下自己会怎么想，所以觉得学任何东西当如此。</description></item><item><title>Apache Airflow 分支与条件流程</title><link>https://yanbin.blog/apache-airflow-branch-and-condition/</link><pubDate>Mon, 02 Jun 2025 15:20:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-airflow-branch-and-condition/</guid><description>
来到稍微复杂一点的流程，虽说 DAG 不能有循环但分支还是可以有的。比如下面的分支流程&lt;/p&gt;
&lt;blockquote&gt;
start &amp;gt;&amp;gt; [task1, task2] &amp;gt;&amp;gt; end
&lt;/blockquote&gt;
在 Airflow UI 中展示出来就是&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2025/apache-airflow-branch-and-condition/airflow-branch-1.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2025/apache-airflow-branch-and-condition/airflow-branch-1-800x257.png"
width="594px"
/&gt;
&lt;/a&gt;
Airflow 默认下游 Task 的 &lt;code&gt;trigger_rule&lt;/code&gt; 是 &lt;code&gt;all_success&lt;/code&gt;,  即要求上游的所有 Task 都必须成功才会执行，否则跟随着失败或跳过，这对于并行处理然后汇集结果的应用是合理的。&lt;br/&gt;&lt;br/&gt;
本文将要使用到的是分支(BranchPythonOperator), 比如共用一个 DAG, 周中与周末执行不同的分支，或根据条件从不同的数据源采集数据，下流的任务则需在任何一个分支成功即可触发。对上面的流程加上辅助任务，使其表达性更强</description></item><item><title>实测 Tomcat maxThreads, acceptCount, maxConnections 参数及关系</title><link>https://yanbin.blog/how-tomcat-maxthreads-acceptcount-maxconnections/</link><pubDate>Wed, 21 May 2025 19:41:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-tomcat-maxthreads-acceptcount-maxconnections/</guid><description>
使用 Tomcat 时应根据服务器的负载和客户端能接受的等待情可适当的调节 maxThreads, acceptCount, maxConnections 的值。这三个参数只有 maxThreads 是最容易理解，即 Tomcat 当前最大同时处理请求的数目，其他两个参数有些模糊。而搜索网络相关的解释发现一些相互矛盾的地方，本文将通过调整这几个值，实际体验它们对请求连接的影响。&lt;br/&gt;&lt;br/&gt;
在测试之前，先看看 Tomcat 官网的解释，你可能不信 AI 的胡说八道，官网仍然是最可信的。在关于 &lt;a href="https://tomcat.apache.org/tomcat-10.0-doc/config/http.html"&gt;The HTTP Connector&lt;/a&gt; 一章中，找到它们三者之间的说明原文是&lt;br/&gt;
&lt;blockquote&gt;
Each incoming, non-asynchronous request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the &lt;code&gt;maxThreads&lt;/code&gt; attribute). If still more simultaneous requests are received, Tomcat will accept new connections until the current number of connections reaches &lt;code&gt;maxConnections&lt;/code&gt;. Connections are queued inside the server socket created by the &lt;strong&gt;Connector&lt;/strong&gt; until a thread becomes available to process the connection. Once &lt;code&gt;maxConnections&lt;/code&gt; has been reached the operating system will queue further connections. The size of the operating system provided connection queue may be controlled by the &lt;code&gt;acceptCount&lt;/code&gt; attribute. If the operating system queue fills, further connection requests may be refused or may time out.
&lt;/blockquote&gt;
用 Google 翻译后</description></item><item><title>Apache Airflow 3.0 使用 Asset-Aware DAG(producer/consumers)</title><link>https://yanbin.blog/apache-airflow-3-0-us-asset-aware-dagproducer-consumers/</link><pubDate>Wed, 21 May 2025 14:23:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-airflow-3-0-us-asset-aware-dagproducer-consumers/</guid><description>
继续玩弄那个小风车，先前买的 《Data Pipelines with Apache Airflow》 一眼没看直接作废，因为是基于 Apache Airflow 2.x 的，3.0 既出立马又买了该书的第二版，倒是基于 Apache Airflow 3.0 的，但写书之时 3.0 尚未正式推出，所以书中内容与实际应用有许多出入。&lt;/p&gt;
&lt;br/&gt;
Apache Airflow 自 2.4 起就支持基于 Asset 事件触发 DAG，那时叫做 Data-aware，从 Apache Airflow 3.0 起更名为 Asset-Aware, 并且在 UI 上也会显示使用到的 Assets。那么 Asset-Aware  解决什么问题呢，它采用了 Producer/Consumer 模式可把依赖的某一共同资源的  DAG 串联起来。比如某一个 Producer DAG 写了文件到 s3://asset-bucket/example.csv, (发布一个事件 ), 然后相当于订阅了该事件所有相关 Consumer DAG 都会得到执行。&lt;br/&gt;&lt;br/&gt;
Airflow 的 Asset 使用 URI 的格式&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;s3://asset-bucket/example.csv&lt;/li&gt;
&lt;li&gt;file://tmp/data/export.json&lt;/li&gt;
&lt;li&gt;postgresql://mydb/schema/mytable&lt;/li&gt;
&lt;li&gt;gs://my-bucket/processed/report.parquet&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Apache Airflow 任务中使用模板或上下文</title><link>https://yanbin.blog/apache-airflow-task-use-template-context/</link><pubDate>Tue, 20 May 2025 23:34:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-airflow-task-use-template-context/</guid><description>
本文大概记录一下在 Apache Airflow 的 Task 或 Operator(这两个基本是同一概念) 中如何使用 模板(Template) 和上下文(Context). Airflow 的模板引擎用的  Jinja Template, 它也被 FastAPI 和 Flask 所采纳。首先只有构造 Operator 时的参数或参数指定的文件内容中，或者调用 Operator render_template() 方法才能用模板语法，像 &lt;code&gt;{{ ds }}&lt;/code&gt;.  Apache Airflow 有哪些模板变量可用请参考: &lt;a href="https://airflow.apache.org/docs/apache-airflow/stable/templates-ref.html#variables"&gt;Templates references / Variables&lt;/a&gt;, 本文将会打印出一个 Task 的 context 变量列出所有可用的上下文变量， 不断的深入，最后在源代码找到相关的定义。&lt;/p&gt;
&lt;br/&gt;
通过使用模板或上下文，我们能能够在任务中使用到 Apache Airflow 一些内置的变量值，如 DAG 或任务当前运行时的状态等 。&lt;br/&gt;&lt;br/&gt;
当我们手动触发一个 DAG 时, 在 &lt;code&gt;Configuration JSON&lt;/code&gt; 中输入的参数也能在 context 中找到. 所有的 Operator  继承链可追溯到 AbstractOperator -&amp;gt; Templater, 因此所有的 Operator(Task) 都能通过调用 Templater.render_template() 方法对模板进行渲染，该方法的原型是</description></item><item><title>Docker Compose 简单配置 Apache Airflow 3.0(PostgreSQL)</title><link>https://yanbin.blog/use-docker-compose-config-apache-airflow-3-0-postgresql/</link><pubDate>Wed, 07 May 2025 23:14:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-docker-compose-config-apache-airflow-3-0-postgresql/</guid><description>
Apache Airflow 重新唤起我的注意力是因为 Airflow 3.0 在近日 April 22, 2025 发布了，其二则是我们一直都有计划任务的需求，以下几种方案都太简陋&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;用 Windows 的计划任务或 Linux 的 Cron 都不易管理，且有单点故障问题&lt;/li&gt;
&lt;li&gt;在 Java Spring 项目中使用集群模式的 Quartz 有些麻烦，且对于 AutoScaling 也不怎么友好&lt;/li&gt;
&lt;li&gt;AWS 上用 CloudWatch Rule + AWS Lambda 的方案可靠性没有问题，但不适于监控 &lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
因此还有必要再次尝试 Apache Airflow, 它有集中管理的界面，各个部件都是可伸缩的，如 WebServer, Workers 等。特别是刚出的 Apache Airflow 3.0 带来以下主要新特性&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;新的服务化架构，各个部件间耦合度降低&lt;/li&gt;
&lt;li&gt;多语言支持，借助了 Task SDK, 可望用 Java, JavaScript, TypeScript 等语言写 DAG&lt;/li&gt;
&lt;li&gt;DAG 支持版本控制，可回溯历史&lt;/li&gt;
&lt;li&gt;支持事件驱动，即  DAG 可响应外部事件，如文件到达，消息队列等&lt;/li&gt;
&lt;li&gt;引入了资产驱动调度功能，可根据数据资产的变化 进行触发，可以说是事件驱动的一类&lt;/li&gt;
&lt;li&gt;全新的 React UI 界面&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Java ArrayList 默认容量及增长策略</title><link>https://yanbin.blog/java-arraylist-default-capacity-increase-policy/</link><pubDate>Tue, 06 May 2025 00:32:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-arraylist-default-capacity-increase-policy/</guid><description>
早先对 Java ArrayList 的扩容理解是在 new ArrayList() 时会默认建立一个内部容量为 16(这个数值还是错的，往后看) 大小的数组，然而插入数据容量不足时会扩容为原来的 1.5 倍，并用 System.arraycopy()  移动原来的数组到新的大数组中，所以为了频繁的内部扩容操作，在已知 ArrayList 将来大小的情况下，应该在创建 ArrayList 时指定大小，如 new ArrayList(1000)。那么是否指定初始容量对性能会有多大的影响仍缺乏感性的认识。&lt;/p&gt;
&lt;br/&gt;
本文通过具体的测试主要掌握以下知识&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;new ArrayList() 默认容量大小(JDK 8 以前是 10, JDK 8 及以后为 0)&lt;/li&gt;
&lt;li&gt;ArrayList 何时进行扩容，以及每次扩容多少&lt;/li&gt;
&lt;li&gt;new ArrayList() 时是否指定初始容量值的性能对比&lt;/li&gt;
&lt;li&gt;除了 ArrayList 自动扩容外，它会不会自动缩容呢？&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;h3&gt;new ArrayList() 的默认容量多少及增容策略&lt;/h3&gt;
就像 JDK 8 的 HashMap 引入了红黑树改善性，随着 JDK 版本的升级 ArrayList 的内部实现也在演进。回到 JDK 7, 当我们不指定容量 new ArrayList() 创建一个对象时的实现是</description></item><item><title>Java 中显式 throw 与调用只 throw 异常方法的区别</title><link>https://yanbin.blog/java-explicitly-throw-and-call-only-throw-function/</link><pubDate>Wed, 16 Apr 2025 14:14:53 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-explicitly-throw-and-call-only-throw-function/</guid><description>
Java 代码中如果显式的用 &lt;code&gt;throw&lt;/code&gt; 关键字抛出异常，那么在该分支中其后的语句不可到达，并且即使对于有返回值的函数也不必写 &lt;code&gt;return&lt;/code&gt;  语句了。像下面的代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
以上代码是合法的。要清洁代码的话，最后的 &lt;code&gt;return num + 1&lt;/code&gt;  不必写在 &lt;code&gt;else&lt;/code&gt; 条件中，这样写只是为了验证抛出异常后不必有返回值。&lt;br/&gt;&lt;br/&gt;
比如我们想对该代码进行重构，把 &lt;code&gt;throw&lt;/code&gt; 语句抽取到一个方法中，以便于在该方法中集中处理错误信息，于是变成了</description></item><item><title>使用 Cucumber Java 进行 BDD 测试循序渐进</title><link>https://yanbin.blog/cucumber-java-bdd-steps-in/</link><pubDate>Thu, 27 Mar 2025 14:32:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cucumber-java-bdd-steps-in/</guid><description>
不得不承认因为 ChatGPT 为代表的 AI 的出现，让许多技术博客的写作者积极性大大降低。但本着以学习掌握知识为目的，实战，写下来对加强学习仍然是非常有意义的。如果一直使用 AI 来解决技术问题，知识永远是 AI 的，至于说有了 AI 本应没有主动学习必要的性的话，永远保持像一张白纸，A4 大小，那真就无话可说了。&lt;/p&gt;
&lt;br/&gt;
开发过程驱动有分 TDD(Test-Driven Development) 和 BDD(Behavior-Driven Development)，大致的理解是 TDD 更关注实现细节，BDD 更接近于 QA 的测试，对领域的测试。BDD 从抽象中来讲更适于做面向用户的集成测试。当然在 AI 生成代码的年代可能单玩测试反而不那么重要，因为更多是一次性代码。&lt;br/&gt;&lt;br/&gt;
BDD 给人最典型的印象是 Scenario/Given/When/Then, BDD 最流行的测试框架当属 &lt;a href="https://cucumber.io"&gt;Cucumber&lt;/a&gt;, 它以插件的方式支持众多编程语方，如官方支持的用 JavaScript, Java, Kotlin, Ruby, Lua, Scala, C++, Go, OCaml, 还有其他半官支持的 Python, Swift/ObjC, Perl, .NET(C#, F#, VB), 以及非官方支持的 Rust, D, Groovy 等。&lt;br/&gt;&lt;br/&gt;
另外还有一个专供 Java 的轻量级 &lt;a href="https://jbehave.org/"&gt;JBehave&lt;/a&gt;, 不过个人更推荐用 Cucumber, 因为 Cucumber 得到更多 IDE 如 IntelliJ, Eclipse, VS Code 等的支持，并能与 JUnit 4, JUnit 5, TestNG, 以及  Spring Boot 项目集成，内置的测试报告插件，多语言当然是个亮点。&lt;br/&gt;&lt;br/&gt;
本文主要关注 Maven 项目中如何使用 Cucumber, 循序渐进的从简单的测试开始，然后跃进到与 JUnit 5/ JUnit 4 的结合，以及普通 Unit Test 和 BDD 测试如何并存且可区分的执行，或者在 Maven 中创建独立的  src/bdd 目录单独存放 BDD 测试用例。</description></item><item><title>为 FastAPI 的 SwaggerUI 定制 CSS 样式</title><link>https://yanbin.blog/customize-fastapi-swaggerui-css/</link><pubDate>Fri, 14 Mar 2025 23:54:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/customize-fastapi-swaggerui-css/</guid><description>
FastAPI 比起 Flask 而言一个十分便利的功能是它内置对 Swagger UI 文档的支持，然而默认生成的 Swagger UI 也总不尽如人意，于是就有了如何通过引入自己的样式(或样式文件)对默认 Swagger UI 进行定制化的需求。在 ChatGPT 之前，Google 和阅读源代码是齐头并进的选择，自己有了 ChatGPT 之类的 AI, 人们一下就把身段放低了许多，再也不像使用 Google 那样的心态去使用 AI 了。所以呢，第一次支持付了 $8 问问当前号称最厉害的 Grok 3(也算是对 DOGE 的支持吧), 得到答案如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/static&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StaticFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;static&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;static&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;swagger_ui_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;css_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/static/custom_swagger.css&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在网站的 /static 目录下也创建了 custom_swagger.css 文件，然而根本就没有效果，Inspect 浏览器后发现 FastAPI 的 /docs 根本就有加载 /static/custom_swagger.css 文件。</description></item><item><title>SciPy 最优化之最小化</title><link>https://yanbin.blog/scipy-optimize-minimize/</link><pubDate>Thu, 05 Dec 2024 01:06:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scipy-optimize-minimize/</guid><description>
[latexpage]&lt;/p&gt;
&lt;br/&gt;
 SciPy 是一个开源的算法库和数学工具包，可以处理最优化、线性代数、积分、插值、拟合、特殊函数、快速傅里叶变换、信号处理、图像处理、常微分方程求解器等。 它依赖于 NumPy, Pandas 也依赖了 NumPy。本文重点是体验它怎么处理最优化的问题。很多情形下通过 SciPy 的  optimize.minimize 方法寻求目标函数最小值的过程得到最优化的输入与输出。比如寻找二次元函数的根，求解线性/动态规则，金融行业的计算出最优投资组合的资产分配等。为什么 SciPy 没有 maximize 方法呢，因为没有必要，想要找到最大化的值，只要把目标函数的值取反，或者是模或绝对值的最小值。看到 minimize 方法名更让人觉得目标函数会有一个收敛值。&lt;br/&gt;&lt;br/&gt;
虽然 SciPy 对特定的问题有更直白的函数，如求根有 optimize.root, 线性规则 optimize.linprog(现不建议使用)，但各种优化基本都可以回归到 minimize 方法调用。minimize 方法的原型是&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jac&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hessp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
除了必须的目标函数和初始值，还有更多参数，像常用的约束(contraints) - 满足某些特定条件的最优化, 线程或非线性约束等; 求解方法(method) - Powell, Newton-CG 等&lt;br/&gt;&lt;br/&gt;
下面用 optimize.minimize 来求解一些问题</description></item><item><title>跨 CA 签发多个证书的 Nginx mTLS 配置</title><link>https://yanbin.blog/client-certs-with-different-ca-nginx-mtls-config/</link><pubDate>Mon, 02 Dec 2024 22:47:17 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/client-certs-with-different-ca-nginx-mtls-config/</guid><description>
研究过用同一个 CA 签发的服务端和客户端证书的 Nginx mTLS 配置，本文要试验一番服务端和客户端证书由不同 CA 机构签发的情形。这是常有事，比如与客户间采用 mTLS 加密方式，需要文件交付可能是&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;客户端证书由甲方生成，发送客户端私钥和证书(或放在一起的 PKCS#12 格式证书)给乙方&lt;/li&gt;
&lt;li&gt;或由乙方生成客户端私钥或证书，乙方把签发用的 CA 证书发给甲方已配置信任链&lt;/li&gt;
&lt;li&gt;甚至服务端，客户端的证书都由甲方生成的情况下也可能使用不同的 CA 签发&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
下面来测试不同 CA 签发证书的 Nginx mTLS 配置。&lt;br/&gt;&lt;br/&gt;
今天升级了 ChatGPT 为 Plus 版本，可以用 ChatGPT 4o, 确实是比较强，输入 "mtls 不同 ca 签发的服务端客户端证书在 nginx 中的配置" 提示符产生的内容几乎可以直接作为博文。但本人必须遵循本博客非 AI 产生的原则，只参考 ChatGTP 的答案，关键是一个要自己亲自动手验证并理解每一项配置的功用。</description></item><item><title>Wireshark 查看本地浏览器的 HTTPS 加密通信</title><link>https://yanbin.blog/wireshark-inspect-tls-encrypted-packets/</link><pubDate>Sat, 30 Nov 2024 17:00:37 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wireshark-inspect-tls-encrypted-packets/</guid><description>
继续 TLS(或 SSL, HTTPS) 的话题。在我们诊断 HTTP 请求时，为了验证代码中发送了什么样的请求，会用 curl 或 Postman 的辅助，
但它们都可能会带上额外的请求头等信息，最为可靠的办法是用网络抓包工具如 Wireshark, 从中看到的 HTTP 文本协议内容才是真正往外发送的内容。
可是对于 HTTPS 的协议数据用 Wireshark 抓取到了也没用，因为它是加密了的，作为 TLS 的 Payload, 如果不知道加密算法或密钥是解不开来的。
客户端与服务端的密钥交换是采用非对称方式加密的，只通过抓网络包是不可能知道最终确定的密钥是什么，
除非像 Zscaler 那样堂而皇之地作为中间人攻击(man in the middle attack)。&lt;br/&gt;&lt;br/&gt;
但既然是本地浏览器能理解的网页内容，只要浏览器留了口子的话，也是有办法在 WireShark 中显示出抓取到的 HTTPS 的内容，
那就是设置环境变量 SSLKEYLOGFILE, 然后启动浏览器(FireFox 或 Chrome), 就会把通信过程中的密钥记录到文件中，WireShark 中引用该 SSLKEY
文件就能显示出确切的 HTTP 请求的内容。</description></item><item><title>自签发证书配置 HTTPS 单向双向验证</title><link>https://yanbin.blog/self-certs-https-tls-mtls-config/</link><pubDate>Wed, 27 Nov 2024 12:43:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/self-certs-https-tls-mtls-config/</guid><description>
好久以前阅读《HTTP/2 in Action》一书起了个头，又重新放回了书架。近来再次对 HTTPS/TLS 来了劲，自己的博客用的是 Let's Encrypt 签发的证书，这次实践一下自签发证书的过程与配置，并实现单向和双向的认证方式。&lt;br/&gt;&lt;br/&gt;
如果是配置单向认证的过程需要有以下三个证书&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;根(CA) 证书: root.crt&lt;/li&gt;
&lt;li&gt;服务端私钥文件: server.key&lt;/li&gt;
&lt;li&gt;服务端公钥证书: server.crt&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
证书是含有组织与域名或(CA) 信息以及公钥的文件, root.key 和 root.crt 将被用于签发其他的证书。这里的 crt 证书是 x509 格式的。&lt;br/&gt;&lt;br/&gt;
浏览器只会信任某些 CA 机构签发的证书，如 DigiCert, GlobalSign, GoDaddy, Amazon Root CA，Let's Encrypt 等。如果是不被信任 CA 签发的证书，我们在浏览器中打开相应的 HTTPS url 就会看到 'Not Secure - Your connection is not private' 的提示，要继续访问需自行承担可能的安全责任。</description></item><item><title>TLS 与 mTLS 的私钥交换过程</title><link>https://yanbin.blog/tls-and-mtls-private-key-exchange/</link><pubDate>Mon, 25 Nov 2024 16:16:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tls-and-mtls-private-key-exchange/</guid><description>
不管是 HTTPS, SSH, SFTP, SCP 等都涉及到 SSL(Secure Sockets Layer) 或 TLS(Transport Layer Security)，以及使用非对称加密交互私钥的过程。&lt;br/&gt;&lt;br/&gt;
很久很久以前傻傻的认为所谓的非对称加密是像 MD5 那样内容加密后，无法从 MD5 码中还原出原始内容，其实那不就加密，是摘要(Digest)。非对称指的是加密与解密使用是不一样的密钥，即用公钥加密，私有解密。&lt;br/&gt;&lt;br/&gt;
提到 SSL 和 TLS, 顺便了解一下它们的极简史&lt;br/&gt;&lt;br/&gt;
SSL 由 Netscape 于 90 年代开发，SSL1.0(94 年，未公开), SSL 2.0(95 年发布), SSL 3.0(96 年发布), 后来 IETF 出了个 TLS 1.0 作为 SSL 3.0 的继承者，再就是后面的 TLS 1.1(2006), 1.2(2008), 1.3(2018)。2015 年 TLS 正式的取代了 SSL，从此江湖不再有 SSL 了，而我们习惯说的 SSL 只是在向曾经的 Netscape 致敬，其实指代的就是 TLS。&lt;br/&gt;&lt;br/&gt;
HTTPS 并非一直使用非对称加密进行数据通信，而只是用 TLS 安全的交换密钥，而后的数据通信使用私钥进行对称加密。如果数据通信都用非对称的方式性能是不允许的，所以只用非对称的方式进行密钥交换。</description></item><item><title>Ollama - 简化使用本地大语言模型</title><link>https://yanbin.blog/ollama-simple-use-local-llm-model/</link><pubDate>Mon, 11 Nov 2024 22:27:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ollama-simple-use-local-llm-model/</guid><description>
学习完用 Transformers 和 llama.cpp 使用本地大语言模型后，再继续探索如何使用 Ollama 跑模型。Ollama 让运行和管理大语言模型变得更为简单，它构建在 llama.cpp 之上，并有优化，性能表现同样不俗。下面罗列一下它的特点&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;从它的 GitHub 项目 &lt;a href="https://github.com/ollama/ollama"&gt;ollama/ollama&lt;/a&gt;, Go 语言代码 90.8%， C 代码 3.4%&lt;/li&gt;
&lt;li&gt;Ollama 不仅能运行 Llama 模型，还支持 &lt;a href="https://ollama.com/library/phi3"&gt;Phi 3&lt;/a&gt;, &lt;a href="https://ollama.com/library/mistral"&gt;Mistral&lt;/a&gt;, &lt;a href="https://ollama.com/library/gemma2"&gt;Gemma 2&lt;/a&gt; 及其他&lt;/li&gt;
&lt;li&gt;Ollama 支持 Linux, Windows, 和 macOS, 安装更简单，不用像 llama.cpp 那样需从源码进行编译，并且直接支持 GPU 的&lt;/li&gt;
&lt;li&gt;Ollama 有自己的模型仓库，无需申请访问权限，可从 Ollama 拉取所需模型，或 push 自己的模型到 Ollama 仓库pull llama3.2-vision&lt;/li&gt;
&lt;li&gt;Ollama 仓库的模型是量化过的，某个模型有大量的 tag 可选择下载，如 &lt;a href="https://ollama.com/library/llama3.2/tags"&gt;llama3.2 的 tags&lt;/a&gt; 有 1b, 3b, 3b-instruct-q3_K_M, 1b-instruct-q8_0, 3b-instruct-fp16 等&lt;/li&gt;
&lt;li&gt;如果在 Ollama 上没有的模型，可以到 HuggingFace 上下载，或量化后再传到 Ollama 仓库&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
其他更多特性我们将在使用当中体验，仍然是在 i9-13900F + 64G 内存 + RTX 4090 + Ubuntu 22.4 台上进行</description></item><item><title>用 llama.cpp 体验 Meta 的 Llama AI 模型</title><link>https://yanbin.blog/llama-cpp-with-meta-llama-ai-models/</link><pubDate>Sun, 10 Nov 2024 22:06:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/llama-cpp-with-meta-llama-ai-models/</guid><description>
继续体验 Meta 开源的 Llama 模型，前篇 &lt;a href="https://yanbin.blog/get-start-with-llama-3-1-8b-instruct-ai-model"&gt;试用 Llama-3.1-8B-Instruct AI 模型&lt;/a&gt; 直接用 Python 的 Tranformers 和 PyTorch 库加载 Llama 模型进行推理。模型训练出来的精度是 float32, 加载时采用的精度是 torch.bfloat16。&lt;br/&gt;&lt;br/&gt;
注：数据类型 torch.float32, torch.bfloat16, 与 torch.float16 有不同的指数(Exponent)，尾数(Fraction)宽度, 它们都有一位是符号位，所以剩下的分别为指数位和尾数位宽度, torch.float32(8, 23), torch.bfloat16(8, 7), torch.float16(5, 10)。&lt;br/&gt;&lt;br/&gt;
模型依赖于 GPU 的显存，根据&lt;a href="https://sspai.com/link?target=https%3A%2F%2Fgithub.com%2Fray-project%2Fllm-numbers%232x-number-of-parameters-typical-gpu-memory-requirements-of-an-llm-for-serving"&gt;经验&lt;/a&gt;, 采用 16 位浮点数加载模型的话，推理所需显存大小(以 GB 为单) 是模型参数量(以 10 亿计) 的两倍，如 3B 模型需要约 6G 显存。如果对模型进一步量化，如精度量化到 4 位整数，则所需显存大小降为原来的 1/4 到 1/3, 意味着 3B 模型只要 2 G 显存就能进行推理。所以我们可以把一个 3B 的模型塞到手机里去运行，如果是 1B 的模型 int4 量化后内存占用不到 1G(0.5 ~ 0.67)。&lt;br/&gt;&lt;br/&gt;
本文体验 llama.cpp 对模型进行推理，在 Hugging Face 的用户设置页面 &lt;a href="https://huggingface.co/settings/local-apps"&gt;Local Apps and Hardware&lt;/a&gt;, 可看到一些流行的跑模型的应用程序，分别是&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;生成文本的: &lt;a href="https://github.com/ggerganov/llama.cpp"&gt;llama.cpp&lt;/a&gt;, &lt;a href="https://lmstudio.ai/"&gt;LM Studio&lt;/a&gt;, &lt;a href="https://jan.ai/"&gt;Jan&lt;/a&gt;, &lt;a href="https://backyard.ai/"&gt;Backyard AI&lt;/a&gt;, &lt;a href="https://jellybox.com/"&gt;Jellybox&lt;/a&gt;, &lt;a href="https://recurse.chat/"&gt;RecurseChat&lt;/a&gt;, &lt;a href="https://msty.app/"&gt;Msty&lt;/a&gt;, &lt;a href="https://sanctum.ai/"&gt;Sanctum&lt;/a&gt;, &lt;a href="https://github.com/mudler/LocalAI"&gt;LocalAI&lt;/a&gt;, &lt;a href="https://docs.vllm.ai/"&gt;vLLM&lt;/a&gt;, &lt;a href="https://node-llama-cpp.withcat.ai/"&gt;node-llama-cpp&lt;/a&gt;, &lt;a href="https://ollama.com/"&gt;Ollama&lt;/a&gt;, &lt;a href="https://huggingface.co/docs/text-generation-inference/"&gt;TGI&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;文生图的: &lt;a href="https://drawthings.ai/"&gt;Draw Things&lt;/a&gt;, &lt;a href="https://diffusionbee.com/"&gt;DiffusionBee&lt;/a&gt;, &lt;a href="https://github.com/invoke-ai/InvokeAI"&gt;Invoke&lt;/a&gt;, &lt;a href="https://joyfusion.app/"&gt;JoyFusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Oracle, PostgreSQL 字符串排序不一致及调整</title><link>https://yanbin.blog/oracle-postgresql-string-sorting-different-consistent/</link><pubDate>Fri, 01 Nov 2024 18:47:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-postgresql-string-sorting-different-consistent/</guid><description>
有一个使用了不同数据库的应，Oracle 和 PostgreSQL，数据库中的记录完全相同，相同的查询语句(相同的排序，至少从字面上来说是的)取到的记录排序却不同，从而产生了 Bug。 简单演示一下默认排序各自在这两种数据库中的行为, 比如说表中有两条记录，'VFORX' 和 'ibbVA'。此处不创建物理表。&lt;br/&gt;&lt;br/&gt;
Oracle(Linux)&lt;br/&gt;
&lt;blockquote&gt;
select * from (&lt;br /&gt;
    select 'VFORX' as value from dual&lt;br /&gt;
        union&lt;br /&gt;
    select 'ibbVA' from dual&lt;br /&gt;
) order by 1 desc;
&lt;/blockquote&gt;
返回的结果是</description></item><item><title>试用 Llama-3.1-8B-Instruct AI 模型</title><link>https://yanbin.blog/get-start-with-llama-3-1-8b-instruct-ai-model/</link><pubDate>Wed, 30 Oct 2024 15:02:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/get-start-with-llama-3-1-8b-instruct-ai-model/</guid><description>
IT 从业人员累的一个原因是要紧跟时代步伐，甚至是被拽着赶，更别说福报 996. 从早先 CGI, ASP, PHP, 到 Java, .Net, Java 开发是 Spring, Hibernate, 而后云时代 AWS, Azure, 程序一路奔波在掌握工具的使用。而如今言必提的 AI 模型更是时髦，n B 参数, 量化, 微调, ML, LLM, NLP, AGI, RAG, Token, LoRA 等一众词更让坠入云里雾里。&lt;/p&gt;
&lt;br/&gt;
去年以机器学习为名买的(游戏机)一直未被正名，机器配置为 CPU i9-13900F + 内存 64G + 显卡 RTX 4090，从进门之后完全处于游戏状态，花了数百小时对《黑神话》进行了几翻测试。&lt;br/&gt;&lt;br/&gt;
现在要好好用它的 GPU 来体验一下 Meta 开源的 AI 模型，切换到操作系统 Ubuntu 20.04,  用 transformers 的方式试了下两个模型，分别是&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct"&gt;Llama-3.1-8B-Instruct&lt;/a&gt;: 显存使用了 16G，它的老版本的模型是 &lt;a href="https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct"&gt;Meta-Llama-3-8B-Instruct&lt;/a&gt;(支持中文问话，输出是英文)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/meta-llama/Llama-3.2-11B-Vision-Instruct"&gt;Llama-3.2-11B-Vision-Instruct&lt;/a&gt;: 显存锋值到了 22.6G(可以分析图片的内容)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
都是使用的 torch_dtype=torch.bfloat16, 对于 24 G 显存的 4090 还用不着主内存来帮忙。如果用 float32 则需更多的显存，对于 Llama-3.2-11B-Vision-Instruct 使用 float32, 则要求助于主内存，将看到&lt;br/&gt;
&lt;blockquote&gt;
Some parameters are on the meta device because they were offloaded to the cpu.
&lt;/blockquote&gt;
反之，对原始模型降低精度，量化成 8 位或 4 位则更节约显卡，这是后话，这里主要记述使用上面的 Llama-3.1-8B-Instruct 模型的过程以及感受它的强大，可比小瞧了这个 8B 的小家伙。所以在手机上可以离线轻松跑一个 1B 的模型。</description></item><item><title>ECS Task Definition 中需要的 image:tag 明明在 ECR 中存在却找不到 image</title><link>https://yanbin.blog/ecs-task-definition-required-image-tag-exist-in-ecr-but-cannot-pull-image/</link><pubDate>Thu, 17 Oct 2024 21:43:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ecs-task-definition-required-image-tag-exist-in-ecr-but-cannot-pull-image/</guid><description>
这是近些天遇到的一个问题，因为早先使用 ECS 为求快速验证新的 Docker Image, 一直是用相同的 Tag 覆盖 ECR 中原有的 Docker Image，然后停掉 ECS 中相应的 Task, 新的 Task 起来，拉取最新 Docker Image，这样不用重新部署 Infrastructure, 以最小的改动就能达到偷梁换柱的效果。比如下面的情景：&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;ECS 任务定义中所用的 Image 是 123456789012.dkr.ecr.us-east-1.amazonaws.com/demo:1.10&lt;/li&gt;
&lt;li&gt;构建新的 Docker Image, 然后再 docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/demo:1.10&lt;/li&gt;
&lt;li&gt;覆盖后，在 ECR 中将有两个 Tag,  刚 push 的是 &lt;code&gt;1.10&lt;/code&gt;, 被覆盖的变成 &lt;code&gt;-&lt;/code&gt;, 多次覆盖将会产生更多的 &lt;code&gt;-&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;停掉 ECS 相应的 Task, 新的 Task 起来，&lt;span style="color: #000000;"&gt;拉取 123456789012.dkr.ecr.us-east-1.amazonaws.com/demo:1.10 代表的新镜像&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
这种做法在以前是灵验的，每次修改代码，覆盖现有 Tag, 重启 Task 就能快速测试, 不用重新创建 Task Definition 和别的 Infrastructure。&lt;br/&gt;&lt;br/&gt;
然而最近突然不起作用了，本地不断的修改代码，构建新的镜像，覆盖原有 Tag, 重启 Task, 可是依旧跑的是老代码。怀疑 ECR 中的 Image 有问题，用 docker pull 下来看确实是新代码，就差进到 ECS Task 实例中去找问题。而且即使是重新运行 Terraform 来部署整个 Infrastructure 都无济于事，就是 aws_ecs_service 中指定了 force_new_deployment = true 也没辙，因为只要 Docker Image 的 Tag 没变,  Terraform 就认为是 &lt;span style="color: #008000;"&gt;no change&lt;/span&gt;。</description></item><item><title>Python logging 使用笔记</title><link>https://yanbin.blog/python-logging-usages/</link><pubDate>Wed, 16 Oct 2024 16:49:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-logging-usages/</guid><description>
使用 Python 的话用不着像 Java 那样是考虑用 Logback  还是 Log4J 的问题，因为它内置提供了完备功能的 logging 库。虽然 JDK  也有 java.util.logging(JUL), 它的特性其实也不差，如日志级别，输出格式，不同的输出目的地的选择，但在 Logback 和 Log4J 的光环之下几乎无人问津。相比而言 Python 的 logging 却极为受宠，非必要时基本不会去考虑引入第三方的日志库，如 &lt;a href="https://github.com/Delgan/loguru"&gt;Loguru&lt;/a&gt;, &lt;a href="https://github.com/getlogbook/logbook"&gt;LogBook&lt;/a&gt;, &lt;a href="https://github.com/hynek/structlog"&gt;Structlog&lt;/a&gt;, &lt;a href="https://github.com/microsoft/picologging"&gt;Picologging&lt;/a&gt;, 尽管它们也很出色，毕竟是庶出。&lt;/p&gt;
&lt;br/&gt;
&lt;h3&gt;logging 的最基本用法&lt;/h3&gt;
在基本前面加是&lt;code&gt;最&lt;/code&gt; 字，是因为这一节仅仅是如何让 logging 作为 print() 的替代品，暂不涉及到参数的传递，异常的输出，以及格式定制，日志往哪里输出的问题。&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
运行，什么也看不到，因为 Python logging 的默认级别是 warning, 这不符合人的基本认知，一般 logging.info() 起码是用来替代 print() 的，居然直接用无法输出，不知该库的设计者是怎么个想法。</description></item><item><title>多线程环境中使用 Mockito 来 Mock 静态方法</title><link>https://yanbin.blog/mockito-mock-static-method-in-multiple-threading-env/</link><pubDate>Thu, 10 Oct 2024 11:26:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-mock-static-method-in-multiple-threading-env/</guid><description>
回看三年前的一篇日志 &lt;a href="https://yanbin.blog/mockito-3-4-0-mock-static-method/"&gt;Mockito 3.4.0 开始可 Mock  静态方法&lt;/a&gt;，最后对 Mockito 产生的缺憾是它无法用来 Mock 非测试线程(主线程)中的静态方法调用。其实这也是可以变通的，下面慢慢道来。&lt;/p&gt;
&lt;br/&gt;
首先回顾一下 Mockito  的静态方法 Mock 的使用方法，随着 Mockito 版本的升级，引入依赖的方式也发生了些许的变化，以 Maven 项目为例，如果在 JUnit 5 下用 Mockito 的 pom.xml 依赖中为&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.mockito&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;mockito-junit-jupiter&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.14.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
由它引入的全部相关依赖&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2024/mockito-mock-static-method-in-multiple-threading-env/mockito-dependencies.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2024/mockito-mock-static-method-in-multiple-threading-env/mockito-dependencies-800x435.png"
width="405px"
/&gt;
&lt;/a&gt;</description></item><item><title>Java, Python 两种形式的 base64encode</title><link>https://yanbin.blog/java-python-two-styles-base64encode/</link><pubDate>Thu, 19 Sep 2024 13:21:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-python-two-styles-base64encode/</guid><description>
在用 Python 写 Web 服务端代码时，用 base64.encodebytes() 函数对字符串进行编码，然后在 Java 端用 Base64.getDecoder().decode() 时无法解码，难道 base64 编码在两种语言间还有这等差异。Google 一下，得到的答案是在 Java 端要用 Base64.getMimeDecoder().decode() 函数解码。这一问题算是解决了, 不过后来又在 Python 写的 AWS Lambda 中输出&lt;/p&gt;
&lt;blockquote&gt;
return {&lt;br /&gt;
    "statusCode": 200,&lt;br /&gt;
    "body": base64.encodebytes(b"short message"),&lt;br /&gt;
    "isBase64Encoded": True&lt;br /&gt;
}
&lt;/blockquote&gt;
以 AWS Lambda functionURL 的方式来访问，对于 body 中的小字符串是没问题，一旦 body 够大时在 Postman 或 curl 命令中无法直接展示出来，用 curl --output a.out 存成本地文件，打开后看到的是带换行的格式&lt;br/&gt;
&lt;blockquote&gt;
H4sIAAZi7GYC/+19WXfcOLLmX+HxwxzXOS6b2AhiprvnyFtZt7yoJbdr6r74UEpKyq5UpjoXL/fX&lt;br /&gt;
D8AlkysIkCFmpo2H7pJJEBkAAsCHQMQXf/t6HX/YrO83a++P168+xcvVdDH/+yP6lOGnzEciCNHn&lt;br /&gt;
gD7yTufT9evpLD6L1rd/f3Q6n03nsXexXk7nN4+8F4v59fRms4zW8uNtHdjH9KkfPEXoM6JPsf8U&lt;br /&gt;
......
&lt;/blockquote&gt;</description></item><item><title>ThreadLocal, InheritableThreadLocal 以及 TransmittableThreadLocal</title><link>https://yanbin.blog/threadlocal-inheritablethreadlocal-transmittablethreadlocal/</link><pubDate>Fri, 23 Aug 2024 21:53:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/threadlocal-inheritablethreadlocal-transmittablethreadlocal/</guid><description>
ThreadLocal 是 Java 编程人员要掌握的一个基本类，似乎没什么太多要说。但因为本文要牵出 TransmittableThreadlLocal, 再顺带说下几乎隐形的 InheritableThreadLocal。&lt;/p&gt;
&lt;br/&gt;
ThreadLocal 用于保存与线程绑定的数据，它在框架内部使用的很频繁，但凡见到 XxxContextHolder.currentContext() 之类的十之八九用到了 ThreadLocal, 如 Spring 框架中的&lt;br/&gt;&lt;br/&gt;
RequestContextHolder&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RequestContextHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requestAttributesHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NamedThreadLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Request attributes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RequestAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inheritableRequestAttributesHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NamedInheritableThreadLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Request context&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Dubbo 最基础的 RPC 应用(使用 ZooKeeper)</title><link>https://yanbin.blog/dubbo-basic-rpc-call-with-zookeeper/</link><pubDate>Fri, 23 Aug 2024 11:17:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dubbo-basic-rpc-call-with-zookeeper/</guid><description>
看国内的一些项目时 Dubbo 这个词经常闪现，一直也不以为然，未作搜索，当然也不知道它是做什么用的。直到最近阅读关于大型网站架构相关的书中反复提到 Dubbo 后，觉得不能再对它视而不见。Google 了一下，它是在阿里巴巴创建贡献给了 Apache 的开源项目，在阿里巴巴的大型应用中久经考验过的。Dubbo 是什么呢？借用官方 &lt;a href="https://dubbo.apache.org/zh-cn/overview/what/"&gt;Dubbo 介绍&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
Apache Dubbo 是一款 RPC 服务开发框架，用于解决微服务架构下的服务治理与通信问题，官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力， 利用 Dubbo 提供的丰富服务治理特性，可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展，用户可以方便的实现流量拦截、选址的各种定制逻辑。
&lt;/blockquote&gt;
Dubbo 是国内企业贡献的，所以官方有原生的中文文档，它某些时候与 Spring Cloud 齐名，又有些像 AWS 的 ECS Service Discovery, Service Connect 加上 ELB 的功能。</description></item><item><title>使用 Redis 作为消息队列 - Redis Stream</title><link>https://yanbin.blog/redis-as-message-queue-redis-stream/</link><pubDate>Wed, 21 Aug 2024 22:04:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/redis-as-message-queue-redis-stream/</guid><description>
十来天前写过一篇 Redis 之前如何曲线的方式用作消息队列 &lt;a href="https://yanbin.blog/use-redis-as-message-queue-pub-sub-list-sortedset/"&gt;使用 Redis 作为消息队列 - Pub/Sub, List, SortedSet&lt;/a&gt;. 只能说简单的使用方式勉强还行，离真正意义上的消息队列有些距离。而自 Redis 5.0 加入了 Stream 就更进一步，可望朝着作为正规消息队列的 At most once, At least once, 和 Exactly once 方向迈进。&lt;br/&gt;&lt;br/&gt;
如果以 Serverless 方式使用 AWS 的 Redis, 那么既然用到高级消息队列的功能，还能省去使用 AmazonMQ(ActiveMQ 或 RabbitMQ) 或 MSK(Kafka) 的高成本。&lt;br/&gt;&lt;br/&gt;
Redis stream 数据结构像是一个 append-only 日志，但又添加了 O(1) 的随机访问和复杂的消费策略，如消息分组。&lt;br/&gt;&lt;br/&gt;
Redis Stream 的每条消息会有一个唯一 ID, 支持消费组, Redis 用以支持 Stream 的一系列命令是 X 为前缀的, &lt;a href="https://redis.io/commands/?group=stream"&gt;完整的 Stream 命令列表&lt;/a&gt;。</description></item><item><title>调用 AWS Lambda 时如何传送字节数组</title><link>https://yanbin.blog/invoke-aws-lambda-with-bytes/</link><pubDate>Tue, 20 Aug 2024 12:52:35 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/invoke-aws-lambda-with-bytes/</guid><description>
本文主要验证用 Python 写的 AWS Lambda 与 Java 客户端之间如何双向传递二进制数据，这里不涉及到 Lambda 流输入输出的问题。比如一个 Python AWS Lambda 的处理方法声明是&lt;br/&gt;
&lt;blockquote&gt;
def lambda_handler(event, context):&lt;br /&gt;
    pass  # or do something
&lt;/blockquote&gt;
通过我们用 Lambda 调用时会传给 &lt;code&gt;event&lt;/code&gt; 一个 JSON 格式的字符串，反应到 AWS Lambda 时 &lt;code&gt;event&lt;/code&gt; 就是一个字典。但当要传递二进制数据如何做呢？直觉的做法就是用 base64 编码二进制字节为普通的字符串，比如要节约网络传输的数据量，需要对文本进行压缩，格式可以是这样&lt;br/&gt;
&lt;blockquote&gt;
{"input": base64Encode(gzipCompress("text content......"))}
&lt;/blockquote&gt;
然后在 Lambda 端取出 &lt;code&gt;input&lt;/code&gt; 的值作相应的 base64 解码再解压缩。&lt;br/&gt;&lt;br/&gt;
对于大文本，即使是压缩后再编码为 base64 也比直接传送原始文本数据要节约网络带宽。&lt;br/&gt;&lt;br/&gt;
这种方案实际也是可行的，然而我们在实际使用 Java AWS Lambda SDK 时有些动作会自动帮我们实现的，那就是二进制数据自动 base64 编码。</description></item><item><title>Java 使用堆外内存(off-heap memory) 作为缓存</title><link>https://yanbin.blog/java-off-heap-memory-cache/</link><pubDate>Mon, 19 Aug 2024 16:58:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-off-heap-memory-cache/</guid><description>
我们通常用的 Java 缓存基本可认为是扩展了 HashMap 或 ConcurrentHashMap 的实现，它们各自实现自己的缓存策略，如时间与空间的控制，生命周期管理，是否支持分布式，溢出时能否转储到磁盘。关于 Java 本地缓存的存储分为内存与磁盘，内存多数情况下指的是堆内内存(on-heap), 而介于堆内内存与文件存储之间的就是堆外内存(off-heap)&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;堆内存储(on-heap): 操作最快，无需序列化，但大量数据时会影响到 GC 的效率&lt;/li&gt;
&lt;li&gt;堆外存储(off-heap): 存储在 Java 进程内存但非 JVM 堆内(不在 -Xmx 指定的内存范围内)，使用或保存时需进行序列化/反序列化过程(在堆内与堆外转换)，但不受 GC 影响，有助于提它来 GC 的效率&lt;/li&gt;
&lt;li&gt;文件存储：不仅存在序列化与反序列化过程，还带 IO 操作，所以最慢，唯一优点就是大&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
我们查看一下当前 Spring 支持的缓存实现, &lt;a href="https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-caching.html#boot-features-caching-provider"&gt;Supported Cache Providers&lt;/a&gt;, 列有 Generic, JCache(JSR-107), EhCache 2.x, Hazelcast, Infinispan, Couchbase, Redis, Caffeine, Simple， 这其中无一支持堆外缓存，其中的 EhCache 要付费使用 EhCache 3(Big Memory) 才能支持 off-heap。</description></item><item><title>用 C++ 写一个 AWS Lambda Hello World</title><link>https://yanbin.blog/cpp-aws-lambda-hello-world/</link><pubDate>Sat, 08 Jun 2024 14:10:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cpp-aws-lambda-hello-world/</guid><description>
AWS 自 2014 年推出 Lambda 时仅支持 Node.js，而后添加了对 Python, Ruby, Java, C#, F#, PowerShell 的支持，再来到 2018 年可以自定义运行时了，比如用性能较好的 C, C++, Rust, Go 等语言。见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/"&gt;AWS Lambda Now Supports Custom Runtimes and Enables Sharing Common Code Between Functions&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;
如果使用 Python, Java 写 Lambda 时觉得还不得快，不想要明显的预热过程，也许 1000 毫秒的任务只想要 600 毫秒就能完成，内存还希望再压缩一些，那着实能在每月千百万次 Lambda 调用的情况下节省一笔可观的支出，那么可以试一试 C, C++, Rust, Go 等编译成了机器指令的语言，况且前三者没有 GC, 执行效率会更高。&lt;br/&gt;&lt;br/&gt;
本日志记录一下如何用 C++ 创建一个 AWS Lambda, 以及可如何应付 Lambda 的复用。本文主要参考自下面两处&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/compute/introducing-the-c-lambda-runtime/"&gt;Introducing the C++ Lambda Runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub 项目 &lt;a href="https://github.com/awslabs/aws-lambda-cpp"&gt;aws-lambda-cpp&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
自定义运行时可选择 X86_64 或 arm64 的 Amazon Linux 2023 或 Amazon Linux 2。部署时可选择的 runtime 相应有 provided.al2023, provided.al2, 推荐使用 provided.al2023。runtime provided 不被支持了。&lt;br/&gt;&lt;br/&gt;
C++ 代码可选择用 GCC 或 Clang 来编译，既然 AWS Lambda 实际的运行时会用到 Amazon Linux 2023，那我们就直接选择 Docker 镜像 amazonlinux:2023 作为我们的编译环境。</description></item><item><title>创建可直接用 root 用户 ssh 登陆的 Docker 镜像</title><link>https://yanbin.blog/create-root-ssh-docker-image/</link><pubDate>Thu, 06 Jun 2024 14:57:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/create-root-ssh-docker-image/</guid><description>
&lt;p&gt;有时候我们在 Mac OS X 或 Windows 平台下需要开发以 Linux 为运行时的应用，IDE 或可直接使用 Docker 容器，或 SSH 远程连接。
本地命令行下操作虽然可以用 &lt;code&gt;docker exec&lt;/code&gt; 连接正在运行的容器，但 IDE 远程连接的话 SSH 总是一种较为通用的连接方式，
所以我们希望做一个能进行 SSH 连接的 Docker 容器。因为是本地运行的 Docker，我们想直接用 root 连接，以获得在容器中最大的运行权限。
下面以 ubuntu:2004 基础镜像为例，看如何安装启用 ssh 服务以及允许 root 连接.&lt;/p&gt;
&lt;h3 id="创建允许-root--密码登陆的镜像"&gt;创建允许 root + 密码登陆的镜像&lt;/h3&gt;
&lt;p&gt;我们创建一个基本的 Dockerfile 文件，内容为&lt;/p&gt;</description></item><item><title>C 语言静态库与动态库的生成和使用</title><link>https://yanbin.blog/c-static-dynamic-library/</link><pubDate>Fri, 31 May 2024 11:42:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/c-static-dynamic-library/</guid><description>
在 YouTube 上找到一个视频 &lt;a href="https://www.youtube.com/watch?v=k_ccI1Hff60"&gt;动态链接库静态链接库的生成和使用&lt;/a&gt;，它把用 GCC 生成静态库和动态库，以及如何使用他们说的很明白，有条件的可以直接看那个视频。本文就是一个观后的实操和笔记，加添了更多如何查看动态库，静态库，目标文件，执行文件的过程。&lt;/p&gt;
&lt;br/&gt;
为什么要了解静态库和动态库呢？这有助于我们理解多模块的 C/C++ 代码是如何联合工作的。我们多数时候使用的 IDE, 一个 Build 帮我们做了太多的事情，反而使我们眼前一抹黑，这背后有怎么把一个个源文件编译成目标文件(*.o) 文件，或生成静态库/动态库，又如何连接静态库/动态库生成可执行文件，等等。&lt;br/&gt;&lt;br/&gt;
试验中使用的平台是 Linux, 如果没有 Linux 可通过 Docker 容器得到一个，如&lt;br/&gt;
&lt;blockquote&gt;
$ docker run -it -v $(pwd):/work -w /work rust:1.78-buster bash
&lt;/blockquote&gt;
为什么使用 rust:1.78 镜像，其实也没什么特别的，因为当前在学习 Rust, 而正好该镜像中有 GCC 编译器。启动该容器后，为编辑需要，最好安装一个 vim，在容器中运行</description></item><item><title>Rust 调用 C/Rust 生成的动态库</title><link>https://yanbin.blog/rust-call-c-rust-shared-library/</link><pubDate>Thu, 30 May 2024 22:05:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-call-c-rust-shared-library/</guid><description>
在始终是 C/C++ 有着更优越性能的情况下，因而之前介绍过多种 其他不同的语言如何加载使用 C/C++ 写的动态库，有 Go, Python, Java 和 C#。在学习 Rust 之时也有类似的需求。本文的做法是要用到第三方库 &lt;a href="https://docs.rs/libloading/0.8.3/libloading/index.html"&gt;libloading&lt;/a&gt;，这里将参考官方的例子。&lt;/p&gt;
&lt;br/&gt;
先来创建一个动态库，使用和 &lt;a href="https://yanbin.blog/go-invoke-c-dylib-linux/"&gt;Go 调用 C 写的动态库完整例子(Linux版)&lt;/a&gt; 一文中相同的例子，add.c 代码内容如下&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;strcat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
在 Linux 中使用如下命令编译出 libadd.so 动态库文件</description></item><item><title>Java 线程池有限大小工作队列 - 不丢弃任务的实现</title><link>https://yanbin.blog/java-threadpool-bounded-workqueue-block-wait/</link><pubDate>Thu, 09 May 2024 13:47:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-threadpool-bounded-workqueue-block-wait/</guid><description>
我们在创建 Java 线程池，无论是用 Executors, ThreadPoolExecutor, 还是 Spring 的 ThreadPoolTaskExecutor, 如果不指定工作队列的大小的话，默认为 Integer.MAX_VALUE(2147483647), 基本不会把它爆满，但是在许多的任务要执行时大量 Runnable 对象的创建却足以把内存撑爆掉。所以才有必要使用一个有限大小的工作队列，如 5000, 再配上 RejectedExecutionHandler(DiscardOldestPolicy, DiscardPolicy, 或 CallerRunsPolicy)。前两种策略会主动放弃最旧最新的任务，一般不是我们想要的，CallerRunsPolicy 还能主动发挥任务提交者的计算能力，是一种不错的选择(只可能会发生工作队列太小且提交者执行的任务太忙时产生线程池一时的空闲。&lt;/p&gt;
&lt;br/&gt;
所以总结起来我们可以有以下几种实现&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;直接使用 CallerRunsPolicy&lt;/h3&gt;
在工作队列满时有效利用提交任务的线程，不让它闲着，这种实现最简单, 像下面那样声明线程池&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CallerRunsPolicy&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>JDBC 设置 PostgreSQL 查询中 any(?) 的参数</title><link>https://yanbin.blog/jdbc-postgresql-set-array-for-any-question-mark/</link><pubDate>Wed, 01 May 2024 00:00:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbc-postgresql-set-array-for-any-question-mark/</guid><description>
&lt;p&gt;这段时间都纠缠于 Java 如何操作 PostgreSQL 数据库上，千方百计的为求得更好的性能。为此我们用上了 Batch, 或用 id = any(?) 这种更 PostgreSQL 化的数组参数操作。其实它还有更多数组方面的花样可以玩，毕竟 PostgreSQL 数据库有一种广纳百川的胸怀，总有好的新特性能在 PostgreSQL 中首先体验到。&lt;/p&gt;
&lt;p&gt;回到之前的一篇 &lt;a href="https://yanbin.blog/postgres-in-vs-any-comparison"&gt;postgres in (?,?) 和 =any(?) 用法/性能对比&lt;/a&gt;，其中关于如何向查询语句中 &lt;code&gt;id = any(?)&lt;/code&gt; 占位符传入数组参数的代码是&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;select * from users where id = any(?)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PreparedStatement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pstmt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prepareStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pstmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createArrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;int&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ResultSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pstmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在 PreparedStatement(PgPreparedStatement) 中设置数组参数的函数是用</description></item><item><title>JDBC 批量调用数据库 SQL, 函数与存储过程</title><link>https://yanbin.blog/jdbc-batch-sql-function-procedure/</link><pubDate>Fri, 26 Apr 2024 21:23:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbc-batch-sql-function-procedure/</guid><description>
继续上一篇数据库相关操作的话题，在有大量的数据操作时(如增删改，甚至调用函数或存储过程)，我们应该尽可能的采用批量化操作(先摆下结论，后面我们会看到原由)。想像一下我们要向数据库插入 10 万条记录，如果逐条插入的话，客户端与数据库之间将会有 10 万网络请求响应来回; 而假如以 1000 条记录为一个 batch, 客户端与数据库之间的网络请求响应次数将缩小到 100。 业务数据的内容总量未变，但 Batch 操作除了可重用预编译的 Statement 外还, 可避免每次请求中重复的元数据，所以从 100,000 到 100 的缩减在时效上的表现是非常可观的，有时就是 60 分钟与 1 分钟的区别(在最后面测试结果显示这一差异更为恐怖)。&lt;/p&gt;
&lt;br/&gt;
当然, JDBC 的批处理功能具体还要相应驱动的支持，通过数据库连接的 conn.getMetaData().supportsBatchUpdates() 可探知是否支持批量操作。&lt;br/&gt;&lt;br/&gt;
API 方面, 在  &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.sql/java/sql/Statement.html"&gt;Statement&lt;/a&gt; 接口中定义了如下 batch 相关的操作方法&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;void addBatch(String sql): 将显式的 SQL 语句编入到当前 Batch 中&lt;/li&gt;
&lt;li&gt;void clearBatch(): 清除当前 Batch 列表，以便于建立新的 Batch&lt;/li&gt;
&lt;li&gt;int[] executeBatch(): 执行当前 Batch 列表中的语句，返回每条语句受影响行数组成的数组。0 可能表示执行语句无法确知受影响的行&lt;/li&gt;
&lt;li&gt;long[] executeLargeBatch(): 当 Batch 中语句受影响行数可能会超过整数最大值时用这个&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>PostgreSQL 函数与存储过程及调用</title><link>https://yanbin.blog/postgresql-function-procedure-application/</link><pubDate>Wed, 10 Apr 2024 00:57:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/postgresql-function-procedure-application/</guid><description>
PostgreSQL 随着云服务的盛行，越发被广泛的应用，免费开源且有丰富的特性支持，加上性能也很不错，因而备受青睐。PostgreSQL 的函数与存储过程区别并不太大，不像某些数据库的函数与存储过程必须是无副作用或有副作用，在 PostgreSQL 的函数和存储过程中可以进行任何的 SQL 操作。简单列举下 PostgreSQL 的函数与存储过程的区别主要如下：&lt;/p&gt;
&lt;br/&gt;
函数&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;return 或 out 参数返回值，return 可返回单个值或一系列值(return setof 或 return table), 或返回光标(cursor). 函数 return void 就和存储过程差不多了&lt;/li&gt;
&lt;li&gt;函数因其有返回值，所以可通过 select, insert, updata 或 delete 语句来调用，如 select fn1(), delete * from test1 where fn2(c1)=0&lt;/li&gt;
&lt;li&gt;可以用 execute 执行动态 sql, 如 execute 'delete * from ' || 't1'&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
存储过程&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;IN, OUT 或 INOUT 参数，但不直接返回值&lt;/li&gt;
&lt;li&gt;不能用 select, insert 等语句使用&lt;/li&gt;
&lt;li&gt;不能用 execute 执行动态 sql&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
接下来我们来体验一下 PostgreSQL 的函数与存储过程</description></item><item><title>Java 9 前/后使用 JAXB (包括支持 javax.* 或 jakarta.*)</title><link>https://yanbin.blog/java-9-jaxb-including-javax-jakarta/</link><pubDate>Fri, 22 Mar 2024 22:13:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-9-jaxb-including-javax-jakarta/</guid><description>
使用 Java 处理 Object 与 XML 之间的转换时 JAXB(Java Architecture for XML Binding) 仍然被广泛使用。但随着 Java 9 模块化后把 JAXB 从标准 JDK 中移除后，和 Java EE 8(Jakarta EE 8) 到 Jakarta EE 9 的变迁时命名空间由 &lt;code&gt;javax.*&lt;/code&gt; 变成了 &lt;code&gt;jakarta.*&lt;/code&gt;，我们在使用 JAXB 时需作出相应的适配。&lt;/p&gt;
&lt;br/&gt;
本文分别使用 Java 1.8, 17，通过 Maven 插件 jaxb2-maven-plugin 的 xjc (从 xsd 文件生成 Java 类), 和如何切换 &lt;code&gt;jakarta.*&lt;/code&gt; 命名空间，由此可给我们对使用了 JAXB 的项目升级 JDK 时指明方向。从而不致于因不了解每部分组件的具体功效而在 pom.xml 中胡乱配置，比如之前对 jaxb2-maven-plugin 插件本身配置了多余的 org.glassfish.jaxb:jaxb-xjc 和 org.glassfish.jaxb:jaxb-runtime 依赖，也未能理解 jaxb2-maven-plugin 与 org.glassfish.jaxb:jaxb-runtime 之间的版本对应关系。&lt;br/&gt;&lt;br/&gt;
 实验准备，创建简单的 Maven 项目，并在路径 src/main/xsd/ 下新建 Schema 文件 sample.xsd，内容为</description></item><item><title>Windows 安装使用 GCC(mingw-w64)</title><link>https://yanbin.blog/windows-install-use-gcc/</link><pubDate>Mon, 11 Mar 2024 23:44:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-install-use-gcc/</guid><description>
本人近十来年来本地用 Mac OS 开发, 服务器为 Linux, 为什么又要涉及到 Windows 的 GCC 呢？因为有个跨平台的东西用的是 C++, 需要分别编译出目标平台为 Linux 和 Windows 的二进制文件. 然而 C++ 并没有像 Rust 那样一出生就含着 Cargo 那样的工具链，完美的支持跨平台开发，构建。对于 C++ 代码不得不在 Linux 下用 GCC 编译器(Makefile), 而 Windows 下使用的 Visual Studio 的 MSBuild, 为了能统一用 Makefile 文件 + GCC 的方式编译 C++ 项目, 可选择 Windows 平台下也安装 GCC。&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://gcc.gnu.org/"&gt;GCC&lt;/a&gt; 又是什么呢？它是 GNU 的编译工具集，包括对 C, C++, Objective-C, Foratran, Ada, Go 和 D 等一众语言的支持， 和它类似的工具集有 &lt;a href="https://llvm.org/"&gt;LLVM&lt;/a&gt;。GCC 支持多操作系统平台，怎么找到它的各种二进制安装包呢？我们循着官网去找, 打开 GCC 首页 &lt;a href="https://gcc.gnu.org/"&gt;https://gcc.gnu.org/&lt;/a&gt;，从页面的右边栏可找到</description></item><item><title>Rust 语言学习笔记(五)</title><link>https://yanbin.blog/rust-language-learning-5/</link><pubDate>Fri, 02 Feb 2024 14:30:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-language-learning-5/</guid><description>
终于来到了 Rust 的精髓所在了，那就是使之不依赖于垃圾回收又能保障内存安全且高效运行的所有权系统(Ownership System)。想要用 Rust 做一个稍显规模项目必定绕不过它，所有权系统包括所有权(Ownership), 借用(Borrowing), 生命周期(Lifetimes)。&lt;br/&gt;&lt;br/&gt;
以下概念的复述基本是从 《Rust编程: 入门, 实战与进阶》一书中而来，那里面有些内容是来自于官方的 &lt;a href="https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html"&gt;The Rust Programming Languge - Understanding Ownership&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;所有权系统的基本概念&lt;/h3&gt;
Rust 的编程语法很快就能上手，让学习 Rust 曲线陡然大增的也就是这个所有权系统。所有权检测在编译期完成，Rust 能编译出来的代码就是安全高效的。要理解 Rust 的所有权系统必须首先明白以下两组概念：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;栈内存(Stack)，值语义(Value Semantic)，按位复制(浅复制)(Shallow Copy)，复制语义(Copy Semantic)&lt;/li&gt;
&lt;li&gt;堆内存(Heap), 引用语义(Reference Semantic), 深复制(Deep Copy)，移动语义(Move Semantic), 借用(Borrowing)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
和其他语言一样，大小固定的所有基本类型都可以存储在栈上，栈上存取数据总是在栈顶操作，很快，而访问堆内存需要搜索内存地址。所有权系统的主要任务是用来跟踪堆上的数据，即引用语义的数据。</description></item><item><title>从 Rust 官方文档理解 Ownership</title><link>https://yanbin.blog/understand-rust-official-document-ownership/</link><pubDate>Mon, 15 Jan 2024 12:36:19 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-rust-official-document-ownership/</guid><description>
Rust 的 Ownership 感觉仍然很复杂，但 Rust 官方文档 &lt;a href="https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html"&gt;The Rust Programming Language - Understanding Ownership&lt;/a&gt; 所费篇幅似乎并不多。下面就阅读该文档并记录下来对 Rust Ownership 的理解，相信官方的文档会表述的比准确而清晰。&lt;/p&gt;
&lt;br/&gt;
本文中对 Ownership, Move, Reference, Dereference, Mutable, Immutable, Borrow, Owner, Stack, Heap, Scope 等词不进行翻译，以免走样。同时在阅读过程中不进行过度的联想，不与 C/C++ 的引用, 指针, 指针的指针进行关联，力求做一个完全不会 C/C++ 的 Rust 初学者。&lt;br/&gt;&lt;br/&gt;
Ownership 是 Rust 独一无二的特性。内存管理一般是两种，显式分配与释放和 GC, 这两种的弊端无需多说。Rust 另辟溪径，用 Owership 的一系列的规则来指导怎么管理内存，编译期保证程序运行期的内存安全性，不影响运行时性能。学习 Rust 的过程中需要很长时间去适应 Ownership, 从 Rust 开发者(Rustacean) 的经验来说是：随着对 Ownership 的掌握，越来轻松自然的写出安全高效的代码(希望如此)。</description></item><item><title>Rust 语言学习笔记(四)</title><link>https://yanbin.blog/rust-language-learning-4/</link><pubDate>Sun, 07 Jan 2024 14:19:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-language-learning-4/</guid><description>
试手了一下 Rust, 发现止今所学知识尚浅，不少情况处理起来很是茫然，比如经常得到的值是 Result 或 Option, 有时候要 .unwrap(), 有时得后面加个问号，或 .await。还有从自定义函数里返回一个引用都不容易，到处是 move, borrow, owner, lifetime 之类的错误信息。所以说，快充五分钟还真不敢上路，继续学习 Rust 的 Result, 说起它又必须与 Rust 的错误处理联系起来。&lt;/p&gt;
&lt;br/&gt;
编程语言在处理错误无外乎就两种哲学&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;得到正确的结果或触发异常，异常不就地处理则向外传播&lt;/li&gt;
&lt;li&gt;总是能得到一个结果，于结果中获知是否有异常，如 Option&amp;lt;T&amp;gt;, Result&amp;lt;T, E&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
其实本人更钟爱前一种方式，因为正常逻辑可以更流畅的表达，不至于被众多的 if 语句所打断; 而异常的话可以就地解决，或者延迟到某处去集中处理。Java 多是用第一种方式，所以某些框架尽量使用 Unchecked 异常，不强制插入 try/catch 块。&lt;br/&gt;&lt;br/&gt;
第二种方式，在 shell 或 C++ 很常见，比如 shell 下每调用一个命令都有返回数值, 即 $?, 0 为正常，非 0 为有错误; C++ 的 GetLastError() 也是一样的意思。&lt;br/&gt;&lt;br/&gt;
现在所学的 Rust 在处理结果和错误时，采用了第二种方式，不过使用上 Rust 的模式匹配处理起 Option&amp;lt;T&amp;gt;, Result&amp;lt;T, E&amp;gt; 倒也不难，而且 Option&amp;lt;T&amp;gt; 和 Result&amp;lt;T, E&amp;gt; 这样的返回结果还用来向上传播异常，出错时还能追踪到异常栈。</description></item><item><title>SpringBoot 应用出错 Comparison method violates its general contract!</title><link>https://yanbin.blog/springboot-error-comparison-method-violates-its-general-contract/</link><pubDate>Sun, 07 Jan 2024 02:04:08 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot-error-comparison-method-violates-its-general-contract/</guid><description>
出现此错误的大致环境如下&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SpringBoot 2.7.17, SpringWeb 项目，所引用入的 spring-webmvc-5.3.30, spring 6 已解决&lt;/li&gt;
&lt;li&gt;JDK 1.8 或 JDK 17&lt;/li&gt;
&lt;li&gt;依赖了 jackson-dataformat-xml:2.12.6 和 jackson-dataformat-cbor:2.12.6, 它会在 RestTemplate 加上 application/xml, application/cbor 等 Accept 类型&lt;/li&gt;
&lt;li&gt;代码中用 RestTemplate 调用此应用的 Endpoint, 未设置任何头&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
后面会详细列出能重现此问题的 pom.xml 配置及 Java 代码&lt;br/&gt;&lt;br/&gt;
在执行&lt;br/&gt;
&lt;blockquote&gt;
restTemplate.getForEntity("http://localhost:8080/test2", String.class)
&lt;/blockquote&gt;
&lt;br/&gt;
时出现如下错误</description></item><item><title>Rust 语言学习笔记(三)</title><link>https://yanbin.blog/rust-language-learning-3/</link><pubDate>Wed, 03 Jan 2024 14:17:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-language-learning-3/</guid><description>
&lt;h3&gt;引用与解除引用&lt;/h3&gt;
觉得还是有必要继续深入学习一下 Rust 再练手，毕竟仍然看到 &amp;amp; 和 * 符号还有些恍惚，大概就是 C/C++ 里的取地址和取值操作吧，实际上也确实类似。只是叫法略有不同, 还有就是在 C/C++ 多用了指针的概念。&lt;/p&gt;
&lt;br/&gt;
在 C/C++ 中， &lt;code&gt;&amp;amp;&lt;/code&gt;：称作 Address-of Operator, 在 Rust 中称作 Reference Operator, 而 &lt;code&gt;*&lt;/code&gt; 在 C/C++ 和 Rust 中都叫做 Dereference Operator。以前学 C/C++ 经常被一系列的 &amp;amp;, * 打晕了头，如今参考了它们的英文名称立刻变得清晰了起来。&lt;br/&gt;&lt;br/&gt;
就像当初看汇编各种寻址方式弄得头都大了，其实也就是依照约定。</description></item><item><title>使用 Redis 作为消息队列 - Pub/Sub, List, SortedSet</title><link>https://yanbin.blog/use-redis-as-message-queue-pub-sub-list-sortedset/</link><pubDate>Tue, 02 Jan 2024 23:39:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-redis-as-message-queue-pub-sub-list-sortedset/</guid><description>
有好长一段时间没使用 Redis 了，之前用的都是 AWS 上的 Elastic Cache 的 Redis, 那时候还是用的版本还是 4 和 5。在新的项目由于觉得 Elastic Cache Redis 太贵而未曾使用，在去年的 AWS re:Invent 2023 上发布了 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2023/11/amazon-elasticache-serverless/"&gt;Elastic Cache Serverless&lt;/a&gt;，对于非长时间大数据的缓存可以考虑使用。而且还可以使用 Redis 的功能对服务进行解耦合，或作为一致性的协调中心。&lt;br/&gt;&lt;br/&gt;
此文是作为正式研究使用 Redis Stream (Redis 5.0 新特性)  的一个铺垫，探索在 Redis Stream 之前，可以何种方式把 Redis 当成一个消息队列，后面将会具体讲到如何用 Redis Stream 作为一个支持消费者组，能确认消息的更为完备的消息队列。&lt;br/&gt;&lt;br/&gt;
如以下几种方式&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Pub/Sub 订阅模式&lt;/li&gt;
&lt;li&gt;基于 List 的 LPUSH + BRPOP 的实现&lt;/li&gt;
&lt;li&gt;SortedSet, 使用消息 Score 排序功能进行消费&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;h3&gt;Pub/Sub 订阅模式&lt;/h3&gt;
它就像 AWS 的 SNS 一样，只有在线的订阅者才能收到消息，每个消费者会收到相同的消息。相关的 Redis 命令是 *sub*, *pub*, 参考 &lt;a href="https://redis.io/commands/?group=pubsub"&gt;Redis Command group Pub/Sub&lt;/a&gt;。订阅的时候可以直接指定 Channel 名称，或用模式匹配，还可关注某些 Key 的事件。</description></item><item><title>Rust 语言学习笔记(二)</title><link>https://yanbin.blog/rust-language-learning-2/</link><pubDate>Sun, 31 Dec 2023 22:40:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-language-learning-2/</guid><description>
再继续快速学习一下 Rust 的以下几个知识点，就可以开始着手做点小工具了&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;基本数据类型&lt;/li&gt;
&lt;li&gt;复合数据类型&lt;/li&gt;
&lt;li&gt;基本的流程控制&lt;/li&gt;
&lt;/ol&gt;
Rust 设计为有效使用内存考虑的，它提供了非常细力度的数据类型，如整数分为有无符号，宽度从 8 位到 128 位，分别表示为 i8, u8, u128 等。浮点数有 f32 和 f64，以及 bool 和 range 类型。&lt;br/&gt;
&lt;h3&gt;元组&lt;/h3&gt;
元组和 Python 的元组用法类似，Immutable, 可混合类型&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 一个元素时，和 Python 一样，后面附加逗号，否则视括号可选
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup3&lt;/span&gt;: &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;i8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;boo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 类型要一一对应
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup4&lt;/span&gt;: &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 声明一个空元组，元组是不可变的，所以没什么意义
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 元组的拆解
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tup1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 访问用 .index 方式访问
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;数组&lt;/h3&gt;
数组类型中的元素类型相同，表示为  [T; n], T 为类型，n 为元素个数</description></item><item><title>Rust 语言学习笔记(一)</title><link>https://yanbin.blog/rust-language-learning-1/</link><pubDate>Sun, 31 Dec 2023 14:59:44 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rust-language-learning-1/</guid><description>
学了不少编程语言，多数是离不开垃圾回收的，要么像 C++ 仍然是过于复杂，对于通用，编译型编程语言 Rust 是个不错的选择, Rust 不需要垃圾回收器。Rust 是由 Mozilla 主导开发的，设计准则为 "安全，并发，实用", 支持函数式，并发式，过程式以及面向对象的编程风格。Rust 能达到与 C++ 接近的性能，它又是编译型语言，编译出来二进制文件执行时不再依赖于运行时。Rust 有自带的 Cargo 作为依赖管理与构建工具，免除了关键工具的选择综合症。AWS 在今年也推出了 Rust 的 AWS SDK, 所以学习 Rust 的过程中也打算使用它来操作 AWS 的资源。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Rust 的 Hello World&lt;/h3&gt;
在 macOS 下的安装&lt;br/&gt;
&lt;blockquote&gt;
$ brew install rust
&lt;/blockquote&gt;
或&lt;br/&gt;
&lt;blockquote&gt;
$ curl https://sh.rustup.rs | sh    # 安装
$ rustc --version                              # 查看版本
$ rustup update                               # 更新
$ rustup self uninstall                     # 卸载
&lt;/blockquote&gt;
当前 Rust 版本为 1.74.0, 安装后有 rustc, rustdoc, rust-gdb, rust-lldb, cargo 等相关命令</description></item><item><title>Java StackOverflowError 与递归及尾递归优化</title><link>https://yanbin.blog/stackoverflowerror-recursion-tail-recursion/</link><pubDate>Sat, 30 Dec 2023 18:33:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/stackoverflowerror-recursion-tail-recursion/</guid><description>
本篇同样是阅读《100 Java Mistakes and How to Avoid Them》的一则笔记，只是火力全集中在 StackOverflowError 这个单一主题之上, 且主要与递归及尾递归优化相关。一提到 StackOverflow, 恐怕首先是想到那个代码搬运源网站 stackoverflow.com，其次才是代码执行过程中的 StackOverflowError 错误。&lt;br/&gt;&lt;br/&gt;
什么是 StackOverflow, 准确来说是指线程的栈内存不足，无法在栈中分配新的内存(或创建新的栈帧)。我们通常会把它与方法调用关联起来，因为一次方法调用会创建一个新的栈帧，分配在栈上的局部变量(包括基本类型与对象引用)，和栈帧都要占用线程栈内存。而我们平时所说的方法调用栈，或出现异常时打印出的异常栈都是一个概念。&lt;br/&gt;&lt;br/&gt;
StackOverflowError 一般出现在方法调用太深(方法调方法)，手动编写的方法调用一般不容易达到这个限制，所以它与递归调用关系最为密切，递归调用次数太多或甚至没有出口条件无限递归; 也可能是经过一番递归调用后，再正常调用后续方法时出现 StackOverflowError(因为前面的递归调用资源消耗的差不多)&lt;br/&gt;&lt;br/&gt;
何谓递归调用，简单的说就是方法自己调用自己，从而实现循环的效果，或使代码更精练，例如经典排序中的快速和归并排序就要用到递归。但循环与递归又有本质上的区别，循环不增加调用深度；递归却不同，递归分递进与回归两个过程，递进调用时每次调用前都需通过压栈来保留现场，逆向回归时再逐级恢复现场，保留现场的过程就要从线程栈中分配内存空间; 死循环可能不会造成程序异常，但死(无限)递归必定出现 StackOverflowError。</description></item><item><title>《100 Java Mistakes and How to Avoid Them》笔记 3</title><link>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-3/</link><pubDate>Thu, 28 Dec 2023 00:56:44 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-3/</guid><description>
本书的阅读又搁置了许久，虽然感觉 Manning 出版社的这一 &lt;code&gt;100 Mistakes&lt;/code&gt; 系列从书的质量不是那么的高，但开了头还是继续从本书 40% 的位置往下。&lt;/p&gt;
&lt;br/&gt;
开始要讲述到异常了，异常还是有必要认真对待的，比如&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Java 中很容易被 CheckedException 弄得代码不整洁&lt;/li&gt;
&lt;li&gt;缺少必要的参数检查，不舍得抛出异常，视异常为 Bug&lt;/li&gt;
&lt;li&gt;不明确出现异常时后续如何处理，&lt;/li&gt;
&lt;li&gt;或者是捕获而隐藏了异常致使定位错误变得更难。&lt;/li&gt;
&lt;/ol&gt;&lt;br/&gt;
Java 的主要异常大分类是&lt;br/&gt;
&lt;blockquote&gt;
Throwable&lt;br /&gt;
├── Error&lt;br /&gt;
└── Exception&lt;br /&gt;
          └── RuntimeException
&lt;/blockquote&gt;
NullPointerException, 这恐怕是一个最常见的异常，Java 对一个对象是否能为 null 值没什么约束，甚至用 null 来表示业务上的空。比如说方法的参数与返回值，Java 都可以是 null 值，而在 Kotlin 中非明确可为 null 的时不能为 null</description></item><item><title>本地和 ECS 容器(EC2/Fargate) 如何处理 ENTRYPOINT</title><link>https://yanbin.blog/docker-ecs-how-to-handle-entrypoint/</link><pubDate>Sat, 23 Dec 2023 23:10:44 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-ecs-how-to-handle-entrypoint/</guid><description>
不觉一晃还是在五年前记录过一篇 &lt;a href="https://yanbin.blog/dockerfile-difference-between-shell-exec-forms/"&gt;Dockerfile 中命令的两种书写方式的区别&lt;/a&gt;，其中提到过 Dockerfile 中可选择用 ENTRYPOINT 或 CMD 来启动进程，并且在 ENTRYPOINT 和 CMD 都支持  exec, shell 和增强型 shell 方式。如果同时有 ENTRYPOINT 和  CMD(或 docker 运行时的 CMD), 则 CMD 将为 ENTRYPOINT 提供参数。&lt;/p&gt;
&lt;br/&gt;
在原来那篇文中认为 shell 无法接收到 docker stop 或  docker -s SIGTERM 发来的信号，也许是随着 Docker 版本的变迁，Docker 变得越发聪明了起来，无论何种格式的 ENTRYPOINT, 都能够收到 SIGTERM 信号，比如在 Java 的 ShutdownHook 能捕捉到该信号，得以在进程停止之前作必要的清理工作。&lt;br/&gt;&lt;br/&gt;
进行本文相关研究的主因是部署在 ECS(Fargate) 中的 Java Web 服务，Task 总是因为 OutOfMemoryError 被杀掉，而在应用程序日志中却见不着半点线索说 JVM 的 OutOfMemoryError，即使后来给 Fargate 配上了 EFS, 加了 &lt;code&gt;+XX:+HeapDumpOnOutOfMemoryError XX:HeapDumpPath=/efs&lt;/code&gt; JVM参数，在任务被 kill 时在 /efs 上从来就都没生成过内存映像文件。最后发现是因为 JVM 的 -Xmx 配置太高，留给 Fargate 容器的太少的缘故。</description></item><item><title>Vagrant 没了 VirtualBox 的话可以配 Qemu</title><link>https://yanbin.blog/vagrant-leave-virtualbox-for-qemu/</link><pubDate>Sat, 23 Dec 2023 00:30:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vagrant-leave-virtualbox-for-qemu/</guid><description>
开源虚拟机软件 VirtualBox 从当初不可一世的 Sun 易手到 Oracle 之间，变得不那么被许多公司信任了。之前一直是用 Vagrant 搭配 VirtualBox 在 Mac 下使用 Linux 虚拟机，因为不需要用到 Linux 桌面，用 Vagrant 操作虚拟机非常方便。但现在不得不要考虑别的方式，那就是 Vagrant + Qemu。Vagrant 的默认 Provider 是 VirtualBox, 我们搜索 Vagrant Box &lt;a href="https://app.vagrantup.com/boxes/search"&gt;https://app.vagrantup.com/boxes/search&lt;/a&gt; 的时候，注意到  Vagrant 其实支持的 Provider 是一个很长的列表:&lt;/p&gt;
&lt;blockquote&gt;
aws, cloudstack, digitalocean, docker, google, hyperv, libvirt, lxc, openstack, parallels, qemu, rackspace, softlayer, veertu, virtubalbox, vmware, vmware_desktop, vmware_fusion, vmware_ovf, vmware_workstation, vsphere, xenserver
&lt;/blockquote&gt;
有些尚未听说过，还有一些虽说支持，但别人提供的相应的 box 不多，Qemu 还算不错的 virtualbox 的替代品。Qemu 也是一款开源的虚拟机软件，它支持 Linux, macOS 和 Windows  平台，但它本身未提供友好的 UI 工具，所以创建一个 Qemu 虚拟机的命令会让人畏而却步。在 macOS 下有一个 Qemu 的 UI 工具 &lt;a href="https://mac.getutm.app/"&gt;UTM&lt;/a&gt;</description></item><item><title>Docker 容器中使用 Docker - DinD 和 DooD</title><link>https://yanbin.blog/docker-container-as-daemon/</link><pubDate>Sat, 09 Dec 2023 21:39:54 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-container-as-daemon/</guid><description>
&lt;p&gt;突然间研究这个来的缘由是正在从 Jenkins 往 Harness 的过度, 而完全用命令来构建 Docker 镜像变得不一样了。在 Jenkins 中 Agent 本身也是一个 Docker Daemon, 所以 Docker 命令执行无障碍，而 Harness 的所谓的 Agent 就是一个个的运行在 Kubernetes 中的 Docker Container (Pod) 了，这其中没有 Docker Daemon, 又不能连接到 Kubernetes 本身的 Docker Daemon。另外 CloudBees CI/CD 的运行环境与 Harness 类似，也是运行在 Kubernetes 中的 Pod。&lt;/p&gt;
&lt;p&gt;因此可能要使用某个 Docker 容器来作为 Docker Daemon, 所以牵连出对此的研究，相应的方案有 Docker in Docker(DinD) 和  Docker outside of Docker(DooD)。&lt;/p&gt;
&lt;h3&gt;对容器中启动 Docker Daemon 的探索&lt;/h3&gt;
&lt;p&gt;在知晓 DinD 和 DooD 这两个概念本人还试图构建过一个 Docker 镜像，试图用一个 Docker 容器既作 Docker Daemon 又作为 Docker 客户端。在容器中 Docker 安装成功，但无法在容器中启动 Docker Daemon。比如用下面的 Dockerfile</description></item><item><title>理解 JUnit, JaCoCo 到 SonarQube 的过程及 Maven 配置</title><link>https://yanbin.blog/junit-jacoco-sonarqube-maven/</link><pubDate>Wed, 22 Nov 2023 00:09:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-jacoco-sonarqube-maven/</guid><description>
Java 项目需要产生单元测试及代码覆盖率的话一直都是走的 JUnit 单元测试，JaCoCo 基于测试产生测试覆盖率，然后送到 SonarQube 去展示这条路子。当然 SonarQube 还可以帮我们进行代码的静态分析。但对其中的具体使用及过程知晓的并不深，基本就是在 pom.xml 中依葫芦画瓢。本文稍加深入的理解每一步的功效与配置，以 Maven 管理的 Java 项目为例，JUnit 采用是众多旧项目仍然无法摆脱的 JUnit 4。&lt;br/&gt;&lt;br/&gt;
示例项目名称为 JaCoCoSonar, 创建一个 Calc 类，其中有 int add(int op1, int op2) 方法，为其写一个单元测试 CalcTest&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalcTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testAdd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Calc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;h3&gt;单元测试实际是被 maven-surefire-plugin 插件执行的&lt;/h3&gt;
现在开始第一步，执行 &lt;code&gt;mvn test&lt;/code&gt; 看会发生什么，执行过程中控制台显示</description></item><item><title>《100 Java Mistakes and How to Avoid Them》笔记 2</title><link>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-2/</link><pubDate>Wed, 25 Oct 2023 15:59:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-2/</guid><description>
继续阅读本书，编程语言处理数值都有可能出现问题，如溢出，整数的最大最小值不对称，Double.NaN 等。&lt;/p&gt;
&lt;br/&gt;
由于 Java 学了 C，也用 0 开始的数字来表示 8 进制数，如 037, 010 分别是十进制的 31 和 8，这与现实不相符。因为如果你在纸上写下 037, 010, 几乎所有人(除了某些程序员)都会认为它们就是十进制的 37 和 10。但是 Java 表示 2 进制, 16 进制的方式没有问题的，如 0b10, 0x37。IntelliJ IDEA 看到使用 0 开头的 8 进制数会不建议那么使用. 8 进制数字的范围是 0~8, 所以 09 是错误的, 但是 Java 编译器似乎对此很陌生.&lt;br/&gt;&lt;br/&gt;
int a = 09;&lt;br/&gt;&lt;br/&gt;
IntelliJ IDEA 会提示 &lt;code&gt;Integer number too large&lt;/code&gt;, 编译器提示说 &lt;code&gt;java: ';' expected&lt;/code&gt;, 有点驴唇不对马嘴.&lt;br/&gt;&lt;br/&gt;
现在几乎没有必要使用 0 开始的 8 进制数的方式, 或许还有用的就是表示 Unix 下文件权限, 如&lt;br/&gt;&lt;br/&gt;
int fileMode = 0644&lt;br/&gt;&lt;br/&gt;
所以任何时候看到 0 开头的数字都必须仔细检视, 基本可以禁止使用这种方式</description></item><item><title>《100 Java Mistakes and How to Avoid Them》笔记 1</title><link>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-1/</link><pubDate>Sun, 17 Sep 2023 23:46:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/100-java-mistakes-and-how-to-avoid-them-notes-1/</guid><description>
这几日在阅读 Manning 出版社的 《100 Java Mistakes and How to Avoid Them》, 其中列举的确实是一些容易带入到代码中的错误，不少还是通过代码 Review 或单元测试很难发现的问题。也有些看似很弱智，却可能是隐匿许久的定时炸弹，只等某一特定条件出现时即爆。&lt;/p&gt;
&lt;br/&gt;
阅读的同时简单的作了笔记及少许联想，所以内容有些杂乱无条理。最前面介绍了一些静态代码分析工具，也有两个动态分析工具。本书目前还是 Manning 的 MEAP 体验版，未正式发售。一共讲了 100 个常见错误如何避免(例如，怎么用最新 Java(Java 9 -- Java 21) 语法, API 来改进)，以及用静态分析工具，单元测试及早发现。&lt;br/&gt;&lt;br/&gt;
这是读完了 1/4 数量的记录，笔记开始</description></item><item><title>Lombok @With 的纯弊端及如何避免</title><link>https://yanbin.blog/lombok-with-evil-and-how-avoid/</link><pubDate>Sun, 10 Sep 2023 11:52:38 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lombok-with-evil-and-how-avoid/</guid><description>
由于是第一篇写关于 Lombok 的日志，所以有些不情愿去开门见山直接触及 @With, 而要先提一提本人对 Lombok 的接触过程。&lt;/p&gt;
&lt;br/&gt;
两三年之前写 Java 代码一直都是全手工打造。一个数据类，所有必须的 setter/getter, toString, hashcode() 等全体现在源代码中，当然是在 IDE 中自动生成的。听说过 Lombok，但觉得它用了会影响到对源代码的阅读，因为造成代码的行为与源代码所展示的不一致，还可能依赖于特定的 IDE 或构建工具插件，所以一直未真正应用。&lt;br/&gt;&lt;br/&gt;
然而现代语言一直在避免不断书写象 JavaBean 里那一大片样本代码，同时解决试图提高覆盖率写出毫无意义单元测试的烦恼。比如 Scala 发展出了 case class, Kotlin 的 data class, Python 的 @dataclass，还有 JDK 14 引入的及至 JDK 16 转正的  record, 都是为了自动生成 Java 类的 setter/getter/toSring/hashcode/equals 等方法。 所以源代码中看不到实际可调用方法不该再是问题，况且在 JDK 5 加入的 enum 类型本质上也是在源代码的背后生成了一系列的方法和类型声明的。</description></item><item><title>postgres in (?,?) 和 =any(?) 用法/性能对比</title><link>https://yanbin.blog/postgres-in-vs-any-comparison/</link><pubDate>Wed, 09 Aug 2023 11:29:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/postgres-in-vs-any-comparison/</guid><description>
&lt;p&gt;刚刚回顾了一下 &lt;a href="https://yanbin.blog/jdbc-sql-server-pass-list-parameter/"&gt;JDBC 操作 SQL Server 时如何传入列表参数&lt;/a&gt;，即如何给 in (?) 条件直接传入一个列表参数，然而本质上是不支持，最终不得不展开为 in (?, ?,...?) 针对每个元素单独设置参数，不定长的参数对于重用已编译 PreparedStatement 语句的帮助不大。&lt;/p&gt;
&lt;p&gt;那么 JDBC 操作 PostgreSQL 是何种状态呢？展开为多个参数当然是有效的。继续尝试 Spring 提供的 NamedParameterJdbcTemplate 的操作方式&lt;/p&gt;
&lt;blockquote&gt;
String query = "select * from users where id in (:ids)";&lt;br /&gt;
Map&amp;lt;String, Object&amp;gt; parameters = new HashMap&amp;lt;&amp;gt;();&lt;br /&gt;
parameters.put("ids", IntStream.rangeClosed(1, 5).boxed().collect(toList()));&lt;br /&gt;
List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; maps = namedParameterJdbcTemplate.queryForList(query, parameters);
&lt;/blockquote&gt;
&lt;p&gt;执行后查看到实际执行的语句是&lt;/p&gt;
&lt;blockquote&gt;
select * from users where id in (?, ?, ?, ?, ?)
&lt;/blockquote&gt;</description></item><item><title>JDBC 操作 SQL Server 时如何传入列表参数</title><link>https://yanbin.blog/jdbc-sql-server-pass-list-parameter/</link><pubDate>Wed, 09 Aug 2023 01:10:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbc-sql-server-pass-list-parameter/</guid><description>
&lt;p&gt;本文是作为将要对 PostgreSQL 的 in, any() 操作的一个铺垫，也是对先前用 JDBC 操作 SQL Server 的温习。以此记录一下用 JDBC 查询 SQL Server 时如何传递一个列表参数。比如想像一下查询语句&lt;/p&gt;
&lt;blockquote&gt;
select * from users where id in (?)
&lt;/blockquote&gt;
 我们是否能给这里的问题参数传递一个 List 或数组呢？&lt;br/&gt;&lt;br/&gt;
这里所引用的 SQL Server 的 JDBC 驱动是 com.microsoft.sqlserver:mssql-jdbc:11.2.0.jre8&lt;br/&gt;&lt;br/&gt;
我们尝试调用 PreparedStatement.setArray() 方法来设置这个参数&lt;br/&gt;
&lt;blockquote&gt;
pstmt.setArray(1, conn.createArrayOf("int", new Integer[]{1,2,3}));
&lt;/blockquote&gt;
这里会受到两个阻碍，首先&lt;br/&gt;&lt;br/&gt;
SQL Server 的 PreparedStatement 的实现类 SQLServerPreparedStatement 的 setArray() 未实现，反编译出它的 setArray() 方法是</description></item><item><title>AWS Step Function 异步动态调用 Lambda 后汇集结果</title><link>https://yanbin.blog/aws-step-function-async-call-dynamic-lambdas-aggregation/</link><pubDate>Mon, 19 Jun 2023 11:59:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-step-function-async-call-dynamic-lambdas-aggregation/</guid><description>
分布式计算有这么一个需求，主进程准备好输入数据，然后根据输入中某个 Items 动态调用若干计算进程，待到所有计算完成后再汇集结果。这一需求移植到 AWS 上就像是下面这样子&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2023/aws-step-function-async-call-dynamic-lambdas-aggregation/lambda-sync-call-1-1.png"
width="400px"
/&gt;
但在一个 Lambda 中同步调用其他 Lambda 时就有个费时费钱的问题，虽然我们采用线程池来调用 Lambda2, 由于每个同步调用的耗时不相同， Lambda1 最终要等待最慢的那个调用结束后才能对所有结果进行聚集处理。这就是著名的“长板效应”, Lambda1 多数时候是在无谓的等待当中消耗着你的钱财。</description></item><item><title>AWS Lambda 中使用 Python 并发编程</title><link>https://yanbin.blog/aws-lambda-python-in-parallel/</link><pubDate>Fri, 26 May 2023 14:05:53 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-lambda-python-in-parallel/</guid><description>
无论在何处，有多重任务要处理时，并发编程总是要得到考虑的。比如有 IO 等待时的并发或 CPU 密集型时的并行计算，并发通常是指在同一个 CPU 上按时间片轮换执行，并行是任务在不同的 CPU 上执行。能有效使用 CPU 多核的语言可以让线程运行在不同的核上实现并行，如果是启动的子进程能由操作系统运行在其他 CPU 核上。&lt;br/&gt;&lt;br/&gt;
回到 AWS Lambda 中的 Python 代码，如果是处理 IO 等待，使用多线程并发就行，大致的代码如下：&lt;br/&gt;
&lt;blockquote&gt;
with ThreadPoolExecutor(10) as executor:&lt;br /&gt;
    result = executor.map(task_function, task_inputs)
&lt;/blockquote&gt;
以上代码在 AWS Lambda 中是可以运行的。&lt;br/&gt;&lt;br/&gt;
如果是 CPU 密集型的任务，用 Python 的多线程就要歇菜了，因为存在著名的 &lt;a href="https://realpython.com/python-gil/"&gt;Python's GIL&lt;/a&gt; 的约束。
这时候就必须要考虑多进程并行的方式，同时应知晓当前选择的 Lambda 运行环境有多少个 CPU 内核，因为如果是单核的话再多进程也无济于事，没必要启动多于核心数的进程。
底下是本人上篇博客测试收集的不同 AWS Lambda 内存选择对应的 CPU 核心数，以及实际可用内存大小的关系表</description></item><item><title>实测 AWS Lambda 不同内存配置下的 CPU 核心数</title><link>https://yanbin.blog/get-aws-lambda-cpu-count-different-memory/</link><pubDate>Thu, 25 May 2023 15:49:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/get-aws-lambda-cpu-count-different-memory/</guid><description>
目前(2023-05-25) AWS Lambda 的内存选择区间是 128MB ~ 10240MB， 最长运行时间为 15 分钟，但没有 vCPU 个数的选择。vCPU 的数量是基于所选内存大小而有不同的，如果我们在 Lambda 中需使用多进程充分发挥 CPU 性能的话，有必要了解当前 Lambda 所在运行环境的 CPU 内核数，甚至是单核的频率。&lt;br/&gt;&lt;br/&gt;
CPU 个数可用如下 Python 内置的其中一个方法取得&lt;br/&gt;
&lt;blockquote&gt;
multiprocessing.cpu_count()&lt;br /&gt;
os.cpu_count()
&lt;/blockquote&gt;
要获得 CPU 频率或内存的话，将要用到 &lt;code&gt;psutil&lt;/code&gt;  组件的方法，可把 psutil 做成 Lambda  层以引用，或与 Lambda 函数代码一同打成  zip 包。&lt;br/&gt;&lt;br/&gt;
安装方法 &lt;code&gt;psutil&lt;/code&gt;&lt;br/&gt;
&lt;blockquote&gt;
pip install --target . psutil
&lt;/blockquote&gt;
psutil 会安装到当前目录，然后在当前目录下再创建 lambda_function.py 文件，再打包</description></item><item><title>Spring 5 响应式编程研究</title><link>https://yanbin.blog/spring-5-mono-flux-reactor-programming/</link><pubDate>Mon, 08 May 2023 14:55:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-5-mono-flux-reactor-programming/</guid><description>
Spring 5.0 发布之时(2017-09-28) WebFlux 是它的一大亮点，即响应式 Web 编程。因为同一时代的 RxJava 2 和 Akka Actor 具备一定的流行度，Spring 5 也来赶这一趟时髦。于是多线程编程大致两种模式&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CompletableFuture, runAsync, supplyAsync, whenComplete...&lt;/li&gt;
&lt;li&gt;Obervable, observeOn, subscribe, subscribeOn...&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
以及 PlayFramework 的 Action 方法无论返回 Result 还是 CompletableStage&amp;lt;Result&amp;gt;, 内部都是异步的模式。&lt;br/&gt;&lt;br/&gt;
Akka Actor 比 CompletableFuture, RxJava，以及本文将要讨论的 Reactor 更高级的是 Akka System 可以分布式部署，Actor 分布在不同的进程，主机上。 &lt;br/&gt;&lt;br/&gt;
那时候业界已行成了一个 Reactive Stream 规范 org.reactivestreams(Publisher, Subscriber, Subscription, Processor), JDK 9 也奈不住寂寞，无法对 Reactive Stream 置若罔闻，在 2017-09-21 发布时加入了 java.util.concurrent.flow 包(Publisher, Subscriber, Processor, Subscription) 作为自己的 Reactive Stream 规范。&lt;br/&gt;&lt;br/&gt;
然而随着云计算的普及，基于消息系统解耦合的任务分解让代码变得更清晰，编码中甚至不用考虑多线程的行为，部署方式能解决任务执行的效率。&lt;br/&gt;</description></item><item><title>配置 FastAPI/Uvicorn/Hypercorn 的访问日志</title><link>https://yanbin.blog/configure-fastapi-uvicorn-access-log/</link><pubDate>Wed, 03 May 2023 15:10:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/configure-fastapi-uvicorn-access-log/</guid><description>
有了 FastAPI 之后，用 Python 实现 API 或 Web 都不再考虑 Flask 了。Flask 最早在 13 年前的 2010 年 4 月 1 日发布，实现的是 WSGI; FastAPI 较为年轻，于 4 年前的 2018 年 12 月 5 日发布，支持 ASGI。性能方面普遍是 FastAPI 比 Flask 高，编程方面就各取所好吧，使用 Flask 的时候还是 1.x 的版本，最近用 FastAPI 较多，所以无法对比。&lt;br/&gt;&lt;br/&gt;
FastAPI 本身没提供启动 Web 服务的代码，不像 Flask 还能通过 Flask 对象 或 flask 命令启动一个开发用途的 Web 服务，而 FastAPI 必须用其他的组件(ASGI server)来启动，比如各种 *corn 或 Daphne&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt;: FastAPI 官方的出品，默认启用访问日志，相关的参数有 &lt;code&gt;--log-config &amp;lt;path&amp;gt;&lt;/code&gt;, &lt;code&gt;--access-log&lt;/code&gt;/ &lt;code&gt;--no-access-log&lt;/code&gt; 启用或关闭访问日志，默认是开启的。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pgjones.gitlab.io/hypercorn/"&gt;Hypercorn&lt;/a&gt;: 相关的配置选项有 &lt;code&gt;--access-logformat&lt;/code&gt;, &lt;code&gt;--access-logfile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href="https://gunicorn.org/"&gt;Hypercorn&lt;/a&gt;: 它只是实现了 WSGI 规格的服务，所以不兼容 FastAPI, 只能作为 Uvicorn 进程的管理器。Gunicorn 作为 WSGI 服务器有丰富的访问日志配置，但访问日志仍然是由 Uvicorn 输出&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>应用 AWS Lambda 部署 FastAPI</title><link>https://yanbin.blog/deploy-fastapi-on-aws-lambda/</link><pubDate>Mon, 01 May 2023 14:31:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/deploy-fastapi-on-aws-lambda/</guid><description>
前两年用 AWS Lambda 搭配 API Gateway 使用是为了省钱，因为没有请求时不花钱。又由于是 Rest API, 所以实现部分用了 FastAPI 的装饰器，但不实际启动 FastAPI 的 Web 服务，Lambda 的 handler 方法根据 routeKey 手动映射到 FastAPI 的装饰方法。大概实现是&lt;/p&gt;
&lt;blockquote&gt;
def lambda_handler(event: dict, context):&lt;br /&gt;
    fastapi_function = locate_fastapi_function(event['routeKey'])&lt;br /&gt;
    return fastapi_function(&amp;lt;extract parameters from event&amp;gt;)
&lt;/blockquote&gt;
当时也思考着能不能把 Lambda 的请求与 FastAPI 的 Web 服务桥接起来，却又不能真正启动一个  Web 服务，否则 Lambda 调用不能结束。比如说 AWS Lambda 收到请求时快速启动 FastAPI 服务，该服务绑定到  TCP 端口或 Socket 文件都行，然后 Lambda 请求代理到 FastAPI 服务，最后关闭 FastAPI 服务，但是想来都不那么容易实现。</description></item><item><title>理解 Java 线程池 ThreadPoolExecutor</title><link>https://yanbin.blog/understand-java-thread-pool-executor/</link><pubDate>Wed, 19 Apr 2023 10:32:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-thread-pool-executor/</guid><description>
使用 JDK 5 的线程池实现有近 20 年的时间了，快速创建一个线程池经常是调用 Executors 中的工厂方法。但是涉及过更精细的线程池管理控制时不得不用 ThreadPoolExecutor 的构造方法，这也就是为什么有些公司不建议用 Executors 的工厂方法创建线程池，而应该直接创建 ThreadPoolExecutor 或 ForkJoinPool 实例。 &lt;br/&gt;&lt;br/&gt;
例如代码&lt;br/&gt;
&lt;blockquote&gt;
ExecutorService threadPool = Executors.newFixedThreadPool(10);
&lt;/blockquote&gt;
实际上调用的是&lt;br/&gt;
&lt;blockquote&gt;
new ThreadPoolExecutor(nThreads, nThreads,&lt;br /&gt;
                                                0L, TimeUnit.MILLISECONDS,&lt;br /&gt;
                                                new LinkedBlockingQueue&amp;lt;Runnable&amp;gt;());
&lt;/blockquote&gt;
前两个参数 corePoolSize 和 maximumPoolSize 是一样的; OL, TimeUnit.MILLISECONDS 表示线程创建后只要线程池还在就是永生的; workQueue 是一个大小为 Integer.MAX_VALUE 的队列， 几乎可以无限提交任务，耗尽内存&lt;br/&gt;&lt;br/&gt;
不建议用 Executors 的工厂方法的原因大致有二：</description></item><item><title>学习使用 AWS Cognito 并 OAuth2 验证</title><link>https://yanbin.blog/learn-aws-cognito-and-oauth2/</link><pubDate>Tue, 04 Apr 2023 00:56:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/learn-aws-cognito-and-oauth2/</guid><description>
OAuth 是 Open Authorization 的缩写，是一种开放的可为 Web 或桌面应用进行用户验证和授权的协议。例如，在互联网上的许多应用，可不用额外注册帐户而采用第三方的帐户(Gmail, Apple Id 等)登陆并完成授权，这就有 OAuth 身影。&lt;br/&gt;&lt;br/&gt;
当我们提到 OAuth 的时候，常常会碰到 OAuth 1.0, OAuth 2.0, OpenID, 和  Auth0.&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;OAuth 1.0 于 2007 年 4 月 发布(&lt;a href="http://oauth.net/core/1.0/"&gt;OAuthCore 1.0&lt;/a&gt;)，存在严重的安全漏洞，2009 年 6 月发布修正版(&lt;a href="http://oauth.net/core/1.0a/"&gt;OAuthCore 1.0 Revision A&lt;/a&gt;). 较少使用了, 每个 token 加密，但不要求 HTTPS/TLS 协议&lt;/li&gt;
&lt;li&gt;&lt;a href="https://oauth.net/2/"&gt;OAuth 2.0&lt;/a&gt; 于 2012 年 10 月发布，它与  OAuth 1.0 互不兼容，目前多数平台都支持此版本，它强制使用 HTTPS/TLS 协议，更安全，相关的概念有 Access Token, Refresh Token, Bearer Token&lt;/li&gt;
&lt;li&gt;OpenID 侧重于 Authentication, 它是在 OAuth 上层用于鉴定用户是否可以登陆，OAuth 专注在 Authorization。与 OpenID 相对应的有 SAML(Security Assertion Markup Language)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/"&gt;Auth0&lt;/a&gt; 是一个软件产品 -- 身份管理平台(Auth0 Authentication Platform - Identity Access Management)，或者说是一套解决方案，这个缺德的命名纯粹是来搅浑水的。前面的 OAuth 1.0, OAuth 2.0 和 OpenID 都是协议规范，Okta 旗下的 Auth0 使用该名字抢了 OAuth 的光芒。&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
那 Amazon Cognito 是什么呢？它和 Auth0 类似，也是一个身份访问管理平台(Implement secure, frictionless &lt;strong&gt;c&lt;/strong&gt;ustomer &lt;strong&gt;i&lt;/strong&gt;dentity and &lt;strong&gt;a&lt;/strong&gt;ccess &lt;strong&gt;m&lt;/strong&gt;anagement that scales)，提供了用户的登陆验证，权限管理。背后的实现也是 OAuth 2.0, OIDC(OpenID Connection), 和 SAML。因此通过对 Cognito 的学习的另一个目的是由此了解 OAuth 2.0 协议的相关内容。</description></item><item><title>体验 Python FastAPI 的并发能力及线, 进程模型</title><link>https://yanbin.blog/test-compaire-flask-fastapi-tomcat-concurrency/</link><pubDate>Sun, 19 Mar 2023 00:50:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/test-compaire-flask-fastapi-tomcat-concurrency/</guid><description>
本文进行实际测试 FastAPI 的并发能力，即同时能处理多少个请求，另外还能接收多少请求放在等待队列当中; 并找到如何改变默认并发数; 以及它是如何运用线程或进程来处理请求。我们可以此与 Flask 进行对比，&lt;a href="https://yanbin.blog/python-flask-concurrency-thread-process/"&gt;参考 Python Flask 框架的并发能力及线,进程模型&lt;/a&gt;，是否真如传说中所说的 FastAPI 性能比 Flask 强, FastAPI 是否对得起它那道闪电的 Logo。&lt;/p&gt;
&lt;br/&gt;
本文使用 JMeter 进行测试，测试机器为 MacBook Pro, CPU 6 核超线程，内存 16 Gb。&lt;br/&gt;&lt;br/&gt;
对于每一种类型 Web 服务基本的测试是每秒发送 2 个请求，连续发送 1000 个，500 秒发送完所有请求，程序中 API 方法接受到请求后 sleep 800 秒，保证在全部 1000 个请求送出之前一直占着连接，并有充足的时间对连接进行分析。在测试极端并发数时，由于在 Mac OS X 尽管设置了 ulimit 最多也只能创建 4000 多一点线程，所以在模拟更多用户数时，JMeter 在远程 Linux(Docker 或虚拟机) 上运行测试用例。&lt;br/&gt;&lt;br/&gt;
请求的 URL 是 http://localhost:8080/?id=${count}, 带一个自增序列用以识别不同的请求， JMeter 的 Thread Group 配置为 Number of Threads (users): 1000, Ramp-up period (seconds): 500</description></item><item><title>Python __slots__ 的用法笔记</title><link>https://yanbin.blog/python-__slots__-notes/</link><pubDate>Tue, 28 Feb 2023 00:13:24 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-__slots__-notes/</guid><description>
Python 是一个动态语言，可以动态的给实例或类增减属性或方法，给类添加的属性会影响到前后所有创建的实例。但是使用 &lt;code&gt;__slots__&lt;/code&gt; 属性可以限定类或实例属性和方法，如果没有 &lt;code&gt;__slots__&lt;/code&gt; 的话实例的属性和方法包含在实例的 &lt;code&gt;__dict__&lt;/code&gt; 字典中，类的属性和方法包含在类的 &lt;code&gt;__dict__&lt;/code&gt; 字典中。&lt;/p&gt;
&lt;br/&gt;
在使用 &lt;code&gt;__slots__&lt;/code&gt; 按常规写法可能会出现的问题大概有&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;AttributeError: 'Xxx' object has no attribute 'yyy'&lt;/li&gt;
&lt;li&gt;AttributeError: 'Xxx' object attribute 'yyy' is read-only&lt;/li&gt;
&lt;li&gt;ValueError: 'yyy' in __slots__ conflicts with class variable&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
我们来看下面的例子</description></item><item><title>AWS SNS 订阅到 HTTP 的过程及消息报文</title><link>https://yanbin.blog/aws-sns-subscribe-http-endpoint-messages/</link><pubDate>Thu, 23 Feb 2023 00:33:56 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-sns-subscribe-http-endpoint-messages/</guid><description>
AWS SNS(Simple Notification Service) 以消息订阅，推送的方式对组件进行解藕。当有新消息发送到 SNS 主题中，SNS 会向当前所有的订阅者发送一个消息(广播)，它本身不像 SQS 那样会存储消息，而只是一个纯粹的消息路由。SNS 消息可以订阅到 Amazon Kinesis Data Firehose, SQS, Lambda, Email, Email-JSON, HTTP, HTTPs, Platform application endpoint, 和 SMS。同邮件列表一样，订阅 SNS 消息也是需要确认的，不然 SNS 消息就可能恶意满天飞。&lt;/p&gt;
&lt;br/&gt;
本文试验如何用 HTTP 端点订阅 SNS 消息，订阅确认，以及发送消息到 SNS 主题后消息推送到 HTTP 端点的细节，重点是了解订阅及被推送过来消息时的 HTTP 报文内容。SNS 的 HTTP 端点订阅需要一个公网上的 HTTP URL, 对 SNS 可见，所以我在本地测试时在家中路由器上加一个端口映射，对 Modem 获得的公有 IP 的 8080 端口访问转发到写此文用所用机器的 8080 端口上。&lt;br/&gt;&lt;br/&gt;
在本机需要在 8080 端口上启动一个 HTTP 服务以迎接 AWS 消息的到来，比如用 python 3 的话，简单运行命令 &lt;code&gt;python -m http.server 8080&lt;/code&gt;。如果不想在 API 代码中分析 HTTP 报文数据，只需打开 &lt;a href="https://www.wireshark.org/"&gt;Wireshark&lt;/a&gt;(过滤条件 tcp.port=8080 &amp;amp;&amp;amp; http) 抓取 8080 上的 HTTP 数据通信即可。在 API 代码中如何处理 HTTP 请求数据不是本文的重点。</description></item><item><title>Python Flask 框架的并发能力及线,进程模型</title><link>https://yanbin.blog/python-flask-concurrency-thread-process/</link><pubDate>Sat, 18 Feb 2023 15:07:07 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-flask-concurrency-thread-process/</guid><description>
本文旨在测试 Python Flask 框架的默认并发能力，即同时能处理多少个请求，以及请求等待队列大致有多大; 并找到如何改变默认并发数。虽然网上或许很容易找到它们的默认并发数，但通过实验的方式可以得到更感性的认识。&lt;/p&gt;
&lt;br/&gt;
本文写作时使用的环境为&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;测试机器为 MacBook Pro, CPU 6 核超线程，内存 16 Gb&lt;/li&gt;
&lt;li&gt;JMeter 5.5 -- 连续发送请或压力测试&lt;/li&gt;
&lt;li&gt;Python 3.10.9&lt;/li&gt;
&lt;li&gt;Flask 2.2.2&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
从 JMeter 每半秒发送一个请求，连续发送 1000 个，程序中 API 方法接受到请求后 sleep 800 秒，保证在全部 1000 个请求送出之前一直占着连接，以此来找到同时被处理的请求数目，并且有足够的时间统计当前的 TCP 连接数。在测试极端规模的并发数时，由于在 Mac OS X 很难突破 5000 个线程的限制，这时就让 JMeter 分布到远程 Linux(Docker 或虚拟机) 上执行。&lt;br/&gt;&lt;br/&gt;
请求的 URL 是 http://localhost:8080/?id=${count}, 带一个自增序列用以识别不同的请求， JMeter 的 Thread Group 配置为 Number of Threads (users): 1000, Ramp-up period (seconds): 500</description></item><item><title>远程方式执行 JMeter 测试</title><link>https://yanbin.blog/execute-jmeter-test-remotely/</link><pubDate>Sat, 18 Feb 2023 02:58:56 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/execute-jmeter-test-remotely/</guid><description>
JMeter 是一个极好的测试 Web API 及压力测试的工具，另一个的话就是 Python 版的 &lt;a href="https://locust.io/"&gt;LOCUST&lt;/a&gt;(它也能远程运行测试)。JMeter 的测试可以在本地模拟并发用户，那么为什么要远程执行 JMeter 测试呢？因为一台机器能模拟的并发用户数受限，一个用户就是对应着一个 Java 线程。比如我在 MacBook Pro(内存 16Gb) 上无论如何调整 &lt;code&gt;ulimit -n&lt;/code&gt;, &lt;code&gt;ulimit -u&lt;/code&gt;, 或用 JAVA_TOOL_OPTIONS, HEAP, JVM_ARGS 设置 &lt;code&gt;-Xmx&lt;/code&gt;, 调大到 10 G, 或用 -Xss 调小栈大小，都无法让 JMeter 模拟的用户数达到 5000。&lt;br/&gt;&lt;br/&gt;
文后有本人亲自测试 Java/Python 在 Mac OS X 和 Linux 下可创建多少个线程&lt;br/&gt;&lt;br/&gt;
如果能够远程运行 JMeter 的测试就能突破单机上的线程限制了，比如 Mac OS X 不行，找个 Linux 远程机器(可以是虚拟机)来执行，一台机器不够，找多个。想要模拟 15000 个并发用户，测试可分配到 5 台机器上执行，每个节点跑 3000 个用户并发就行，有点操控肉机的感觉。</description></item><item><title>使用 ECS Exec 直通 ECS 容器会话(适用于 Fargate 和 EC2)</title><link>https://yanbin.blog/ecs-exec-connect-ecs-container-on-fargate-and-ec2/</link><pubDate>Tue, 24 Jan 2023 14:42:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ecs-exec-connect-ecs-container-on-fargate-and-ec2/</guid><description>
基于 EC2 的 ECS 服务，要看看容器内的状态，一直以来都是先 SSM(Simple System Manager) 或 SSH 进到 EC2 实例，然后再 &lt;code&gt;docker exec -it &amp;lt;container-id&amp;gt; sh&lt;/code&gt;, 查看容器的控制台日志则用 &lt;code&gt;docker logs &amp;lt;container-id&amp;gt; [--follow]&lt;/code&gt;. 但是对使用 Farget 的 ECS 服务就无能为力了，因为找不到 SSM 或 SSH 的主体, 只能通过程序日志来大概了解容器内发生的事了。&lt;br/&gt;&lt;br/&gt;
Amazon 在 2021-03-15 推出了一个新的特性 &lt;code&gt;ECS Exec&lt;/code&gt; 允许我们直接连接 Fargate 或 EC2 中的容器会话，见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/03/amazon-ecs-now-allows-you-to-run-commands-in-a-container-running-on-amazon-ec2-or-aws-fargate/"&gt;Amazon ECS now allows you to run commands in a container running on Amazon EC2 or AWS Fargate&lt;/a&gt;. ECS Exec 支持 Container Agent 版本为 1.50.2 及以上的 ECS Optimized AMI 系列，和 Fargate Platform Version 1.4.0(Linux) 或 1.0.0(Windows) 及以上。&lt;br/&gt;&lt;br/&gt;
ECS Exec 的实现原理是以往在 EC2 实例上启动的 SSM Agent，也在容器内部启动一份，然后命令 &lt;code&gt;aws ecs execute-command&lt;/code&gt; 直指容器本身。参考本人写过的一篇 &lt;a href="https://yanbin.blog/aws-session-manager-ec2-instance/"&gt;AWS Session Manager 管理 EC2 实例&lt;/a&gt;，连接过程中唯一的不同就是容器中也运行了一个 SSM Agent, 所以这个容器也就无所谓是在 EC2 实例还是在 Fargate 中。&lt;br/&gt;&lt;br/&gt;</description></item><item><title>Python 基于多环境的配置方式</title><link>https://yanbin.blog/python-multi-envs-configurations/</link><pubDate>Fri, 13 Jan 2023 09:28:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-multi-envs-configurations/</guid><description>
部署到不同环境的应用会使用到各自的配置，如 Dev, QA, Stg, Prod 有自己的数据库等资源。Spring Boot 可采用 Profile 对应不同的环境，不同 Profile 选择自己的配置文件 &lt;code&gt;application-${profile}.properties&lt;/code&gt;。本人还是偏爱在同一个文件中分组配置，容易查错与编辑，类如在 application.properties 文件中以下面的方式&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java-properties" data-lang="java-properties"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;db.host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;aaa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;%dev.db.host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;bbb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;%prod.db.host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ccc&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
那么在 Python 的项目中应该如何针对不同环境进行配置呢？大概有以下几种&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;不同环境的 Config 类&lt;/li&gt;
&lt;li&gt;YAML 文件&lt;/li&gt;
&lt;li&gt;TOML 文件&lt;/li&gt;
&lt;li&gt;JSON 文件&lt;/li&gt;
&lt;li&gt;INI 文件&lt;/li&gt;
&lt;li&gt;dotenv(.env) 文件&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
第一种方式是本人推荐的，其他的方式只是在不同格式的配置文件中，按环境组织不同的配置值，其他方式的不同配置读入内存中基本是体现为字典变量。在 Python 配置中要支持像配置的 placeholder(像 ${host} 还需自己实现。</description></item><item><title>升级到 Spring Boot 3 后 javax.inject.Named 不可用</title><link>https://yanbin.blog/upgrade-to-spring-boot-3-javax-inject-named-unvailable/</link><pubDate>Thu, 12 Jan 2023 12:03:48 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/upgrade-to-spring-boot-3-javax-inject-named-unvailable/</guid><description>
为了紧跟 Spring 6 的步伐，Spring Boot 在 2022 年 11 月 24 日释放了 3.0.0. 当前版本是 3.0.1(2022-12-23)。Spring 6 要求用 JDK 17+, Spring Boot 3 自然也要上 JDK 17+ 才能使用，对于一直死死抱住 JDK 8 不放的要升级到 Spring Boot 3 就是个比较大的挑战。&lt;/p&gt;
&lt;br/&gt;
Spring Boot 到底带来了什么显著的特性呢？&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;依赖于 Spring 6, 最低 Java 17, 兼容 Java 19&lt;/li&gt;
&lt;li&gt;支持生成 GraalVM 本地映像，取代实验性的 Spring Native 项目&lt;/li&gt;
&lt;li&gt;最低 Java EE 9 和支持 Jakarta EE 10&lt;/li&gt;
&lt;li&gt;依赖从 Java EE 迁移到 Jakarta EE API&lt;/li&gt;
&lt;li&gt;升级到 Tomcat 10&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
从 Spring Boot 2.x 升级到 Spring Boot 3 的指南请阅官方的文档 &lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide"&gt;Spring Boot 3.0 Migration Guide&lt;/a&gt;。Spring Boot 1 的项目还得老老实实的先升级到 Spring Boot 2，如果是早期的 Spring Boot 2，第一步是升级到 Spring Boot 2.7.x， 一步步来，免得步子大了扯到X。再到是把 JDK 换成  17 或更新的版本，编译，运行，有问题就改代码。</description></item><item><title>Spring Boot Web 输出 Tomcat 的访问日志到控制台</title><link>https://yanbin.blog/spring-boot-web-tomcat-access-logs-to-console/</link><pubDate>Wed, 26 Oct 2022 12:54:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-boot-web-tomcat-access-logs-to-console/</guid><description>
当我们直接使用 Tomcat  时，访问日志的配置在 $TOMCAT_HOME/conf/server.xml 中&lt;br/&gt;
&lt;blockquote&gt;
&amp;lt;Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"&lt;br /&gt;
              prefix="localhost_access_log" suffix=".txt"&lt;br /&gt;
              pattern="%h %l %u %t &amp;amp;quot;%r&amp;amp;quot; %s %b" /&amp;gt;
&lt;/blockquote&gt;
产生的日志文件在 $TOMCAT_HOME/logs 目录中，生成以日期戳进行区分的滚动日志文件，如 localhost_access_log.2022-10-25.txt 等&lt;br/&gt;&lt;br/&gt;
而在我们使用  Spring Boot Web 时, 默认的嵌入式 Web 应用服务器是 Tomcat，我们可以在 Spring 属性文件中配置是否启用 Tomcat 访问日志(默认不启用)。属性文件中的配置针对的是如何输出访问日志到文件，而我们有时候需要输出访问日志到控制台而不非文件，比如 AWS 上的 ECS 应用，控制台的输出可直接发送到 CloudWatch，这样便于分析日志。&lt;br/&gt;&lt;br/&gt;
在 Spring Web 中可以用 Interceptor 或 Filter 来记录访问日志，但在请求出错时无法准确显示出响应时间和状态码，毕竟 Tomcat 的访问日志在代码的外层，进出 Controller 方法的输入输出信息也就掌握的更清楚。&lt;br/&gt;&lt;br/&gt;
本文的任务是探索输出 Spring Boot Web 嵌入式 Tomcat 的访问日志到控制台。有两种方式</description></item><item><title>HDFS 分布式文件系统的搭建与使用</title><link>https://yanbin.blog/hdfs-setup-usage/</link><pubDate>Mon, 10 Oct 2022 00:32:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hdfs-setup-usage/</guid><description>
HDFS(Hadoop Distributed File System) 是 Hadoop 的一个重要的模块，有点像磁盘阵列一样，不过它构建的是分布式网络文件系统。由于数据块从多个节上存取，也就能突破单点的网络带宽和硬件资源的限制而获得更好的性能; 能处理更大的数据，和克服单点故障的问题。许多公司正在使用 HDFS 构建自己的分布式文件系统，还比支持它的应用有 &lt;a href="https://aws.amazon.com/big-data/what-is-spark/"&gt;Spark&lt;/a&gt;, &lt;a href="https://aws.amazon.com/big-data/what-is-presto/"&gt;Presto&lt;/a&gt;, &lt;a href="https://aws.amazon.com/big-data/what-is-hive/"&gt;Hive&lt;/a&gt;, &lt;a href="https://aws.amazon.com/big-data/what-is-hbase/"&gt;HBase&lt;/a&gt;, &lt;a href="https://zeppelin.apache.org/"&gt;Zeppelin&lt;/a&gt; 等。&lt;br/&gt;&lt;br/&gt;
本文将实战自己搭建一个 HDFS 分布式文件系统，体验最基本的 HDFS 文件操作，看看它是如何分布文件块，以及如何进行冗余容错的。&lt;br/&gt;&lt;br/&gt;
本次实战环境:&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;macOS Big Sur 11.7, VirtualBox 6.1.32 r149290, Vagrant 2.2.19&lt;/li&gt;
&lt;li&gt;Vagrant Ubuntu 22.04 LTS 虚拟机&lt;/li&gt;
&lt;li&gt;Open JDK 8&lt;/li&gt;
&lt;li&gt;Hadoop 3.3.4&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
我们将使用 4 个 Vagrant 虚拟机，其中一个为 NameNode, 其余为 DataNode。HDFS 沿袭了传统的 Master/Slave 系统架构，但因目前像传统的计算机名词 PC, CRT 被恶意使用的当下，Master/Slave 相应的更名为 NameNode 和 DataNode。在通常的系统中, Master 兼具协调与数据存储的功能，而 Slave 只存储数据，而 HDFS 的 NameNode 仅保管文件的元信息，数据块存储在 DataNode 中。&lt;br/&gt;</description></item><item><title>细数 DB2 数据库的 Identity 字段</title><link>https://yanbin.blog/talk-db2-database-identity-column/</link><pubDate>Mon, 03 Oct 2022 12:06:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/talk-db2-database-identity-column/</guid><description>
每种数据都有自己独特的自增列的声明方式，如 Oracle 的 Sequence, SQL Server 的 Identity, MySQL 的 auto_increment, PostgreSQL 的 Sequence 或 Serial。和 PostgreSQL 类似，DB2 也提供两种自增列的声明方式，它们是 Sequence 和 Identity。而本文主要着墨于 DB2 的 Identity 字段，并讲述它与 Sequence 的某种联系，以及它对数据表的导入的影响。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;DB2 的 Sequence&lt;/h3&gt;
在 DB2 中声明一个 Sequence 与表的 Identity 字段的参数差不多，我们可以看作 Identity 是一个内联的 Sequence。先来看如何创建一个序列</description></item><item><title>向量间距离/相似度及用 Python 进行计算</title><link>https://yanbin.blog/python-calculate-vector-distance/</link><pubDate>Tue, 20 Sep 2022 15:25:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-calculate-vector-distance/</guid><description>
[latexpage]计算距离的目的也是为了确定两个向量的相似度，这里的向量可以是纯数学的数组，或者是一系列带有某些可量化特征值的物件。写作本文的原由是需要用 Numpy 计算两个实际对象的相似度，实现代码非常简单，因此更不能满足于此，借此机会多多了解下向量之间距离和相似度的概念，还回顾下一些相关的数学知识。&lt;br/&gt;&lt;br/&gt;
计算两个向量的相似度有许多的方法，如&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;欧氏距离(Euclidean Distance): 点间直线距离，数值越小越相似&lt;/li&gt;
&lt;li&gt;夹角余弦(Cosine): 余弦相似度(Cosine Similarity)，计算两个向量之间的夹角，值在  -1 ～ 1 之间&lt;/li&gt;
&lt;li&gt;曼哈顿距离(Manhattan Distance): 点间在坐标系上的绝对轴距总和&lt;/li&gt;
&lt;li&gt;切比雪夫距离(Chebyshev Distance): 像国际象棋中的王从一格子到另一个格子间的距离&lt;/li&gt;
&lt;li&gt;标准化欧氏距离(Standardized Euclidean distance): 先对各个分量进行标准化，再求欧氏距离&lt;/li&gt;
&lt;li&gt;其他距离和相关系数，如马氏距离(Mahalanobis Distance), 兰氏距离(Lance Williams Distance); 皮尔逊相关系数(Pearson Correlation Coefficient), 杰卡德相似系数(Jaccard similarity coefficient)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
本文主要关注到欧氏距离和余弦相似度这两个数值的求解上。</description></item><item><title>为 S3 中的 CSV 文件创建带 Partition 的 Athena 表</title><link>https://yanbin.blog/create-athena-table-with-partitions-for-s3-csv/</link><pubDate>Mon, 19 Sep 2022 14:13:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/create-athena-table-with-partitions-for-s3-csv/</guid><description>
CSV 文件是纯文本的，对人阅读和编辑来说是最友好的描述表格数据的格式。虽然当前处理大数据时会用到 JSON, avro, parquet 等数据格式，但是在处理平面数据时 CSV 仍然被广泛使用。&lt;br/&gt;&lt;br/&gt;
S3 Select 能支持 CSV, JSON 和 parquet 格式数据的直接查询。在 AWS s3 控制台选择一个 CSV 文件，从右上的 &lt;code&gt;Object actions&lt;/code&gt; 下拉选项上选择 &lt;code&gt;Query with S3 Select&lt;/code&gt; 就能直接查询该文件的内容，而无须下载后打开文件。&lt;br/&gt;&lt;br/&gt;
如 S3 Select 查询语句&lt;br/&gt;
&lt;blockquote&gt;
SELECT * from s3object WHERE Name='Tom' LIMIT 5
&lt;/blockquote&gt;
如果 CSV 带 Header 的话，请勾选上 &lt;code&gt;Exclude the first line of CSV data&lt;/code&gt;。当然 S3 Select 查看任意的文本文件也行，只是把它当成一个不规则的 CSV 文件来对待。&lt;br/&gt;&lt;br/&gt;
S3 Select 只能针对单个 S3 文件查询，如果要对一组 CSV 文件同时进行查询的话就要用到 Athena。把相同 Schema 的一系列 CSV 文件放到 S3 的某一个目录中，我们可为它们创建一个  Athena 表，然后查询该 Athena 表就会从对应 S3 目录中扫描所有的 CSV 文件。</description></item><item><title>《HTTP/2 in Action》阅读笔记(二)</title><link>https://yanbin.blog/http-2-in-action-reading-notes-2/</link><pubDate>Tue, 30 Aug 2022 23:32:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/http-2-in-action-reading-notes-2/</guid><description>
要开始步入 HTTP/2 的，&lt;a href="https://httparchive.org/reports/state-of-the-web#h2"&gt;https://httparchive.org/reports/state-of-the-web#h2&lt;/a&gt; 显示了使用 HTTP/2 的趋势，和其他数据，如 HTTP/3 的应用趋势。随着互联网的发展，展示一个网页请求的资源越来越多，传输的总字节数在变大，平均近几M - &lt;a href="https://www.speedcurve.com/blog/web-performance-page-bloat/"&gt;https://www.speedcurve.com/blog/web-performance-page-bloat/&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://www.webpagetest.org"&gt;https://www.webpagetest.org&lt;/a&gt; 可以用测试网页中所有资源加载的瀑布图，包括 DNS 的查询，网络连接，SSL 协商及 CPU, 带宽使用情况。&lt;br/&gt;&lt;br/&gt;
HTTP/1.1 效率较低下，只能同步发送处理请求，即请求，得到响应后才能发下一个请求，一个网会加载许多的资源，如图片, js, css 等。&lt;br/&gt;&lt;br/&gt;
HTTP/1.1 的 Pipeling 像 Redis 那样的 pipeline, 通过一个连接传递多个 HTTP 请求，再依次返回结果，但这种方案并未得到支持与应用。</description></item><item><title>SpringBoot 启用 GZIP 对响应进行压缩</title><link>https://yanbin.blog/springboot-enable-gzip-respone-compression/</link><pubDate>Tue, 30 Aug 2022 09:52:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot-enable-gzip-respone-compression/</guid><description>
SpringBoot Web 应用默认是不启用响应数据的压缩，对大的文本类型的响应数据进行压缩是十分必要的，如 JSON, XML 等应用数据，甚至是 JS, CSS 等。&lt;br/&gt;&lt;br/&gt;
早先的 Web 应用基本是要配置一个叫做 &lt;code&gt;GzipFilter&lt;/code&gt; 之类的东西，然后判断请求的 &lt;code&gt;Accept-Encoding&lt;/code&gt; 是否含有 &lt;code&gt;gzip&lt;/code&gt;, 再对需要的 &lt;code&gt;Content-Type&lt;/code&gt; 响应类型的数据进行压缩。&lt;br/&gt;&lt;br/&gt;
在使用了 SpringBoot 之后，在碰到有压缩响应的需求的时候，第一件事情应该要想到是否能通过在 &lt;code&gt;application.properties&lt;/code&gt;(或 application.yml) 配置就行。于是查阅 SpringBoot 2.7.x 的帮助文档 &lt;a href="https://docs.spring.io/spring-boot/docs/2.7.x/reference/htmlsingle/"&gt;Spring Boot Reference Document&lt;/a&gt;, 搜索关键字 &lt;code&gt;compression&lt;/code&gt;，翻几页就能找到 17.3.6. &lt;a href="https://docs.spring.io/spring-boot/docs/2.7.x/reference/htmlsingle/#howto.webserver.enable-response-compression"&gt;Enable HTTP Response Compression&lt;/a&gt;, 介绍了三个配置项</description></item><item><title>Python 实现 RSA 非对称加解密</title><link>https://yanbin.blog/python-implement-rsa-encryption/</link><pubDate>Thu, 18 Aug 2022 10:57:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-implement-rsa-encryption/</guid><description>
在阅读《HTTP/2 in Action》的 HTTPS 一节后，不觉一脚踏入到非对称加密这一领地而不能自拔。与非对称加密相对应的是对称加密，有点像是由一把钥匙反锁的门，只能用同一把钥匙打开; 而非对称加密是用一把钥匙反锁门，但只能用另一把特定的钥匙才能打开它，锁门的叫做公钥，开门的叫做私钥。&lt;br/&gt;&lt;br/&gt;
在此之前我理解的非对称加密以为是像 MD5 那种摘要(Digest), 由明文生成的 MD5 摘要信息是无法还原出原始数据的，谬以为那就是所谓的非对称。&lt;br/&gt;&lt;br/&gt;
1976 年，两位美国计算机学家 Whitefield Diffie 和 Martin Hellman 提出了非对称加解密的的构思。1977 年三位数学家 Ron &lt;strong&gt;R&lt;/strong&gt;ivest, Adi &lt;strong&gt;S&lt;/strong&gt;hamir 和 Leonard &lt;strong&gt;A&lt;/strong&gt;dleman 实现了非对称加密算法，即 RSA，取自这三个的姓的首字母&lt;br/&gt;&lt;br/&gt;
具体的 RSA 算法原理可参考阮一峰的两篇网络日志：&lt;a href="https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html"&gt;RSA算法原理 (一)&lt;/a&gt; 和 &lt;a href="https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html"&gt;RSA算法原理 (二)&lt;/a&gt;, 大致就是通过互质的两个数，计算欧拉函数, 模反元素，最终算法公钥和私钥，公钥加密的数据只能用用私钥解密，以当前的算力，只要 RSA 的密钥足够长，如 1024 位以上，私钥是无法通过公钥推断出来的。</description></item><item><title>《HTTP/2 in Action》阅读笔记(一)</title><link>https://yanbin.blog/http-2-in-action-reading-notes-1/</link><pubDate>Tue, 16 Aug 2022 23:14:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/http-2-in-action-reading-notes-1/</guid><description>
本书买来有一段时日了，一直还未开始阅读，关于网络上 HTTP/2 的真实使用状态查到以下信息&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;截止 2021 年 1 月 8 日，有 50% 的网站正在使用 HTTP/2，参见 &lt;a href="https://w3techs.com/technologies/details/ce-http2"&gt;Usage statistics of HTTP/2 for websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;另一篇介绍 &lt;a href="https://almanac.httparchive.org/en/2020/http#http2-adoption"&gt;HTTP/2 Adoption&lt;/a&gt; 说 2020 年就有 64% Web 请求是基于 HTTP/2 的&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
得看看我们自己的网站对 HTTP/2 的支持以及使用状况&lt;br/&gt;&lt;br/&gt;
于此同时，HTTP/3(gQUIC) 也上了议程，作为 &lt;a href="https://datatracker.ietf.org/doc/html/rfc9114"&gt;HTTP/3&lt;/a&gt; 的提议标准的 RFC9114 也于 2022 年 6 月 6 日由 IETF 发布。&lt;br/&gt;&lt;br/&gt;
本书首先介绍的是关于 HTTP 协议从 0.9 到 1.0, 再到 1.1 的变迁史。&lt;br/&gt;&lt;br/&gt;
关于 IPv4 和 IPv6&lt;br/&gt;&lt;br/&gt;
IP 报文的前四位表示协议版本，所以 0100 就是 4。版本 0 ～ 3 是留给实验用的，5 设计为 Intenet 流协议的，例如实时音视频，像 VoIP, 实际上也因为地址数量的限制没被使用。所以就跳到了 IPv6，字节为  0110。后续的 IP 协议版本 7, 8, 9 都被预定了(比如中国的 IPv9 - 冷)，所以再有新的 IP 协议版本的放丈是 IPv10</description></item><item><title>Spring Boot 如何选择 Cache 实现的</title><link>https://yanbin.blog/how-springboot-select-cache-implementation/</link><pubDate>Mon, 15 Aug 2022 15:46:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-springboot-select-cache-implementation/</guid><description>
写作此篇是作为对 &lt;a href="https://yanbin.blog/spring-cache-different-cache-types/"&gt;Spring 使用 Cache 解析及使用不同类型的 Cache&lt;/a&gt; 一文的补充，该文中提到了自定 CacheManager 及配置 &lt;code&gt;spring.cache.type&lt;/code&gt; 来选择自己的 Cache 实例，但对 Spring 是如何确定具体 Cache 实现未作展开。本文将介绍选择 Cache 实现的几种方式&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;默认选择 Cache&lt;/li&gt;
&lt;li&gt;声明 Spring Bean &lt;code&gt;Cache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;声明 Spring Bean &lt;code&gt;CacheManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;spring.cache.type&lt;/code&gt; 属性选择&lt;/li&gt;
&lt;li&gt;引入相应的 Cache 实现依赖&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Diagram as Code -- 用 Python 画框架图</title><link>https://yanbin.blog/diagram-as-code-python/</link><pubDate>Wed, 10 Aug 2022 23:41:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/diagram-as-code-python/</guid><description>
最近注意到一个很有意思的项目 &lt;a href="https://diagrams.mingrammer.com/"&gt;Diagrams&lt;/a&gt;, 用 Python 代码来绘制架构或流程图,
以前基本用 &lt;a href="https://www.gliffy.com/"&gt;Gliffy&lt;/a&gt; 来画。继一系列 X as X, 如 PaaS, SaaS, IaaS, CaC(Configuration as Code),
IaC(Infrastructure as Code) 等，Diagrams 喊出了 Diagram as Code 的口号。其实，在这之前, Markdown 就做了许多 Diagram as Code 的事情，
也许更准确说是 Diagram as Document。&lt;/p&gt;
&lt;br/&gt;
熟练的程序员大概不喜欢用可视化设计器来生成 GUI 代码，那会让代码变得极不简洁，而是直接写，眼中看到的是代码，头脑中即时产生映像。&lt;br/&gt;&lt;br/&gt;
Diagrams 就是这样一款写 Python 代码产生架构或流程图的库，它绘制的架构图支持主要的云服务提供商，如&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;知名的(本人认为的): AWS, Azure, GCP, IBM, DigitalOcean, AlibabaCloud, OCI(Oracle Cloud Infrastructure, 不是 Open Container Initiative), OpenStack&lt;/li&gt;
&lt;li&gt;刚了解到的: Google 的 Firebase, Elastic(ElasticSearch 出品方也有自己的平台), Outscale&lt;/li&gt;
&lt;li&gt;以及应用平台 K8S, Saas 和 OnPrem 中的元素&lt;/li&gt;
&lt;li&gt;通用元素，编程语言及流程图，还能定制自己的节点图&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>学习 Airflow 第一篇章</title><link>https://yanbin.blog/learning-airflow-get-started/</link><pubDate>Tue, 09 Aug 2022 14:50:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/learning-airflow-get-started/</guid><description>
Airflow 起初是由 Airbnb 开发的, 用于调度和监控工作流的平台，后来开源了, 并于 2019 年 1 月成为了 Apache 的顶级项目。它是用 Python 编写的，管理的工作流是有向无环图(DAG - Directed Acyclic Graph), 这能满足绝大多数情况下的需求。&lt;br/&gt;&lt;br/&gt;
顺带插一句，Airflow 用了与 Google Photos 极其相似的 Logo，不知算不算侵权。&lt;br/&gt;&lt;br/&gt;
说到工作调度，头脑中很快会掠过 Cron, 计划任务, Quartz, Spring Schedule, 和 Control-M。除了商业的 Control-M 有调度和监控工作流的功能外，其他的基本只用来调度任务，监控全靠自己的日志。&lt;br/&gt;&lt;br/&gt;
还有一个类似的工具是由&lt;a href="https://www.analysys.cn/"&gt;易观&lt;/a&gt;贡献给 Apache 的 &lt;a href="https://dolphinscheduler.apache.org/"&gt;DolphinScheduler&lt;/a&gt;, 它处理的也是 DAG 工作流，用 Java 实现的，所以体量大，硬件要求会高些。它的工作流的创建是通过 Web UI 可视化界面完成的，对程序员来说不那么友好。奇怪的是, 作为 Apache 旗下的项目，项目首页面是中文的，启动后控制台默认界面也是中文的。&lt;br/&gt;&lt;br/&gt;
而 Airflow 功能就厉害了， 它可动态管理工作流，易于扩展，可集群化进行伸缩，更有一个漂亮的 UI 用于实时监控任务。基于以上特性 Airflow 是很适于执行数据的 ETL(Extract, Transform, Load) 操作的。&lt;br/&gt;&lt;br/&gt;
这么好的开源产品, 免不了又被 AWS 盯上了, 以 &lt;a href="https://aws.amazon.com/managed-workflows-for-apache-airflow/"&gt;Amazon Managed Workflows for Apache Airflow(MWAA)&lt;/a&gt; 服务进行出售，费用还真不菲。AWS 创造性的以 vCPU 数量，DAG 数量限制进行分层次进行收费，远比自己启动一两个 EC2 实例部署 Airflow 贵的多。但 MWAA 有个方便的特性就是 DAG 文件可以放到 S3 中自动部署，相信自己部署的 Airflow 也能进行扩展而从 S3 加载 DAG。</description></item><item><title>Spring 使用 Cache 解析及使用不同类型的 Cache</title><link>https://yanbin.blog/spring-cache-different-cache-types/</link><pubDate>Thu, 14 Jul 2022 17:12:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-cache-different-cache-types/</guid><description>
要在一个 Spring 应用中开启缓存方法返回结果的功能很简单，不需要额外的依赖，相关的的注解  @Cacheable, @CacheConfig, @CachePut, @CacheEvict, @EnableCache 等来自 spring-context 包。默认的的 Cache 实现是把数据存入到 ConcurrentMap 中，所以数据一直在内存中，除非显式的调用被 @CacheEvict 的方法来清理。实际进行数据缓存时会有更复杂的策略，如元素个数，占用内存，过期时间，何时使用磁盘等，而且不同的数据类型应有不同的缓存策略。&lt;br/&gt;&lt;br/&gt;
因此，除了使用默认的 ConcurrentMap 作为缓存外，还可通过配置属性 &lt;code&gt;spring.cache.type&lt;/code&gt; 来使用其他类型的缓存，如 Caffeine, Couchbase, EhCache, INfinispan, JCache, Redis 等，或自定义 CacheManager 来使用 Guava Cache。</description></item><item><title>自定义 Spring Web Controller 方法的参数</title><link>https://yanbin.blog/customize-spring-web-controller-method-parameter/</link><pubDate>Thu, 07 Jul 2022 15:14:09 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/customize-spring-web-controller-method-parameter/</guid><description>
在 Spring Web Controller 方法中的参数可用 org.springframework.web.bind.annotation 下的各种注解来说明参数值从哪儿获得，比如我们熟知的 @PathVariable, @RequestParam, @RequestHeader, @RequestBody, 还有较少使用的 @ReqeustAttribute, @SessionAttribute, @RequestPart, @MatrixVariable, @ModelAttribute, @AuthenticationPrincipal, @CurrentSecurityContext 等。其实在它们背后工作的是相应的 HandlerMethodArgumentResolver 的子孙们，当然还有 HttpMessageConverter 的各个实现类还默默的对输入数据进入类型转换。&lt;/p&gt;
&lt;br/&gt;
为进一步深入了解 Spring Web 如何获得用户输入，我们先尝试一下不常用的注解，然后实现一个自己的注解参数 @ProductId, 它来从 queryString 或 requestHeader 中获得 productId。写作本文的起因是在上一篇 &lt;a href="https://yanbin.blog/springboot-security-jwt-token-how-to-abcs/"&gt;理解 Spring Boot Security + JWT Token 的简单应用&lt;/a&gt; 里, JwtTokenFilter 住 SecurityContextFilter 放一个 Authentication 实例, 在 Controller 方法中便能用 @AuthenticationPrincipal 自动注入 authentication.getPrincipal() 的值。</description></item><item><title>理解 Spring Boot Security + JWT Token 的简单应用</title><link>https://yanbin.blog/springboot-security-jwt-token-how-to-abcs/</link><pubDate>Sat, 02 Jul 2022 09:08:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot-security-jwt-token-how-to-abcs/</guid><description>
项目中有用到 Spring Security 来控制 API 的访问权限，但对于配置应用它基本上是照葫芦画瓢。至于为什么要调用方法&lt;/p&gt;
&lt;blockquote&gt;
SecurityContextHolder.getContext().setAuthentication()
&lt;/blockquote&gt;
并且能从 HttpServletRequest 中得到 Authentication。还有，只要在 Controller 的方法中添加一个带 @AuthenticationPrincipal 注解的参数&lt;br/&gt;
&lt;blockquote&gt;
public String sampleApi(@AuthenticationPrincipal DecodedJWT decodedJWT) {...}
&lt;/blockquote&gt;
之后，decodedJWT 便自动有了值，诸如此类的，此前一概模糊不清。&lt;br/&gt;&lt;br/&gt;
早先配置 spring-security-config 是通过继承 WebSecurityConfigurerAdapter, 覆盖它的 configure(HttpSecurity http) 来配置访问规则等。在 spring-security-config 5.7.x 开始不建议用 WebSecurityConfigurerAdapter, 而是借由 SecurityFilterChain 来配置 HttpSecurity 中的规则 ，或者通过 WebSecurityCustomizer 完成定制。</description></item><item><title>Java 调用本地动态库的组件(javah, JNA, JNR-FFI)</title><link>https://yanbin.blog/java-jni-libraries/</link><pubDate>Mon, 20 Jun 2022 16:20:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-jni-libraries/</guid><description>
还是很 久很久以前，当初有 Java 调用本地动态库需求的时候，尝试过用 javah/native 原生的方式在 Java 中使用动态库，再就是小试了 &lt;a href="https://sourceforge.net/projects/jnative/"&gt;JNative&lt;/a&gt;，它调用动态库只需 Java 端的动作, 它最后的更新日期是 9 年前 2013-04-26，基本是应该选择放弃了。&lt;br/&gt;&lt;br/&gt;
关于 JNative 的使用写过两篇&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/java-jnative-dll-concise"&gt;Java调用动态库最简便方法和最好用的组件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/jnative-java-pass-struct"&gt;使用JNative,在Java中传递一个C/C++结构参数到动态库中&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
如今想继续发掘下是否有别的更好的调用本地库的 JNI 组件，找到有&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.teamdev.com/jniwrapper"&gt;JNIWrapper&lt;/a&gt;：居然是一个收费的，而且价格不菲，不作绍&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nativelibs4java/BridJ"&gt;BridJ&lt;/a&gt;：也是 7 年前才有过代码的更新&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/java-native-access/jna"&gt;JNA&lt;/a&gt;(Java Native Access): 也就它稍为活跃一点点&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jnr/jnr-ffi"&gt;JNR-FFI&lt;/a&gt;：最近几个月也有更新，不知道使用体验如何&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Python 3.10 关键新特性</title><link>https://yanbin.blog/python-3-10-new-features/</link><pubDate>Wed, 15 Jun 2022 14:14:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-10-new-features/</guid><description>
Python 3.10 于 2021-10-04 发布，至今已大半年，目前 AWS 的 Lambda 尚未直接支持，但用 Docker 镜像的方式使用 AWS Lambda 是可以使用 Python 3.10。Python 一年一发布的节奏比 Java LTS 还紧密。下一个版本 Python 3.11 预计在 2022-10-03 发布。在学习 Python 3.10 之前先回顾一下 Python 3.7, 3.8, 3.9 的特性(不想关心之前版本的变迁可直接跳跃到下方的 Python 3.10 新特性去)&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://yanbin.blog/python-3-7-what-is-new/"&gt;Python 3.7 所带来的新特性&lt;/a&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;breakpoint()&lt;/li&gt;
&lt;li&gt;数据类(@dataclass)&lt;/li&gt;
&lt;li&gt;类型提示强化和延迟注解求值&lt;/li&gt;
&lt;li&gt;时间精度的提高&lt;/li&gt;
&lt;li&gt;保证字典的顺序&lt;/li&gt;
&lt;li&gt;async 和 await 成为关键字&lt;/li&gt;
&lt;li&gt;asyncio.run() 简化事件循环&lt;/li&gt;
&lt;li&gt;上下文变量(ContextVar) - 可实现 ThreadLocal 和 SLF4J 的 MDC 功能&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Python 中泛型的实现</title><link>https://yanbin.blog/python-generic-implementation/</link><pubDate>Wed, 15 Jun 2022 12:44:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-generic-implementation/</guid><description>
在学习 Python 3.10 新特性时，其中有个类型别名(TypeAlias), 所举的例子是&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;StrCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Cache[str]&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# a type alias&amp;lt;br /&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LOG_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;LOG[DEBUG]&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# a module constant&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
可写成&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;StrCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TypeAlias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Cache[str]&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# a type alias&amp;lt;br /&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LOG_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;LOG[DEBUG]&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# a module constant&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这让 StrCache 更像是一个类型别名，而不是一个看起来明显就是 &lt;code&gt;Cache[str]&lt;/code&gt; 的字符串变量(实际上它确实是)。&lt;br/&gt;&lt;br/&gt;
本文不在 TypeAlias 本身，而是从 &lt;code&gt;Cache[str]&lt;/code&gt; 能看出 Python 似乎也能支持像 Java 那样的泛型, 就像 Python 内置支持的 List[str] 或 list[str] 那样。&lt;br/&gt;&lt;br/&gt;
那么来看 Python 怎么去实现一个只能放入字符串的  &lt;code&gt;Cache[str]&lt;/code&gt; Cache, 而不能放入别的类型。</description></item><item><title>Python 函数重载实现</title><link>https://yanbin.blog/python-overload/</link><pubDate>Wed, 15 Jun 2022 10:18:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-overload/</guid><description>
Python 不支持函数重载，在同一个模块中声明同名方法不会报错，只会不停的覆盖，无论参数个数是否不同，最终只会保留最后一个函数&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo(a)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo(a, b)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo(a: str)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
输出</description></item><item><title>Python 3.9 新特性回顾</title><link>https://yanbin.blog/python-3-9-new-features/</link><pubDate>Thu, 05 May 2022 16:21:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-9-new-features/</guid><description>
Python 3.10 虽已于 2021/10/04 发布，但目前主要使用的 Python 版本仍然是 3.9。之前有两篇介绍了 Python 3.7 和 3.8 带来的新特性&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/python-3-7-what-is-new/"&gt;Python 3.7 所带来的新特性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/python-3-8-new-features/"&gt;体验一下 Python 3.8 带来的主要新特性&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
于此，再补充一下 Python 3.7 和 3.8 各自的发布日期是 2018/06/27 和 2019/10/14。Python 3.9 是在 2020/10/05 发布，由此看出 Python 是每年一发布。&lt;br/&gt;&lt;br/&gt;
每个版本的主要新特性就是它们的亮点，不关注新特性也就不能很好的掌握这种语言，除非是直接使用汇编或字节码指令，他们的变迁比较缓慢。&lt;br/&gt;&lt;br/&gt;
对于以 Python 3.9 为现阶段基准版本使用来说，更有必要了解一下 Python 3.9 的新特性，不然别人一见代码就仿佛是以二战时的打法应对现代战争。&lt;br/&gt;&lt;br/&gt;
Python 3.9 主要有哪些新特征呢？总结起来就是&lt;br/&gt;&lt;br/&gt;
字典的更新/合并, 字符串新增删除前/后缀的方法，datetime 支持时区了, Executor.shutdown() 可取消未执行的任务，类型提示可直接用 list[str], dict[str, int] 这样表示泛型</description></item><item><title>构建 AWS AMI 镜像(EC2 Image Builder + Terraform)</title><link>https://yanbin.blog/build-aws-ami-with-ec2-image-builder-terraform/</link><pubDate>Tue, 12 Apr 2022 00:22:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/build-aws-ami-with-ec2-image-builder-terraform/</guid><description>
使用到 AWS 的 EC2 服务时，选择一个基础镜像后，要定制的话需要在 userdata 中写上一堆脚本。如果不想每次重复 userdata，或者要更快速的初始化一个虚拟机，就应该定制自己的 AMI，特别是在 Batch, ECS, EKS 选择的基础镜像还不方便使用 userdata。&lt;br/&gt;&lt;br/&gt;
定制一个 AMI, 我们可以用 &lt;code&gt;aws create-image&lt;/code&gt; 命令，或是 HashiCorp 提供的 &lt;a href="https://www.packer.io/"&gt;Packer&lt;/a&gt;(它不仅支持 AWS, 还能为 阿里云，Azure, Google 云，vmware, docker, Vagrant 等定制镜像)。而我们这里将要介绍的仍然是 HashiCorp 公司的 Terraform 并结合 AWS 的 EC2 Image Builder 服务来构建 AMI 镜像。&lt;br/&gt;&lt;br/&gt;
EC2 Image Builder 是 2019 年 12 月 1 日推出来的服务，见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-ec2-image-builder/"&gt;Introducing EC2 Image Builder&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
构建一个镜像的基本过程是选择一个基础镜像来启动一个实例，然后在该实例中做一系列的操作，再保存操作后的状态为自己的镜像。这和用 Dockerfile 定制自己的 Docker 镜像是类似的。</description></item><item><title>macOS 如何定位 JAVA_HOME</title><link>https://yanbin.blog/macos-how-to-locatate-java_home/</link><pubDate>Fri, 18 Mar 2022 13:03:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/macos-how-to-locatate-java_home/</guid><description>
多数的 Java 入门教程都是要求同时设置 JAVA_HOME 和 PATH(包含 $JAVA_HOME/bin) 两个环境变量，反正两个都有了就保险。其实一般情况下系统能在 PATH 中找到 java 程序时就知道 JAVA_HOME, 基本上只要配置 PATH 就行，而 JAVA_HOME 环境变量是可选的。但也有例外，比如 TOMCAT 就可能要求有 JAVA_HOME 环境变量。&lt;br/&gt;&lt;br/&gt;
在 macOS 下，JAVA_HOME 与 PATH 的关系又显得有点微妙了。一个新的 macOS 系统，它自带有 java 命令&lt;br/&gt;
&lt;blockquote&gt;
$ which java&lt;br /&gt;
/usr/bin/java
&lt;/blockquote&gt;
你要直接执行它的话&lt;br/&gt;
&lt;blockquote&gt;
$ java&lt;br /&gt;
The operation couldn’t be completed. Unable to locate a Java Runtime.&lt;br /&gt;
Please visit http://www.java.com for information on installing Java.
&lt;/blockquote&gt;
所以它实际上只是执行 java 的辅助入口，没有实际的 JDK 或 JRE 是没用的。</description></item><item><title>使用原生的 Windows Docker 容器</title><link>https://yanbin.blog/windows-native-docker-container/</link><pubDate>Fri, 25 Feb 2022 01:41:35 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-native-docker-container/</guid><description>
一谈到 Docker 容器，按照以往的惯性思维，那就是 Linux 容器(LXC)，和 Windows 没多大关系，顶多也就是在 Windows 的 Linux 虚拟机中跑 Docker 容器。&lt;br/&gt;&lt;br/&gt;
不过自从 Windows Server 2016 开始，出现了 Windows 原生的 Docker 容器，它再也不只是 Linux 下的专利了。Docker 容器中可以运行 Windows 系统了, 每个 Windows 容器共享宿主机的 Windows 内核(--isolation=process,)，或使用一个高度优化虚拟机中的 Windows 内核(--isolation=hyperv)。&lt;br/&gt;&lt;br/&gt;
我们说自 Windows Server 2016 开始，包括现在的 Windows Server 2019, Windows Server 2022, 还有桌面系统的 Windows 10 和  11 上 借助于 &lt;a href="https://store.docker.com/editions/community/docker-ce-desktop-windows"&gt;Docker Desktop&lt;/a&gt; 也能跑 Windows 容器。&lt;br/&gt;&lt;br/&gt;
原本在 Windows 桌面版上安装 Docker Desktop 就能用来运行 Linux 容器，由此可知在 Windows 桌面版上(如 Windows 7, 10, 11) 可运行两种类型的容器&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Linux 容器: 每个容器运行的是 Linux 实例，用 cgcroups 命名空间隔离资源。默认的，使用 Docker Desktop 的 LinuxEngine&lt;/li&gt;
&lt;li&gt;Windows 容器：容器中运行的是 Windows 实例，进程隔离模式是容器共享主 机的 Windows 内核，Hyper-V 隔离模式是容器使用高度优化虚拟机的内核。需启用 Windows 的 Hyper-V 特性，并切换 Docker Desktop 使用 WindowsEngine&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Python 连接和操作 DB2 和 Oracle 数据库</title><link>https://yanbin.blog/python-connect-db2-oracle-databases/</link><pubDate>Tue, 22 Feb 2022 18:27:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-connect-db2-oracle-databases/</guid><description>
使用 Python 就难免要应对到不同数据库连接的问题，Python 目前也没有 Java 使用 JDBC 瘦客户端驱动那么方便。本人在用 Python 连接 SQL Server 时经常还是会有些问题，此文只着力于如何用 Python 连接 DB2 和 Oracle 数据库。&lt;br/&gt;&lt;br/&gt;
从本文中我们将会学到&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Python 围绕着 ibm_db 来操作 DB2&lt;/li&gt;
&lt;li&gt;以 Python DB-API 2.0 规范来操作 DB2&lt;/li&gt;
&lt;li&gt;cx_Oracle Python 库 + Oracle Instant Client 操作 Oracle&lt;/li&gt;
&lt;li&gt;用 Oracle 的  SID 还是 Service Name 来连接数据库&lt;/li&gt;
&lt;li&gt;Python 中借助 JayDeBeApi 使用 JDBC 驱动来操作数据库(以 Oracle 为例)&lt;/li&gt;
&lt;li&gt;从 JayDeBeApi 中我们了解到 JPype 有助于我们在 Python 中调用 Java &lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;h3&gt;Python 操作 DB2&lt;/h3&gt;
连接 DB2 数据库要简单的多，只要安装 ibm-db 库</description></item><item><title>DB2 "The transaction log for the database is full" 问题的解决</title><link>https://yanbin.blog/db2-the-transaction-log-for-the-database-is-full-fix/</link><pubDate>Wed, 16 Feb 2022 22:22:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/db2-the-transaction-log-for-the-database-is-full-fix/</guid><description>
在使用 DB2 的 Community 版本的 Docker 镜像 &lt;code&gt;ibmcom/db2&lt;/code&gt; 进行测试，启动 Docker 容器的命令是&lt;br/&gt;
&lt;blockquote&gt;
$ docker run -name db2server --privileged=true -p 50000:50000 \ &lt;br /&gt;
    -e LICENSE=accept \&lt;br /&gt;
    -e DB2INSTANCE=db2user \&lt;br /&gt;
    -e DB2INST1_PASSWORD=password123 \&lt;br /&gt;
    -e DBNAME=test \&lt;br /&gt;
    ibmcom/db2
&lt;/blockquote&gt;
当使用多线程以及 JDBC 的 Batch Update 时，出现 "The transaction log for the database is full" 问题，一旦出现这个问题时，用数据库客户端连接后即使执行一条简单的 insert/update 语句也会报同样的错误。于是只能减少线程数和 Batch Update 时的记录来勉强过关，但性能上与其他数据库就有很大的差别了。</description></item><item><title>Windows 下的软件包管理(Chocolatey)</title><link>https://yanbin.blog/windows-package-management-chocolatey/</link><pubDate>Mon, 14 Feb 2022 14:58:03 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-package-management-chocolatey/</guid><description>
在 Mac OS X 或各个 Linux 发行版都有自己的软件包管理工具，如&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Mac OS X: brew,  MacPorts 已鲜有人使用了&lt;/li&gt;
&lt;li&gt;Debian 系列: apt, 或 apt-get, 还有用 snap 的&lt;/li&gt;
&lt;li&gt;RedHat 系列: yum, 或 dnf&lt;/li&gt;
&lt;li&gt;Arch 系: pacman&lt;/li&gt;
&lt;li&gt;SUSE 系: YaST 或  Zypper&lt;/li&gt;
&lt;li&gt;Alpine 系: apk, 如 apk add openssh&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
基于现代的远程服务器管理，还总是用 RDP(远程桌面)来连接就显得有点那个了，效率上与命令行终端连接方式也无法企及，更不消说同时管理多台服务器。所以在命令行下或 PowerShell 中的 Windows  包管理工具也应运而生，让我们也能用远程 SSH 或 PowerShell 来管理 Windows 服务器，进行安装配置</description></item><item><title>AWS Windows EC2 实例的 userdata 应用笔记</title><link>https://yanbin.blog/aws-windows-ec2-instance-userdata-note/</link><pubDate>Mon, 14 Feb 2022 13:59:26 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-windows-ec2-instance-userdata-note/</guid><description>
因为平常主要是使用 EC2 的 Linux 实例，所以之前写过的一篇关于 UserData 的日志 &lt;a href="https://yanbin.blog/launch-aws-ec2-instance-userdata-knowledge/"&gt;创建 AWS EC2 实例时 userdata 的一些知识&lt;/a&gt; 默认就是讲的有关 Linux 实例的 UserData。本文补充上 Windows 的 EC2 实例 UserData 的基本使用，参考自 AWS 官方文档 &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-user-data.html"&gt;Run commands on your Windows instance at launch&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
Windows 的 UserData 被谁执行，依据所选择 AMI 的不同有以下三种方式&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;EC2Launch v2: 最新方式，只是被当前预览版的 AMI 所支持，它支持 YAML 配置的脚本&lt;/li&gt;
&lt;li&gt;EC2Launch: 当前方式，Windows Server 2016 及更新版&lt;/li&gt;
&lt;li&gt;EC2Cofnig: 旧有方式， Windows Sever 2012 R2 及旧版本&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>流畅的 Python 读书笔记(四)</title><link>https://yanbin.blog/fluent-python-reading-notes-4/</link><pubDate>Tue, 08 Feb 2022 22:46:23 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fluent-python-reading-notes-4/</guid><description>
&lt;h3&gt;Python 的函数是一等对象&lt;/h3&gt;
因为它符合编程语言理论家对 "一等对象 -- first-class object" 的定义&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;运行时创建&lt;/li&gt;
&lt;li&gt;可赋值给变量或数据结构的属性&lt;/li&gt;
&lt;li&gt;能作为函数参数&lt;/li&gt;
&lt;li&gt;能被函数返回 &lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
依据这种定义，还有我们最为熟悉的 JavaScript 的函数也是一等对象，Java 的函数都是依附于类或对象存在的，不是一等对象。&lt;br/&gt;&lt;br/&gt;
Python 的文档字符串(docstring) 是放在模块，函数，类中的第一个纯字符串。可用单个引号(单引号或双引)，通常因为有大段的文字会用三引号的字符串，比如&lt;br/&gt;
&lt;blockquote&gt;
def foo():&lt;br /&gt;
    '''doing nathing'''&lt;br /&gt;
    pass
&lt;/blockquote&gt;
代码中用 &lt;code&gt;foo.__doc__&lt;/code&gt; 能查看到到 docstring，或用 help(foo), doc(foo) 都能输出包含 docstring 的信息</description></item><item><title>流畅的 Python 读书笔记(三)</title><link>https://yanbin.blog/fluent-python-reading-notes-3/</link><pubDate>Fri, 04 Feb 2022 10:23:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fluent-python-reading-notes-3/</guid><description>
书中的数据结构还差文本和字节序列那一章未阅读完。Python 的 str 是 unicode 类型，编码在应用方面基本上就是 .decode(), .encode() 方法的调用，默认编解码时用 UTF-8 就行，差不多就不用太深入了。&lt;/p&gt;
&lt;br/&gt;
Python 的 bytes 和 bytearray 中的元素都是介于 0 ~ 255(含) 之间的整数，即一个字节，bytes 的切片是 bytes, bytearray 的切片还是 bytearray。bytes 和 bytearray 的输出(__repl__) &lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;可打印的 ASCII 码以 ASCII 字符贵&lt;/li&gt;
&lt;li&gt;特殊字转义，如 \r, \n, \r, 和 \\&lt;/li&gt;
&lt;li&gt;其他字符以十六进制转义输出，如 \xc3&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
像 endswith, replace, strip, translate, upper 等函数可以直接用来处理 bytes,如</description></item><item><title>JDBC 连接串中指定当前 schema(含 Oracle, DB2, PostgreSQL 和 SQLServer)</title><link>https://yanbin.blog/jdbc-conn-string-specify-current-schema/</link><pubDate>Thu, 03 Feb 2022 11:22:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbc-conn-string-specify-current-schema/</guid><description>
现在流行数据都有 Schema 的概念，一般作为数据库对象(表，函数，存储过程等)的命名空间。所以在数据库端往往存在 实例/数据库/Schema 这样层级划分。对于 DB2 和 Oracle 用客户端创建一个新的数据库并非易事，灵活的在数据库中较轻量的划分隔离空间的办法因数据库类型而异&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;MySQL: 创建数据库(create database), create schema 是 create database 的别名&lt;/li&gt;
&lt;li&gt;PostgreSQL: create database 创建新的数据库，或在当前数据库下用 create schema 创建 schema&lt;/li&gt;
&lt;li&gt;SQLServer: 和 PostgreSQL 一样的自由，create database 创建新的数据库，或在当前数据库下用 create schema 创建 schema&lt;/li&gt;
&lt;li&gt;DB2： 用 create schema 创建新的 schema, 或创建数据库对象时直接加上前缀，create table abc.test1..., 没有 abc schema 则会自动创建&lt;/li&gt;
&lt;li&gt;Oracle: create schema 较麻烦，涉及到 authorization. 但可以通过 create user 创建新用户后就有了对应的新 schema&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
下面我们来了解下在 PostgreSQL/SQLServer 中创建新的 schema，如何在 JDBC 连接字串中指定默认 schema, 同时也涉及到 database/schema/user 的创建以及在 SQL 中如何切换。</description></item><item><title>流畅的 Python 读书笔记(二)</title><link>https://yanbin.blog/fluent-python-reading-notes-2/</link><pubDate>Thu, 27 Jan 2022 17:05:18 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fluent-python-reading-notes-2/</guid><description>
继续啃这本略微有些旧的书，《Fluent Python》第二版出版在即，预计今年四月份，它将会讲解到更新版本 Python 的特性，书中有提到 Python 3.10。第一版读下来也不会是浪费时间的。&lt;/p&gt;
&lt;br/&gt;
还是数据结构，现在来到字典，dict 是 Python 语言的基石，在它内部也被广泛应用，比如 type(globals()) 是个  dict, globals() 的内容有我们能调用的全局函数。如果在编程中不想创建新对象的话，dict 几乎能表述需要的数据结构。&lt;br/&gt;&lt;br/&gt;
dict 就是一个 hash 表，它是 collections.abc 下 Mapping 和 MutableMapping 的子类。和其他语言中的字典一样，dict 的 key 必须实现了 &lt;code&gt;__hash__()&lt;/code&gt; 和 &lt;code&gt;__qe__()&lt;/code&gt; 方法，否则不能作为 key，比如 list 是不能作 key 的。两个key 的 hash() 相等的话，那么  key1 == key2, 反之不一定。所以当 __eq__ 依赖于可变状态就不要去实现 __hash__ 方法.</description></item><item><title>配置 AWS Lambda Python Logging</title><link>https://yanbin.blog/config-aws-lambda-python-logging/</link><pubDate>Tue, 25 Jan 2022 22:17:29 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/config-aws-lambda-python-logging/</guid><description>
通常在 Python 应用中简单的配置使用内置的 logging 是这样的&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%(asctime)s&lt;/span&gt;&lt;span class="s1"&gt; - &lt;/span&gt;&lt;span class="si"&gt;%(levelname)s&lt;/span&gt;&lt;span class="s1"&gt; - &lt;/span&gt;&lt;span class="si"&gt;%(module)s&lt;/span&gt;&lt;span class="s1"&gt;(&lt;/span&gt;&lt;span class="si"&gt;%(funcName)s&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%(lineno)d&lt;/span&gt;&lt;span class="s1"&gt;) - &lt;/span&gt;&lt;span class="si"&gt;%(message)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging_format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
假如文件名为 test.py, 用 python test.py 执行后输出&lt;br/&gt;
&lt;blockquote&gt;
2022-01-25 21:02:47,231 - INFO - test(&amp;lt;module&amp;gt;:6) - hello world
&lt;/blockquote&gt;
&lt;h3&gt;在 Lambda 中的现象&lt;/h3&gt;
可是这同样的代码放到 AWS Lambda Python 代码中却不灵验了，&lt;code&gt;logging.info()&lt;/code&gt; 将得不到任何输出。</description></item><item><title>流畅的 Python 读书笔记(一)</title><link>https://yanbin.blog/fluent-python-reading-notes-1/</link><pubDate>Sun, 23 Jan 2022 17:53:16 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fluent-python-reading-notes-1/</guid><description>
用了一段时间的 Python, 觉得还是有必要读一下《流畅的Python》这本书，它虽然是基于 Python 3.4 的，但 Python 自身的很多特性希望了解的更多，更深，或巩固，或扫扫死角。&lt;/p&gt;
&lt;br/&gt;
对于少量属性的对象可以用 collections.namedtuple 快速构建一个类  Card = collections.namedtuple('Card', ['rank', 'suit']), 用 type(Card) 看到的就是一个  class, 第一个参数 Card 是类名，第二个参数列表里是属性名，然后用 card = Card('7', 'diamonds') 创建一个实例。PyCharm 也能正确识别出 Card 构建与使用对象时的属性 rank 和 suit.&lt;br/&gt;&lt;br/&gt;
现代从 Python 3.7 开始引入了 @dataclasses.dataclass 比 namedtuple 要方便些&lt;br/&gt;
&lt;blockquote&gt;
@dataclasses.dataclass&lt;br /&gt;
class Card:&lt;br /&gt;
    rank: str&lt;br /&gt;
    suit: str = None
&lt;/blockquote&gt;
card = Card(rank='7', suit='diamonds')&lt;br/&gt;&lt;br/&gt;
或者用 pydantic 的 BaseModel 都比先前的 namedtuple 好用</description></item><item><title>Celery(分布式任务队列)入门学习笔记</title><link>https://yanbin.blog/celery-distributed-task-queue-learning/</link><pubDate>Mon, 17 Jan 2022 02:50:38 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/celery-distributed-task-queue-learning/</guid><description>
&lt;p&gt;在步入到 AWS 后，设计一个典型的分布式计算任务模式是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;提交任务的客户端把一组组待计算任务的输入编制成消息发送到 SQS 或 SNS 队列中&lt;/li&gt;
&lt;li&gt;SQS 消息可被  ECS 或 Lambda 处理, SNS 消息还能触发 Lambda，ECS/Lambda 完成实际的计算任务&lt;/li&gt;
&lt;li&gt;结果可以保存到 Redis, S3 或别处, 如果提交任务端想要获取计算结果，可用 ID 来追踪&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用 ECS 的好处是可以基于 SQS 的消息数进行 AutoScaling 配置，决定 Worker 的规模; 用 Lambda 适当的用 Concurrency 数来限定 Lambda 的实例数。&lt;/p&gt;
&lt;p&gt;而 Python 的 Celery 让这一切变得更简单，它其实就是以上设计的一个集成方案。它以配置的方式选择使用任务队列(Broker), 结果存储方式(Backend), 让任务提交与 Worker 的代码实现简单化。</description></item><item><title>RabbitMQ 初体验(安装,概念及应用)</title><link>https://yanbin.blog/rabbitmq-installation-concepts-usage/</link><pubDate>Sun, 16 Jan 2022 01:01:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rabbitmq-installation-concepts-usage/</guid><description>
之前工作中用过 JMS 的 IBM MQSeries, 自己试玩过 ActiveMQ, 再就是 Kafka, 再到 AWS 上的 SQS 等消息队列。打算调教一下 Python 的 Celery，它首推用 RabbitMQ 作为它的消息，当然也可选择 Redis 或 AWS 的 SQS，首先感觉有必要体验一下 RabbitMQ。&lt;br/&gt;&lt;br/&gt;
RabbitMQ 是一个 AMQP(Advanced Message Queuing Protocol) 的开源实现, 相关的实现产品还有 &lt;a href="http://www.openamq.org/"&gt;OpenAMQ&lt;/a&gt;, &lt;a href="http://stormmq.com/"&gt;StormMQ&lt;/a&gt;, &lt;a href="http://qpid.apache.org/"&gt;Apache Qpid&lt;/a&gt;, &lt;a href="http://www.redhat.com/products/mrg/"&gt;Red Hat Enterprise MRG&lt;/a&gt;, Microsoft&lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-overview"&gt; Azure Service Bus&lt;/a&gt; 等，AMQP 与 JMS 还存在一些交集。&lt;br/&gt;&lt;br/&gt;
本文不打算介绍太多的 RabbitMQ 的一些概念，主要是体验一下如何安装，怎么发送和接受消息，初次体验就不直接上 Docker 了，用 Docker 根本不知道 RabbitMQ 是个什么东西，所以用一个 Ubuntu 20.04 虚拟机来一步步安装。&lt;br/&gt;&lt;br/&gt;
先用 Vagrant 准备一个虚拟机，&lt;code&gt;Vagrantfile&lt;/code&gt; 文件内容如下</description></item><item><title>小心 Python 函数默认参数的陷阱</title><link>https://yanbin.blog/careful-python-function-default-parameter/</link><pubDate>Fri, 14 Jan 2022 21:01:44 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/careful-python-function-default-parameter/</guid><description>
Python 的函数参数支持默认值，这是本人一直喜欢的特性，Python 不支持方法重载，但默认参数可起到类似的效果，还不用写多个函数。现在支持默认参数的语言普遍的，像 C++, C#, Ruby, Groovy, PHP, Scala, JavaScript 等，Java 还不行。&lt;/p&gt;
&lt;br/&gt;
但是特别要小心，Python 的函数默认值与其他的语言是不同的，直接违反了最直观的常识 -- 默认参数应该是省略就每次用同样的默认值，传的话就用传入的值。&lt;br/&gt;&lt;br/&gt;
当我在 IntelliJ IDEA 中写类似如下的代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
我的 SonarLint 插件就要抱怨了
&lt;a href="https://yanbin.blog/posts/2022/careful-python-function-default-parameter/python-default-parameter-1.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2022/careful-python-function-default-parameter/python-default-parameter-1-800x122.png"
width="800px"
/&gt;
&lt;/a&gt;
说是&lt;br/&gt;
&lt;blockquote&gt;
SonarLint: Change this default value to "None" and initialize this parameter inside the function/method&lt;br /&gt;
Default argument value is mutable
&lt;/blockquote&gt;</description></item><item><title>Python 类实现的装饰器及简陋 REST API</title><link>https://yanbin.blog/python-class-implemented-decorator/</link><pubDate>Wed, 05 Jan 2022 01:54:50 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-class-implemented-decorator/</guid><description>
学习了函数实现的 Python 装饰器后，关于装饰器的内容还没完。Python 装饰器还是属于元编程的范畴，一谈到元(Meta), 元编程，往往能用简单的方式实现比较神奇的效果 -- 小渣男的非死不可除外。Python 还允许用类来实现装饰器，原理上就是能让 Python 对象函数用，见之前的一篇 &lt;a href="https://yanbin.blog/add-python-methods-dynamically/"&gt;Python 对象当函数使用及动态添加方法&lt;/a&gt;。关键就是类实现 &lt;code&gt;__call__&lt;/code&gt; 函数，对象就变成 &lt;code&gt;callable&lt;/code&gt;, 与函数的装饰器实现归纳起来就是：一个 Python 类型能不能用 @ 当作装饰器来用只需看它是否是 &lt;code&gt;callable&lt;/code&gt;。&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;quack&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;duck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Duck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duck&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;duck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
而且因为有了类，带属性的装饰器也会更简单，装饰器的属性就是构造函数的参数。还是来看怎么用类重新实现前面的 &lt;code&gt;my_decorator&lt;/code&gt; 装饰器</description></item><item><title>Python 中带属性的装饰器</title><link>https://yanbin.blog/python-decorator-with-property/</link><pubDate>Sat, 01 Jan 2022 18:13:48 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-decorator-with-property/</guid><description>
刚刚完成 &lt;a href="https://yanbin.blog/python-ellipsis-star-slash-args-kwargs/"&gt;由 Python 的 Ellipsis 到 *, /, *args, **kwargs 函数参数&lt;/a&gt;,
又回想起在 &lt;a href="https://yanbin.blog/master-python-decorators/"&gt;熟悉和应用 Python 的装饰器&lt;/a&gt;，关于带属性的装饰器一直未交代，安心不下来，Python 中带属性的装饰器用得非常普遍，如 Flask 的 &lt;code&gt;@app.route('/')&lt;/code&gt;。&lt;/p&gt;
&lt;br/&gt;
我们一看到 Python 的装饰器(Decorator) 会很直截的与 Java 的注解(Annotation) 联系起来，其实除了都用 &lt;code&gt;@&lt;/code&gt; 符号外是存在很大区别的。正如它们被翻译成的中文名那样，Java 的注解在一定程度上就是一个注释，只要没有注解处理器处理它们就可以被忽略，Java 要用反射来处理注解。而 Python 的装饰器更象是代理，函数一旦被装饰后，调用目标函数时是无法挣脱装饰器函数的控制的，是硬核的。</description></item><item><title>由 Python 的 Ellipsis 到 *, /, *args, **kwargs 函数参数</title><link>https://yanbin.blog/python-ellipsis-star-slash-args-kwargs/</link><pubDate>Fri, 31 Dec 2021 21:39:50 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-ellipsis-star-slash-args-kwargs/</guid><description>
早先对 Python *args, **kwargs 参数有所了解，也知道参数列表中的 &lt;code&gt;/&lt;/code&gt; 表示 Positional Only， &lt;code&gt;*&lt;/code&gt; 很少见。然而在使用 FastAPI 时看到路由函数中表示默认值采用了 &lt;code&gt;...&lt;/code&gt; 的方式又重新激发起我对 Python 函数参数的 &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;*args&lt;/code&gt;, 和 &lt;code&gt;**kwargs&lt;/code&gt; 的兴趣。&lt;br/&gt;&lt;br/&gt;
如 FastAPI 官方文档 &lt;a href="https://fastapi.tiangolo.com/tutorial/request-forms-and-files/"&gt;Request Forms and Files&lt;/a&gt; 中的&lt;br/&gt;
&lt;blockquote&gt;
@app.post("/files/")&lt;br /&gt;
async def create_file(file: bytes = File(...), fileb: UploadFile = File(...), token: str = Form(...)):
&lt;/blockquote&gt;
默认值的 &lt;code&gt;File(...)&lt;/code&gt;, &lt;code&gt;Form(...)&lt;/code&gt;， 起初还以为 &lt;code&gt;...&lt;/code&gt; 只是真正意义上的省略号，使用时需传入适当的参数，后来发现 &lt;code&gt;...&lt;/code&gt; 居然是一个 Python 实实在在的内置对象。</description></item><item><title>学习使用 AWS API Gateway V2</title><link>https://yanbin.blog/study-aws-apigateway-v2/</link><pubDate>Thu, 23 Dec 2021 00:46:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/study-aws-apigateway-v2/</guid><description>
&lt;p&gt;关于 AWS API Gateway V1, 写过一篇笔记 &lt;a href="https://yanbin.blog/lambda-api-gateway-with-api-key/"&gt;Lambda + API Gateway 创建需 API Key 验证的 API&lt;/a&gt;。 AWS 又推出了 API Gateway V2(服务管理/理解层面), 它同样可以用来作 HTTP-PROXY 调用 REST API, WebSocket; AWS-PROXY 调用 Lambda, 还能直接调用 AWS 的其他服务，如 StepFunction, SQS 等。&lt;/p&gt;
&lt;p&gt;但是  API Gateway V2 把 V1 中的 API Key 验证功能去掉了，这有点为了赚钱耍无赖了，先前是 API Key 验证不过时不会调用 Lambda, 现在可用 Lambda 来验证 API 调用，也就是不管 API Key 对与不对，都会去调用 Lambda 从而实现从你的帐户上扣钱的功能。&lt;/p&gt;
&lt;p&gt;在 V1 中创建整套服务的过程基本是  Resource -&amp;gt; Method -&amp;gt; Integration -&amp;gt; Stage。而在 V2 中的过程是 Integration -&amp;gt; Route -&amp;gt; Stage, 把 Resource 和 Method  合而为一，比如 Route Key 写成 &lt;code&gt;GET /users&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;下面照旧以 Terraform 的方式来叙述使用 API Gateway V2 如何实现 HTTP 代理，调用 Lambda, 及使用 AWS 服务(以 SQS 为例)，或与 VPC 内部的服务集成。首先是一个基本的框架，含 API 本身和 Stage</description></item><item><title>Java 直接插入 CLOB/BLOB 数据到 Oracle 数据库</title><link>https://yanbin.blog/java-insert-clob-blob-into-oracle-table/</link><pubDate>Tue, 21 Dec 2021 23:15:15 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-insert-clob-blob-into-oracle-table/</guid><description>
向数据库中插入 CLOB 或 BLOB 类型的数据，Oracle 总是比其他类型的数据库操作上要麻烦多了。当然，对于不大于 4K 长度的 CLOB 字符串在 JDBC 中可简单的用 &lt;code&gt;PreparedStatement.setString(idx, "short string")&lt;/code&gt; 。如果要插入大于 4K 长度的内容，网上找来的例子许多都是分两步走&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;先插入 EMPTY_CLOB() 或 EMPTY_BLOB()&lt;/li&gt;
&lt;li&gt;然后 SELECT 原来的记录 &lt;code&gt;FOR UPDATE&lt;/code&gt;, 再更新先前插入的记录&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
这就存在两个问题，含 CLOB/BLOB 的表必需要有主键，还有因为 &lt;code&gt;FOR UPDATE&lt;/code&gt; 的使用我们需要开启事物，不能采用自动提交。&lt;br/&gt;&lt;br/&gt;
其实还有更简单的方法可直接插入大的 CLOB/BLOB 数据，要用到 Oracle JDBC 驱动的 setStringForClob(),  CLOB.createTemporary(), 或 BLOB.createTemporary() 方法。来看下面的例子，例子中只演示 CLOB, 类似的方法可应用于 BLOB, NCLOB。&lt;br/&gt;&lt;br/&gt;
本文中所使用的 Oracle JDBC 驱动比较老，是 ojdbc:ojdbc:5。Docker 启动一个本地的 Oracle 11G 作为测试数据库&lt;br/&gt;
&lt;blockquote&gt;
$ docker run -d -p 1521:1521 -p 8080:8080 wnameless/oracle-xe-11g-r2
&lt;/blockquote&gt;
&lt;br/&gt;
默认的 SID 是 xe, 数据库用户名和密码分别是 system/oracle</description></item><item><title>FastAPI - 一款新型的 Python Web 框架(对比 Flask)</title><link>https://yanbin.blog/fastapi-new-python-web-framework-vs-flask/</link><pubDate>Mon, 20 Dec 2021 02:58:24 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fastapi-new-python-web-framework-vs-flask/</guid><description>
近日曾想尽办法为 Flask 实现 Swagger UI 文档功能，找到的实现方式基本上是 &lt;a href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt; + Flasgger, 记录在 &lt;a href="https://yanbin.blog/flask-integrate-with-swagger-ui/"&gt;Flask 应用集成 Swagger UI&lt;/a&gt;。然而不断的 Google 过程中偶然发现了一款集成了 Swagger UI 的比 Flask 还好的 Python Web 框架 -- &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt; 。起初想要在标题中表达的意思大概是 Flask + Swagger = FastAPI, 后来发现 FastAPI 的闪亮点不仅如此，于是乎又找了些 Flask 与 FastAPI 对比的文章读一读，在文后附有链接。&lt;br/&gt;&lt;br/&gt;
本文不对 Flask 与 FastAPI 的各个方面对进行对比，本人兴趣依然还是在 FastAPI 的 Swagger UI 功能，以及与 Flask 的 Blueprint 类似的特性。如果要拿 Flask 与 FastAPI 比较的话，应该用 Flask 2.x, 因为它开始支持类似 @app.get 的装饰器，并引入了 async 路由函数。&lt;br/&gt;&lt;br/&gt;
Flask 是在 2010 年发布的，它构建于 WSGI(Python Web Server Gateway Interface) 之上的，产品环境中运行需与 uWSGI, Gunicorn 搭配，或用 mod_wsgi 模块与 Apache 集成。因发布较早，所以目前应该有较多的使用者。Flask 2.0 需要 Python 3.6+ 的支持，如果支持 &lt;code&gt;async&lt;/code&gt;, 需 Python 3.7+&lt;br/&gt;&lt;br/&gt;
FastAPI 发布于 2018 年，构建于 ASGI(Asynchronous Server Gateway Interface) 之上，在 IO 密集型的应用中有更优越的性能。生成环境中配合 ASGI 服务器，如 &lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt; 或 &lt;a href="https://gitlab.com/pgjones/hypercorn"&gt;Hypercorn&lt;/a&gt;. FastAPI 最为亮丽的特性是集成了 Swagger UI -- 外加一个福利 &lt;a href="https://github.com/Rebilly/ReDoc"&gt;ReDoc&lt;/a&gt;。FastAPI 需 Python 3.6+ 版本。</description></item><item><title>Python async, await 的理解与使用</title><link>https://yanbin.blog/python-async-await-keywords/</link><pubDate>Fri, 17 Dec 2021 14:37:01 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-async-await-keywords/</guid><description>
关于 Python 中 async, await 关键字的一些知识在去年的一篇 &lt;a href="https://yanbin.blog/how-flask-work-with-asyncio/"&gt;探索 Flask 对 asyncio 的支持&lt;/a&gt; 有讲到，一直没在实际上用过它们，所以基本上也就忘干净了。随着 &lt;a href="https://flask.palletsprojects.com/en/2.0.x/"&gt;Flask&lt;/a&gt; 2 加入了 async 的特性，以及 FastAPI 从一开始支持 async, 又觉得有必要重新温习一下 Python 中如何使用 async, await 关键字了。&lt;br/&gt;&lt;br/&gt;
注：由于 Flask 支持了 async, 号称 async 化 Flask 的  &lt;a href="https://pgjones.gitlab.io/quart/"&gt;Quart&lt;/a&gt; 项目开始变得无足轻重了。&lt;br/&gt;&lt;br/&gt;
本文主要的学习材料是在 YouTube 上的一个视频 &lt;a href="https://www.youtube.com/watch?v=Bm96RqNGbGo"&gt;Fear and Awaiting in Async (Screencast)&lt;/a&gt;, 其中用 Python REPL 以 Live 的形式展示，对 async, await 关键字循序渐进的讲解。&lt;br/&gt;&lt;br/&gt;
如今不少语言都支持 async, await 关键字，如 C#, JavaScript, Kotlin, Rust 等，还有今天的主角 Python。而 Java 仍然很重视函数返回值的意义，未支持 async, 只能显式的返回 Future/CompletableFuture, 而且自己控制如何在线程池中执行。</description></item><item><title>Flask 应用集成 Swagger UI</title><link>https://yanbin.blog/flask-integrate-with-swagger-ui/</link><pubDate>Tue, 07 Dec 2021 19:46:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/flask-integrate-with-swagger-ui/</guid><description>
成熟的 Web  API 框架总有一款 API 文档与之伴随，当前最知名的莫过于支持 Open API 的 Swagger 了。Python 的 Flask 框架支持 Swagger UI 也有几条路子&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/flasgger/flasgger"&gt;Flasgger&lt;/a&gt; : 好像是 flask-swagger 的 fork&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/getsling/flask-swagger"&gt;flask-swagger&lt;/a&gt;: 许久未更新了，不用考虑&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rantav/flask-restful-swagger"&gt;flask-restful-swagger&lt;/a&gt;: 到目前也两年未更新了&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flask-restplus.readthedocs.io/en/stable/swagger.html"&gt;Flask-RESTPlus 的 Swagger 特性&lt;/a&gt;: 真需要用到 Flask-RESTPlus 就可以用它&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
单纯用 Flask 构建 API 的话，细数起来也就 Flasgger 比较合适，如果甩开 Flask 而用 &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt; 的话，就不用操心 Swagger 了，因为 FastAPI 原生的支持 Swagger。&lt;br/&gt;&lt;br/&gt;
本文中我们将体验如何使用 Flasgger, 关于使用方法，在它源码的 &lt;a href="https://github.com/flasgger/flasgger"&gt;README.md&lt;/a&gt; 已经描述的很清楚了。Flasgger 提供了以下几种主要的使用方式</description></item><item><title>逐步理解 Flask 的 Blueprint(蓝本)</title><link>https://yanbin.blog/understand-flask-blueprint/</link><pubDate>Mon, 06 Dec 2021 14:30:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-flask-blueprint/</guid><description>
Python 的 &lt;a href="https://flask.palletsprojects.com/"&gt;Flask&lt;/a&gt; 框架能让我们快速的建立一个轻量级的 Web 或 REST API。对于小应用由一个 @app 装饰一撸到底就行，当项目稍具规模或要更清晰就要考虑模块化，于是来到了我们今天的话题，首先是&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;为什么需要 Blueprint?&lt;/h3&gt;
比如说我们一定超级简单的 Flask 应用 main.py 的代码如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hello world!&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Terraform 的变量使用及赋值规则</title><link>https://yanbin.blog/terraform-variables-input-priority/</link><pubDate>Tue, 30 Nov 2021 23:52:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/terraform-variables-input-priority/</guid><description>
&lt;p&gt;本人在 AWS 上建立基础架构的时候倾向于用 Terraform, 而不是 AWS 自家的 CloudFormation。倒不是因为 Terraform 支持多种类型的 Provider，而是它能模块化，不同项目共享 AWS 资源时也更方便。而使用 CloudFormation 的话，一不小心就会写出一个超大的 YAML 文件来，要使用共同的 AWS 资源时，谁来创建是个不小的问题。CloudFormation 稍值得称道的地方也就是有一个可视化的  Stack，但把  Terraform 执行的 state 存到 S3 也不差。使用 Terraform 还有一个最享受的地方就是它的官方文档组织的非常清晰。&lt;/p&gt;
&lt;p&gt;出品 Terraform 的公司 HashiCorp 秉持着 Infrastructure as Code，近日在忙着 &lt;a href="https://www.barrons.com/articles/cloud-software-provider-hashicorp-targets-13-billion-valuation-with-ipo-51638231582"&gt;IPO&lt;/a&gt;, 估值在 $13B, Ticker 将为 &lt;a href="https://finance.yahoo.com/quote/HCP"&gt;HCP&lt;/a&gt;(该链接直接可用时便以上市)，介时也值得关注一下。扯远了，回到日常使用 Terraform 的变量上来。&lt;/p&gt;
&lt;p&gt;本文直接参考自 Terraform 官网的 &lt;a href="https://www.terraform.io/docs/language/values/variables.html"&gt;Input Variables&lt;/a&gt;。不管英文好不好都应该读原文去理解 Terraform 变量的详细使用方法。这里主要是了解各种输入变量的方式，以及验证一下当同时用多种方式输入重复的变量时，以谁为最终的结果。</description></item><item><title>SpringBoot2 应用 Axis 1.4 开发 WebService</title><link>https://yanbin.blog/springboot2-develop-axis-1-4-webservice/</link><pubDate>Mon, 15 Nov 2021 21:00:32 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot2-develop-axis-1-4-webservice/</guid><description>
有了前一篇 &lt;a href="https://yanbin.blog/axis-1-4-develop-webservice/"&gt;应用 Axis 1.4 开发 WebService&lt;/a&gt; 的对 Axis 1 较为深刻的理解后，现在正式给古老的 Axis 1.4  拉个伴，那就是 SpringBoot2。SpringBoot2 + Axis 1 的主要工作就是把 Axis 的 &lt;a href="https://github.com/apache/axis-axis1-java/blob/master/axis-war/src/main/webapp/WEB-INF/web.xml"&gt;web.xml&lt;/a&gt; 用 SpringBoot2 的方式进行转述。&lt;br/&gt;&lt;br/&gt;
在 SpringBoot 中用 Axis 1 后，有两个特性不再支持&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;不再支持 jws 即时发布 Web Service，不能直接搬用 url-pattern *.jws，没继续深究，实际中希望这么部署的方式用得较少&lt;/li&gt;
&lt;li&gt;不再支持 SOAPMonitorService，它是一个 Java Applet,  Java Applet 在新版的 JDK 中已被移除，早就不推荐使用了&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
在 SpringBoot 中配置 Servlet 或 ServletListener 有两种方式&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;ServletRegistrationBean/ServletListenerRegistrationBean&lt;/li&gt;
&lt;li&gt;@WebServlet/@WebListener&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
spring-boot-starter 引入了 log4j-to-slf4j, jul-to-slf4j, 所以不需要配置 log4j.properties, 需要的话可用 logback.xml 配置日志输出。</description></item><item><title>应用 Axis 1.4 开发 WebService</title><link>https://yanbin.blog/axis-1-4-develop-webservice/</link><pubDate>Sun, 14 Nov 2021 18:03:18 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/axis-1-4-develop-webservice/</guid><description>
Axis 1 的最后一个版本还是 2006-04-22 发布的 1.4 Final 版，当前的版本是  Axis 2， 即于  2021-08-01 发布的  Axis v1.8.0 版本。想想在  2006 年 4 月份，软件开发是一种什么样的景象，JDK 5 于 2004-09-30 发布，同年 12 月 12 日 JDK 6 才出来, Spring 还是 1.x 的版本。那时候仍是 EJB 兴旺的年代，微服务概念的出现还有等好多年。&lt;br/&gt;&lt;br/&gt;
那为什么还要学习 Axis 1.4 呢？目的就是为了放弃，先前的 Axis 1.4 的项目不稍加理解，怎么能顺利的过度到 Axis 2 呢？要说眼下更好的用来开发 SOAP WebService 的库应该首选 &lt;a href="http://cxf.apache.org/"&gt;Apache CXF&lt;/a&gt;。本来拟定的文章标题的 Springboot2 应用 Axis 1.4 开发 WebService, 但一发挥就用力过猛，只得下回另立新篇来再加上 Springboot2 了，因此本篇就是为 SpringBoot2 与 Axis 1 的结合铺路的。&lt;br/&gt;&lt;br/&gt;
什么是 &lt;a href="https://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt;，Simple Object Access Protocol, 简单对象访问协议，一种 XML-RPC 的实现。谁都敢号称简单，与当今的 REST API 一对照，任何人都会觉得 SOAP 是把一个远程调用搞得无比的复杂。SOAP 有 1.1 和 1.2 两个版本，现在提 SOAP 基本就是 SOAP 1.2 的代名词。另外，SOAP 1.2 开始也承认不简单，不再明确为 Simple Object Access Protocol 的缩写，而叫做 Messaging Framework (Second Edition)。</description></item><item><title>mvn 命令上传文件到 Maven 仓库</title><link>https://yanbin.blog/mvn-deploy-file-to-maven-repository/</link><pubDate>Sat, 13 Nov 2021 23:51:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mvn-deploy-file-to-maven-repository/</guid><description>
针对一个 Maven 的 Java 项目，我们执行 mvn deploy 命令时想要把生成的 jar 包上传到 Maven 仓库(本文将使用私有的 Nexus 仓库)中去。所要用到的插件 &lt;a href="http://maven.apache.org/plugins/maven-deploy-plugin/usage.html"&gt;Maven Deploy Plugin&lt;/a&gt;，本文实际就是讲述如何用该插件上传 jar 包到 Maven  仓库，更多用法请参考该插件的官方文档。&lt;br/&gt;&lt;br/&gt;
本文关键性的两个配置文件是 pom.xml 和 settings.xml。前者配置仓库的地址，后者中配置用户名和密码。要确定 Maven 使用了哪个 settings.xml 文件，用 &lt;code&gt;mvn -X&lt;/code&gt; 查看，比如下面的输出&lt;br/&gt;
&lt;blockquote&gt;
[DEBUG] Reading global settings from /usr/local/Cellar/maven/3.8.3/libexec/conf/settings.xml&lt;br /&gt;
[DEBUG] Reading user settings from /Users/yanbin/.m2/settings.xml
&lt;/blockquote&gt;
Maven 还允许在执行 mvn 命令时用  -s 或 --settings 参数指定 settings.xml 文件，如 mvn deploy --settings settings.xml&lt;br/&gt;&lt;br/&gt;
所以对于 settings.xml 文件的修改，可修改全局的，用户的或参数 --settings 指定的。</description></item><item><title>AWS Assume IAM role 的使用</title><link>https://yanbin.blog/how-to-assume-aws-iam-role/</link><pubDate>Tue, 09 Nov 2021 21:03:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-to-assume-aws-iam-role/</guid><description>
AWS 要授权给他人访问指定资源有哪几种方式呢？&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;创建一个 AWS 帐号让别人用，那是 AWS 干的事&lt;/li&gt;
&lt;li&gt;在自己帐号下创建一个用户，把 Access Key ID 和 Secret Access Key 告诉别人。可为该用户限定权限，但任何获得那两个 Key 的人都能使用该用户。&lt;/li&gt;
&lt;li&gt;创建一个 IAM Role, 并指定谁(帐号或 Role) 能以该 Role 的身份来访问。被 Assume 的 Role 可限定权限和会话有效期。&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
用 Assume Role 的方式具有更高的安全可控性，还不用维护 Access Key ID 和 Secret Access Key。比如在构建和部署时通常是有一个特定的 Account, 然后 Assume 到别的 IAM Role 去操作资源。&lt;br/&gt;&lt;br/&gt;
本文将详细介绍在帐号 A 创建一个 IAM Role(标注为 R) 并分配一些权限，然后允许另一个帐号 B 以 IAM Role - R 的身份来访问帐号 A 下的资源。IAM Role 将用 awscli 来创建，Assume Role 的过程用 awscli 和 boto3 Python 代码两种方式来演示。</description></item><item><title>构建 AWS Lambda Python Docker 镜像</title><link>https://yanbin.blog/build-aws-lambda-python-docker-image/</link><pubDate>Mon, 08 Nov 2021 17:00:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/build-aws-lambda-python-docker-image/</guid><description>
AWS 的 Lambda 在 2020-12-01 开始支持用 Docker 镜像存放代码，见 &lt;a href="https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/"&gt;New for AWS Lambda - Container Image Support&lt;/a&gt;。AWS Lambda 最初的对发布包的限制是 50M, 解压后(因为执行前需要解压缩)不能超过 250M，对于压缩比小于 1/5 的包来说，要突破 50M 部署包的限制就要用 2018-11-29 推出的层(&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html"&gt;layer&lt;/a&gt;), 即把 Lambda 的依赖可以组织为层，每个 Lambda 可引用最多 5 个层，但最终 Lambda 加上层所解压后的大小仍然有 250 M 的限制。&lt;br/&gt;&lt;br/&gt;
对于使用了大量依赖的 Lambda，比如 Python 中用了 Pandas 之类的数学分析包，250M 的大小是不够的，所以才有了 Docker 镜像化的 Lambda, 镜像的大小限制一下蹦到 10G，要构建出一个 10G Lambda 用的 Linux 镜像, 那绝对是个巨兽，至少目前是超越我的想像力，除非往里面塞入大量的业务数据。关于 Lambda 有哪些限制，请参阅 &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html"&gt;Lambda quotas&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
介绍完 Lambda 引入 Docker 镜像的背景后，本文接下来只关注如何构建一个 Python Lambda 镜像，对于如何部署 Docker 化的 Lambda, 不在本文的范围之内。主要的参考文档为 AWS Lambda 官方的 &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/python-image.html"&gt;Deploy Python Lambda functions with container images&lt;/a&gt;.</description></item><item><title>创建和发布自己的 Python 包到 PyPI 上</title><link>https://yanbin.blog/create-publish-your-own-pypi-python-package/</link><pubDate>Wed, 27 Oct 2021 16:58:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/create-publish-your-own-pypi-python-package/</guid><description>
&lt;p&gt;像 Java 可发布包到 Maven 仓库，NodeJS 发布包到 NPM 一样，我们也可以创建自己的 Python 包并发布到 &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; 仓库中去。或者内部使用的包，只须发布到私有的 Nexus 服务器上。&lt;/p&gt;
&lt;p&gt;本文中的例子将创建一个 Python 包 &lt;code&gt;bounded-executor&lt;/code&gt;, 并发布到 PyPI 上。为什么创建这个包呢？原因是直接用 Python 的 ThreadPoolExecutor 或  ProcessPoolExecutor 来提交任务的话，任务的等待队列是没有边界的，这就会造成因提交任务过快而使得内存爆满。本包最为合适的名称应该是 &lt;code&gt;bounded-pool-executor&lt;/code&gt;, 可是名字已被他人使用，但此外的实现有所不同，ThreadPoolExecutor 用 Queue(maxsize) 来控制，而 ProcessPoolExceutor 用 BoundedSemaphore 来控制。&lt;/p&gt;
&lt;p&gt;我们以经典的 Python 工程目录结构为例，构建的核心是执行 &lt;code&gt;setup.py&lt;/code&gt; 中的 &lt;code&gt;setup&lt;/code&gt; 函数，由此来理解 &lt;code&gt;setup&lt;/code&gt; 的最主要配置与关键命令做了些什么。这样有助于我们理解其他的 Python 包管理工具的底层行为，从中我们可以对比 poetry 的 build, install, 和 publish 命令。</description></item><item><title>理解 Docker Client/Server 架构, 找寻 Docker Desktop 替代品</title><link>https://yanbin.blog/docker-desktop-replacement-docker-machine/</link><pubDate>Thu, 21 Oct 2021 00:12:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-desktop-replacement-docker-machine/</guid><description>
&lt;!-- wp:paragraph --&gt;&lt;br/&gt;
&lt;p&gt;本文继上篇 &lt;a href="https://yanbin.blog/replace-docker-desktop-with-hyperkit-minikube/"&gt;摆脱 Docker Desktop 即将到来的收费&lt;/a&gt; 进一步寻找符合自己需求的 Docker Desktop 替代品，前面试用过 hyperkit + minikube, Podman, 最终还是确定了用 docker-machine。之所以选择了它是基于下面几个需求：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接 VPN 后 Docker 还要能继续工作 (通过 socket 文件和 localhost 与 Docker Host 通信不惧怕 VPN 连接. 因为 VPN 会接管路由表，所以用 IP 来连接 Docker Host 的话，VPN 连上后将可能无法访问 Docker Host)&lt;/li&gt;
&lt;li&gt;能与 IDE 进行集成开发与调试  (IntelliJ IDEA 能与 Docker Desktop, Docker Machine, TCP socket 和 SSH 上的 Docker Host 集成调试，但无法与 Daemonless 的 Podman 集成)&lt;/li&gt;
&lt;li&gt;DOCKER_HOST 能是远程机器 (由于 Podman 设计为 Daemonless，也就没有 Docker Host, 无法进行远程构建)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文力图更深入的理解 Docker 的架构来解释最后选择的来由，清楚了原理后可以自主创建一个 Docker Host，连 docker-machine 也可以不用。比如创建一个 AWS EC2 实例作为 Docker Host, 然后在本地执行 docker 命令进行镜像的构建与容器的运行，这时候镜像构建过程与容器执行的环境是在 EC2 上，再也不用先把本地的文件上传(scp 或 rsync) 到 EC2 上，然后 ssh  到 EC2 去执行 docker 命令了。&lt;/p&gt;
&lt;p&gt;一个小插曲：本人曾经随手在  &lt;code&gt;～/Downloads&lt;/code&gt; 目录下建立一个只有 &lt;code&gt;FROM busybox&lt;/code&gt; 一行的 &lt;code&gt;Dockerfile&lt;/code&gt; 文件，然后运行 &lt;code&gt;docker build ...&lt;/code&gt;  命令，结果每次都提示磁盘空间不足，本机磁盘还非常宽裕，Docker Machine 也分配了 20 G 内存，怎么会不够了呢？登入到 Docker Machine  后 &lt;code&gt;df&lt;/code&gt; 确实没空间了。四处找原因，原来是 &lt;code&gt;docker build ...&lt;/code&gt; 一执行，不管 3721 首先把当前目录下的所有文件全部拷贝到 Docker Machine 中去，&lt;code&gt;~/Downloads&lt;/code&gt; 目录中下了几十个 G 的内容，所以把 Docker Machine 给挤暴掉。解决办法就是要把  &lt;code&gt;Dockerfile&lt;/code&gt; 放到一个没有无用文件的独立目录中去，这也是为什么 &lt;code&gt;Dockerfile&lt;/code&gt; 中的 &lt;code&gt;COPY&lt;/code&gt; 命令只能从当前目录中拷贝文件的原因。</description></item><item><title>Python 中创建 PostgreSQL 数据库连接池</title><link>https://yanbin.blog/python-create-postgresql-connection-pool/</link><pubDate>Thu, 14 Oct 2021 14:07:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-create-postgresql-connection-pool/</guid><description>
征战 Java 多年，习惯于使用数据库之前都必须创建一个连接池，即使是单线程的应用，只要有多个方法中需用到数据库连接，建立一两个连接的也会考虑先池化他们。连接池的好处多多，1) 如果反复创建连接相当耗时，2) 对于单个连接一路用到底的应用，有连接池时避免了数据库连接对象传来传去，3) 忘记关连接了，连接池幸许还能帮忙在一定时长后关掉，当然密集取连接的应用势将耗尽连接，3) 一个应用打开连接的数量是可控的&lt;br/&gt;&lt;br/&gt;
接触到 Python 后，在使用 PostgreSQL 也自然而然的考虑创建连接池，使用时从池中取，用完后还回去，而不是每次需要连接时创建一个物理的。Python 连接 PostgreSQL 是主要有两个包，&lt;a href="https://pypi.org/project/py-postgresql/"&gt;py-postgresql&lt;/a&gt; 和 &lt;a href="https://pypi.org/project/psycopg2/"&gt;psycopg2&lt;/a&gt;, 而本文的实例将使用后者。&lt;br/&gt;&lt;br/&gt;
Psycopg 在 &lt;a href="https://www.psycopg.org/docs/pool.html"&gt;psycopg2.pool&lt;/a&gt; 模块中提供了两个连接池的实现在，它们都继承自 psycopg2.pool.AbstractConnectionPool, 该抽象类的基本方法是&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;getconn(key=None): 获取连接&lt;/li&gt;
&lt;li&gt;putconn(conn, key=None, close=False): 归还连接&lt;/li&gt;
&lt;li&gt;closeall(): 关闭连接池中的所有连接&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Python zipfile 只借助内存进行压缩与解压缩</title><link>https://yanbin.blog/python-zipfile-compress-decompress-in-memory/</link><pubDate>Wed, 13 Oct 2021 22:54:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-zipfile-compress-decompress-in-memory/</guid><description>
Python &lt;a href="https://docs.python.org/3/library/zipfile.html"&gt;zipfile&lt;/a&gt; 模块压缩与解压缩通常是对物理磁盘文件进行操作，比如参照官方的例子，生成压缩文件的代码是&lt;br/&gt;
&lt;blockquote&gt;
with zipfile.ZipFile('spam.zip', 'w') as myzip:&lt;br /&gt;
    myzip.write('eggs.txt')&lt;br /&gt;
    myzip.write('beef.txt')
&lt;/blockquote&gt;
这样就生成了一个包含两个文件的压缩包 spam.zip, 相当于命令 &lt;code&gt;zip spam.zip egges.txt beef.txt&lt;/code&gt; 的效果。用  &lt;code&gt;unzip -l spam.zip&lt;/code&gt; 命令就能看到其中的两个文件。相应的解压缩的代码如下&lt;br/&gt;
&lt;blockquote&gt;
with zipfile.ZipFile('spam.zip', 'r') as myzip:&lt;br /&gt;
    print(myzip.filelist())  # 可获得压缩包中的文件列表信息&lt;br /&gt;
    myzip.extractall()
&lt;/blockquote&gt;
同样是把压缩包 spam.zip 解压缩文件到当前目录中，相当于命令 &lt;code&gt;unzip spam.zip&lt;/code&gt; 的效果。&lt;br/&gt;&lt;br/&gt;
前面顺便也是熟悉一下 zipfile 模块的常见用法，但有时候我们可能从数据库中，从网络上收到的是字节数据，希望直接处理字节的压缩解压缩，而不借助于中间的磁盘文件，因为通过磁盘文件来处理必须进行善后处理以及可能的资源的竞争，在内存宽裕的情况下效率也是个问题。</description></item><item><title>Python Poetry 项目中相对路径模块引用的问题</title><link>https://yanbin.blog/python-poetry-relative-import-issues/</link><pubDate>Sat, 09 Oct 2021 00:20:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-poetry-relative-import-issues/</guid><description>
最近一直在折腾 Python 项目，通过对几个 Python 项目依赖管理与构建工具的对比，最后选择了 &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt;。它管理依赖，构建与发布包还是简单的多，不需要处理 setup.py, setup.cfg 和 Makefile 文件, 甚至都不需要了解 wheel 是什么就能往 PyPI 发布包了。&lt;/p&gt;
&lt;br/&gt;
可是，别看 Poetry 的官网一直守护着一副小清新的形像，其实照样处处是坑，其中一个就是与相对引用有关的问题。我们来看下什么样的现像，最后的结论就是：在 Python 中避免使用相对路径引用，因为相对路径的上下文经常在变，然后必要时先执行 &lt;code&gt;poetry install&lt;/code&gt;, 甚至把入口代码拉到包外头去。&lt;br/&gt;&lt;br/&gt;
什么是相对引用与绝对路径引用，比如在一个包 &lt;code&gt;my_package&lt;/code&gt; 中有两个模块(Python 文件) app.py 和 utils, app.py 中对 utils 资源的引用可以写成&lt;br/&gt;
&lt;blockquote&gt;
from utils import md5               # 不确定 utils 是一个包还是一个模块，有点像是隐式相对路径模块引用&lt;br /&gt;
from .utils import md5              # 同一目录中的 utils 模块&lt;br /&gt;
import .utils&lt;br /&gt;
from ..utils import md5             # 上一级目录中的 utils 模块 (如果 utils.py 在与 app.py 上一级目录的话)&lt;br /&gt;
from my_package.utils import md5  # 绝对引用，总是从包名开始
&lt;/blockquote&gt;
注意 from 后面的 &lt;code&gt;.&lt;/code&gt; 与 &lt;code&gt;..&lt;/code&gt;，相对路径引用不能直接 import, 如不能 &lt;code&gt;import .utils.md5&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;</description></item><item><title>Lambda + API Gateway 创建需 API Key 验证的 API</title><link>https://yanbin.blog/lambda-api-gateway-with-api-key/</link><pubDate>Wed, 06 Oct 2021 22:40:09 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lambda-api-gateway-with-api-key/</guid><description>
希望在标题上尽量包含更多的信息，原本命题为: Lambda + API Gateway 创建需 API Key 验证的 API(Docker + Python + Terraform), 但是觉得太长了，于是只取了前半部份。仍然要在开头部分强调一下本文件打算要实现什么&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;在 AWS 用 Lambda 和 API Gateway 创建 API&lt;/li&gt;
&lt;li&gt;创建的 API 是 public 的，需要用 x-api-key 来验证&lt;/li&gt;
&lt;li&gt;Lambda 的实现代码打包在了一个 Docker 镜像中&lt;/li&gt;
&lt;li&gt;整个 AWS 的基础架构(包括 ECR, Lambda, API Gateway 及权限等)是由 Terraform 脚本创建管理的&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
目标明确，我们直冲到代码的目录结构来，项目目录为 api-gateway-demo, Github 上的链接为 &lt;a href="https://github.com/yabqiu/api-gateway-demo"&gt;api-gateway-demo&lt;/a&gt;. 后面详叙还会把其中每一个文件的内部给列出来</description></item><item><title>Python 依赖管理与构建工具(CookieCutter, PyScaffold, PyBuilder, Poetry)</title><link>https://yanbin.blog/python-dependency-management-build-tools/</link><pubDate>Thu, 30 Sep 2021 22:28:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-dependency-management-build-tools/</guid><description>
Python 历时这么久以来至今还未有一个事实上标准的项目管理及构建工具，以至于造成 Python 项目的结构与构建方式五花八门。这或许是体现了 Python 的自由意志。不像 Java 在经历了最初的手工构建，到半自动化的 Ant, 再到 Maven 基本就是事实上的标准了。其间 Maven 还接受了其他的 Gradle(Android 项目主推), SBT(主要是 Scala 项目), Ant+Ivy, Buildr 等的挑战，但都很难撼动 Maven 的江湖地位，而且其他的差不多遵循了 Maven 的目录布局。&lt;/p&gt;
&lt;br/&gt;
回到 Python，产生过 pip, pipenv, conda 那样的包管理工具，但对项目的目录布局没有任何约定。关于构建很多还是延续了传统的 Makefile 的方式，再就是加上 setup.py 和 build.py 用程序代码来进行安装与构建。关于项目目录布局，有做成项目模板的，然后做成工具来应用项目模板。下面大概浏览一下四个工具的使用&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;CookieCutter&lt;/li&gt;
&lt;li&gt;PyScaffold&lt;/li&gt;
&lt;li&gt;PyBuilder&lt;/li&gt;
&lt;li&gt;Poetry&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>找到 Linux 后台程序的打印输出</title><link>https://yanbin.blog/find-linux-backend-process-output/</link><pubDate>Thu, 30 Sep 2021 00:27:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/find-linux-backend-process-output/</guid><description>
最近才狠命的折腾数码日购入的一个  DS920+ NAS，在上面用计划任务来同步两个目录的文件，似乎是在登陆它的 SSH 后的 rsync 同步命令与放在计划任务里的执行效果不一样。于是想看看计划任务里的 &lt;code&gt;rsync -av --delete folder1 folder&lt;/code&gt; 时的控制台输出。到哪里去找呢？登陆到 SSH 于只能用 &lt;code&gt;ps&lt;/code&gt; 命令看到它的进程 ID&lt;/p&gt;
&lt;blockquote&gt;
&lt;div data-pm-slice="1 1 []" data-en-clipboard="true"&gt;yanbin@nas:~$ ps -ef|grep rsync&lt;br /&gt;
SynoRsy+ 10270 1 0 Sep28 ? 00:00:00 /usr/bin/rsync --daemon&lt;br /&gt;
root 14067 14066 51 23:03 ? 00:00:02 rsync -av folder1/ folder2/&lt;br /&gt;
root 14076 14067 0 23:03 ? 00:00:00 rsync -av folder1/ folder2/&lt;br /&gt;
root 14077 14076 58 23:03 ? 00:00:02 rsync -av folder1/ folder2/&lt;/div&gt;
&lt;/blockquote&gt;
&lt;div data-pm-slice="1 1 []" data-en-clipboard="true"&gt;Google 了一下找到了这篇 &lt;a href="https://unix.stackexchange.com/questions/15693/see-the-stdout-redirect-of-a-running-process"&gt;See the STDOUT redirect of a running process&lt;/a&gt;， 关键就是用 &lt;code&gt;ls -l /proc/&amp;lt;pid&amp;gt;/fd&lt;/code&gt; 列出进程所有打开的文件描述符&lt;/div&gt;</description></item><item><title>用 Python 定义 Schema 并生成 Parquet 文件</title><link>https://yanbin.blog/python-define-schema-generate-parquet-file/</link><pubDate>Fri, 24 Sep 2021 15:06:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-define-schema-generate-parquet-file/</guid><description>
原来用 Java 和 Python 实现过 Avro 转换成 Parquet 格式，所以 Schema 都是在 Avro 中定义的。这里要尝试的是如何定义 Parquet 的 Schema, 然后据此填充数据并生成 Parquet 文件。&lt;br/&gt;&lt;br/&gt;
本文将演示两个例子，一个是没有层级的两个字段，另一个是含于嵌套级别的字段，将要使用到的 Python 模块有 pandas 和 pyarrow&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;简单字段定义&lt;/h2&gt;
&lt;h3&gt;定义 Schema 并生成 Parquet 文件&lt;/h3&gt;</description></item><item><title>AWS DynamoDB 的常用操作</title><link>https://yanbin.blog/aws-dynamodb-common-operations/</link><pubDate>Sun, 19 Sep 2021 23:03:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-dynamodb-common-operations/</guid><description>
AWS 提供的 NoSQL 数据库有 DynamoDB, DocumentDB(即 MongoDB), 和 Keyspaces(即 Cassandra)。还有一个神秘的早已消失于 AWS 控制台之外的 SimpleDB，它只能通过 API 才能使用。因为 AWS 有意要把它藏起来，不愿被新用户看到它，希望用 DynamoDB 替代它，关于用 aws cli 如何体验 AWS SimpleDB 可见本文后面部分。&lt;br/&gt;&lt;br/&gt;
DynamoDB 所设计的读写容量参数的概念，AWS 为其标榜是为保证一致性与明确的性能表现，实际上不如说是一个赚钱的计量单位，为了钱反而是把一个简单的事情弄复杂了。当需要全局索引时，必须为全局索引设定读写容量，连索引的钱也不放过。本文只为体验对 DynamoDB 的常用操作，不管吞吐量的问题，所以不用关心读写容量的问题。&lt;br/&gt;&lt;br/&gt;
DynamoDB 后端用 SSD 存储，不像 Elasticache 是把数据放在内存当，对了 Elasticache 也是 AWS 提供了 NoSQL 服务。DynamoDB 每条记录(Item) 的大小限制为 400K.</description></item><item><title>创建 AWS EC2 实例时 userdata 的一些知识</title><link>https://yanbin.blog/launch-aws-ec2-instance-userdata-knowledge/</link><pubDate>Sat, 18 Sep 2021 22:51:25 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/launch-aws-ec2-instance-userdata-knowledge/</guid><description>
我们在初始一个 AWS EC2 实例时，可以通过 user data 让 EC2 第一次启动后做些事情，可以放置 shell script 或 cloud-init 指令。在控制台设置 user data 可用明文文本，由 awscli 创建时可使用一个文件，或者通过 API 用 base64 编码的内容。&lt;br/&gt;&lt;br/&gt;
下面是 user data 被执行时需知晓的一些知识&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;是脚本时必须以 &lt;code&gt;#!&lt;/code&gt; 开始，俗称 Shebang, 如 &lt;code&gt;#!/bin/bash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;user data是以 &lt;code&gt;root&lt;/code&gt; 身份执行，所以不要用 &lt;code&gt;sudo&lt;/code&gt;, 当然创建的目录或文件的 owner 也是 &lt;code&gt;root&lt;/code&gt;，需要 &lt;code&gt;ec2-user&lt;/code&gt; 用户访问的话需要 &lt;code&gt;chmod&lt;/code&gt; 修改文件权限，或者直接用 &lt;code&gt;chown ec2-user:ec2-user -R abc&lt;/code&gt; 修改文件的所有者()&lt;/li&gt;
&lt;li&gt;脚本不能交互，有交互时必须想办法跳过用户输入，如 &lt;code&gt;apt install -y xzy&lt;/code&gt;, 带个  &lt;code&gt;-y&lt;/code&gt; 标记&lt;/li&gt;
&lt;li&gt;如果脚本中需访问 AWS 资源，权限由 Instance Profile 所指定的 IAM role 决定&lt;/li&gt;
&lt;li&gt;user data 中的脚本会被存储在  &lt;code&gt;/var/lib/cloud/instances/&amp;lt;instance-id&amp;gt;/user-data.txt&lt;/code&gt; 文件中，因此也可以从这里验证 user data 是否设置正确。或者在 EC2 实例上访问 http://169.254.169.254/latest/user-data 也能看到 user data 的内容。并且在 EC2 实例初始化后不被删除，所以以此实例为基础来创建一个新的 AMI 需把它删除了&lt;/li&gt;
&lt;li&gt;user data 的大小限制为 16 KB, 指 base64 编码前的大小&lt;/li&gt;
&lt;li&gt;cloud-init 的输出日志在 &lt;code&gt;/var/log/cloud-init-output.log&lt;/code&gt;, 它会捕获 cloud-init 控制台的输出内容&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>摆脱 Docker Desktop 即将到来的收费</title><link>https://yanbin.blog/replace-docker-desktop-with-hyperkit-minikube/</link><pubDate>Sat, 11 Sep 2021 00:01:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/replace-docker-desktop-with-hyperkit-minikube/</guid><description>
最近本人在 Mac OS X 下安装的 Docker Desktop 经常蹦出要升级的窗口，&lt;code&gt;Skip this update PRO&lt;/code&gt; 不能用，&lt;code&gt;Install &amp;amp; Restart&lt;/code&gt;  也总是失败，所以每次只能 &lt;code&gt;SNOOZE&lt;/code&gt;。原来觉得事有蹊跷，后来才知道这或许与近日吵得沸沸扬扬的 Docker Desktop 收费计划有关。Docker Desktop 是专门为 Windows 和 Mac OS X 定制的，Mac OS X 和 Windows  都是开发人员用的平台。Linux 下可以不用 Docker Desktop，Linux 下 Docker 之间甚至一层窗户纸都没有，实际部署的 Docker 服务也是运行在 Linux 下的。&lt;/p&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2021/replace-docker-desktop-with-hyperkit-minikube/docker-desktop-update.png"
width="483px"
/&gt;
墙里边微信正在谋划着对聊天记录的云存储收年费，外边是 Docker 在向大公司和开发人员下手，这难道是美国加州版的共同富裕？</description></item><item><title>DynamoDB Stream 应用及触发 Lambda 函数</title><link>https://yanbin.blog/dynamodb-stream-and-trigger-lambda/</link><pubDate>Wed, 08 Sep 2021 23:27:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dynamodb-stream-and-trigger-lambda/</guid><description>
&lt;p&gt;DynamoDB Stream 的实质就是一个依附于表的流，对表的增删改像关系型数据的触发器一样，以日志形式按顺记录到该流中。我们可以用 API 去读取其中的记录，或用来触发一个 Lambda。DynamoDB Stream 非常类似于 Kinesis 的 Stream, 它们都是有 Shard 的概念，但是 DynamoDB Stream 的 Shard 数目是不太确定的。而且还能用 KCL(Kinesis Client Library) 来操作 DynamoDB Stream。&lt;/p&gt;
&lt;p&gt;DynamoDB Stream 和 Kinesis Stream 有几个通用的 API 操作，像 list_streams(), describe_stream(), get_shard_iterator() 和 get_records() 函数。同时呢，在设置 Lambda 的触发器时，选择 DynamoDB Stream 与 Kinesis Stream 时可配置的参数几乎是一样的，有 Batch size, Batch window, Starting position 以及重试策略。而且也是一个 Shard 只能同时启动一个 Lambda 实例，由于 DynamoDB Stream 的 Shard 数目不太确定，所以它能同时启动几个 Lambda 实例也不确定的。&lt;/p&gt;
&lt;p&gt;另外， 一个 DynamoDB Stream 只能最多被两个 Consumer 消费，而可用来消费 Kinesis Stream 的 Consumer 数目是不受限的。DynamoDB Stream 中的记录保存时间为 24 小时， Kinesis Stream 中记录保存时间也是可配置的。我们创建一个 DynamoDB 表时还能启用 &lt;code&gt;Amazon Kinesis data stream details&lt;/code&gt;, 即把对 DynamoDB 的操作记录直接发送到 Kinesis Stream 中去，这样就能操作熟悉的 Kinesis Stream，Shard 数目可设定，坏处就是 Kinesis Stream 较费钱。</description></item><item><title>如何调试 ECR Docker 镜像</title><link>https://yanbin.blog/how-to-debug-ecr-docker-image/</link><pubDate>Sat, 04 Sep 2021 22:47:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-to-debug-ecr-docker-image/</guid><description>
&lt;p&gt;常常因为在 AWS 上部署的 ECS 或 EKS 服务，甚至是使用了 ECR 镜像的 Lambda 服务这样或那样的原因无法启动，或其他莫名的异常，这时候最好能直接调试 ECR 上的 Docker 镜像，比调试用于打包 Docker 的源代码更接近真实环境。&lt;/p&gt;
&lt;p&gt;要调试 Docker 镜像需要先从 ECR 中下载到 AWS 服务用的镜像，下面以运行 Java 的 Docker 为例，同时用 IntelliJ IDEA 关联源代码进行远程调试。&lt;/p&gt;
&lt;p&gt;从 ECR 下载 Docker 镜像部分可参考 &lt;a href="https://yanbin.blog/push-docker-image-to-amazon-ecr-repository/"&gt;推送 Docker 镜像到 Amazon ECR 仓库&lt;/a&gt;, 那篇文章写作之时可能与现在略有不同。具体需直接进到 ECR 的页面，如 https://console.aws.amazon.com/ecr/repositories/private/&amp;lt;aws_account_id&amp;gt;/&amp;lt;ecr_name&amp;gt;?region=&amp;lt;region&amp;gt;，点击 &lt;code&gt;View push commands&lt;/code&gt; 可看到用 AWS CLI 如何登陆 ECR，现在看到的在 macOS/Linux 下的命令是(假设 AWS AccountId 是 123456789, region 是 us-east-1)&lt;/p&gt;
&lt;blockquote&gt;
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
&lt;/blockquote&gt;
&lt;p&gt;假如 ECR 仓库名是 test-java, 那么要下载 tag 1.0.1 的命令是</description></item><item><title>Python 子进程与子进程池的应用</title><link>https://yanbin.blog/python-subprocess-process-pool/</link><pubDate>Thu, 02 Sep 2021 20:44:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-subprocess-process-pool/</guid><description>
去年记录过一篇如何使用 Python 的线程，线程池的日志 &lt;a href="https://yanbin.blog/python-programming-with-threads/"&gt;Python 多线程编程&lt;/a&gt;, 需用到 threading.Thread, concurrent.futures.ThreadPoolExecutor。本文可以当作是上一文 Python 多线程编程的姊妹篇。&lt;br/&gt;&lt;br/&gt;
Python 的多线程受到 GIL(Global Interpreter Lock) 的限制，GIL 是一把加到了 Python 的解释器的锁，使得在任意时刻只允许一个 Python  进程使用 Python 解释器，也就是任意时刻，Python 只有一个线程在运行。&lt;br/&gt;&lt;br/&gt;
GIL 严重影响了计算密集型(CPU-bound) 的多线程程序，此时的多线程与单线程性能没什么差异，也发挥不了多核的威力。但对 I/O 密集型(I/O-bound) 影响不大，因为 CPU 多数时候是在等待。&lt;br/&gt;&lt;br/&gt;
为了突破 GIL 的 CPU 密集型程序的限制，可以使用非 CPython 解释器，如 Jython, IronPython 或 PyPy, 更为现实的做法就是使用子进程来替代线程去承担较为繁重的计算任务，因为 GIL 是加在进程上的，所以新的进程有独立的 GIL.</description></item><item><title>Python 调用动态库时 Segmentation fault (core dumped) 问题</title><link>https://yanbin.blog/python-call-shared-object-segmentation-fault-core-dumped/</link><pubDate>Mon, 23 Aug 2021 22:07:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-call-shared-object-segmentation-fault-core-dumped/</guid><description>
这几天一直纠缠在如何调用动态库的问题上，先是 Go 语言，而后迁移到 Python 语言。在测试 Python 调用动态库时，出现过 "Segmentation fault (core dumped)" 的问题，本文记录下怎么去寻找线索，找到并解决问题的。&lt;br/&gt;&lt;br/&gt;
出现 "Segmentation fault (core dumped)" 的原因是多方面的，比如在 C/C++ 语言中&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;内存访问越界(数组越界，strcpy, strcat, sprintf, strcmp 等字符串函数读写越界)&lt;/li&gt;
&lt;li&gt;多线程使用了线程不安全的函数&lt;/li&gt;
&lt;li&gt;多线程读写的数据未加锁保护&lt;/li&gt;
&lt;li&gt;非法指针(NULL 指针，随意的指针类型转换&lt;/li&gt;
&lt;li&gt;堆栈溢出(如大的分配在栈上的局部变量)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
用 Python 来调用动态库很大的可能性会是内存访问越界</description></item><item><title>Python 调用 C 动态库(Linux)</title><link>https://yanbin.blog/python-call-c-shared-library-linux/</link><pubDate>Mon, 23 Aug 2021 11:49:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-call-c-shared-library-linux/</guid><description>
&lt;p&gt;&lt;a href="https://yanbin.blog/go-invoke-c-dylib-linux/"&gt;Go 调用 C 写的动态库完整例子(Linux版)&lt;/a&gt; 弄完了 Go 语言如何调用动态库，又开始琢磨起 Python 怎么调用动态库，首先仍然是以前一篇中的 C 实现为例，C 函数为原型为 &lt;code&gt;char * Add(char* src, int n)&lt;/code&gt;, 由于用符号直接定位函数，所以无需 C 的头文件。本文仍然是以 Linux 平台为例，GCC 编译为动态库 so 文件。并实验了两个例子，一个为基本的类型，char* 和  int, 再一个就是在 C 中使用到了结构体指针和无类型指针(void*) 时，如何在 Python 进行调用。&lt;/p&gt;
&lt;p&gt;测试环境为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Linux Ubuntu 20.04&lt;/li&gt;
&lt;li&gt;gcc: gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0&lt;/li&gt;
&lt;li&gt;Python: 3.8.10&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Go 运行期加载 C 动态库(Linux版)</title><link>https://yanbin.blog/go-load-c-dynamic-library-programmatically/</link><pubDate>Tue, 17 Aug 2021 21:13:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-load-c-dynamic-library-programmatically/</guid><description>
前面写的一篇 &lt;a href="https://yanbin.blog/go-invoke-c-dylib-linux/"&gt;Go 调用 C 写的动态库完整例子(Linux版)&lt;/a&gt;，是在告诉编译器用 /* #cgo ...*/ 的方式去加载动态库 &lt;code&gt;libadd.so&lt;/code&gt;，这让代码丧失了一定的灵活性，比如同样的函数由多个动态库提供了不同的实现。这就需要做到在 Go 程序中可根据不同的输入条件选择不同的动态库实现，大概是&lt;br/&gt;
&lt;blockquote&gt;
if 条件1 {&lt;br /&gt;
    loadLibrary("libadd1.so")&lt;br /&gt;
    调用其中的实现函数 add&lt;br /&gt;
else if 条件 2 {&lt;br /&gt;
    loadLibrary("libadd2.so")&lt;br /&gt;
    调用其中的实现函数 add&lt;br /&gt;
else {&lt;br /&gt;
    loadLibrary("libaddx.so")&lt;br /&gt;
    调用其中的实现函数  add
&lt;/blockquote&gt;
当然上面那样写是不行的，首先每一个动态库应该在程序运行期间只加载一次，定位的函数应该要缓存起来复用。</description></item><item><title>Go 语言使用 Go Modules(go.mod) 来管理依赖</title><link>https://yanbin.blog/go-use-go-mod-manage-dependencies/</link><pubDate>Tue, 17 Aug 2021 15:08:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-use-go-mod-manage-dependencies/</guid><description>
前几日系统性差不多读完了一本讲解 Go 语言的书籍，记录下几篇笔记，现在终于能够开始看看专题性的知识了。首先就是关于 Go 如何管理依赖的问题，Java 经历了最早逐个下载  jar  包，到现在用 maven 来描述项目依赖，及进行项目的构建。而 Go 的起点还是要高一些，从一开始就有  &lt;code&gt;go get&lt;/code&gt;, &lt;code&gt;go install&lt;/code&gt; 命令来从中心库下载依赖。但管理多版本依赖是个问题，也没有明确的方式来怎么描述一个项目的依赖。&lt;br/&gt;&lt;br/&gt;
因此也就产生过一些第三方的 Go 依赖管理&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://glide.readthedocs.io/"&gt;Glide&lt;/a&gt;(使用 glide.yaml 文件描述依赖)&lt;/li&gt;
&lt;li&gt;&lt;a href="github.com/tools/godep"&gt;Godep&lt;/a&gt; (长时间没维护的项目)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kardianos/govendor"&gt;govendor&lt;/a&gt; (不再维护了，在 Go modules 出来后也不被推荐使用)&lt;/li&gt;
&lt;li&gt;还有其他的 &lt;a href="https://github.com/pote/gpm"&gt;GPM&lt;/a&gt;, &lt;a href="https://github.com/constabulary/gb"&gt;gb&lt;/a&gt;, &lt;a href="https://github.com/mattn/gom"&gt;gom&lt;/a&gt;, &lt;a href="https://github.com/FiloSottile/gvt"&gt;gvt&lt;/a&gt; 等都相继退出了历史的舞台&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
也就是说 Go Modules 才是王道，在理解 Go Modules 之前也许有必要了解一下传统 GOPATH 管理依赖的方式，其他第三方的管理工具用不着去学习了，早已成了云烟。&lt;br/&gt;&lt;br/&gt;
Go 语言首次在  1.11 版本中引进了 Go Modules, 在这之前或者没有启用 GO111MODULE 的话，我们用 go get 或 go install 来下载依赖，编译器会从 GOPATH 和 vendor 文件夹中查找包。</description></item><item><title>Go 调用 C 写的动态库完整例子(Linux版)</title><link>https://yanbin.blog/go-invoke-c-dylib-linux/</link><pubDate>Mon, 16 Aug 2021 12:02:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-invoke-c-dylib-linux/</guid><description>
总有那么一些老的，或高效的库是用 C/C++ 实现的，于是在其他语言中如果使用动态共享库就成了个问题。Java 要调用动态库需要用 JNI, 更快捷的话可使用第三方包装好的 JNI 调用库。在 Java 中要映射 C/C++ 的类型麻烦些，因为 Java 没有指针类型，所以从这方面来讲 Go 调用动态库幸许会更简单些。&lt;br/&gt;&lt;br/&gt;
下面我们自己在 Linux 下做一个动态库(.so 文件  - &lt;strong&gt;S&lt;/strong&gt;hared &lt;strong&gt;O&lt;/strong&gt;bject)，然在用 Go 来使用它。本文所用的操作系统为 Ubuntu20.04, 以 gcc  作为编译器。动态库的生成过程参考自 &lt;a href="https://www.cnblogs.com/jiqingwu/p/linux_dynamic_lib_create.html"&gt;Linux动态库生成与使用指南&lt;/a&gt;</description></item><item><title>Go 语言新手笔记（五）</title><link>https://yanbin.blog/go-language-notes-5/</link><pubDate>Sun, 15 Aug 2021 08:14:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-language-notes-5/</guid><description>
终于来到的 Go 的网络编程了，来写一个 TCP 服务端与客户端的程序。要用到 Go 语言的  net 包，是一个标准的 Listen+Accept 结构, 下面是一个简单的 TCP Server/Client 端的例子，启动了 Server 端口，可以用 telnet 去连接，也可以用 client.go 来连接</description></item><item><title>Go 语言新手笔记（四）</title><link>https://yanbin.blog/go-language-notes-4/</link><pubDate>Sat, 14 Aug 2021 13:32:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-language-notes-4/</guid><description>
学到了 Go 语言本身的语法之后，开始步入 Go 的应用部分，掌握一个语言不得不接触的就是它对文件，网络的操作。&lt;/p&gt;
&lt;br/&gt;
在正式进入主题之前不得不抱怨一下 Go 的官方编码规范中的代码缩进格式。曾经 Google 出的 Java 缩进规范是用两个空格(通常是用四个空格)，并且是杜绝用  Tab 进行缩进的，因为 Tab 的宽度是不很确定的。&lt;br/&gt;&lt;br/&gt;
然而 Google 建议 Go 的代码格式化工具  gofmt 却反常规的强制使用 Tab 进行缩进，而且是。本人用 IntelliJ IDEA 编写 Go, 即使在 Go 的 Code Style 中把 &lt;code&gt;Use tab character&lt;/code&gt; 去掉也是用 Tab 缩进，原因是在 Code Style &amp;gt; Go 的  Other 中有项 Run gofmt [] On code reformat, 这项也不能勾选。&lt;br/&gt;&lt;br/&gt;
不明白 &lt;code&gt;go fmt&lt;/code&gt; 或 &lt;code&gt;gofmt&lt;/code&gt; 为何强势而无理的用 Tab 进行缩进，看到多数地方以 8 个空格来显示一个  Tab，实在是难看，像下面这样子的</description></item><item><title>Go 语言新手笔记（三）</title><link>https://yanbin.blog/go-lanague-notes-3/</link><pubDate>Sat, 14 Aug 2021 00:24:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-lanague-notes-3/</guid><description>
终于要开始了解 Go 的结构体和接口了。Go 的结构体只是一种纯粹的数据类型，而不像 C/C++ 的结构体里还能添加方法。Go 的 struct 更像是 Python 的 dataclass, 或 Java 的 record。Go 的结构体是值类型，通过 new 函数来创建，在 C/C++ 中，只要是 new 得到的就是指针。&lt;/p&gt;
&lt;br/&gt;
结构体的字段名称也可以用 &lt;code&gt;_&lt;/code&gt;, 相当一个点位填充，字段也可以只有类型没有名称，称之为匿名字段。</description></item><item><title>Go 语言新手笔记（二）</title><link>https://yanbin.blog/go-language-notes-2/</link><pubDate>Fri, 13 Aug 2021 15:22:01 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-language-notes-2/</guid><description>
Go 语方的数组声明方式别具一格，把 [] 看得太重了，看下面的各种声明方式&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这是一个数组，如果省略 ..., 写成 []int{18, 20} 就是一个 slice&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;//指定位置初始化&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... 可省略&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// d 的类型是 [20]int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// e 的类型是 *[20]int, 这是一个数组指针&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 多维数组&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 类似其他语言一样，只有第一维才能用 [...]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
再次强调 Go 的数组是值类型，作为函数参数传递会产生副本。避免传入数组产生副本消耗过多内存，办法是可传数组指针或用切片，切片是第一选择。&lt;br/&gt;&lt;br/&gt;
数组的大小最大为 2GB，用 &lt;code&gt;==&lt;/code&gt; 或 &lt;code&gt;!=&lt;/code&gt; 比较两个数组时它们必须类型和长度一致。</description></item><item><title>Go 语言新手笔记（一）</title><link>https://yanbin.blog/go-language-notes-1/</link><pubDate>Fri, 13 Aug 2021 00:35:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/go-language-notes-1/</guid><description>
Go 语言 2009 年面世，几个成功项目 Docker, Kubernetes, etcs, consul, flannel, 还有 Terraform。Go 编译运行快，听说学习也不难。&lt;/p&gt;
安装好 Go 后，有两个环境变量很重要， GOPATH 是工作目录，GOROOT 是 Go 的安装目录, 如 /usr/local/go。GOPATH 允许多个目录，用 : 或 ;(Windows 下) 分隔，当 GOPATH 有多个目录，go get 命令的包放在第一个目录下。&lt;br/&gt;&lt;br/&gt;
$GOPATH 目录中约定的三个子目录&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;src: 存放 .go, .c, .h, .s 等源码文件，go run, go install 等命令也要在该目录下执行&lt;/li&gt;
&lt;li&gt;pkg: 存放编译时生成的中间文件，如 .a&lt;/li&gt;
&lt;li&gt;bin: 存放编译后的可执行文件 &lt;/li&gt;
&lt;/ol&gt;
Go 编程是声明了不用的全局变量是绝对的罪过，编译这一关过不了。Go 用 var 声明变量，如 var b bool = true。Go 有精确长度的丰富的基本类型，如 int8, int16, int32, int64, unit8, float32(6位精度), float64(15位精度) 等，单纯写成  int, unit 时，长度与系统有关。Go 的字符串用 UTF-8 格式编码的 Unicode 字符，每个字符对应一个 rune 类型，rune 是字符的意思，相当于 int32.</description></item><item><title>pm2 使用笔记</title><link>https://yanbin.blog/pm2-notes/</link><pubDate>Thu, 12 Aug 2021 10:20:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/pm2-notes/</guid><description>
PM 2 是什么，官网上 &lt;a href="https://pm2.keymetrics.io/"&gt;https://pm2.keymetrics.io/&lt;/a&gt;
是说 PM2(Process Manager 2) 是 Node JS 的高级的，产品级的进程管理工具。为什么叫做 PM2，那有没有 PM 或 PM 1 呢，没有，它起步就是
2. PM2 相对于 Node JS 有点类似于 &lt;a href="https://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; 和 Python Web 应用的关系。
但 PM2 比 Gunicorn 功能更强大, 其实 PM2 还能用来管理其他任何进程，不局限于 Node JS 的。&lt;br/&gt;&lt;br/&gt;
最好的入门学习教程是官方的 &lt;a href="https://pm2.keymetrics.io/docs/usage/quick-start/"&gt;Quick Start&lt;/a&gt;。从中我们可以大概看到 PM2 的主要应用：&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;对进程的管理与监控&lt;/li&gt;
&lt;li&gt;多种进程重启策略&lt;/li&gt;
&lt;li&gt;日志的管理&lt;/li&gt;
&lt;li&gt;使用配置文件&lt;/li&gt;
&lt;li&gt;集群模式&lt;/li&gt;
&lt;li&gt;用作静态 Web 服务&lt;/li&gt;
&lt;li&gt;进行应用部署&lt;/li&gt;
&lt;li&gt;使用环境变量&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
PM2 是 Node JS 应用，所以它是跨平台的。</description></item><item><title>Docker Compose In Practice</title><link>https://yanbin.blog/docker-compose-in-practice/</link><pubDate>Thu, 24 Jun 2021 13:49:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-compose-in-practice/</guid><description>
Continue to advance to Kubernetes, the last article &lt;a href="https://yanbin.blog/english-docker-swarm-in-action/"&gt;Docker Swarm In Action&lt;/a&gt;, After understanding Swarm, it is necessary to get familiar with Docker Compose. Docker Swarm forms a cluster of Docker hosts. As long as the Manager node is notified when the service is deployed, it will automatically find the corresponding node to run containers. Compose is another concept entirely. It organizes multiple associated containers into a whole for deployment, such as a load balance container, multiple web containers, and a Redis cache container to make up a whole bundle.&lt;br/&gt;&lt;br/&gt;
Revealed by its name, Compose, the concept of container orchestration was officially established. Later, Kubernetes, which we will learn, it's a tool for higher-level organization, operation and management of containers. Because Compose organizes the containers, it can start multiple associated containers with one command, instead of starting one container separately.&lt;br/&gt;&lt;br/&gt;
Regarding the installation of Docker Compose, Docker Desktop under Mac OS X comes with docker-compose; since Docker Compose is written in Python, we can use  &lt;code&gt;pip install docker-compose&lt;/code&gt;  to install it, and use its commands after installation &lt;code&gt;docker-compose&lt;/code&gt;.</description></item><item><title>Docker Swarm In Action</title><link>https://yanbin.blog/english-docker-swarm-in-action/</link><pubDate>Thu, 24 Jun 2021 13:11:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/english-docker-swarm-in-action/</guid><description>
Before officially entering into Kubernetes, I hope to know something about the earlier Docker Swarm, although it is basically no long used currently. Swarm provides a cluster of Docker hosts. There is at least one Manager (or more) in the cluster, plus 0 or more Workers. The main idea is that a Docker service (container) is executed by the Swarm cluster manager, and it will find the corresponding host in the cluster to execute the container. Both Manager and Worker can to run Docker containers, but only Manager can execute management commands.&lt;/p&gt;
&lt;br/&gt;
The following two pictures shows the difference between running Docker containers on a host and a Swarm cluster host.&lt;br/&gt;&lt;br/&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2021/english-docker-swarm-in-action/docker-2.png"
width="256px"
/&gt;
Run all containers in one host&lt;/td&gt;
&lt;td&gt;  =&amp;gt;&lt;/td&gt;
&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2021/english-docker-swarm-in-action/docker_swarm.png"
width="256px"
/&gt;
Run container together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
&lt;p data-wp-editing="1"&gt;</description></item><item><title>Introduce Vagrant and common usages</title><link>https://yanbin.blog/english-vagrant-intro-config-commands/</link><pubDate>Thu, 24 Jun 2021 12:18:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/english-vagrant-intro-config-commands/</guid><description>
Many of my demos about Kafka, Docker, Python, Kubernates, and etc. are made with Vagrant virtual machines. It is time to write a blog for some frequently used Vagrant commands. Vagrant is a member of the HashiCorp family. Others of HashiCorp's famous tools include  &lt;a href="https://learn.hashicorp.com/terraform"&gt;Terraform&lt;/a&gt; , &lt;a href="https://learn.hashicorp.com/consul"&gt;Consul&lt;/a&gt; ,  &lt;a href="https://learn.hashicorp.com/vault"&gt;Vault&lt;/a&gt; , &lt;a href="https://learn.hashicorp.com/boundary"&gt;Boundary&lt;/a&gt; , &lt;a href="https://learn.hashicorp.com/packer"&gt;Packer&lt;/a&gt; , &lt;a href="https://learn.hashicorp.com/nomad"&gt;Nomad&lt;/a&gt; and &lt;a href="https://learn.hashicorp.com/waypoint"&gt;Waypoint&lt;/a&gt; . &lt;br/&gt;&lt;br/&gt;
Speaking of Vagrant, I have to mention the similar Docker, in fact, they are quite different, while they both give people the external feeling that they are command line control Linux. Vagrant is essentially a virtual machine shell, allowing us to use Vagrant commands to interact with the virtual machine more conveniently, instead of switching back and forth between the host machine and the virtual machine, it is more convenient to manage multiple virtual machines in one single terminal; while Docker is a container, The essence of a container is a process on the host machine, but it is isolated from the file system, process, network, etc. of the process with a namespace, making the container process look like a virtual OS.&lt;br/&gt;&lt;br/&gt;
Vagrant is a tool for the development environment, and Docker is a tool for the deployment environment; Vagrant operates a standard Linux or Windows operating system, and the Docker is very critical on image size. Docker image is usually a trimmed system, just has necessary command to run our service. Since Vagrant corresponds to a virtual machine, the operation status with Vagrant and the installed software will be retained after Vagrant virtual machine shutdown, while the operations status in Docker are all for the current container (copy-on-write), which does not affect the corresponding image , Unless it is committed as a new image with docker commit.&lt;br/&gt;&lt;br/&gt;
Understand that Vagrant is just the shell of a virtual machine, so it requires different virtual machine implementations, such as VirtualBox, Hyper-V, VMware, etc., and we can use Vagrant to interact with Docker as well. Vagrant can support multiple Operation Systems.</description></item><item><title>AWS Session Manager connect to EC2 instance</title><link>https://yanbin.blog/english-aws-session-manager-ec2-instance/</link><pubDate>Thu, 24 Jun 2021 10:01:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/english-aws-session-manager-ec2-instance/</guid><description>
The most common way to manage a remote machine is SSH (Unix/Linux, Mac) or PowerShell/RDP (Windows), which requires the remote machine to open the corresponding access port and firewall, credentials or SSH Key. When selecting an EC2 instance on AWS console, we can click the "Connect" button, which provides three connection options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EC2 Instance Connect: Requires EC2 to be configured with SSH Key, sshd is started, ssh inbound port allowed by Security Group, &lt;code&gt;ec2-instance-connect&lt;/code&gt; installed(sudo yum install ec2-instance-connect)&lt;/li&gt;
&lt;li&gt;Session Manager: This is what we are going to talk about next. sshd is not required(SSH key is not needed of cause). Security Group only requires outbound port 443. &lt;/li&gt;
&lt;li&gt;SSH client: Client SSH to EC2 instance, start sshd, allow inbound ssh port 22 by Security Group, use SSH Key or username and password in AMI, or configure to login with domain account after joining the domain.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
AWS Session Manager provides access to EC2 instances through a browser or AWS CLI, and even machines or virtual machines in the local datacenter (requires advanced-instances tier support) , and no longer depends on SSH.&lt;span id="more-10627"&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;Session Manager Overview&lt;/h3&gt;
Session Manager determines who can or cannot be accessed by the IAM access policy. It can be forwarded through the local port, the operation log in the session can be recorded as an audit, and can configure to send a message to Amazon EventBridge or SQS when session open or closed. The session log encrypted by a KMS key.</description></item><item><title>为 Java 注册 classpath: 协议用 URL 读取文件</title><link>https://yanbin.blog/register-classpath-protocol-for-java/</link><pubDate>Thu, 17 Jun 2021 12:50:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/register-classpath-protocol-for-java/</guid><description>
本文为 Java 注册 classpath 协议读取文件的目的就是要让下面的代码能工作起来&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IOUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;classpath:/db.properties&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
假设在 classpath 下有个文件 db.properties, 比如在 Maven 项目的 src/main/resources 目录中，或是在某个 jar 包的根位置。如果我们直接执行上面的代码将会得到异常&lt;br/&gt;
&lt;blockquote&gt;
Exception in thread "main" java.net.MalformedURLException: unknown protocol: classpath&lt;br /&gt;
    at java.net.URL.&amp;lt;init&amp;gt;(URL.java:617)&lt;br /&gt;
    at java.net.URL.&amp;lt;init&amp;gt;(URL.java:507)
&lt;/blockquote&gt;
说是不认识的 classpath 协议。&lt;br/&gt;&lt;br/&gt;
前面代码是有实际用途的，比如说我们使用 XML 时就能支持远程协议&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DocumentBuilderFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;newDocumentBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://www.w3schools.com/xml/note.xml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDocumentElement&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getTagName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Java 10 ~ 16 一路向前冲(新特性一箩筐)</title><link>https://yanbin.blog/java-10-16-new-features/</link><pubDate>Sat, 08 May 2021 00:06:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-10-16-new-features/</guid><description>
Java 一路突突突， 版本 16 在 2021-03-16 都发布了, 而我们一直碍于 Java 9 的大改还在 Java 8 上原地踏步, 以往每当有新版本 JDK 发布后都是很快就验证，立马升级。&lt;a href="https://www.codejava.net/java-se/java-se-versions-history"&gt;Java SE versions history&lt;/a&gt;) 列出了所有 Java 的历史版本的发布日期。在今天(2021-05-04) 网站 &lt;a href="https://www.oracle.com/java/technologies/javase-downloads.html"&gt;Java SE Downloads&lt;/a&gt; 上直接提供下载的 Java SE 版本有以下三&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Java SE 16.0.1&lt;/li&gt;
&lt;li&gt;Java SE 11.0.11(LTS)&lt;/li&gt;
&lt;li&gt;Java SE 8u291&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
两个 LTS(长期支持) 版 8 和  11，外加一个目前最新的非  LTS 版本 16, 其他的版本都被归档到了 &lt;a href="https://www.oracle.com/java/technologies/oracle-java-archive-downloads.html"&gt;Java Archive&lt;/a&gt;. 查看一下 &lt;a href="https://www.oracle.com/java/technologies/java-se-support-roadmap.html"&gt;Java 支持的 roadmap&lt;/a&gt;, 几个 LTS 版本的服务支持年限&lt;br/&gt;
&lt;blockquote&gt;
版本本      发布日       原定支持至      延期支持至&lt;br /&gt;
Java 8      2014/3       2022/3             &lt;span style="color: #800000;"&gt;&lt;strong&gt;2030/12&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;
Java 11     2018/9      2023/9              2026/9&lt;br /&gt;
Java 17     2021/9      2026/9              2029/9
&lt;/blockquote&gt;
注意到 Java 8 将比 Java 11 和将来的 Java 17 生命力还顽强，一下就觉得这么久坚守在 Java 8 的阵地上不应觉得有什么好害羞的。眼看着下一个 LTS 版本的 Java 17 就要在今年 9 月份发布了，Java 11 看来是要错过了，等准备好和 Java 8 告别时要直接跳到 Java 17 了。</description></item><item><title>探索 Apache, Tomcat, SpringBoot 对请求数据的解压缩</title><link>https://yanbin.blog/apache-tomcat-springboot-decompress-request-body/</link><pubDate>Tue, 04 May 2021 12:21:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-tomcat-springboot-decompress-request-body/</guid><description>
通常我们都会配置 Web 服务端对响应数据进行压缩，如用 Apache 的 mod_deflate 模块，或配置 Tomcat connector 启用压缩，又或者是在 Java Web 项目中加 Web Filter 来压缩特定的响应数据。这样客户端发送 HTTP 请求时在头中声明如 Accept-Encoding: gzip，服务端就可能会对响应数据进行压缩，同时带上 Content-Encoding: gzip 响应头。&lt;br/&gt;&lt;br/&gt;
有时候 HTTP Post 的数据太大同样会要求客户端在传输数据之前对请求数据进行压缩，本文主要关注服务端如何自动解压客户端发出的压缩数据。&lt;br/&gt;&lt;br/&gt;
先以 Apache2 为例，以 Ubuntu 20.04 为例，用命令 &lt;code&gt;apt-get install apache2&lt;/code&gt; 安装 Apache 2.4.41, 它自动启用了 &lt;a href="https://httpd.apache.org/docs/current/mod/mod_deflate.html"&gt;mod_deflate&lt;/a&gt; 模块。mod_deflate 模块的配置文件 /etc/apache2/modes-enabled/deflate.conf 内容如下&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apache" data-lang="apache"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class="s"&gt;mod_deflate.c&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;IfModule&lt;/span&gt; &lt;span class="s"&gt;mod_filter.c&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AddOutputFilterByType&lt;/span&gt; DEFLATE text/html text/plain text/xml text/css
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AddOutputFilterByType&lt;/span&gt; DEFLATE application/x-javascript application/javascript application/ecmascript
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AddOutputFilterByType&lt;/span&gt; DEFLATE application/rss+xml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AddOutputFilterByType&lt;/span&gt; DEFLATE application/xml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/IfModule&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
它表示只对以上特定的响应数据类型进行压缩，下面来测试下对 html 内容的压缩</description></item><item><title>Python 转换 Apache Avro 数据为 Parquet 格式</title><link>https://yanbin.blog/python-convert-avro-to-parquet/</link><pubDate>Sat, 01 May 2021 00:11:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-convert-avro-to-parquet/</guid><description>
前面尝试过用 Java 转换 Apache Avro 数据为 Parquet 格式，本文用 Python 来做同样的事情，并且加入 logicalType: date 类型的支持。本测试中的 Avro 数据也是由 Python 代码生成的。&lt;br/&gt;&lt;br/&gt;
重复一句 Avro 与 Parquet 的最粗略的区别：Avro 广泛的应用于数据的序列化，如 Kafka，它是基于行的格式，可被流式处理，而 Parquet 是列式存储格式的，适合于基于列的查询。&lt;br/&gt;&lt;br/&gt;
第一步，生成 Avro 数据文件 user.avro, 须先安装 fastavro&lt;br/&gt;
&lt;blockquote&gt;
pip install fastavro
&lt;/blockquote&gt;
&lt;h3&gt;生成 user.avro 的代码&lt;/h3&gt;</description></item><item><title>Vagrant 简介与常用操作及配置</title><link>https://yanbin.blog/vagrant-intro-config-commands/</link><pubDate>Fri, 30 Apr 2021 12:24:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vagrant-intro-config-commands/</guid><description>
前方许多有关于 Kafka, Docker, Python 和 Kubernates 的文章都是在 Vagrant 虚拟机中做的 Demo，经常用到的一些 Vagrant 命令是时候有必要写篇日志记录下来。Vagrant 是 HashiCorp 家族中的一员，HashiCorp 旗下著名的工具还有  &lt;a href="https://learn.hashicorp.com/terraform"&gt;Terraform&lt;/a&gt;, &lt;a href="https://learn.hashicorp.com/consul"&gt;Consul&lt;/a&gt;,  &lt;a href="https://learn.hashicorp.com/vault"&gt;Vault&lt;/a&gt;, &lt;a href="https://learn.hashicorp.com/boundary"&gt;Boundary&lt;/a&gt;, &lt;a href="https://learn.hashicorp.com/packer"&gt;Packer&lt;/a&gt;, &lt;a href="https://learn.hashicorp.com/nomad"&gt;Nomad&lt;/a&gt; 和 &lt;a href="https://learn.hashicorp.com/waypoint"&gt;Waypoint&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;
说起 Vagrant，不得不提起与之仿佛类似的 Docker，其实它们相差还是比较大的，只因它们给人的外在感觉都是命令行控制 Linux。Vagrant 实质是一个虚拟机的外挂，让我们更方便的用 Vagrant 命令与虚拟机交互，而不用在宿主机与虚拟机间来回切换，管理多个虚拟机就更得心应手了; 而 Docker 是一个容器，容器的本质是宿主机上的一个进程，只是用命名空间与该进程的文件系统，进程，网络等进行了隔离，使得该容器进程看似一个虚拟  OS。&lt;br/&gt;&lt;br/&gt;
Vagrant 是开发环境的部署工具, 而 Docker 是运行环境的部署工具; Vagrant 操作的是一个标准的 Linux 或  Windows 操作系统，而 Docker 的镜像考虑到发布服务的个头，通常是一个裁剪的系统，去除了服务器非必要的命令。既然 Vagrant 对应的是虚拟机，那么在 Vagrant 中的操作，安装的软件在 Vagrant 退出后都会保留下来，而 Docker 中操作的都是当前容器(copy-on-write)，并不影响所对应的镜像, 除非用 docker commit 固化为新的镜像.&lt;br/&gt;&lt;br/&gt;
明白了 Vagrant 只是一个虚拟机的皮，那他在不同的硬件平台或操作系统下需要与不同的 Provider, 如 VirtualBox, Hyper-V, VMware 等配合工作，还能用 Vagrant 来操作 Docker。&lt;br/&gt;&lt;br/&gt;
有了 Vagrant, 从此不再需要下载不同操作系统的 ISO 安装镜像文件，耗时的逐步安装操作系统，也不用手工的下载别人安装好并导出的虚拟机文件，一切有点类似 Docker 一样从远程公共仓库中选择系统即可。&lt;br/&gt;</description></item><item><title>使用 Java 转换 Apache Avro 为 Parquet 数据格式(依赖更新)</title><link>https://yanbin.blog/convert-apache-avro-to-parquet-in-java-use-hadoop-common-instead-of-hadoop-core/</link><pubDate>Thu, 25 Feb 2021 21:44:05 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/convert-apache-avro-to-parquet-in-java-use-hadoop-common-instead-of-hadoop-core/</guid><description>
在上篇 &lt;a href="https://yanbin.blog/convert-apache-avro-to-parquet-format-in-java/"&gt;使用 Java 转换 Apache Avro 为 Parquet 数据格式&lt;/a&gt;
实现把 Avro 数据转换为 Parquet 文件或内存字节数组，并支持 LogicalType。其中使用到了 hadoop-core 依赖，注意到它传递的依赖都非常老旧，
到官方 Maven 仓库一看才发现还不是一般的老&lt;/p&gt;
&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2021/convert-apache-avro-to-parquet-in-java-use-hadoop-common-instead-of-hadoop-core/avro-to-parquet-1-1.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2021/convert-apache-avro-to-parquet-in-java-use-hadoop-common-instead-of-hadoop-core/avro-to-parquet-1-1-800x133.png"
width="596px"
/&gt;
&lt;/a&gt;
长时间无人问津的项目，那一定有它的替代品。对啦，据说 hadoop-core 在 2009 年 7 月份更名为 hadoop-common 了，没找到官方说明，只看到 StackOverflow 的
&lt;a href="https://stackoverflow.com/questions/28856096/differences-between-hadoop-common-hadoop-core-and-hadoop-client#:~:text=Hadoop-core%20is%20the%20same,logging%20and%20codecs%20for%20example."&gt;
Differences between Hadoop-coomon, Hadoop-core and Hadoop-client?&lt;/a&gt; 是这么说的。 应该是这么个说法，不然为何 hadoop-core 一直停留在  1.2.1 的版本，
而且原来 hadoop-core 中的类在 hadoop-common 中可以找到，如类 org.apache.hadoop.fs.Path。不过在 hadoop-core-1.2.1 中的 &lt;code&gt;fs/s3&lt;/code&gt; 包不见，
这么重要的 s3 文件系统没了。</description></item><item><title>使用 Java 转换 Apache Avro 为 Parquet 数据格式</title><link>https://yanbin.blog/convert-apache-avro-to-parquet-format-in-java/</link><pubDate>Tue, 23 Feb 2021 23:39:06 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/convert-apache-avro-to-parquet-format-in-java/</guid><description>
Avro 和 Parquet  是处理数据时常用的两种编码格式，它们同为 Hadoop 大家庭中的成员。这两种格式都是自我描述的，即在数据文件中带有 Schema。
Avro 广泛的应用于数据的序列化，如 Kafka，它是基于行的格式，可被流式处理，而 Parquet 是列式存储格式的，适合于基于列的查询，不能用于流式处理。&lt;/p&gt;
&lt;br/&gt;
既然是一个系统中可能同时用到了这两种数据存储格式，那么就可能有它们之间相互转换的需求。本文探索如何从 Avro 转换为 Parquet 格式数据，以 Java 语言为例，所涉及到的话题有&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;转换 Avro 数据为 Parquet 文件&lt;/li&gt;
&lt;li&gt;如何支持 Avro 的 LogicalType 类型到 Parquet 的转换, 以 date 类型为例&lt;/li&gt;
&lt;li&gt;实现转换 Avro 数据为 Parquet 字节数组(内存中完成 Avro 到 Parquet 的转换)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
本文例子中所选择 Avro 版本是当前最新的 1.10.1</description></item><item><title>AWS Session Manager 管理 EC2 实例</title><link>https://yanbin.blog/aws-session-manager-ec2-instance/</link><pubDate>Tue, 16 Feb 2021 13:16:26 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-session-manager-ec2-instance/</guid><description>
管理一个远程机器最常规的做法是 SSH(Unix/Linux, Mac) 或 PowerShell/RDP(Windows)，这就要求远端机器要开通相应的访问端口及打开防火墙，配置好登陆用的用户名密码或 SSH Key。当选择一个 EC2 实例的时候，可以点击 "Connect" 按，它提供有三种连接选择：&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;EC2 Instance Connect: 要求 EC2 配置了 SSH Key, 启动了 sshd 并开启了 ssh 的 Security Group，还要在实例上安装了 &lt;code&gt;ec2-instance-connect&lt;/code&gt;(如安装命令 sudo yum install ec2-instance-connect)&lt;/li&gt;
&lt;li&gt;Session Manager: 这就是我们本文要讲述的，sshd 不用启动，Security Group 只要求能往连接外部的 443 端口，SSH Key 不需要&lt;/li&gt;
&lt;li&gt;SSH client: 客户端 SSH 到 EC2 实例，需要打开 sshd 其 22 号端口接受连接的 Security Group，用 SSH Key 或 AMI 中的用户名和密码，或配置加入了域后使用域帐号验证登陆&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
AWS 的 Session Manager 提供了通过浏览器或 AWS CLI 来访问 EC2 实例，&lt;span style="text-decoration: underline;"&gt;甚至是本地机房的机器或虚拟机(需 advanced-instances tier 的支持)&lt;/span&gt;，不再依赖于 SSH。</description></item><item><title>博客升级后的维护日志[2021-02-04]</title><link>https://yanbin.blog/blog-upgrade-maintenance-logs/</link><pubDate>Thu, 04 Feb 2021 21:27:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/blog-upgrade-maintenance-logs/</guid><description>
自去年 10 月底搬家后就基本没再写日志了，其间常登陆后台看到 WordPress 及其插件不停的有提示升级，每次都是能升就就升，至少前台的页面显示没多大问题。只有过一个小问题，左边最新评论的 Widget 显示不出东西来，把最后更新日期为 8 年前的 &lt;a href="https://wordpress.org/plugins/wp-recentcomments/"&gt;WP-RecentComments&lt;/a&gt; 插件变成了 &lt;a href="https://wordpress.org/plugins/decent-comments/"&gt;Decent Comments&lt;/a&gt; 后解决。&lt;/p&gt;
&lt;br/&gt;
昨天才开始琢磨着写一篇关于 Mockito Mock 静态方法的日志，才意识到一直以来不知拒绝的跟风似的升级 WordPress 主体和插件给后台带来了不得不面对的问题。&lt;br/&gt;&lt;br/&gt;
首先，传文件总是失败，在 Media 或 &lt;code&gt;Add Media&lt;/code&gt; 时也无法浏览图片，翻看 Apache2 的错误日志也没找到问题。无奈，手工通过 SCP 上传吧，一登陆到服务器上却发现即使界面提示失败，但文件实际上是在已上传到了服务器上了的。先这么着吧，切换编辑器到文本模式手工写 &amp;lt;img&amp;gt; 标签来引用图片。</description></item><item><title>Mockito 3.4.0 开始可 Mock 静态方法</title><link>https://yanbin.blog/mockito-3-4-0-mock-static-method/</link><pubDate>Wed, 03 Feb 2021 13:36:23 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-3-4-0-mock-static-method/</guid><description>
Java 单元测试最趁手的 Mock 组件当属 Mockito，虽然它最初是基于继承来实现 Mock 的，所以对私有方法，私有属性，静态方法，
final 类，final 方法，构造函数无能为力。于是有时不得不引入 JMockit 或 PowerMockit 来辅助。不过现在的 Mockito 功力有所增强。&lt;br/&gt;&lt;br/&gt;
首先是 Mockito 2.1.0 开始可以 Mock final 类和 final 方法，要在 classpath 下创建个文件
&lt;code&gt;mockito-extensions/org.mockito.plugins.MockMaker&lt;/code&gt;, 内容为 &lt;code&gt;mock-maker-inline&lt;/code&gt;。
之前写过一篇介绍：&lt;a href="https://yanbin.blog/mockito-mock-final-class-final-method/"&gt;Mockto 也能 Mock final 类和 final 方法了&lt;/a&gt;，其中也探索了它的实现细节，使用到了 ByteBuddy 修改字节码。&lt;br/&gt;&lt;br/&gt;
从 &lt;a href="https://github.com/mockito/mockito/blob/caac3535daaeb9370cd25f23694d3d059a72d8d9/doc/release-notes/official.md#340"&gt;Mockito 3.4.0&lt;/a&gt;
通过类似的 mockto-extensions 扩展的方式，实现了对静态方法的 Mock。所有使用到的接口是 org.mockito.MockedStatic，
它当前在 Mockito 3.7.7 中还是一个试验性方法 @Incubating，能拿来用就行。</description></item><item><title>从 Notbook 到 JupyterLab, 再配上代码帮手 Kite</title><link>https://yanbin.blog/from-notbook-to-jupyterlab-with-kite/</link><pubDate>Tue, 13 Oct 2020 22:42:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/from-notbook-to-jupyterlab-with-kite/</guid><description>
又一月有半未落下一个符号了，越来越喜爱用 Python 来高效的辅助日常工作了。先前在 Shell 下使用 Python 代码，因为 Vim 可以配置 Kite 进行代码的自动完成。很早就知道 Jupyter Notebook, 但觉得它还不够漂亮，又没代码提示，也就一直没理会它，后来看到 Kite 可以支持 &lt;a href="https://jupyter.org/"&gt;JupyterLab&lt;/a&gt;, 一启动 JupyterLab 的那一瞬间，果然比 Notebook 高大上了许多。有了 Kite  加持，写代码更是顺畅的多，当然还飞不起来。Notebook  应该是一个更经典的工具，它的一系列插件还得稍稍移植到 JupyterLab 上去才能用。&lt;br/&gt;&lt;br/&gt;
本文快速体验一下 JupyterLab, 从自身到几个基本插件的安装。本人使用的操作系统是 Mac OS X, 刚开始用 &lt;code&gt;python -m venv jupyterlab-venv&lt;/code&gt; 创建的虚拟环境中用 &lt;code&gt;pip&lt;/code&gt; 来安装，后来发现不知何时在我的 Python 3.8 的 bin 目录中居然安装有 jupyter 和 jupyter-lab 命令，&lt;code&gt;pip install jupyterlab&lt;/code&gt; 后 &lt;code&gt;jupyter-lab --version&lt;/code&gt; 和 &lt;code&gt;jupyter lab --version&lt;/code&gt; 显示的是不同版本</description></item><item><title>Python 处理 JSON 必要时我选择 ujson 和 orjson</title><link>https://yanbin.blog/python-json-choose-ujson-if-necessary/</link><pubDate>Fri, 21 Aug 2020 00:06:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-json-choose-ujson-if-necessary/</guid><description>
在 Python 使用用  &lt;code&gt;json.dumps(data)&lt;/code&gt; 时突然发现特别慢，&lt;code&gt;data&lt;/code&gt; 本身不大，但是一个包含很多元素的列表，所以促使本人寻找一个替代的 JSON 处理库。大概对比了一个 ujson(&lt;a href="https://github.com/ultrajson/ultrajson"&gt;UtltraJSON&lt;/a&gt;), python-rapidjson(&lt;a href="https://github.com/python-rapidjson/python-rapidjson"&gt;RapidJSON&lt;/a&gt;) 和 Python 自带的 json 库。还有一个 &lt;a href="https://github.com/simplejson/simplejson"&gt;simplejson&lt;/a&gt; 是为兼容 Python 2.6 以前用的(json 是 Python 2.6 新加入的 API)，性能有些差。&lt;br/&gt;&lt;br/&gt;
基本上姜还是老的辣，想要收获更好的性能，还得仰赖传统的 C/C++ 语言，ujson 是用纯 C 写的，RapidJSON 是 C++ 写的，后者还是十美分的开源产品。json, ujson, rapidjson 三者 &lt;code&gt;loads()&lt;/code&gt; 方法的性能差别不太明显，但 &lt;code&gt;dumps()&lt;/code&gt; 大对象时 Python 自带的 json 库就要考验用户的耐心了。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;注：最开始本来认定了 ujson 为最佳选择, 所以先从 usjon 和 rapidjson 切入的，后来写作本文的过程中，从 usjon 的自我介绍中发现了 Rust 写的 orjson 很显眼，才加入了 orjson 的测试，看来 orjson 更值得拥有。&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>解决 jvisualvm 启动后长时间 Computing description... 的问题</title><link>https://yanbin.blog/fix-jvisualvm-stuck-at-computing-description-issue/</link><pubDate>Tue, 18 Aug 2020 20:20:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fix-jvisualvm-stuck-at-computing-description-issue/</guid><description>
Java 虚拟机分析工具用 JDK 自带的 &lt;code&gt;jconsole&lt;/code&gt;, &lt;code&gt;jvisualvm&lt;/code&gt;, 和  &lt;code&gt;jmc&lt;/code&gt;(Java Mission Control) 就已经非常好了，还真极少情况下(甚至没有)非得用商业的 Profiler 工具如 YourKit Java Profiler 或 JProfiler 的情况。用于实时观察 JVM 的内存, CPU, 线程等运行状况，对比 Heap 快照，发现线程死锁的应用情景，我比较喜欢用 &lt;code&gt;jvisualvm&lt;/code&gt;(VisualVM)。&lt;br/&gt;&lt;br/&gt;
有很长一段时间，因为在家办公司，只要连接到公司的 VPN 后再执行 &lt;code&gt;jvisualvm&lt;/code&gt; 来打开 VisualVM 时，会有很长的时间(可能长达 10 几分钟)卡在窗口右下角状态栏的 &lt;code&gt;Computing description...&lt;/code&gt;，要等到它消失后才能开始连接 JVM，这时候我的 Java 应用可能早就退出了。要是本地不连 VPN 的话就正常，启动 VisualVM 是正常的，但调试有些工作项目又必须连接公司的 VPN。&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2020/fix-jvisualvm-stuck-at-computing-description-issue/jvisualvm_computing_description.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2020/fix-jvisualvm-stuck-at-computing-description-issue/jvisualvm_computing_description-800x71.png"
width="404px"
/&gt;
&lt;/a&gt;
这种使用 VisualVM 的体验有如恶梦一般，还是有经常要用到 VisualVM 的需求，所以再也不能忍受这种无谓的等待。依然是 Google + StackOverflow 的模式，找到原来罪魁祸首是 &lt;code&gt;/etc/hosts&lt;/code&gt; 中的 &lt;code&gt;127.0.0.1&lt;/code&gt; 这个条目。</description></item><item><title>用一个例子理解 ES6 的 export/import 用法</title><link>https://yanbin.blog/example-understand-es6-export-import/</link><pubDate>Sun, 09 Aug 2020 17:57:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/example-understand-es6-export-import/</guid><description>
使用了一段时间的 Vue.js 以后，其中有大量的 ES6 的 &lt;code&gt;export/import&lt;/code&gt; 用法，如&lt;br/&gt;
&lt;blockquote&gt;
import axios from 'axios';&lt;br /&gt;
import Home from '../views/Home.vue';&lt;br /&gt;
export default {&lt;br /&gt;
  data() { .... }
&lt;/blockquote&gt;
因为目前对 ES6 的 &lt;code&gt;export/import&lt;/code&gt; 用法是一知半解的，所以基本上都是基于 vue-cli 创建的项目上依葫芦画瓢，更是不太理解 vue 的项目是如何由 &lt;code&gt;main.js&lt;/code&gt; -&amp;gt; &lt;code&gt;App.vue&lt;/code&gt; 把所有的 router, views, 和 components 串联起来的。为了进一步理清 Vue.js 项目的初步运行机制，现在开始着手试图通过一个例子来尽可能多的理解 ES6 的 export/import 用法。&lt;br/&gt;&lt;br/&gt;
我不是标题党，所以不可能命名如：理解 export/import 一篇就够了，全网最全诸如此类的。且本人博客主要是记录自己学习过程的，而非为了招人眼球。所以尽力吧，在一个代码例子中尽可能多的展示 export/import 的用法，为不增加代码的复杂性，有些技巧在代码后进行解释。</description></item><item><title>Vue.js 项目使用 vue-prism-editor 语法高亮与编辑器</title><link>https://yanbin.blog/vue-js-vue-prism-editor/</link><pubDate>Thu, 06 Aug 2020 02:32:25 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vue-js-vue-prism-editor/</guid><description>
记录一下在 Vue.js 项目中使用 vue-prism-editor 进行语法高亮显示代码，并兼具代码编辑功能。JavaScript 中有几个比较典型的语法高亮显示的代码库，例如 &lt;a href="http://shjs.sourceforge.net/"&gt;SHJS&lt;/a&gt;, &lt;a href="http://alexgorbatchev.com/SyntaxHighlighter/"&gt;SyntaxHighlighter&lt;/a&gt;, &lt;a href="https://craig.is/making/rainbows"&gt;Rainbow&lt;/a&gt;,   &lt;a href="https://highlightjs.org/"&gt;highlight.js&lt;/a&gt;。本博客曾经使用过 SyntaxHighlighter 来显示日志中的示例代码，现今所采用的是基于 PHP 的 &lt;a href="https://github.com/aramk/crayon-syntax-highlighter"&gt;Crayon Syntax Highlighter&lt;/a&gt;。除了只为了高亮显示代码外，有时候还需处理在线编辑代码，许多年前试用过 &lt;a href="https://codemirror.net/"&gt;CodeMirror&lt;/a&gt;, 而今天要上手 &lt;a href="http://prismjs.com/"&gt;Prism.js&lt;/a&gt; 也能够支持代码编辑。 &lt;br/&gt;&lt;br/&gt;
官方 Demo: &lt;a href="https://prism-editor.netlify.com/"&gt;prism-editor.netlify.com&lt;/a&gt;&lt;br /&gt;
Codesandbox:  &lt;a href="https://codesandbox.io/s/61yrlnlnmn"&gt;https://codesandbox.io/s/61yrlnlnmn&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
为了给 Vue.js 项目提供方便，有人专门做了一个 Vue.js 的插件 &lt;a href="https://github.com/koca/vue-prism-editor"&gt;vue-prism-editor&lt;/a&gt;，本文直接使用该插件，而非直接使用 Prism.js。并且项目蓝本用 &lt;code&gt;@vue/cli 4.4.6&lt;/code&gt; 的命令&lt;br/&gt;
&lt;blockquote&gt;
$ vue create vue-prism-editor-demo
&lt;/blockquote&gt;
创建的，创建过程可参考 &lt;a href="https://yanbin.blog/flask-vue-js-integration-dev-deploy/#create-vue-project"&gt;Flask 和 Vue.js 开发及整合部署实例&lt;/a&gt; 的 &lt;strong&gt;创建 Vue 项目&lt;/strong&gt;一节，此文简单些，就只选择 &lt;code&gt;default (babel, eslint)&lt;/code&gt; 默认项来创建</description></item><item><title>Python 包管理及虚拟环境的应用(四: conda)</title><link>https://yanbin.blog/python-package-manager-venv-virtualenv-4/</link><pubDate>Fri, 31 Jul 2020 02:05:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-package-manager-venv-virtualenv-4/</guid><description>
一年半以前写了关于 Python 包管理及虚拟环境系列&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/python-package-manager-venv-virtualenv-1/"&gt; Python 包管理及虚拟环境的应用(一: pip)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/python-package-manager-venv-virtualenv-2/"&gt;Python 包管理及虚拟环境的应用(二: virtualenv)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/python-package-manager-venv-virtualenv-3/"&gt;Python 包管理及虚拟环境的应用(三: pipenv)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
其中历数了 &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;python3 -m venv&lt;/code&gt;, &lt;code&gt;pipenv&lt;/code&gt;, 并提到了 &lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt; 和 conda。但对 pyenv 和 conda 未作介绍，其中 conda 似乎不该错过。 Anaconda 着力于为数据分析提供支撑，并与 Jupyter Notebook 有更完美的结合，而且 PyCharm 中对它也有很好支持。因此本文来了解一下 conda 以及 Anaconda。&lt;br/&gt;&lt;br/&gt;
首先 Anaconda 是什么，它是一个用于科学计算的，跨平台的包管理与 Python 环境的工具，它方便的解决了多版本 Python 并存，切换及第三方包安装的问题。所以 Anaconda 不只是像 &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;pipenv&lt;/code&gt; 那样的创建管理 Python 虚拟环境，还承担了系统软件的安装管理，像 Mac 下的 &lt;code&gt;brew&lt;/code&gt; 那样的功能。一般来说我们没有必要安装 Anaconda, 只需要安装 Miniconda 获得 &lt;code&gt;conda&lt;/code&gt; 这个命令行工具即可。</description></item><item><title>Python 3.7 所带来的新特性</title><link>https://yanbin.blog/python-3-7-what-is-new/</link><pubDate>Fri, 24 Jul 2020 23:41:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-7-what-is-new/</guid><description>
Python 接触的晚，所以接着 &lt;a href="体验一下%20Python 3.8 带来的主要新特性"&gt;体验一下 Python 3.8 带来的主要新特性&lt;/a&gt; 继续往前翻，体验一下 Python 3.7 曾经引入的新特性，爱一门语言就要了解她真正的历史。一步一步慢慢给 Python 来个起底。&lt;/p&gt;
&lt;br/&gt;
先来看看 Python 网站的各版本使用情况 &lt;a href="https://w3techs.com/technologies/details/pl-python/3"&gt;Usage statistics of Python Version 3 for websites&lt;/a&gt;, 这里统计的 Python 开发的网站的数据，应该有 Python 3 大规模的用于其他领域。单网站应用 Python  来说，Python 2 还有大量遗留代码，Python 3 还是 3.6 为主，Python 的升级还任重道远。本人也是谨慎的在从 3.7 迁移到 3.8 的过程中，AWS 的 Lambda 都支持 3.8，直接上 3.8 也没什么历史负担。以下是从网站使用 Python 统计情况中的两个截图&lt;br/&gt;&lt;br/&gt;
&lt;table style="border-collapse: collapse; width: 100%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="width: 100%;"&gt;
&lt;img class="alignleft post-image"
src="https://yanbin.blog/posts/2020/python-3-7-what-is-new/python3.7-new-features-1.png"
width="299px"
/&gt;
&lt;img class="alignleft post-image"
src="https://yanbin.blog/posts/2020/python-3-7-what-is-new/python3.7-new-features-2.png"
width="400px"
/&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;a href="https://www.python.org/downloads/release/python-370/"&gt;Python 3.7.0&lt;/a&gt; 发布于 2018-06-27, 这篇文章 &lt;a href="https://realpython.com/python37-new-features/"&gt;Cool New Features in Python 3.7&lt;/a&gt; 详细介绍了 Python 3.7 的新特性，本文也是从其中挑几个来体验体验。</description></item><item><title>Python 线程池使用有限大小的工作队列</title><link>https://yanbin.blog/python-thread-pool-using-bounded-working-queue/</link><pubDate>Thu, 23 Jul 2020 23:05:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-thread-pool-using-bounded-working-queue/</guid><description>
在去年的一篇 &lt;a href="https://yanbin.blog/python-programming-with-threads/"&gt;Python 多线程编程&lt;/a&gt; 中学习了 Python 中如何使用多线程来调度任务，工作中也不时从自己的博客中找来参考。在运用当中不时的碰到内存消耗殆尽情况，直接把命令行窗口打死，不得不强行关窗口或杀进程。之前一直未意识到问题所在，只知任务太多就必死无疑，现在要用 Python 来处理大量任务了，必须着手来解决一下它。其实原因很简单，和 Java 的 ThreadPoolExecutor 一样(看它们用的类名都是一样的)。Java 的  ThreadPoolExecutor 内部使用了一个 &lt;code&gt;Integer.MAX_VALUE&lt;/code&gt; 的 LinkedBlockingQueue 来存放提交的待处理的任务，所以基本上就是一个无底洞，自然解决办法也是类似的，需要一个 Bounded Queue 来存放任务列表。&lt;br/&gt;&lt;br/&gt;
在解决该问题之前自己也不妨来温习一下 Python 中使用线程池的基本模式，下面的模板代码曾经是我的最爱：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;process &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;done&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在上面的 &lt;code&gt;with&lt;/code&gt; 上下文中会进行以下几步</description></item><item><title>Nginx + Gunicorn + Flask 集成配置</title><link>https://yanbin.blog/nginx-gunicorn-flask-integration/</link><pubDate>Sat, 18 Jul 2020 01:48:53 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nginx-gunicorn-flask-integration/</guid><description>
想要配置好 Apache + mod_wsgi + Flask 问题真是太多了，随便换一个 Linux 发行版，或者不同的 Python 版本就得让 Google 排很多温室气体。Nginx 可以使用 uWSGI 和  Flask 串联起来，配置起来第一直觉好像也不容易，所以打算换一种方式，直接用一个 Python 的 WSGI HTTP Server, 必要的话再往前面加一个反向代理的 Nginx。这里就来试下 Nginx + Gunicorn + Flask  的完整配置。&lt;br/&gt;&lt;br/&gt;
在 Flask 的官方网站 &lt;a href="https://flask.palletsprojects.com/en/1.1.x/deploying/#deployment"&gt;Deployment Options&lt;/a&gt; 提到了各种生产环境下的部署办法，与 &lt;a href="https://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; 类似的工具还有 &lt;a href="https://uwsgi-docs.readthedocs.io/en/latest/"&gt;uWSGI&lt;/a&gt;, &lt;a href="http://www.gevent.org/"&gt;Gevent&lt;/a&gt;, &lt;a href="https://twistedmatrix.com/trac/wiki/TwistedWeb"&gt;Twited Web&lt;/a&gt;, &lt;a href="https://pgjones.gitlab.io/hypercorn/"&gt;Hypercorn&lt;/a&gt;, &lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt;, &lt;a href="https://github.com/django/daphne"&gt;Daphne&lt;/a&gt;(来自于 django)</description></item><item><title>关于 Apache 与 Flask 的集成经历</title><link>https://yanbin.blog/about-apache-flask-integration/</link><pubDate>Thu, 16 Jul 2020 03:18:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-apache-flask-integration/</guid><description>
在 Flask 的官方文档 &lt;a href="https://flask.palletsprojects.com/en/1.1.x/deploying/mod_wsgi/"&gt;mod_wsgi(Apache)&lt;/a&gt;, 说来倒是轻巧，实际操作起来不得不时刻要凝视眼前的无数大坑，或许 Linux, Apache 都用上比较新的版本会好一些。而我所用的环境是 AWS 上的 EC2, AMI 镜像发行版用 cat /proc/version 看到的是 Red Hat 7.2.1-2，内核为 4.14。照着 Flask  的官方文档是没做成功的，yum install mod_wsgi 只能安装到 Python2 的模块，pip install mod_wsgi 也不成功，所以只能使用 mod_wsgi 的源码来编译安装。&lt;br/&gt;&lt;br/&gt;
要能顺利编译过 mod_wsgi, 需要安装 Apache 2.2 和 Python 3.6 的 dev 版本。它的仓库里 Python3 最高版本只有 Python 3.6，要 Python 3.7, 3.8 可从源码编译安装。 &lt;br/&gt;
&lt;blockquote&gt;
$ yum install gcc&lt;br /&gt;
$ yum install httpd-devel&lt;br /&gt;
$ yum install python36-devel
&lt;/blockquote&gt;
Red Hat 7.2 下的 Apache 真心的难用，不叫 Apache2 也不叫 httpd2，不像 Debian 系统下的 Apache2 做了模块化，基本上 Red Hat 7.2 的 httpd 的配置全在一个文件中 /etc/httpd/conf/httpd.conf。</description></item><item><title>Flask 的替代品 aiohttp 和 Quart</title><link>https://yanbin.blog/flask-replacement-aiohttp-quart/</link><pubDate>Sun, 12 Jul 2020 02:27:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/flask-replacement-aiohttp-quart/</guid><description>
本来只是为了研究一下 Flask  怎么去支持早已在 Python 的支持的 coroutine 功能，没想步子越迈越大，直顶到 aiohttp Web 服务器和 Flask  的异步实现版本 Quart。Flask 得费了好一番功夫去获得 &lt;code&gt;EventLoop&lt;/code&gt;，可知 aiohttp 和 Quart 的路由方法直接就允许 &lt;code&gt;async&lt;/code&gt; 的，那个 &lt;code&gt;EventLoop&lt;/code&gt; 自然就在其中。从 &lt;code&gt;async&lt;/code&gt; 的路由方法出发去调用别的异步方法就是一件十分轻松的事情。&lt;br/&gt;&lt;br/&gt;
下面来稍稍体验一下用分别用  aiohttp 和 Quart 实现简单的异步服务器，我们的关注点在它的异步路由。&lt;br/&gt;&lt;br/&gt;</description></item><item><title>探索 Flask 对 asyncio 的支持</title><link>https://yanbin.blog/how-flask-work-with-asyncio/</link><pubDate>Sun, 12 Jul 2020 02:07:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-flask-work-with-asyncio/</guid><description>
源于自己折腾的一个小 Flask 项目中，后台需访问多个 HTTP 服务，目前采用 ThreadPoolExecutor 多线程的方式处理的。但因访问 HTTP 服务有前后关联关系，如得到请求 A 的结果后再访问 B，这似乎用 Promise.then().then() 编程方式更合适些。于是巡着这一路子，翻出 Python 的各种相关部件来，比如 Python 对 coroutine(协程) 的支持，asyncio, 及后面的 async/await 关键子，&lt;a href="https://docs.aiohttp.org/"&gt;aiohttp&lt;/a&gt; 组件，requests 的 async 替代品有 &lt;a href="https://docs.aiohttp.org/"&gt;aiohttp&lt;/a&gt;, &lt;a href="https://github.com/spyoungtech/grequests"&gt;grequests&lt;/a&gt;, 和 &lt;a href="https://www.python-httpx.org/"&gt;httpx&lt;/a&gt;，aiohttp  可替代 Flask, 最后竟然找到了一个更彻底的 Flask 的 Async 版本 &lt;a href="https://pgjones.gitlab.io/quart/"&gt;Quart&lt;/a&gt;。 &lt;/p&gt;
&lt;br/&gt;
Python 3.4 引入了 asyncio 模块，基于生成器(yield 和 yield from) 和 @asyncio.coroutine 的方式来支持 coroutine(协程), 到 Python 3.5 后有了 async/await(@asyncio.corouting 替换为 async, yield from 替换为 await) 关键字，协程的实现变得更为简单。Python 3.4  使用 coroutine 的方式我们跳过，直接看&lt;br/&gt;</description></item><item><title>Python datetime 处理时区信息</title><link>https://yanbin.blog/python-datetime-timezone/</link><pubDate>Tue, 07 Jul 2020 16:47:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-datetime-timezone/</guid><description>
在 &lt;a href="https://yanbin.blog/python-date-libraries-usages/"&gt;Python 常用日期处理 -- 内置模块 datetime&lt;/a&gt; 探讨了 Python  如何使用 datetime, 如果是一个跨时区的应用(Web 应用都是)，就不能只存储一个时间而不带时区，如此，全球用户将会看到一个相同的时间字符串，白天黑夜就错乱了。比说用户信息的更新时间存储为 2020-07-07 13:46:08, 上海的用户和芝加哥的用户看到的是同一个时间字符串，实质上却相差好多个小时。&lt;/p&gt;
&lt;br/&gt;
我们可以这么做，在服务端只存储一个 Timestamp 长整型值或 UTC 时间，Timestamp 是无关乎时区的，它总是相对于一个 UTC 时间的偏移值; 然后由客户端根据本地时区来显示当地时间。不过在服务端存储为 Timestamp 或 UTC 可读性就不强了，打开文件看到 Timestamp 整形值，大脑是无法直接转换为日期，UTC 时间略好一些。&lt;br/&gt;&lt;br/&gt;
另一种做法可在服务端存储为开发者便于理解的带时区的时间，如 2020-07-07T13:46:08.342+08:00, 客户获得该时间，因为带有时区信息也就能转换为客户端本地时间。&lt;br/&gt;&lt;br/&gt;
客户端请求时还可以把本地的时区信息传送给服务端，由服务端转换为相应的本地时间发送给客户端，但 HTTP 头信息默认不带时区信息，客户端必须主动发送它。</description></item><item><title>Flask, Vue.js 中集成 Bootstrap/BootstrapVue</title><link>https://yanbin.blog/flask-vue-js-integrate-with-bootstrap-bootstrapvue/</link><pubDate>Sat, 04 Jul 2020 16:57:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/flask-vue-js-integrate-with-bootstrap-bootstrapvue/</guid><description>
紧接上一篇 &lt;a href="https://yanbin.blog/flask-vue-js-integration-dev-deploy/"&gt;Flask 和 Vue.js 开发及整合部署实例&lt;/a&gt;，来体验一下它们与 Bootstrap/BootstrapVue 的集成。漂亮的网站少不得一个好的 CSS 框架，现在有许许多的 CSS 框架可选，纯 CSS 的, 轻量级的, 含 JS 的 CSS 框架，如 &lt;a href="https://purecss.io/"&gt;Pure&lt;/a&gt;, &lt;a href="https://bulma.io/"&gt;Bulma&lt;/a&gt;, &lt;a href="https://picturepan2.github.io/spectre/"&gt;Spectre&lt;/a&gt;, 国产的 &lt;a href="https://element.eleme.io/#/en-US"&gt;Element&lt;/a&gt; 等。而我总觉得 Bootstrap 更是五臟俱全，像 Element 专为 Vue.js 打造的一样，Bootstrap 也有 BootstrpVue 那样一个结晶品。&lt;/p&gt;
&lt;br/&gt;
本文准确的内容应该是关于 Vue.js 与 Bootstrap/BootstrapVue 的话题，与 Flask 没什么事，不过这里呢，还是强拉上 Flask, 由 Flask 的 API 来产生一些数据。&lt;br/&gt;&lt;br/&gt;
同样是阅读 &lt;a href="https://zhuanlan.zhihu.com/p/76588212"&gt;Flask和Vue.js构建全栈单页面web应用【通过Flask开发RESTful API】&lt;/a&gt;的一个实践品。本篇基于 &lt;a href="https://yanbin.blog/flask-vue-js-integration-dev-deploy/"&gt;Flask 和 Vue.js 开发及整合部署实例&lt;/a&gt; 中的步骤建立的项目 &lt;a href="https://github.com/yabqiu/flask-vue-app.git"&gt;flask-vue-app&lt;/a&gt;，方便起见，用了一个新项目名称 &lt;a href="https://github.com/yabqiu/flask-vue-bootstrap"&gt;flask-vue-bootstrap&lt;/a&gt;。记得我们为 Flask 和 Vue.js 分别建立了 backend  和 frontend 两个子项目。</description></item><item><title>Flask 和 Vue.js 开发及整合部署实例</title><link>https://yanbin.blog/flask-vue-js-integration-dev-deploy/</link><pubDate>Wed, 01 Jul 2020 03:58:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/flask-vue-js-integration-dev-deploy/</guid><description>
想做些简单的 Web 工具，首先想到的是 Flask + Vue.js, 当然可以完全用 Flask 自己的页面模板 Jinja2, 但一个网站项目不能享受到像 Vue.js, React 类似框架的灵活性真是太可惜了。于是 Flask 只专注于 API, 页面逻辑全用 Vue.js 的组合就成了我的首选，Flask 方面还能进一步选择 FlaskRESTful 框架。还需做得更漂亮的话，CSS 框架可选择 Bootstrap 或与 Vue 紧密集成的 BootstrapVue, 这是后话。&lt;br/&gt;&lt;br/&gt;
本文主要参考 &lt;a href="https://zhuanlan.zhihu.com/p/76588212"&gt;Flask和Vue.js构建全栈单页面web应用【通过Flask开发RESTful API】&lt;/a&gt;的前部分，英文原文在这里 &lt;a href="https://testdriven.io/blog/developing-a-single-page-app-with-flask-and-vuejs/"&gt;Developing a Single Page App with Flask and Vue.js&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
开发过程中我们可以保持 Flask 和  Vue.js 为单独的两个项目，并启动各自的服务，比如 Flask 是 http://localhost:5000, Vue.js 项目通过 &lt;code&gt;npm run serve&lt;/code&gt; 启动在 http://localhost:8080，借助于 node js 的功能，修改 Vue.js 项目的内容能够自动刷新网页。要是开发中把静态文件全放在  Flask 项目中，那么任何对静态文件的修改都必须重启 Flask  服务。虽然 Debug 模式启动的 Flask 在看到它的目录中有任何修改时也能自动重启，但对静态文件的修改重启 Flask 没这个必要性。 &lt;br/&gt;&lt;br/&gt;
但部署时需进一步整合，最终只需要启动 Flask  服务，而无须两个，方便部署。如果是以 Docker 容器的方式发布，使用 docker-compose 来编排两个容器来发布也还算不错。更专业的部署方式应该是 Vue.js 的静态内容放到专门的 Web 服务器，如  Apache/Nginx 中，Flask 也通过 wsgi 与 Web 服务器集成起来。</description></item><item><title>Docker attach 后断开时不退出容器</title><link>https://yanbin.blog/docker-attach-deattach-without-exit-container/</link><pubDate>Mon, 29 Jun 2020 19:53:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-attach-deattach-without-exit-container/</guid><description>
&lt;code&gt;docker attach&lt;/code&gt; 可以连接上 Docker 容器的标准输入，输出和错误输出。比如 &lt;code&gt;docker attach&lt;/code&gt; 连接后就能显示容器中用 ENTRYPOINT/CMD 启动进程的输出内容内容。想要断开会话连接怎么做呢？&lt;code&gt;ctrl - c&lt;/code&gt;, 控制台是不再显示了，可以容器也被终止了，显然这是一个危险的操作。&lt;/p&gt;
&lt;br/&gt;
&lt;code&gt;ctrl - c&lt;/code&gt; 不仅仅关闭了 &lt;code&gt;docker attach&lt;/code&gt; 本身，因为它的默认参数 &lt;code&gt;--sig-proxy&lt;/code&gt; 是 &lt;code&gt;true&lt;/code&gt;，所以 &lt;code&gt;SIGKILL&lt;/code&gt; 信号同时传递到了 ENTRYPOINT/CMD 的 PID 1 的进程，所以形成了双杀的效果。我们来试一下&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2020/docker-attach-deattach-without-exit-container/docker-attach-1.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2020/docker-attach-deattach-without-exit-container/docker-attach-1-800x244.png"
width="786px"
/&gt;
&lt;/a&gt;
如果是在生产环境下，用 &lt;code&gt;docker attach&lt;/code&gt; 连接下控制台看下输出，然后不小心像终止 &lt;code&gt;tail -f&lt;/code&gt; 命令一样用 &lt;code&gt;ctrl - c&lt;/code&gt;  退出，结果服务断了。所以更为小心的操作还是用 &lt;code&gt;docker logs&lt;/code&gt;。 &lt;code&gt;docker attach&lt;/code&gt; 的优势只是在于能查看实时的控制台输出。</description></item><item><title>Mockito 的 anyString(), any(Foo.class) 等不能匹配 null 值</title><link>https://yanbin.blog/mockito-anystring-anyfoo-class-cannot-match-null-values/</link><pubDate>Wed, 24 Jun 2020 02:33:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-anystring-anyfoo-class-cannot-match-null-values/</guid><description>
使用 Mockito Mock 方法式，一直以为可以用 &lt;code&gt;anyString()&lt;/code&gt;, &lt;code&gt;any(Foo.class)&lt;/code&gt; 等匹配 &lt;code&gt;null&lt;/code&gt; 值，其实不行，&lt;code&gt;null&lt;/code&gt; 值必须显式的用 &lt;code&gt;null&lt;/code&gt;, 或 &lt;code&gt;eq(null)&lt;/code&gt; 来匹配。&lt;code&gt;anyString()&lt;/code&gt;, &lt;code&gt;anyInt()&lt;/code&gt; 等只能匹配非 &lt;code&gt;null&lt;/code&gt; 值，查看它们的返回值实际是 "" 和 0 等, 而更为特别的是 &lt;code&gt;any(Foo.class)&lt;/code&gt; 看到的是 &lt;code&gt;null&lt;/code&gt;, 仍然不能匹配 &lt;code&gt;null&lt;/code&gt; 值。进一步用 &lt;code&gt;Mockito.mockingDetails(mock).printInvocations()&lt;/code&gt; 打印出的内容，&lt;code&gt;anyString()&lt;/code&gt;, &lt;code&gt;any(Foo.class)&lt;/code&gt; 都会显示为 &lt;code&gt;null&lt;/code&gt; 值。&lt;/p&gt;
&lt;br/&gt;
说的有点罗嗦，看下面的例子, 被测试类 UserDao，sql 和 sqlArguments 由各自的 setter 方法来控制，默认它们都为 &lt;code&gt;null&lt;/code&gt;</description></item><item><title>Python 列表的排序 - sort/sorted</title><link>https://yanbin.blog/python-sort-list-sort-sorted/</link><pubDate>Sun, 21 Jun 2020 21:13:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-sort-list-sort-sorted/</guid><description>
&lt;a href="https://yanbin.blog/python-collection-map-reduce-operations/"&gt;Python 集合的遍历,推导及 filter/map/reduce 操作&lt;/a&gt; 中讲了对集合的 filter, map 和 reduce 操作，那还有 sort 排序呢？像 Java 一样，Python  也提供了 sort() 和 sorted() 方法。&lt;br/&gt;&lt;br/&gt;
sort() 是 list 的实例方法， sorted() 是一个内置函数。Python 中也是只有 list 才有顺序。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;list.sort() 方法&lt;/h3&gt;
查看 Python 3 中的 &lt;code&gt;list.sort()&lt;/code&gt; 方法(help(list.sort))&lt;br/&gt;
&lt;blockquote&gt;
Help on method_descriptor:&lt;br /&gt;
&lt;br /&gt;
sort(self, /, *, key=None, reverse=False)&lt;br /&gt;
Stable sort *IN PLACE*.
&lt;/blockquote&gt;
Python 的 list.sort() 方法和 Java List.sort() 方法一样的，都是 &lt;code&gt;IN PLACE&lt;/code&gt; 排序，没有返回值。实际看下各种排序场景</description></item><item><title>创建 Python 的 list, set, tuple 和 dict</title><link>https://yanbin.blog/create-python-list-set-dict/</link><pubDate>Wed, 17 Jun 2020 00:16:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/create-python-list-set-dict/</guid><description>
本文主要探讨一下在 Python 各种创建 list, set, tuple 和 dictionary 的方式。首先看&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;最常用的创建方式&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;alist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# type(alist) &amp;lt;class &amp;#39;list&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;aset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# type(aset) &amp;lt;class &amp;#39;set&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;atuple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# type(atuple) &amp;lt;class &amp;#39;tuple&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;adict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;k1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;k2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# type(adict) &amp;lt;class &amp;#39;dict&amp;#39;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
以上相当于是针对右边的值调用了相应的构造函数，如 list([1, 2]), set({1, 2}), tupe((1, 2)), dict({'k1': 1, 'k2': 2})&lt;br/&gt;&lt;br/&gt;
创建 set 和 dictionary 都是用大括号 &lt;code&gt;{}&lt;/code&gt;, 对于 tuple 如果是单个元素时，要附加一个逗号&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;atuple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果省略逗号，会怎样呢？</description></item><item><title>归并排序算法解析</title><link>https://yanbin.blog/the-last-basic-merge-sorting/</link><pubDate>Mon, 18 May 2020 14:05:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/the-last-basic-merge-sorting/</guid><description>
对于基本的排序算法，前面介绍了冒泡，选择，插入和希尔(增强版本的插入), 还有快速排序，现在还剩下最后一种基本的排序算法，那就是归并排序。归并排序像快速排序一样采用递归算法对列表进行分而治之，每次平均一分为二，分到只有一个元素为止。如果列表为空或只有一个元素时，那么从定义上来说它就是有序的; 当然归并排序的拆分最终不会有空列表的情况。拆分成一个个元素后再往回归并，归并是指将两个较小的有序列表归并为一个有序列表的过程。比如说两个单元素列表归并为两个元素的有序列表，两个双元素的列表归并为四个元素的有充列表，不断往上进行，最后形成一个有序的完整列表。&lt;/p&gt;
&lt;br/&gt;
从维基百科的词条 &lt;a href="https://en.wikipedia.org/wiki/Merge_sort"&gt;Merge sort&lt;/a&gt; 找到下图，很深刻的描绘了归并排序的完整过程，红色箭头拆分，绿色箭头归并</description></item><item><title>希尔(Shell) 排序 - 增强版插入排序算法</title><link>https://yanbin.blog/shell-sort-enhanced-insertion-sort/</link><pubDate>Mon, 04 May 2020 03:27:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/shell-sort-enhanced-insertion-sort/</guid><description>
&lt;p&gt;前面讲过的几种排序多是以排序逻辑来命名的，例如冒泡，选择和插入排序，以及其他如归并排序，当然还有觉得自己足够牛 X 快速排序命名。而本文要学习的排序算法叫做希尔排序是以其设计者 Donlad Shell 命令的排序算法，该算法在 1959 年公布，能以作者来命名的算法应该是很不错的，令设计者引以为傲的。最初写出冒泡和选择排序的就没以作者来命名，可能不好意说，更可能是公共思维。&lt;/p&gt;
&lt;p&gt;那么什么是希尔排序呢？它实际上是插入排序算法的增强版本，又称递减增量排序算法。它对待排序列表进行间隔式分段插入处理，从而总体上减少了元素的移动次数而达到性能的大大提升。那么理解希尔排序之前一定要先了解插入排序。那么为什么说希尔排序既是递减又是增量呢？</description></item><item><title>插入排序算法解析</title><link>https://yanbin.blog/insertion-sort-how-to/</link><pubDate>Mon, 27 Apr 2020 02:46:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/insertion-sort-how-to/</guid><description>
前面说过最原始的复杂度为 O(n&lt;sup&gt;2&lt;/sup&gt;) 的冒泡和选择排序，也跳跃到了复杂度为  O(n log n) 的快速排序，现在又再看一个复杂度同样为 O(n&lt;sup&gt;2&lt;/sup&gt;) 的插入排序。从排序名称结合代码我们理解了为什么叫做冒泡或是选择，快速排序自认高名，那么何以这又谓之插入排序呢？是怎么插入，从左边往右边插，还是从右边往左边插，这得搞清它的排序原理：&lt;/p&gt;
&lt;br/&gt;
它在列表较低的一端维护一个有序的子列表(从最左端一个元素开始)，并逐个将每个新元素(高端的)"插入"这个子列表。插入的时候遍历低端列表，找准位置插入便是，插入点后的元素需后移，当所有高端的元素插入完成了，整个列表就变得有序了。&lt;br/&gt;&lt;br/&gt;
整个排序操作示意图如下：</description></item><item><title>理解 Python 类的变量,方法与属性</title><link>https://yanbin.blog/understand-python-class-definition/</link><pubDate>Mon, 20 Apr 2020 23:43:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-python-class-definition/</guid><description>
熟悉了传统的 C++/Java 类定义的风格，来感受一下 Python 是如何定义类的。本篇是阅读 《The Quick Python Book》第二版关于类定义的笔记，由原书内容进一步引申，不过是依照本人的思考顺序来组织的。在理解 Python 类定义的同时头脑中应该闪现出 JavaScript/Java 如何定义类的情景。&lt;/p&gt;
&lt;br/&gt;
&lt;h3&gt;最简单的类定义&lt;/h3&gt;
&lt;blockquote&gt;
class MyClass:&lt;br /&gt;
    pass
&lt;/blockquote&gt;
由于 &lt;code&gt;class MyClass&lt;/code&gt; 后面要有个冒号，而冒号后总得有点东西才能表示该类定义结束了，于是放个 &lt;code&gt;pass&lt;/code&gt; 当占位符。Python 也像 Java 一样，有一个根类，叫做 object，例如上面的定义&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__bases__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nc"&gt;object&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;gt;,)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;inspect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nc"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;gt;, &amp;lt;class &amp;#39;&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
我们能看到它隐式的基类是 &lt;code&gt;object&lt;/code&gt;, 而不用显式的声明为 &lt;code&gt;class MyClass(object)&lt;/code&gt;。看到 &lt;code&gt;__bases__&lt;/code&gt; 属性是一个 Tuple, 意识到  Python 是支持多重继承的。</description></item><item><title>真正有些水准的排序算法 - 快速排序</title><link>https://yanbin.blog/real-advanced-sorting-algorithm-quick/</link><pubDate>Wed, 15 Apr 2020 15:41:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/real-advanced-sorting-algorithm-quick/</guid><description>
冒泡和选择排序的简单粗暴也许在某些人眼里都不能称作算法，现在要进入一种更优雅的排序算法，快速排序。它使用分而治之(Divide and Conquer, D&amp;amp;G) 的策略，要应用到递归调用。快速排序敢说自己快速，也确实比选择排序快很多很多。冒泡和选择排序，尤其是选择排序是非常自然的排序算法，而快速排序就不是一般人会随意想出来的。&lt;br/&gt;&lt;br/&gt;
快速排序的演绎需要用递归来思考循环的问题，然而我之前总是在及力用循环来避免递归调用，有趣的是诸如 Haskell 等函数式编译语言根本没有循环，只能用递归来编写循环的效果。来看一个简单的例子，比如要从 1 加到 100，我们很自然会用循环从 1 累加到 100，如果换成递归，看下面的代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt; &lt;span class="c1"&gt;# 5050&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
递归有助于我们把大问题分解为小问题，上面代码的思维是数组的和总是很一个元素加上剩下元素列表的和，直到最后元素列表为空(和为 0)。</description></item><item><title>两种最基本的排序算法: 冒泡和选择</title><link>https://yanbin.blog/two-basic-sorting-algorithms/</link><pubDate>Tue, 14 Apr 2020 17:36:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/two-basic-sorting-algorithms/</guid><description>
因 COVID-19 漫延各自居家，也更有闲时，便拣起一本关于算法的书籍来研究。本不是科班出身，算法方面自然是自己的薄弱环节。平时用各种 SDK，只大概听说了些算法，仅能就自己如何选择哪种实现而作为参考。&lt;br/&gt;&lt;br/&gt;
如今阅读的是一本入门的书籍，名为 《算法图解》，英文版书名是 《Grokking Algorithms》。 该书图文并茂，十分适合初学者，关于排序最基本莫过于冒泡与选择排序。该书并未提及冒泡，而是直接从选择排序切入，在阅读本书之前我就一直对这两咱排序方式傻傻不分。一直以为头脑中的选择排序就是冒泡排序，那就来看下什么是真正的冒泡排序。</description></item><item><title>Kubernetes 学习笔记(二) - 部署和访问应用</title><link>https://yanbin.blog/kubernetes-learning-2-run-service/</link><pubDate>Thu, 09 Apr 2020 22:53:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kubernetes-learning-2-run-service/</guid><description>
前边折腾了各种安装 Kubernetes 集群的操作，还跑到 AWS 上撸了一把 EKS，也在 Kubernetes 上部署过服务。继续更深一步的学习如何部署应用和怎么通过 Service 去访问 Pod 中的应用，顺带看看内部的网络是怎么流转的。&lt;br/&gt;&lt;br/&gt;
测试平台还是以本地启动的三个 Vagrant 虚拟机组成的 Kubernetes 集群，安装方法见 &lt;a href="https://yanbin.blog/kubernetes-learning-1"&gt;Kubernetes 学习笔记(一) - 初上手&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;k8s-master (172.28.128.14)&lt;/li&gt;
&lt;li&gt;k8s-node1 (172.28.128.10)&lt;/li&gt;
&lt;li&gt;k8s-node2 (172.28.128.11)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
测试应用的镜像为 &lt;code&gt;yanbin/python-web&lt;/code&gt;, 代码见 github 上的 &lt;a href="https://github.com/yabqiu/python-web-docker/blob/master/app.py"&gt;yabqiu/python-web-docker/app.py&lt;/a&gt;, 一个默认启动在  80 端口上的 Flask Web 应用，输出为当前 hostname  和一个唯一标识符。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;部署应用&lt;/h3&gt;
《每天5分玩转Kubernetes》里用的 Kubernetes 是 1.7 版本，其中还在用 &lt;code&gt;kubectl run&lt;/code&gt; 的方式来部署应用(它会产生一个隐式的 deployment 对象)，该方式已在 &lt;a href="https://github.com/kubernetes/kubernetes/blob/9d1179360cfc9fb6e905252cccd482b987d45393/CHANGELOG/CHANGELOG-1.12.md#deprecations-and-removals"&gt;Kubernetes 1.12 中不推荐使用了&lt;/a&gt;，建议用 &lt;code&gt;kubectl create deployment...&lt;/code&gt;，而实际中更应该用 &lt;code&gt;yaml&lt;/code&gt; 文件编排后再 &lt;code&gt;kubectl apply -f &amp;lt;your-yaml-file&amp;gt;&lt;/code&gt;, 这样多种对象可以编写在一起，更方便日后同样的命令更新各种对象，或者用 &lt;code&gt;kubectl delete -f &amp;lt;your-yam-file&amp;gt;&lt;/code&gt; 批量删除所创建的对象。</description></item><item><title>Kubernetes 集群中节点的 INTERNAL-IP 问题</title><link>https://yanbin.blog/kubernetes-cluster-internal-ip-issue/</link><pubDate>Wed, 08 Apr 2020 23:46:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kubernetes-cluster-internal-ip-issue/</guid><description>
用自己 &lt;a href="https://yanbin.blog/kubernetes-learning-1"&gt;Kubernetes 学习笔记(一) - 初上手&lt;/a&gt; 一文中的方法用 Vagrant 虚拟机安装的 Kubernetes 集群，部署应用什么的都没问题，然而却在用&lt;br/&gt;
&lt;blockquote&gt;
$ kubectl exec -it &amp;lt;pod-name&amp;gt; -- sh
&lt;/blockquote&gt;
试图登陆 docker 容器时出问题了，总是报错说&lt;br/&gt;
&lt;blockquote&gt;
error: unable to upgrade connection: pod does not exist
&lt;/blockquote&gt;
kubectl 登陆不了 docker 容器，而且  kubectl logs 也会报一样的错，必须到具体的工作节点上用 docker exec 或 docker logs 才能访问到该节点上的容器信息。&lt;br/&gt;&lt;br/&gt;
这就不太对头，网上找了下原因，结果是因为节点间通信时选错了 IP 地址。&lt;br/&gt;&lt;br/&gt;
比如三个 Vagrant 虚拟机分别是&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;k8s-master (172.28.128.14)&lt;/li&gt;
&lt;li&gt;k8s-node1 (172.28.128.10)&lt;/li&gt;
&lt;li&gt;k8s-node2 (172.28.128.11)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
在 k8s-master 中初始集群时用的命令也是指定的 172.28.128.14 IP 地址</description></item><item><title>Java 普通线程池与 ForkJoinPool 的效果对比</title><link>https://yanbin.blog/common-threadpool-vs-forkjoinpool/</link><pubDate>Wed, 08 Apr 2020 00:14:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/common-threadpool-vs-forkjoinpool/</guid><description>
Java 多线程编程常用的一个接口是 &lt;code&gt;ExecutorService&lt;/code&gt;, 其实就一个线程池的接口，一般由两种方式创建线程池，一为 Executors 的工厂方法，二则创建 ForkJoinPool 实例，当然也有直接使用 ThreadPoolExecutor 的。&lt;/p&gt;
&lt;br/&gt;
关于什么时候用 &lt;code&gt;ForkJoinPool&lt;/code&gt; 或普通的线程池(如 Executors.newFixedThreadPool(2) 或 new ThreadPoolExecutor(...)) 不过多的述说。如果要运用到 ForkJoinTask 的话就要用 ForkJoinPool, 它是 Java7 新引入的线程池类型。&lt;br/&gt;&lt;br/&gt;
关于 Java7 的 fork-join 框架可参考很多年前的一篇 &lt;a href="https://yanbin.blog/java-fork-join-framework-memo/"&gt;Java 的 fork-join 框架实例备忘&lt;/a&gt;。ForkJoinPool 的一个典型特征是能够进行 &lt;a href="https://en.wikipedia.org/wiki/Work_stealing"&gt;Work stealing&lt;/a&gt;。它也是 Akka actor 效率高效的一个有力保证。&lt;br/&gt;&lt;br/&gt;
本文只能某一种情形下在选择普通线程池与 ForkJoinPool 的区别，直接说吧，普通线程更容易造成死锁，而 ForkJoinPool 却能应对相同的状况。</description></item><item><title>AWS EKS 执行 kubectl 时 error: You must be logged in to the server (Unauthorized)</title><link>https://yanbin.blog/aws-eks-kubectl-error-you-must-be-logged-in-to-the-server-unauthorized/</link><pubDate>Mon, 06 Apr 2020 17:10:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-eks-kubectl-error-you-must-be-logged-in-to-the-server-unauthorized/</guid><description>
在 AWS 上创建好 EKS 后，想要在本地用  kubectl 来管理 EKS，必须用 &lt;code&gt;aws eks update-kubeconfig&lt;/code&gt; 来更新本地的 &lt;code&gt;~/.kube/config&lt;/code&gt; 文件或者 &lt;code&gt;KUBECONFIG&lt;/code&gt; 环境变量指向的别的配置文件。&lt;br/&gt;&lt;br/&gt;
比如说你创建 EKS 的用户在本地 &lt;code&gt;~/.aws/credentials&lt;/code&gt; 中的 profile 是 &lt;code&gt;my-aws-profile&lt;/code&gt;, 那么完整的 &lt;code&gt;update-kubeconfig&lt;/code&gt; 命令就是&lt;br/&gt;
&lt;blockquote&gt;
$ aws eks --profile my-aws-profile --region us-east-1 update-kubeconfig --name myeks-cluster&lt;br /&gt;
Updated context arn:aws:eks:us-east-1:069762108088:cluster/myeks-cluster in /Users/yanbin/.kube/config
&lt;/blockquote&gt;
再来执行 &lt;code&gt;kubectl get pods&lt;/code&gt; 如果出现错误 &lt;span style="color: #800000;"&gt;error: You must be logged in to the server (Unauthorized)&lt;/span&gt;，肯定是因为你使用的 awscli 命令比较老(到底有多老呢？在 github 上已经无法追溯了，大概是 1.16.266 之前的)。大约生成的 ~/.kube/config 文件末尾像下面那样</description></item><item><title>搭建使用 AWS 的 Kubernetes EKS 服务</title><link>https://yanbin.blog/setup-aws-kubernetes-eks-service/</link><pubDate>Sun, 05 Apr 2020 00:45:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/setup-aws-kubernetes-eks-service/</guid><description>
前面从无到有或是分别以 Docker Desktop, Minikube, kind 来搭建过 Kubernetes 集群。而如今各大云服务提供商基本都推出了各自的 Kubernetes 服务，例如：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Google GKE - Google Kubernetes Engine&lt;/li&gt;
&lt;li&gt;Amazon EKS - Amazon Elastic Kubernetes Service&lt;/li&gt;
&lt;li&gt;Microsoft AKS - Azure Kubernetes Service&lt;/li&gt;
&lt;li&gt;IBM Cloud Kubernetes Service&lt;/li&gt;
&lt;li&gt;Alibaba Cloud Container Service&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
所以对 Kubernetes 的进一步学习过程中何不一跃而直上云霄，直接尝试 AWS 的 EKS 如何搭建。EKS 是在 2018 年 6 月份正式推出，见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/06/amazon-elastic-container-service-for-kubernetes-eks-now-ga/"&gt;Amazon Elastic Container Service for Kubernetes Now Generally Available&lt;/a&gt;。EKS 在 AWS 上是与 ECS 并列的服务，它们的功能也比较类似，都是伸缩性的容器服务，ECS 配置管理更分散，EKS 本身就是一个集群管理工具。它们也有些共同的东西，如 Auto Scaling Groups, Launch Templates。&lt;br/&gt;&lt;br/&gt;
现在用 Terraform 脚本来演示一下如何创建一个 EKS 集群，并启动三个 EC2 Worker 节点(EKS 也支持 Fargate Worker 节点)，并部署一个应用。Terraform 脚本将会列出完成该任务的基本要素，也将会看看背后发生了什么。</description></item><item><title>几种简单安装 Kubernetes 集群的方法</title><link>https://yanbin.blog/several-method-installing-kubernetes-cluster/</link><pubDate>Sun, 29 Mar 2020 19:00:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/several-method-installing-kubernetes-cluster/</guid><description>
在 &lt;a href="https://yanbin.blog/kubernetes-learning-1/"&gt;Kubernetes 学习笔记(一) - 初上手&lt;/a&gt; 中一上手就尝试了最原始级的安装 Kubernetes 的方式，花了不少时间，好处是能更好的理解 Kubernete 的组成以及各节点是如何协同工作的。从《Kubernetes in Action》第二版中了解了几种简单的方法，为什么要把以下几种方式列出来呢？为了让看到上篇的同学们不至于对 Kubernetes 的安装过程望而却步。下面的前两种方式都是创立的单节点的 Kubernetes 集群。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;一：启用 Docker Desktop 的 Kubernetes 特性&lt;/h3&gt;
Docker Desktop 的 Community 版本从  18.06.0-ce-mac70 2018-07-25 开始加入了对 Kubernetes 的支持，在 2018-11-19 后，Docker Desktop 开始用 2.0.0.0 这样的版本。当前的 Docker Desktop Community 版是 2.2.0.4，所带的 Kubernetes 是 v1.15.5，要启用它只要进到它的 &lt;code&gt;Preferences...&lt;/code&gt;,</description></item><item><title>Docker 容器内进程与 Namespace</title><link>https://yanbin.blog/docker-process-namespace/</link><pubDate>Sun, 29 Mar 2020 15:20:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-process-namespace/</guid><description>
原本是继续阅读《每天5分钟玩转Kubernetes》一书的，发现该书所用的 Kubernetes 版本着实有点老旧( 1.7), 当前版本是 1.18。操作起来有些不同，所以找来了最新的 《Kubernetes in Action》第二版 来看，该书还在写作当中。第二章全是讲 Docker 的内容，本人读书有个不好的习惯，就是不喜欢跳过跳过。看了总会有收获的，这不，就从中稍微理清了 Docker 容器内进程与 Namespace 的关系。&lt;br/&gt;&lt;br/&gt;
Docker 容器间的进程本质上是宿主主上的一个进程，它能相互隔离靠的是 chroot, namespace 和 cgroup(对 CPU, 内存，磁盘，带宽等的配额)。千万不要认为启动一个 Docker 容器就是启动了一个虚拟机。&lt;br/&gt;&lt;br/&gt;
其中 namespace 实现了以下几项资源的隔离&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Mount: 挂载点(文件系统)&lt;/li&gt;
&lt;li&gt;PID: 进程 ID&lt;/li&gt;
&lt;li&gt;Network: 网络设备，网络栈，端口等&lt;/li&gt;
&lt;li&gt;IPC： 进程间通信，信号量，消息队列和共享等&lt;/li&gt;
&lt;li&gt;UTS： 主机名和域名&lt;/li&gt;
&lt;li&gt;User ID: 用户和组 ID&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Kubernetes 学习笔记(一) - 初上手</title><link>https://yanbin.blog/kubernetes-learning-1/</link><pubDate>Thu, 26 Mar 2020 20:03:38 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kubernetes-learning-1/</guid><description>
经过前几天从 Docker Swarm 到 Docker Compose 的历练之后，终于踏上了 Kubernetes 的学习征程了。虽说前两者并非必要的学习 Kubernetes 的基础，但了解它们之后与 Kubernetes 中的一些概念可以进行对比理解。&lt;/p&gt;
&lt;br/&gt;
本系列是阅读 《每天5分钟玩转Kubernetes》的笔记，书名倒是来的轻松，对我来说每天 5 分钟根本吸收不了什么知识。如果把阅读该书比作台上的话，基本就是台上 5 分钟，台下 50 分钟都不够，实际操作起来不光是版本有差异，还会出现各种意外带来惊喜与折磨。另外该书所采用的版本是 Kubernetes 1.7.4, 当前为 1.18，有不小的差别。&lt;br/&gt;&lt;br/&gt;
Kubernetes 是当今容器编排引擎的事实标准，以前为 Google 内部项目的 Omega 开源为 Kubernetes。传说中的容器编排三足鼎力，Kubernetes, Docker Swarm 和 Apache Mesos, 现在已是 Kubernetes 的一骑绝尘。Mac 下的 Docker Desktop 版本已内置了 Kubernetes 的支持。AWS 中在 ECS 外也支持了 EKS(Elastic Kubernetes Service)。</description></item><item><title>Docker Compose 实践</title><link>https://yanbin.blog/docker-compose-in-action/</link><pubDate>Tue, 24 Mar 2020 13:56:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-compose-in-action/</guid><description>
继续向 Kubernetes 进发，上一篇 &lt;a href="https://yanbin.blog/docker-swarm-cluster-in-action/"&gt;Docker Swarm 集群模式实操&lt;/a&gt; 了解完 Swarm 后，有必要对 Docker Compose 了解一番。Docker Swarm 是把 Docker 宿主机组成集群，部署服务时只要告知 Manager 节点，它就会自动找到相应节点去运行相应的容器。Compose 完全是另一个概念，它把相关联的多个容器组织成一个整体来部署，如由负载容器，多个 Web 容器和一个 Redis 缓存容器构成一个整体。&lt;br/&gt;&lt;br/&gt;
由其名 Compose 正式有了容器编排这么一个概念，后来将要学的 Kubernetes 是更高级别的组织，运行，管理容器的工具。Compose  由于把容器组织起来了，所以能够一条命令启动多个相关联的容器，而无需单独启动一个一个的容器。&lt;br/&gt;&lt;br/&gt;
关于 Docker Compose  的安装，在 Mac OS X 下的  Docker Desktop 自带了 docker-compose; 由于 Docker Compose 是用 Python 编写的，我们可以用  &lt;code&gt;pip install docker-compose&lt;/code&gt; 的安装，安装后使用它的命令就是 &lt;code&gt;docker-compose&lt;/code&gt;。&lt;br/&gt;&lt;br/&gt;
下方的 Docker Container, Swarm, Compose 三者之间的关系图很形像</description></item><item><title>Docker Swarm 集群模式实操</title><link>https://yanbin.blog/docker-swarm-cluster-in-action/</link><pubDate>Sun, 22 Mar 2020 23:55:55 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-swarm-cluster-in-action/</guid><description>
在正式进入 Kubernetes 之前希望能对早先的 Docker Swarm 有所了解，虽然它目前基本上没什么用处。Swarm 提供的是 Docker 宿主机的集群，集群中至少有一个 Manager(或多个), 再加上 0 或多个 Worker。它的大意是有个 Docker 服务(容器)通过 Manager 告诉用所管理的 Swarm 集群来执行，它就会在该集群中找到相应的宿主机来执行。Manager 和 Workder 都可用来运行 Docker 容器，但只有 Manager 才能执行部分管理命令。&lt;/p&gt;
&lt;br/&gt;
下面两张图很好的体现了我们平时在一个宿主机上和 Swarm 集群宿主机上运行 Docker 容器的区别&lt;br/&gt;&lt;br/&gt;
&lt;table style="border-collapse: collapse; width: 60.5933%;"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="width: 26.231%; text-align: center;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2020/docker-swarm-cluster-in-action/docker-2.png"
width="256px"
/&gt;
所有容器自己扛&lt;/td&gt;
&lt;td style="width: 1.65785%;"&gt;  =&amp;gt;&lt;/td&gt;
&lt;td style="width: 25.7504%; text-align: center;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2020/docker-swarm-cluster-in-action/docker_swarm.png"
width="256px"
/&gt;
容器大家一起扛&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
&lt;p data-wp-editing="1"&gt;Docker 在 1.12 之后自带了 Swarm 模式。本文实质上对 &lt;a href="https://www.jianshu.com/p/df744c4e375e"&gt;使用Docker Swarm模式搭建Swarm集群&lt;/a&gt; 的一次实际操作与体验。只是自己方便使用了三个 Vagrant 虚拟机进行实践，其中一个 Swarm Manager 节点，两个 worker 节点</description></item><item><title>记录自己常用的一些 Linux Shell 脚本</title><link>https://yanbin.blog/frequently-used-linux-shell-script/</link><pubDate>Sat, 21 Mar 2020 18:57:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/frequently-used-linux-shell-script/</guid><description>
常要在 Linux 下分析日志或其他类型的文件，基本用的命令也就 grep, awk, sed, cut, vim, cat, find, xargs, tail, more 或 less。本人工作平台为 Mac OS X, 而 Mac 下的 grep, sed, awk 的行为与 Linux 下的 GNU 标准的相应命令是有差别的, 所以我总是在 Mac 下安装 GNU 的 grep, sed, awk 等命令来替代系统默认的。&lt;br/&gt;&lt;br/&gt;
比如安装下面的命令&lt;br/&gt;
&lt;blockquote&gt;
$ brew install findutils gawk gnu-sed grep
&lt;/blockquote&gt;
以上会安装 GNU 的 find, awk, sed 和 grep 命令，使用时要加个前缀，如 gfind, gawk, gsed 或 ggrep，也可以设置别名或符号链接来替换掉系统的相应命令。&lt;br/&gt;&lt;br/&gt;
注： &lt;code&gt;brew install coreutils&lt;/code&gt; 会安装众多 Linux 下的基本命令替代品，ls, cat, cut 都在其中，使用它也是要加上前缀 &lt;code&gt;g&lt;/code&gt;, 如 gls, gcut 等。&lt;br/&gt;&lt;br/&gt;
以下 grep, find, grep, sed 以 GNU 的行为为准。</description></item><item><title>运行时动态创建 Spring Bean</title><link>https://yanbin.blog/dynamic-creating-spring-bean-runtime/</link><pubDate>Wed, 18 Mar 2020 19:19:23 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dynamic-creating-spring-bean-runtime/</guid><description>
通常我们注册 Spring Bean 是通过像 @Named, @Bean, @Component, @Service 这样的注解来注册的，或者用更为古老的 XML 配置文件的方式。难免有时候要根据实际业务需求在 Spring  运行期间动态注册 Spring Bean, 比如基本某种形式的配置文件或系统属性来注册相应的 Bean, 这好像又回到了 XML 文件注册方式，也不尽然。&lt;br/&gt;&lt;br/&gt;
那为什么在运行期还要去注册 Spring Bean 呢，直接 new 对象不行吗？当然行得通，不过这样的话就不能更好的使用到  Spring IOC 的好处了。像待注册的 Bean 构造函数可以直接用到其他的 Spring  对象，或 @Value 引入环境变量，还有 @PostContruct 这样的行为。&lt;br/&gt;&lt;br/&gt;
最初思考如何注册 Spring Bean 时还是费了不少周折，如今清晰了许多。了当的说，不管是 Spring  初始时还是运行时，注册 Bean 的关键(应是唯一) 入口就是 &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/support/BeanDefinitionRegistry.html"&gt;BeanDefinitionRegistry&lt;/a&gt; 接口的方法</description></item><item><title>Python print 立即打印内容到重定向的文件</title><link>https://yanbin.blog/python-redirect-print-immediately/</link><pubDate>Mon, 16 Mar 2020 11:17:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-redirect-print-immediately/</guid><description>
看到本文标题也许要奇怪了，Python 的 print 难道不是也上可以看到结果的吗？在 Python shell 下只要 &lt;br/&gt;
&lt;blockquote&gt;
&amp;gt;&amp;gt; print('Hello world!')&lt;br /&gt;
Hello world!
&lt;/blockquote&gt;
不就立马能看到控制台输出的 "Hello world!" 吗。或者是一个 Python 脚本文件 hello.py&lt;br/&gt;
&lt;blockquote&gt;
import time&lt;br /&gt;
&lt;br /&gt;
for i in range(3):&lt;br /&gt;
    print('Hello {}'.format(i))&lt;br /&gt;
    time.sleep(3)
&lt;/blockquote&gt;
然后执行 &lt;code&gt;python hello.py&lt;/code&gt; 的话，我们也同样能看到在控制台下在预定的每 3 秒输出一行&lt;br/&gt;
&lt;blockquote&gt;
Hello 0&lt;br /&gt;
Hello 1&lt;br /&gt;
Hello 2
&lt;/blockquote&gt;
但执行下面的命令试图重定向输出到文件时的效果就不一样了</description></item><item><title>体验一下 Python 3.8 带来的主要新特性</title><link>https://yanbin.blog/python-3-8-new-features/</link><pubDate>Sat, 14 Mar 2020 14:09:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-3-8-new-features/</guid><description>
学习理解一个软件非常好的方法就是跟随每一个版本演进的新特性，好比一个人被别人看着长大的，知子莫若父。因此每个版本的 Changelogs 或 What's New 是非常值得一读的，见 &lt;a href="https://docs.python.org/3/whatsnew/3.8.html"&gt;What's New In Python 3.8&lt;/a&gt;。因为接触 Python 比较晚，没能即时的重前往后的看过 Python 各版本的新特性，于是采用倒叙的方式来看 Python 的演进也不错。&lt;br/&gt;&lt;br/&gt;
在正式进入 Python 3.8 新特性前，先来看 Python 3.8 的安装和以及介绍一个非常棒的 Python 命令行解析器 bpython 作为 python 命令，ipython, idle 的替代品。&lt;br/&gt;&lt;br/&gt;
Python 3.8 各平台的安装包可在 &lt;a href="https://www.python.org/downloads/release/python-380/"&gt;https://www.python.org/downloads/release/python-380/&lt;/a&gt; 找到。比如 Mac OS X 下可下载 &lt;a href="https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg"&gt;https://www.python.org/ftp/python/3.8.0/python-3.8.0-macosx10.9.pkg&lt;/a&gt;，然后执行安装。安装完后，我机器上的 &lt;code&gt;/usr/local/bin/python3&lt;/code&gt; 就指向到了 Python 3.8, 要用 Python 3.7 的话命令是 python3.7; pip3 却仍然是老版本的。&lt;br/&gt;&lt;br/&gt;
bpython 的安装要用相应版本的  pip install bpython, 如 &lt;code&gt;/Library/Frameworks/Python.framework/Versions/3.8/bin/pip3 install bpython&lt;/code&gt;, 或者创建的虚拟环境中安装, python -m venv .venv; source .venv/bin/activate; pip install bpython。安装后启动  bpython 进入一个更智能的 Python 命令解析器。py&lt;br/&gt;&lt;br/&gt;
下面正式了解 Python 3.8 的主要新特性</description></item><item><title>Kafka Connect 介绍和使用</title><link>https://yanbin.blog/kafka-connect-how-to/</link><pubDate>Thu, 31 Oct 2019 23:42:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kafka-connect-how-to/</guid><description>
继续把 Kafka 捋一捋，还剩两个主要的组件了，分别为 Kafka Connect 和 Kafka Streams。而其中的 Kafka Connect 是在 Kafka 0.9.0.0 开始加入的我，Connect 的出现让 Kafka 与外部世界更紧密连接起来了，进而可以让其他外围组件通过 Connect 的 Source 与 Sink 紧密的团结在以 Kafka 为核心的消息中心。从此不再总是以标准的 Kafka Consumer 和 Producer 与外部联络。&lt;br/&gt;&lt;br/&gt;
Kafka Connect 主要由两部分组成，Source Connector 和  Sink Connector，这两个来自于 Akka Stream 这一 Reactive 框架的概念，即往 Kafka 流入数据的 Connector 是 Source, 从 Kafka 导出数据的是 Sink。 要自己实现 Kafka 的 Connector  需要用到 &lt;code&gt;org.apache.kafka:connect-api&lt;/code&gt; 组件，不包含在 kafka-clients 依赖中，其中定义了两个主要抽像类&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span style="color: #800000;"&gt;org.apache.kafka.connect.source.SourceConnector&lt;/span&gt; extends Connector&lt;/li&gt;
&lt;li&gt;&lt;span style="color: #800000;"&gt;org.apache.kafka.connect.sink.SinkConnector&lt;/span&gt; extends Connector&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>启用并测试 Kafka 的 SASL + ACL 认证授权</title><link>https://yanbin.blog/kafka-enable-saslacl/</link><pubDate>Tue, 29 Oct 2019 00:41:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kafka-enable-saslacl/</guid><description>
&lt;p&gt;Kafka 默认情况下是没有启用安全机制，这让能连接到 Broker 的客户端可以为所欲为，自 Kafka 0.9.0.0 版本引入了安全配置，但是需要进行一些配置来开启它。Kafka 安全主要包含三个方面：认证(authentication)，授权(authorization), 和信道加密(encryption)。其中认证机制和授权分别通过 SASL(&lt;strong&gt;S&lt;/strong&gt;imple &lt;strong&gt;A&lt;/strong&gt;uthentication and &lt;strong&gt;S&lt;/strong&gt;ecurity &lt;strong&gt;L&lt;/strong&gt;ayer)和  ACL(&lt;strong&gt;A&lt;/strong&gt;ccess &lt;strong&gt;C&lt;/strong&gt;ontrol &lt;strong&gt;L&lt;/strong&gt;ist) 来实现。本篇主要演示 SASL + ACL 的配置，未涉及 SSL 信道加道，所以没有配置 Kerberos, 客户端与 Broker 之间的数据传输仍然以明文(PLAINTEXT) 传输，这在内网使用 Kafka 基本没问题。&lt;/p&gt;
&lt;p&gt;开启 SASL 和 ACL 需要在 Broker 和 Client 端进行相应的配置，要为两端创建包含用户认证信息的 JAAS(&lt;strong&gt;J&lt;/strong&gt;ava &lt;strong&gt;A&lt;/strong&gt;uthentication and &lt;strong&gt;A&lt;/strong&gt;uthorization &lt;strong&gt;S&lt;/strong&gt;ervice) 文件。听其名就是为 Java 代码服务的，不知客户端要支持别的语言(如 Python) 时应该如何配置客户端。</description></item><item><title>Kafka 集群间数据镜像实测</title><link>https://yanbin.blog/kafka-mirror-maker-in-action/</link><pubDate>Mon, 28 Oct 2019 00:56:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kafka-mirror-maker-in-action/</guid><description>
&lt;p&gt;由于数据安全，网速等要求，许多公司都会建立多个数据中心，每个数据中心有独立的 Kafka 集群。为保持不同中心间的数据同步，就有必要在 Kafka 集群间进行数据镜像。&lt;code&gt;kafka-mirror-maker&lt;/code&gt; 命令或应用 Kafka Connect 可用于在多个 Kafka 集群相同的 Topic 之间互间同步数据。&lt;/p&gt;
&lt;p&gt;这里就来体验一下不同的 Kafka 集群间如何用 &lt;code&gt;kafka-mirror-maker&lt;/code&gt; 进行 topic 数据镜像。测试环境选择用两个 Vagrant 虚拟机，当然同一个主机上在不同的 ZooKeeper chroot 或不同的端口中也能演示同样的功能。&lt;/p&gt;
&lt;p&gt;首先要两启两个 Vagrant 虚拟机，这里用的是 Ubuntu Server 18.04。需要在本地建立两个目录, 分别是 ubuntu-server-1 和 ubuntu-server-2, 在各自目录中建立 Vagrantfile 文件，内容如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ubuntu/disco64&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;virtualbox&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2048&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;private_network&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dhcp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ubuntu-server-1&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 另一台机器指定 ubuntu-server-2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;以下启动 Vagrant 虚拟机，安装 JDK8 和 启动 ZooKeeper, Kafka 分别要在两个目录 (ubuntu-server-1 和 ubuntu-server-2) 中各执行一遍。</description></item><item><title>Redis 知识点乱记</title><link>https://yanbin.blog/redis-notes-chaos/</link><pubDate>Mon, 14 Oct 2019 21:39:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/redis-notes-chaos/</guid><description>
以下内容完全毫无章序，是阅读 《redis设计与实现(第二版)》一书所划的一些自己助记用的重点。本不访放到博客上来，只称放在个人 Evernote 当中，于此纯粹为了自己往后查阅，所以请不要读它。&lt;br/&gt;&lt;br/&gt;
&lt;div&gt;
&lt;div&gt;OBJECT ENCODING key    可以查数据存储的底层结构类型&lt;/div&gt;
&lt;div&gt;如&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;redis&amp;gt; SADD numbers 1 3 5 7 9&lt;/div&gt;
&lt;div&gt;redis&amp;gt; OBJECT ENCODING numbers&lt;/div&gt;
&lt;div&gt;"intset"&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;redis&amp;gt; ZADD fruit-price 5 banana&lt;/div&gt;
&lt;div&gt;redis&amp;gt; OBJECT ENCODING fruit-price&lt;/div&gt;
&lt;div&gt;"quicklist"&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;Redis 的对象带有访问时间记录信息，该信息可用于计算数据库键的空转时长&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;redis&amp;gt; type msg    返回值对象的类型&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;字符串对象编码可用 int，raw 或者 embstr&lt;/div&gt;</description></item><item><title>用 Java 把内存中的表格数据合并到 SQL Server 表中</title><link>https://yanbin.blog/merge-in-memory-table-data-into-sql-server-table/</link><pubDate>Thu, 26 Sep 2019 22:49:25 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/merge-in-memory-table-data-into-sql-server-table/</guid><description>
&lt;p&gt;承接近两年前的 &lt;a href="https://yanbin.blog/sqlserver-insert-multiple-rows-onetime"&gt;用 PreparedStatement 向 SqlServer 中一次性插入多条记录&lt;/a&gt;，其文后用 User-Defined Type 可用下面简单的代码把 Java 本地内存中表格数据一股脑的刷入到 SQL Server 数据库表格中&lt;/p&gt;
&lt;blockquote&gt;
String sql = "INSERT INTO Customers SELECT * FROM ?";&lt;br /&gt;
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) conn.prepareStatement(sql);&lt;br /&gt;
SQLServerDataTable dataTable = ..... // 生成好的本地表格数据&lt;br /&gt;
pstmt.setStructured(1, "CustomersTableType", dataTable);&lt;br /&gt;
pstmt.execute();
&lt;/blockquote&gt;
&lt;p&gt;上面的 dataTable 本地表格类型变量容易生成，关键是必须在正式数据库数须预先用 &lt;code&gt;CREATE TYPE&lt;/code&gt; 创建好 &lt;code&gt;CustomersTableType&lt;/code&gt; 这个用户自定义类型，这会受权限的约束。如果由 DBA 预先完全依照目标表来创建好这个用户自定义类型，又无法确定是否总是要操作该目标表的所有字段。&lt;/p&gt;
&lt;p&gt;数据库是允许我们创建临时的用户自定义类型</description></item><item><title>等待所有的 CompletableFuture 完成</title><link>https://yanbin.blog/wait-all-completablefuture-done/</link><pubDate>Mon, 23 Sep 2019 23:03:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wait-all-completablefuture-done/</guid><description>
&lt;p&gt;现实中有这样的用法，创建一批在线程池中运行的  CompletableFuture 实例，然后等待它们全部执行完再继续后面的操作。比如说 AWS 的 Lambda, 单单提交任务到线程池，不等待所有任务全部完成便退出主线程的话，AWS 便认为 Lambda 执行完毕，无视线程池中正在执行的任务而强行结束该 Lambda 实例。&lt;/p&gt;
&lt;p&gt;以往我们通常的作法如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ExecutorService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IntStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeClosed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapToObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CompletableFuture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;done &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPool&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompletableFuture&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;all done&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果所有的任务均无异常，上面的代码能得到预想的结果，只要上面打印出 &lt;code&gt;all done&lt;/code&gt; 的话真的就是表明所有的任务都完成了。但是在循环 join &lt;code&gt;futures&lt;/code&gt; 中的每一个 CompletableFuture 时，只要碰到任意一个任务有异常时，便立即抛出给外部线程，不在乎是否还有其他任务正在执行。此时，如果外部未予捕获，当然</description></item><item><title>Redis 中使用服务端 Lua 脚本</title><link>https://yanbin.blog/redis-builtin-lua-script/</link><pubDate>Sun, 22 Sep 2019 01:26:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/redis-builtin-lua-script/</guid><description>
Redis 自 2.6 版本起加入了服务端的 Lua 脚本支持，即增添了 &lt;code&gt;EVAL&lt;/code&gt;, &lt;code&gt;EVALSHA&lt;/code&gt;, &lt;code&gt;SCRIPT&lt;/code&gt; 相关命令。Lua 为何物，Lua 是一个非常轻量级，强大，高效，可内嵌的脚本语言; 产自于巴西，源码和二进制包都只有 200 多 KB。当前版本的 Redis 5.0.5 中 Lua 引擎版本是 Lua 5.1(自 Redis 2.6 起就没变，当前 Lua 为 5.3.5)，可用 Redis 命令 &lt;code&gt;eval "return _VERSION" 0&lt;/code&gt; 查看到。&lt;/p&gt;
&lt;br/&gt;
本文就要探究一下如何在 Redis 中使用 Lua 脚本，以及如何简化与 Redis 的交互。比如说在 Redis 中要先获一个值，然后根据这个值再去 Redis 中获得另一个相关联的值，如果不使用 Lua 脚本就会有两次与 Redis 交互，引入 Lua 脚本可以只用一次操作。&lt;br/&gt;&lt;br/&gt;
本文不具体讲述 Lua 语言本身，只涉及到与 Redis 相关的 Lua 特性。现在来体验下 Lua 中嵌入 Lua 脚本的基本操作。</description></item><item><title>Python 函数声明先后顺序的问题</title><link>https://yanbin.blog/python-function-declaration-order/</link><pubDate>Sat, 20 Jul 2019 23:25:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-function-declaration-order/</guid><description>
&lt;p&gt;在 Python 中如果把函数定义写在调用的下方可能会出错，例如下面的代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;执行时会报出错误&lt;/p&gt;
&lt;blockquote&gt;
NameError: name 'foo' is not defined
&lt;/blockquote&gt;
&lt;p&gt;这时候要把 &lt;code&gt;foo()&lt;/code&gt; 调用代码放到该函数的声明后面&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样执行就一切正常了。这仿佛像是 C 语言中的函数调用需要提前声明一般，例如在 C 语言中要调用后头的定义的函数要写成</description></item><item><title>AWS Python Lambda 使用 Layer</title><link>https://yanbin.blog/aws-python-lambda-layer/</link><pubDate>Sat, 20 Jul 2019 21:33:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-python-lambda-layer/</guid><description>
使用 Python 书写 AWS Lambda 的一个好处就是能够在控制台中直接编辑源代码，非常方便进行快速验证测试 AWS 环境相关的。这只限于使用 AWS 为 Python Lambda 运行时提供的默认组件(比如 boto3)，尚若需要在自己的 Python Lambda 中使用其他的组件(如 redis), 就不得不把自己的代码及依赖打成一个 zip 包再部署，这时候就无法在控制台直接编辑代码了，也只能坠入本地修改代码，重新打包上传测试的循环当中。&lt;br/&gt;&lt;br/&gt;
欲了解 Python Lambda 中除了 boto3 外还能直接使用别的什么组件，可点击此链接 &lt;a href="https://gist.github.com/gene1wood/4a052f39490fae00e0c3"&gt;https://gist.github.com/gene1wood/4a052f39490fae00e0c3&lt;/a&gt; 查看当前。该 gist 也还提供了代码 &lt;a href="https://gist.github.com/gene1wood/4a052f39490fae00e0c3#file-list_aws_lambda_modules-py"&gt;code to run in Lambda&lt;/a&gt; 来获得所有依赖。试了下在 Python Lambda 中，用通常的&lt;br/&gt;
&lt;blockquote&gt;
help('modules')    # 或&lt;br /&gt;
help('modules package')
&lt;/blockquote&gt;
竟然连大名鼎鼎的 boto3 都无法列出来。&lt;br/&gt;&lt;br/&gt;
回到正题来，如果既想用第三方的依赖，又想要在控制台中直接编辑代码进行测试，是否有他法呢？有，那就是 AWS 在 2018 年 11 月推出的 Lambda 层。见 &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/"&gt;AWS Lambda Now Supports Custom Runtimes and Enables Sharing Common Code Between Functions&lt;/a&gt;, 这里的层除了能用来提供 Python 依赖，还许自定义运行时，如 C++ 或 Rust 等写 Lambda 都不是梦。&lt;br/&gt;&lt;br/&gt;
AWS 的服务就像个大口袋，何时偷偷的加添了什么服务，或出了什么新的我，不时关注它的 &lt;a href="https://aws.amazon.com/new/?whats-new-content-all.sort-by=item.additionalFields.postDateTime&amp;amp;whats-new-content-all.sort-order=desc&amp;amp;wn-featured-announcements.sort-by=item.additionalFields.numericSort&amp;amp;wn-featured-announcements.sort-order=asc"&gt;What's New with AWS&lt;/a&gt; 必是个好习惯。</description></item><item><title>开启 Python 组件 Boto3 在 IDE 中的智能提示</title><link>https://yanbin.blog/aws-boto3-python-ide-autocomplete/</link><pubDate>Sat, 13 Jul 2019 13:44:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-boto3-python-ide-autocomplete/</guid><description>
&lt;p&gt;在用 Python 编写 AWS 服务时，要用到 Boto 3 组件，而像 boto3.client('s3') 获得的对象只能被 IDE 识别为一个 BaseClient, 具体包含什么操作方法是在运行时由参数 &lt;code&gt;s3&lt;/code&gt; 指示的基于 JSON 文件所描述的。因此 IDE 对 &lt;code&gt;s3 = boto3.client('s3')&lt;/code&gt; 的 &lt;code&gt;s3&lt;/code&gt; 对象无法提供有效的智能提示，每次用 Boto 3 时不得不打开 Boto 3 的在线 API 文档来对照。长此以往，总觉麻烦且效率低下，有种一直摸着石头过河的感觉。那么，是否有办法让 IDE 智能提示出各种 boto3.client('&amp;lt;service&amp;gt;') 的实际操作呢？网上找了找，确实有这个需求，解决办法有&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/botostubs/"&gt;botostubs&lt;/a&gt;: 与 boto3 API 保持更新(每三天)，并支持众多 IDE, 试过在 IntelliJ IDEA 和 Visual Studio Code 中可用&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/pyboto3/"&gt;pyboto3&lt;/a&gt;: 上次更新在两年前, &lt;a href="https://github.com/wavycloud/pyboto3"&gt;https://github.com/wavycloud/pyboto3&lt;/a&gt;, 只在 Python 2.7 下测试过&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/autoboto/"&gt;autoboto&lt;/a&gt;: 需有智能提示，但改变了应用 Boto 3 组件的方式，不建议使用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文重点推荐 botostubs, 下面会叙说具体理由，在进入正是之前，不妨来回顾一下直接使用 Boto 3 时没有好的智能提示的问题</description></item><item><title>转换 Iterator 为 Java 8 的 Stream</title><link>https://yanbin.blog/convert-iterator-to-java-8-stream/</link><pubDate>Wed, 03 Jul 2019 21:20:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/convert-iterator-to-java-8-stream/</guid><description>
&lt;p&gt;Java 中有关抽象的可遍历的对象有 Iterator, Iterable 和 Java 8 的 Stream, Iterable 可简单的用如下代码转换为 Stream&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;StreamSupport.stream(iterable.spliterator(), false)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;再回过头来，为什么要把 Iterator 或 Iterable 转换为 Stream, 因为 Iterator 和 Iterable 只提供有限的遍历操作，如 Iterator 接口的全部四个方法&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;hasNext()&lt;br /&gt;
next()&lt;br /&gt;
forEachRemaining(consumer)&lt;br /&gt;
remove()&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同样 Iterable 也只有 &lt;code&gt;iterator()&lt;/code&gt;, &lt;code&gt;forEach(consumer)&lt;/code&gt;, 和 &lt;code&gt;spliterator()&lt;/code&gt; 方法。而 Java 8 的 Stream 就大不一样的，带有大量的链式操作方法，如 filter, map, flatMap, collect 等。&lt;/p&gt;
&lt;p&gt;因此如果我们已有一个 Iterator 类型，能够被转换为 Stream 类型的话将会大大简化后续的转换，处理操作。具体的从 Iterator 到 Stream 的转换方式有两种</description></item><item><title>试手 RxJava 2.x 及对线程的初步理解</title><link>https://yanbin.blog/rxjava-get-started/</link><pubDate>Wed, 19 Jun 2019 23:57:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rxjava-get-started/</guid><description>
在进行数据流处理过程中，需要一个高效苗条的流处理组件，比如对输入流能进行分组(窗口)，能进行流量控制(Back Pressure - 背压)，这也就涉及到响应式编程，流处理框架。这方面如果直接基于 Akka actor 来构建 Akka ActorSystem 也是比较复杂，依赖的组件也不少。还有构筑在 Akka actor 之上的 Akka Streams，再往上的 Flink Streaming，它们都有像滑动，滚动窗口的概念，但是依赖更不得了。一个基本的 Flink Streaming 的项目会依赖到 45 M 以上的第三方组件，如果用它来写一个数据流处理的共享组件，那真是要命。Spring 5 也开始带上了自己的 Reactive-Streams 实现 Spring Reactor, 想要把它从 Spring 中单独抽离出也非易事。&lt;/p&gt;
&lt;blockquote&gt;
Flink Streaming 组件依赖：org.apache.fling:flink-streaming-java_2.12:1.80, 会依赖于其他诸如 akka-stream, akka-actor, flink-core, flink-clients, scala-library 等非常多的东西
&lt;/blockquote&gt;
而另一个著名的响应式框架 RxJava 2 就清爽多了，完全没有第三方依赖，要说有也就是定义了四个接口的 reactive-streams(2 KB 大小)，就自身那个  rxjava-2.2.9.jar 包只有 2.3 M，这才叫轻量级。因为它设计来是能被应用于 Android 客户端应用的，Andriod 上的 rxandriod-1.2.1.aar 只有 9 K。所以 RxJava 2.x 太适合用来写一些小的共享组件了。</description></item><item><title>Java 与'嵌入式' PostgreSQL 数据库的单元测试</title><link>https://yanbin.blog/java-postgresql-unit-test/</link><pubDate>Mon, 03 Jun 2019 23:14:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-postgresql-unit-test/</guid><description>
&lt;p&gt;在我们对数据库 DAO 类进行单元测试时，通常不应该依赖于一个外部数据库，所以会选用特定比较接近于真实数据库类型的内存或嵌入式数据库，如 HSQLDB(HyperSQL), H2, Derby 等。但总难免会用到特定数据库的特性，这时候就无法用前述各种数据库进行测试了。非要单元测试中覆盖到所用的数据库特性的话可以选择用 docker，如 &lt;a href="https://www.testcontainers.org/"&gt;Testcontainers&lt;/a&gt;, 经过模块扩展，它可以由 docker 来启动许多种类型的数据库，MySQL, Postgres, Oracle-XE, MS SQL Server, Couchbase 等等，详情见 &lt;a href="https://www.testcontainers.org/modules/databases/"&gt;Database containers&lt;/a&gt;。刚了解到的是它的模块化的无限可能，像支持 Kafka Containers 和 Localstack Module 等。&lt;/p&gt;
&lt;p&gt;这里就不走 Testcontainers 那条路 -- 要求构建服务器上也要有 docker。早先希望能找到一种嵌入式或内存 PostgreSQL 数据库，后来发现 PostgreSQL 未能提供 In-Process 和 In-Memory 的启动方式，好在 PostgreSQL 是开源，有人可以把它改造为小型的可由测试代码启停的本地数据库。有两个具有代表性的组件，分别是 &lt;a href="https://github.com/opentable/otj-pg-embedded"&gt;OpenTable Embedded PostgreSQL Component&lt;/a&gt; 和 &lt;a href="https://github.com/yandex-qatools/postgresql-embedded"&gt;Embedded PostgreSQL Server&lt;/a&gt;，它们都号称是 Embedded，所谓嵌入式，其实是进测试进程外的数据库。&lt;/p&gt;
&lt;p&gt;下面简单体验下两个组件的用法</description></item><item><title>使用 Google Guava Striped 实现基于 Key 的并发锁</title><link>https://yanbin.blog/google-guava-striped-key-based-fine-grain-locks/</link><pubDate>Sun, 02 Jun 2019 12:13:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/google-guava-striped-key-based-fine-grain-locks/</guid><description>
写 Java 代码至今，在应对可能冲突的共享资源操作时会尽量用 JDK 1.5 开始引入的并发锁(如 Lock 的各类实现类, ReentrantLock 等) 进行锁定，而不是原来的 &lt;code&gt;synchronized&lt;/code&gt; 关键字强硬低性能锁。&lt;br/&gt;&lt;br/&gt;
这里是应用 JDK 1.5  的 &lt;code&gt;Lock&lt;/code&gt; 的基本操作步骤&lt;br/&gt;
&lt;blockquote&gt;
private Lock lock = new ReentrantLock();
private void operate() {&lt;br /&gt;
    // 安全操作 ....&lt;br /&gt;
    lock.lock();&lt;br /&gt;
    try {&lt;br /&gt;
        // 对共享资源的操作 ...&lt;br /&gt;
    } finally {&lt;br /&gt;
        lock.unlock();&lt;br /&gt;
    }&lt;br /&gt;
}
&lt;/blockquote&gt;
如此，&lt;code&gt;operate()&lt;/code&gt; 就是一个线程安全的方法，任何对它的调用都安排到了一个队列里等着。但有时候上锁需要考虑更细的粒度，下面是一个演示案例，引出第一个问题&lt;br/&gt;</description></item><item><title>Java 元注解及 Spring 组合注解应用</title><link>https://yanbin.blog/java-spring-meta-annotation/</link><pubDate>Sat, 18 May 2019 00:47:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-spring-meta-annotation/</guid><description>
&lt;p&gt;Java 1.5(Tiger) 个人认为最为激动人心的两个特性是泛型与注解(&lt;a href="https://javapapers.com/core-java/java-features-and-history/"&gt;Java Versions, Features and History&lt;/a&gt;)。泛型自然是不必说了，注解对 Java 世界的改变比泛型伟大的多(现在框架的注解配置)，在 Java 1.5 之前我们只能在 Javadoc 注释中做文章，于是只能用 XDoclet 那样不伦不类的东西。Java 的注解发展到现在几乎可以使用在书写代码时的任何地方，见 &lt;code&gt;java.lang.annotation.ElementType&lt;/code&gt; 中的类型，囊括了 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER(since 1.8), TYPE_USE(since 1.8)。&lt;/p&gt;
&lt;p&gt;Java 1.5 基本确定了注解的基本框架，包括元注解(meta-annotation); 直到 Java 8 又扩展了注解的使用范围，列举如下：&lt;/p&gt;
&lt;p&gt;创建类实例&lt;br /&gt;&lt;br/&gt;
new@Interned MyObject();&lt;/p&gt;
&lt;p&gt;类型映射&lt;br /&gt;&lt;br/&gt;
myString = (@NonNull String) str;&lt;/p&gt;
&lt;p&gt;implements 语句中&lt;br /&gt;&lt;br/&gt;
class UnmodifiableList&amp;lt;T&amp;gt; implements@Readonly List&amp;lt;@Readonly T&amp;gt; { ... }&lt;/p&gt;
&lt;p&gt;throw exception声明&lt;br /&gt;&lt;br/&gt;
void monitorTemperature() throws@Critical TemperatureException { ... }&lt;/p&gt;
&lt;p&gt;解析前面 ElementType Java 8 增加的 TYPE_PARAMETER和 TYPE_USE 注解使用新场合。ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中。ElementType.TYPE_USE 表示该注解能写在使用类型的任何语句中(如: 声明语句、泛型和强制转换语句中的类型)</description></item><item><title>Python 常用日期处理 -- calendar 与 dateutil 模块</title><link>https://yanbin.blog/python-datetime-calendar-dateutil/</link><pubDate>Wed, 01 May 2019 10:50:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-datetime-calendar-dateutil/</guid><description>
&lt;p&gt;本文紧承上一篇 &lt;a href="https://yanbin.blog/python-date-libraries-usages/"&gt;Python 常用日期处理&lt;/a&gt;，因制于篇幅的大小需求才临时分立新篇，这里要简单提到 calendar 和 dateutil 模块的使用，其中 calendar 是 Python 内置的。相比于上一篇而言，此处主旨会更明确一些，只记录三个应用案例，分别是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用 dateutil 灵活的解析 datetime 字符串&lt;/li&gt;
&lt;li&gt;给定起始日期后的连续日期&lt;/li&gt;
&lt;li&gt;给定起始日期后连续的月末日期&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;dateutil 灵活的解析 datetime 字符串&lt;/h3&gt;
&lt;p&gt;使用 Python 内容的  date 或 datetime, 构造它们的实例时需要逐个的传入年月日或时分秒，或者要调用  &lt;code&gt;fromisoformat()&lt;/code&gt; 方法解析严格的字符串表示格式。而 dateutil.parser 的 parse() 方法就显得特别的聪明和随意，它可以智能的解析更丰富的字符串表示方式。详细的支持格式请参考官方文档的 &lt;a href="https://dateutil.readthedocs.io/en/stable/examples.html#parse-examples"&gt;parse examples&lt;/a&gt;，恐怕官方文档也未列举完全，只要觉得合理的时间字符串就可以尝试去解析。下方是一些例子</description></item><item><title>Python 常用日期处理 -- 内置模块 datetime</title><link>https://yanbin.blog/python-date-libraries-usages/</link><pubDate>Wed, 24 Apr 2019 23:30:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-date-libraries-usages/</guid><description>
&lt;p&gt;仅以此篇记录一下个人常用的 Python 处理日期的库与函数，主要涉及的类库有 Python 自带的 &lt;a href="https://docs.python.org/3/library/datetime.html#"&gt;datetime&lt;/a&gt;, &lt;a href="https://docs.python.org/3/library/time.html#module-time"&gt;time&lt;/a&gt; 和 &lt;a href="https://docs.python.org/3/library/calendar.html"&gt;calendar&lt;/a&gt;，以及第三方的 &lt;a href="https://dateutil.readthedocs.io/en/stable/index.html#"&gt;dateutil&lt;/a&gt;。说到日期处理基本上要覆盖的概念有 date, time, datetime, timezone, calendar, 时间的比较与差值，解析与格式化显示等。&lt;/p&gt;
&lt;p&gt;在 datetime 模块中类之间的继承关系如下：&lt;/p&gt;
&lt;blockquote&gt;
object&lt;br /&gt;
├── &lt;strong&gt;date&lt;/strong&gt;&lt;br /&gt;
│      └── &lt;strong&gt;datetime&lt;/strong&gt;&lt;br /&gt;
├── &lt;strong&gt;time&lt;/strong&gt;&lt;br /&gt;
├── &lt;strong&gt;timedelta&lt;/strong&gt;&lt;br /&gt;
└── tzinfo&lt;br /&gt;
         └── timezone
&lt;/blockquote&gt;
&lt;p&gt;我们着重体验一下前面粗体显示的 datetime, date, time, timedelta 对象, timezone 也不是不重要，有时候也可能只需要处理本地时间。</description></item><item><title>Spring ServiceLocator 介绍及应用</title><link>https://yanbin.blog/spring-servicelocator-pattern/</link><pubDate>Tue, 02 Apr 2019 22:24:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-servicelocator-pattern/</guid><description>
&lt;p&gt;在 Spring 中应用 ServiceLocator 方式来获取 Spring Bean 的介绍参考了那么多，其实还是数官方的 &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html"&gt;ServiceLocatorFactoryBean&lt;/a&gt; JavaDoc 文档最言简意该了。Spring 的 ServiceLocator 仿佛用处不大，说到底就是类似于下方找寻某个 Spring Bean 一样：&lt;/p&gt;
&lt;blockquote&gt;
ApplicationContext context = ...;
Service service = context.getBean(ServiceImpl.class);
Service service = context.getBean("myService");
&lt;/blockquote&gt;
&lt;p&gt;只是有了 ServiceLocatorFactoryBean(它本质上就是一个 FactoryBean) 后我们不需要直接与 ApplicationContext 打交道，且多个的 Spring Bean 可以从相关的一个 FactoryBean 获得。下面用两个例子来演示(代码中刨去了 package 和 import 部分的代码)&lt;/p&gt;
&lt;h3&gt;一：实现类只有一个 Spring Bean 时&lt;/h3&gt;
&lt;p&gt;接口类 Parser(我们要定位就是它的实现类)</description></item><item><title>Spring 中 FactoryBean 的使用</title><link>https://yanbin.blog/spring-work-with-factorybean/</link><pubDate>Thu, 07 Mar 2019 21:01:38 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-work-with-factorybean/</guid><description>
&lt;p&gt;许久没记录笔记了，这回来重新熟悉一下 Spring 中 FactoryBean 的使用，顾名思义，它是用来获得相应 Bean 的工厂的。它与另一个 Spring 中的接口 BeanFactory 的作用不一样的，不能多说了。FactoryBean 和 BeanFactory 都是在 &lt;code&gt;org.springframework.beans.factory&lt;/code&gt; 包中，谁能一看类名搞清楚它们的差别？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;FactoryBean: 用于创建某个特定的 Spring bean 的工厂类&lt;/li&gt;
&lt;li&gt;BeanFactory: Spring 上下文的最顶层接口，如 &lt;code&gt;ApplicationContext&lt;/code&gt; 就继承了该接口，它可称之为所有 Spring bean 的工厂&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这儿说的是第一个 FactoryBean, 它的接口声明是&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;FactoryBean&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getObjectType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;isSingleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;它最终的效果是，Spring 容器中注册一个名称为 abcFactoryBean 的 &lt;code&gt;AbcFactoryBean&lt;/code&gt; 实例，通后名称 &lt;code&gt;abcFactoryBean&lt;/code&gt; 获得的实际上是相应 &lt;code&gt;AbcFactoryBean.getObject()&lt;/code&gt; 返回的对象，类型为 &lt;code&gt;getObjectType()&lt;/code&gt;, &lt;code&gt;isSingleton()&lt;/code&gt; 是否是单例。</description></item><item><title>Python 对象当函数使及动态添加方法</title><link>https://yanbin.blog/add-python-methods-dynamically/</link><pubDate>Fri, 08 Feb 2019 02:42:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/add-python-methods-dynamically/</guid><description>
&lt;p&gt;继续阅读 《Python Tricks: The Book》，书中说到 "Objects Can Behave Like Functions", 就是把对象当成函数来调用，在普通对象后加个括号就能调用相应的 &lt;code&gt;__call__&lt;/code&gt; 函数。下面是书中的例子&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;class Adder:&lt;br /&gt;
    def __init__(self, n):&lt;br /&gt;
        self.n = n&lt;br /&gt;
&lt;br /&gt;
    def __call__(self, x):&lt;br /&gt;
        return self.n + x&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后是应用类 &lt;code&gt;Adder&lt;/code&gt; 的代码&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;plus_3 = Adder(3)&lt;br /&gt;
plus_3(4)  # 普通对象 plus_3 当成函数来用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面 &lt;code&gt;plus_3&lt;/code&gt; 是一个普能的对象，并非一个函数，但如果把它当成函数来看待，那么 &lt;code&gt;plus_3(4)&lt;/code&gt; 就会去寻找相应的 &lt;code&gt;__call__&lt;/code&gt; 函数。</description></item><item><title>Python 函数参数的拆解</title><link>https://yanbin.blog/python-function-argument-unpacking/</link><pubDate>Wed, 06 Feb 2019 00:59:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-function-argument-unpacking/</guid><description>
本文为阅读 《Python Tricks: The Book》一书的 3.5 Function Argument Unpacking 的笔记与扩充理解。函数参数拆解是定义可变参数(VarArgs) &lt;code&gt;*args&lt;/code&gt; 和 &lt;code&gt;**kwargs&lt;/code&gt; 的反向特性。&lt;br/&gt;&lt;br/&gt;
&lt;code&gt;*args&lt;/code&gt; 和 &lt;code&gt;**kwars&lt;/code&gt; 是函数可定义一个形参来接收传入的不定数量的实参。&lt;br/&gt;&lt;br/&gt;
而这里的函数参数拆解是形参定义多个，在调用时只传入一个集合类型对象(带上 * 或 ** 前缀)，如 &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, 甚至是 &lt;code&gt;generator&lt;/code&gt;, 然后函数能自动从集合对象中取得对应的值。&lt;br/&gt;&lt;br/&gt;
如果能理解下面赋值时的参数拆解和 Python 3.5 的新增 &lt;code&gt;*&lt;/code&gt; &lt;code&gt;**&lt;/code&gt; 操作，那么于本文讲述的特性就好理解了。&lt;br/&gt;&lt;br/&gt;
唯一的不同时作为参数的集合传入函数时必须前面加上 &lt;code&gt;*&lt;/code&gt; 或 &lt;code&gt;**&lt;/code&gt;, 以此宣告该参数将被拆解，而非一个整体作为一个函数参数。加上 &lt;code&gt;*&lt;/code&gt; 或 &lt;code&gt;**&lt;/code&gt; 与 Java 的 &lt;code&gt;@SafeVarargs&lt;/code&gt; 有类似的功效，最接近的是 Scala 的 `foo(Array[String]("d", "e") : &lt;span style="color: #800000;"&gt;_*&lt;/span&gt;)` 写法。参见：&lt;a href="https://yanbin.blog/java-scala-call-varargs-method/"&gt;Java 和 Scala 调用变参的方式&lt;/a&gt;</description></item><item><title>Python 集合的遍历,推导及 filter/map/reduce 操作</title><link>https://yanbin.blog/python-collection-map-reduce-operations/</link><pubDate>Tue, 29 Jan 2019 19:21:37 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-collection-map-reduce-operations/</guid><description>
&lt;p&gt;借鉴于其他多数语言中集合的 map/reduce 操作，也想总结一下在 Python 中如何对集合进行 map/reduce。而不是对于 Python 集合只会用简单的 &lt;code&gt;for ... in&lt;/code&gt; 遍历，处于之间的是 Python 的  Comprehension 操作，更倾向于译作推导; 在 Scala 中也有类似的  &lt;code&gt;for-comprehension&lt;/code&gt; 语法。&lt;/p&gt;
&lt;p&gt;因此本文将涉及到三个方面的知识，基本的集合遍历操作，集合的推导，与 filter/map/reduce 操作。我无法写出诸如 掌握 Python 集体看这一篇就够了 的文章，但基本由本篇出发能了解到 Python 集合的基本遍历，转换操作。其余如切片，和更多能作用于 Python 集合的函数未有提及, 请查阅相关文档。&lt;/p&gt;
&lt;h3&gt;集合的基本遍历操作&lt;/h3&gt;
&lt;p&gt;这一块主要是复习功课， 希望由此熟练掌握常用的集合遍历操作方式</description></item><item><title>PostgreSQL 批量插入, 更新和合并操作</title><link>https://yanbin.blog/postgresql-unnest-batch-crud-merge/</link><pubDate>Tue, 29 Jan 2019 01:39:34 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/postgresql-unnest-batch-crud-merge/</guid><description>
就在 2019 年 1 月份微软收购了 PostgreSQL 数据库的初创公司 &lt;a href="https://www.citusdata.com/"&gt;CitusData&lt;/a&gt;, 在云数据库方面可以增强与 AWS 的竟争。AWS 的 RDS 两大开源数据库就是 MySQL(Aurora 和 MariaDB 是它的变种) 和 PostgreSQL。&lt;br/&gt;&lt;br/&gt;
而 PostgreSQL 跳出了普通关系型数据库的类型约束，它灵活的支持 JSON, JSONB, XML, 数组等类型。比如说字段类型可以是各种形式的数组，一维或多维。&lt;br/&gt;
&lt;blockquote&gt;
create table t1(&lt;br /&gt;
    address varchar(5)[3],&lt;br /&gt;
    counter integer[3][3],&lt;br /&gt;
    schedule text[][]&lt;br /&gt;
)
&lt;/blockquote&gt;
上面只是认识了一下 PostgreSQL 这一亮眼的特性，本篇重点不在如何定义操作数组类型的字段，而是对于普通的非数组字段类型如何用与数组相关的 &lt;code&gt;unnest&lt;/code&gt; 关键字进行记录的批量插入，更新以及合并操作。&lt;br/&gt;&lt;br/&gt;
在正式使用介入 &lt;code&gt;unnest&lt;/code&gt; 之前先熟悉一下 PostgreSQL 的 upsert(update insert) 操作。受其他数据库的影响，总以后 PostgreSQL 也应该支持 &lt;code&gt;merge into&lt;/code&gt; 语句，而且竟然 PostgreSQL 官方也有文档介绍 &lt;a href="https://www.postgresql.org/message-id/attachment/23520/sql-merge.html"&gt;MERGE&lt;/a&gt; 有模有样的，然而试了一下根本就不支持 &lt;code&gt;merge into&lt;/code&gt; 操作。</description></item><item><title>用 AWS Secrets Manager 存储和管理密钥</title><link>https://yanbin.blog/aws-secrets-manager-store-secrets/</link><pubDate>Sat, 05 Jan 2019 01:48:35 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-secrets-manager-store-secrets/</guid><description>
&lt;p&gt;目前我们在 AWS 上把密钥，API Key  等信息是存储在  AWS Systems Manager 的 Parameter Store 中，它只提供了用 KMS Key 加密存储字符串的功能，最大字符串大小是 4096 个字符，它是免费的。&lt;/p&gt;
&lt;p&gt;最近发现 AWS 上有一个新的服务 AWS Secrets Manager(2018 年 4 月发布的)，听起来用它来存储密钥信息更高大上些。它同样提供了用 KMS Key 加密存储字符串的功能，字符串最大也是 4096 个字符。从 AWS Web 控制台上看可配置用 Key/Value 的形式，其实本质也是存储为一个 JSON 字符串。&lt;/p&gt;
&lt;p&gt;Secrets Manager 与 Parameter Store 更多的功能是能与 RDS 集成 -- 选择数据库收集数据库的配置信息(主机名，端口，实例等), 还有就是可配置定期更新密钥，这对一个安全的系统定期改密码很重要。对于定期更新密钥的未作深入研究，AWS Secrets Manager 本身知道如何轮换 RDS 数据库的密钥，其他的需要一个 Lambda 来支持。</description></item><item><title>Python 多线程编程</title><link>https://yanbin.blog/python-programming-with-threads/</link><pubDate>Fri, 04 Jan 2019 19:31:19 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-programming-with-threads/</guid><description>
这几天一直浸淫在对 Python 的学习当中，对于一个更习惯 Java 语言的人来说，在接接触 Python 各种概念时会不停的与 Java 进行碰撞。譬如这里要说到的线程，Python 能如何像 Java 一样创建并执行单个线程，以及是否也能使用线程池来进行多作务的执行呢？&lt;br/&gt;&lt;br/&gt;
整个读完了《THE Quick Python Book》一书也只字未提多线程，然而对于有长时间的 IO 等待的程序，对于当今普及的多核以及核内超线程的 CPU 来说，不使用多线程来并行或并发处理任务是万万不能的，否则效率的差别是数量级的。&lt;br/&gt;&lt;br/&gt;
基于与 Java 多线程编程进行的比较，主要着力于两个问题：1）创建并执行新的线程，2）线程池中执行任务&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;创建并执行新的线程&lt;/h3&gt;
默认的，代码是在主线程中执行，主线程名称为 &lt;code&gt;MainThread&lt;/code&gt;。如果要创建一个子线程并执行需要用到模块 &lt;code&gt;threading&lt;/code&gt;。下面的是基本的代码</description></item><item><title>熟悉和应用 Python 的装饰器</title><link>https://yanbin.blog/master-python-decorators/</link><pubDate>Thu, 03 Jan 2019 23:10:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/master-python-decorators/</guid><description>
&lt;p&gt;Python 在语法上除了冒号与强制缩进外其实也没有太多令人眼前一亮的东西，倒是它的装饰器(Decorator) 值得玩味。初读 《THE Quick Python Book》一书，关于 Decorator(装饰器) 这一节匆匆而过，只是觉得它像 Java 注解一样的东西，没太细究。后来慢慢看到还是不少地方在用装饰器，如 Python 的属性 &lt;code&gt;@property&lt;/code&gt;, &lt;code&gt;@name_attr.setter&lt;/code&gt;, 还有 Flask 中用于定义路由的 &lt;code&gt;@app.route('/')&lt;/code&gt; 等。&lt;/p&gt;
&lt;p&gt;因此还是有必要花些功夫去更深入的了解 Python 的装饰器，从目前对装饰器的理解，它兼具 Java 的注解与代理的功能，而且比 Java 中自定义注解的处理与动态代理的实现要简单的多，甚至不需要特别牵涉到到面向方面的编程这么一个专门的概念。Python 的装饰器并非指的设计模式中的装饰器模式，Python 的装饰器主要还是关于代理，或叫方法拦截，切面的。&lt;/p&gt;
&lt;p&gt;装饰器简单说来就是一个高阶函数，即一个函数作为另一个函数的参数，比如说函数 A 作为函数 B 的参数，然后函数 B 的实现有能力决定实际调用 A 的前后作点手脚，甚至压根不调用 A。由此，装饰器完全可以实现面向方面的 @Before, @After, @Around, @AfterReturning, @AfterThrowing 所有语义。&lt;/p&gt;
&lt;p&gt;Python 中的函数像 JavaScript 的一样是头等对象(first-class objects)，所以函数本身可以作为参数任意传递，一个函数也可以返回另一个函数。Python 的函数中还允许用相同的 &lt;code&gt;def func():...&lt;/code&gt; 语法定义内部函数。</description></item><item><title>Python 包管理及虚拟环境的应用(三: pipenv)</title><link>https://yanbin.blog/python-package-manager-venv-virtualenv-3/</link><pubDate>Thu, 03 Jan 2019 02:30:22 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-package-manager-venv-virtualenv-3/</guid><description>
前两篇分别学习了 Python 如何进行依赖的管理，以及结合虚拟环境来使用 &lt;code&gt;pip&lt;/code&gt; 进行依赖管理。而有人觉得把 &lt;code&gt;virtualenv&lt;/code&gt; 与 &lt;code&gt;pip&lt;/code&gt; 分开来操作太麻烦了，而且 &lt;code&gt;requirements.txt&lt;/code&gt; 描述依赖的方式十分笨拙，所以在前两者之上创建了 &lt;code&gt;pipenv&lt;/code&gt;, 也谈不上重新发明了轮子吧。&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;3. Pipenv: 新一代依赖管理与虚拟环境&lt;/h2&gt;
倘若不是经由 &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt; 而来到 &lt;code&gt;pipenv&lt;/code&gt;，没有对比也就无法体会到 &lt;code&gt;pipenv&lt;/code&gt; 的妙处的。&lt;code&gt;pipenv&lt;/code&gt; 在总结了 &lt;code&gt;virtualenv/venv&lt;/code&gt; 的缺点之后由 Kenneth Reitz 于 2017 年 1 月发布的新型 Python 依赖管理器。&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;它不再需要单独用 &lt;code&gt;virtualenv&lt;/code&gt; 和 &lt;code&gt;pip&lt;/code&gt;，只要一条命令 &lt;code&gt;pipenv&lt;/code&gt; 完成所有的事&lt;/li&gt;
&lt;li&gt;不用手动管理 &lt;code&gt;requirements.txt&lt;/code&gt; 文件，而是由  &lt;code&gt;pipenv&lt;/code&gt; 自动维护 &lt;code&gt;Pipfile&lt;/code&gt; 和 &lt;code&gt;Pipfile.lock&lt;/code&gt; 文件&lt;/li&gt;
&lt;li&gt;自动创建虚拟环境，并且虚拟环境与项目文件分离&lt;/li&gt;
&lt;li&gt;更详尽的依赖图(例如 &lt;code&gt;pipenv graph&lt;/code&gt;)，像 &lt;code&gt;mvn dependency:tree&lt;/code&gt; 那样显示依赖树&lt;/li&gt;
&lt;li&gt;控制台下输出颜色更丰富&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Python 包管理及虚拟环境的应用(二: virtualenv)</title><link>https://yanbin.blog/python-package-manager-venv-virtualenv-2/</link><pubDate>Thu, 03 Jan 2019 01:16:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-package-manager-venv-virtualenv-2/</guid><description>
原本想在一篇之内覆盖到 Python 的包管理以及各类虚拟环境的应用，没想根本就是一发不可收拾，恐怕两篇都完不了，所以也要进行重构。这里只涉及到 Python 的虚拟环境 &lt;code&gt;venv&lt;/code&gt; 和 &lt;code&gt;virtualenv&lt;/code&gt;，至于标题的话，也不想再改了，只作一，二，三编号，必要时仍能连缀成长篇。最后一篇将单独学习 &lt;code&gt;pipenv&lt;/code&gt; 的应用。&lt;/p&gt;
&lt;br/&gt;
以下序号也是承接上一篇 &lt;a href="https://yanbin.blog/python-package-manager-venv-virtualenv-1/"&gt;Python 包管理及虚拟环境的应用(一)&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;2. Python 虚拟环境&lt;/h2&gt;
关于创建 Python 项目的虚拟环境，有三个工具可用, &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;virtualenv&lt;/code&gt;, 以及后面单独要学到的 &lt;code&gt;pipenv&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/venv.html"&gt;&lt;code&gt;venv&lt;/code&gt;&lt;/a&gt; , 即 &lt;code&gt;python3 -m venv&lt;/code&gt; 命令，Python 3.3 及新版本自带了，为 Python 3.4 及以后的版本创建的虚拟环境会有 &lt;code&gt;pip&lt;/code&gt; 和 &lt;code&gt;setuptools&lt;/code&gt; 命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;virtualenv&lt;/code&gt; 需要单独安装，但是它支持 Python 2.7 和  Python 3.3+， 创建的虚拟环境中带有 &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;setuptools&lt;/code&gt; 和 &lt;code&gt;wheel&lt;/code&gt; 命令&lt;/li&gt;
&lt;li&gt;另外，&lt;code&gt;pyvenv&lt;/code&gt; 脚本也可用来创建 Python 虚拟环境，不过它自 Python 3.6 不推荐使用，建议用 &lt;code&gt;python3 -m venv&lt;/code&gt; 命令&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;</description></item><item><title>Python 包管理及虚拟环境的应用(一: pip)</title><link>https://yanbin.blog/python-package-manager-venv-virtualenv-1/</link><pubDate>Thu, 03 Jan 2019 01:14:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-package-manager-venv-virtualenv-1/</guid><description>
话说 Python 的哲学之一就是: 用一种方法，最好是只有一种方法来做一件事。可以用 &lt;code&gt;python -m this&lt;/code&gt; 或在  python 交互界面下 &lt;code&gt;import this&lt;/code&gt; 看到 &lt;code&gt;The Zen of Python&lt;/code&gt; 有一句：&lt;/p&gt;
&lt;blockquote&gt;
There should be one-- and preferably only one --obvious way to do it.
&lt;/blockquote&gt;
然而 Python 在关于包管理(依赖管理)一事上却让人面临了众多的选择。&lt;br/&gt;&lt;br/&gt;
即使是 Java 日趋发展庞大的今天，包管理工具也没有如今的 Python 复杂，Java 的包管理工具经历了手工下载 jar 包，Maven, Ant+Ivy, Gradle, sbt, 但主流的也就 Maven 和 Gradle, 并且它们兼具项目构建的功能。&lt;br/&gt;&lt;br/&gt;
这里有一个 Python 包管理工具变迁的视频: &lt;a href="https://www.youtube.com/watch?v=GBQAKldqgZs"&gt;Kenneth Reitz - Pipenv: The Future of Python Dependency Management - PyCon 2018&lt;/a&gt;。再更早的 Python 依赖管理的方式不说，视频中提到了</description></item><item><title>Python 中的 urlencode 和 urldecode 操作</title><link>https://yanbin.blog/python-urlencode-urldecode/</link><pubDate>Tue, 01 Jan 2019 20:05:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-urlencode-urldecode/</guid><description>
&lt;p&gt;Web  编程中由于需要用 Form 或 URL 来传递参数，所以必然会有 urlencode 和 urldecode 的操作，Python Web 也不例外。Python 对 URL 的编解码操作提供了 &lt;code&gt;urllib&lt;/code&gt; 模块，下面例子中所使用的 Python 版本是  3.6.7，不同的 Python 版本可能略有差异。&lt;/p&gt;
&lt;p&gt;简面言之本文就是关于以下六个函数的使用，更多关于 &lt;code&gt;urllib&lt;/code&gt; 的用法请自行进一步研究。&lt;/p&gt;
&lt;blockquote&gt;
from urllib.parse import urlencode, parse_ql, quote, quote_plus, unquote, unquote_plus
&lt;/blockquote&gt;
&lt;p&gt;为什么两个 urlencode 和  urldecode 操作会涉及到六个函数的应用呢，分别来讲述&lt;/p&gt;
&lt;h3&gt;1. urlencode&lt;/h3&gt;
&lt;p&gt;Python 的  &lt;code&gt;urllib&lt;/code&gt; 直接提供了  &lt;code&gt;urlencode&lt;/code&gt; 函数，它的操作数是一个字典</description></item><item><title>Jackson 反序列化 "Y"/"N" 为相应的布尔值</title><link>https://yanbin.blog/jackson-yn-boolean-value-deserialization/</link><pubDate>Sun, 16 Dec 2018 16:07:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jackson-yn-boolean-value-deserialization/</guid><description>
&lt;p&gt;JSON 表示布尔值标准的形式是 &lt;code&gt;true&lt;/code&gt;  和  &lt;code&gt;false&lt;/code&gt;，如果 Java 对应的类型是对象  Boolean，那么在 JSON 中也可以是  &lt;code&gt;null&lt;/code&gt;。如果收到 JSON 数据是用 'Y'/'N', 或 'Yes'/'No' 来表示布尔值的，那么使用 Java 的 Jackson 库如何把它们反序列化为相应的布尔属性值呢？&lt;/p&gt;
&lt;p&gt;如果按照 JSON 规范必须把内容中的布尔值全部转换为 &lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;, 然而再反序列化，否则需要定制 &lt;code&gt;Boolean&lt;/code&gt; 类型的反序列化类，可应用到全局的 boolean 类型，或指派给特定的 boolean 类型属性。&lt;/p&gt;
&lt;p&gt;如果尝试反序列化 &lt;code&gt;Y&lt;/code&gt;, 或 &lt;code&gt;N&lt;/code&gt; 为 Java 的 boolean 值，会有只接受  true 或 false 的异常：&lt;/p&gt;
&lt;blockquote&gt;
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.lang.Boolean from String "Y": only "true" or "false" recognized
&lt;/blockquote&gt;
&lt;p&gt;具体步骤是：</description></item><item><title>让 Python 的数据库查询返回字典记录</title><link>https://yanbin.blog/python-database-query-return-dictionary-result/</link><pubDate>Sun, 16 Dec 2018 12:30:08 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-database-query-return-dictionary-result/</guid><description>
在使用  Python 进行数据库查询，通常情况下 &lt;code&gt;cursor&lt;/code&gt; 的 &lt;code&gt;fetchall&lt;/code&gt;, &lt;code&gt;fetchmany&lt;/code&gt; 返回的是元组(Tuple) 的列表，所以对查询到的结果只能用索引下标来访问，而无法通过字段名来获取值。对 Java JDBC 的 ResultSet 操作，我们有两种获取值的方式，resultSet.getString(1) 和 resultSet.getString('name')。&lt;br/&gt;&lt;br/&gt;
其实只要能用数字索引访问到字段值也就足够了，查询后字段名可以由 &lt;code&gt;cursor.description&lt;/code&gt; 获得。通过字段名来访问值唯一的好处估计是出错的概率小些罢了，比如 result['firstname'], result['lastname'] 总是比 result[1], result[2] 更不容易搞混，错误定位也会更轻松。&lt;br/&gt;&lt;br/&gt;
假如有下面的数据库表与两条记录</description></item><item><title>Python 版的 try-with-resources -- with 上下文管理器</title><link>https://yanbin.blog/python-try-with-resources-with-context-manager/</link><pubDate>Tue, 04 Dec 2018 00:42:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-try-with-resources-with-context-manager/</guid><description>
&lt;p&gt;作为一个  Java 为母语的程序员来讲，学习起其他新的语言就难免任何事都与 Java 进行横向对比。Java 7 引入了能省去许多重复代码的 &lt;code&gt;try-with-resources&lt;/code&gt; 特性，不用每回 try/finally 来释放资源(不便之处有局部变量必须声明在  try 之前，finally 里还要嵌套 try/catch 来处理异常)。比如下面的  Java 代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InputStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileInputStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;abc.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;它相应的不使用 try-with-resources 语法的代码就是</description></item><item><title>用 .pth 文件附加 Python 模块搜索路径</title><link>https://yanbin.blog/pth-file-append-python-module-search-path/</link><pubDate>Thu, 22 Nov 2018 16:57:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/pth-file-append-python-module-search-path/</guid><description>
&lt;p&gt;上一篇 &lt;a href="https://yanbin.blog/python-module-search-path/"&gt;Python 的模块搜索路径&lt;/a&gt;，介绍了 Python 的模块搜索路径，最终起作用的是 &lt;code&gt;sys.path&lt;/code&gt; 路径列表。如果要自定义自己的搜索路径，就是要怎么定制 &lt;code&gt;sys.path&lt;/code&gt; 的内容。可以简单的用 &lt;code&gt;PYTHONPATH&lt;/code&gt; 环境变量前向添加，这儿将要说的是用 &lt;code&gt;.pth&lt;/code&gt; 文件的方式。也可由此进一步理解 Python 依赖管理工具，像  &lt;code&gt;virtualenv&lt;/code&gt; 等的工作原理。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.pth&lt;/code&gt; 文件名是什么，无所谓，Python 只认扩展名。&lt;code&gt;.pth&lt;/code&gt; 文件中每行指定一个路径 -- 绝对或相对路径(相对于本  &lt;code&gt;.pth&lt;/code&gt; 文件所在的目录)，另外还可以空行或 &lt;code&gt;#&lt;/code&gt; 开始的注释行，还能有 &lt;code&gt;import&lt;/code&gt; 语句，大概只用来校验是否能导入成功，程序代码中还是需要显示的 import 模块。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;.pth&lt;/code&gt; 文件放在哪里&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.pth&lt;/code&gt; 文件创建好后应该放到哪里去呢？不是 &lt;code&gt;sys.prefix&lt;/code&gt; 指示的位置，也不是 &lt;code&gt;sys.path&lt;/code&gt; 中任意一个目录，而是  &lt;code&gt;sys.path&lt;/code&gt; 中属于 site.packages 的某一个目录中。可以用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getusersitepackages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsitepackages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;查看到, 看我在  Ubuntu Linux 中看到的内容(为便于阅读，显示列表内容时进行了换行处理)</description></item><item><title>Python 的模块搜索路径</title><link>https://yanbin.blog/python-module-search-path/</link><pubDate>Thu, 22 Nov 2018 03:09:06 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-module-search-path/</guid><description>
&lt;p&gt;一种语言要使用到外部库(模块) 时必然会涉及到从哪里以及按何顺序加载依赖，就像 LD_LIBRARY_PATH, CLASSPATH 那样，Python 也有其默认的模块搜索顺序, 依序找到想要的模块即停止。Python 中 &lt;code&gt;sys.path&lt;/code&gt; 返回的列表包含了模块搜索的顺序，我们可以程序中修改该列表，或用 &lt;code&gt;PYTHONPATH&lt;/code&gt; 环境变量前插路径，甚至是用  &lt;code&gt;.pth&lt;/code&gt; 文件来附加路径。&lt;/p&gt;
&lt;p&gt;简单的，可以执行命令 &lt;code&gt;python3 -c "import sys; print(str(sys.path).replace(',', '\n'))"&lt;/code&gt; 来查看 python3 交互 shell 下的模块搜索路径，类似结果如下：&lt;/p&gt;
&lt;blockquote&gt;
[''&lt;br /&gt;
'/usr/lib/python36.zip'&lt;br /&gt;
'/usr/lib/python3.6'&lt;br /&gt;
'/usr/lib/python3.6/lib-dynload'&lt;br /&gt;
'/home/yanbin/.local/lib/python3.6/site-packages'&lt;br /&gt;
'/usr/local/lib/python3.6/dist-packages'&lt;br /&gt;
'/usr/lib/python3/dist-packages'&lt;br /&gt;
'/usr/lib/python3.6/dist-packages']
&lt;/blockquote&gt;
&lt;p&gt;注意，第一个元素是个空字符串，代表进入 python3 shell 时的当前目录。&lt;/p&gt;
&lt;p&gt;如果在通过一个 py 脚本文件来打印 &lt;code&gt;sys.path&lt;/code&gt; 的话显示稍微有所差异。比如在目录 &lt;code&gt;/home/yanbin/Developers/&lt;/code&gt; 下创建 &lt;code&gt;test.py&lt;/code&gt; 文件，内容为</description></item><item><title>Python 的 __str__ 和 __repr__ 方法比较</title><link>https://yanbin.blog/python-__str__vs__repr__methods/</link><pubDate>Mon, 12 Nov 2018 01:47:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-__str__vs__repr__methods/</guid><description>
&lt;p&gt;阅读到 Strings 中关于转换对象为字符串的内容，介绍了 &lt;code&gt;repr&lt;/code&gt; 函数，趁着还没有真正了解 Python 面向对象的生疏与热度，感性上理解一下 &lt;code&gt;repr&lt;/code&gt; 与 &lt;code&gt;str&lt;/code&gt; 这两个函数的区别。&lt;/p&gt;
&lt;p&gt;Python 的全局方法 &lt;code&gt;repr&lt;/code&gt; 和 &lt;code&gt;str&lt;/code&gt; 会映射到对象的 &lt;code&gt;__repr__&lt;/code&gt; 和 &lt;code&gt;__str__&lt;/code&gt; 的方法调用，还有 &lt;code&gt;str(obj)&lt;/code&gt; 时会调用哪个方法，以及 &lt;code&gt;print(obj)&lt;/code&gt; 和调试 Python 代码时的对象显示会调用哪个方法呢？这就是本文想要印证的内容。&lt;/p&gt;
&lt;p&gt;恰如 Java 的 &lt;code&gt;System.out.println(obj)&lt;/code&gt; 或 &lt;code&gt;"hello" + obj&lt;/code&gt; 都会调用 Java 对象的 &lt;code&gt;toString()&lt;/code&gt; 方法，那么 Python 中是怎么一回事呢？&lt;/p&gt;
&lt;p&gt;来自某本 Python 入门书的解释 &lt;code&gt;repr&lt;/code&gt; 和 &lt;code&gt;str&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;repr&lt;/code&gt;: &lt;strong&gt;formal&lt;/strong&gt; string representation of a Python object&lt;/li&gt;
&lt;li&gt;&lt;code&gt;str&lt;/code&gt;: &lt;strong&gt;informal&lt;/strong&gt; string representation of a Python object，或者说 &lt;strong&gt;printable&lt;/strong&gt; string representation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;首先 &lt;code&gt;repr&lt;/code&gt; 是 representation 的意思，一个是正式，另一个是非正式，看起来 &lt;code&gt;repr&lt;/code&gt; 比 &lt;code&gt;str&lt;/code&gt; 显得重要些。</description></item><item><title>Apache 中运行 Python CGI 程序</title><link>https://yanbin.blog/apache-run-python-cgi-config/</link><pubDate>Sat, 10 Nov 2018 02:16:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-run-python-cgi-config/</guid><description>
&lt;p&gt;Web 程序我还是喜欢用 Apache 来作为入口，因为我用的系统是 Mac OS, Apache 它就静静的躺上那儿了。最好是都像 PHP 那样的程序，无需启动额外的服务，这种使用方式的缺陷是不太适合于做微服务。&lt;/p&gt;
&lt;p&gt;这儿呢，我也是来探索如何在 Apache 中运行 Python 的 CGI 程序，这主要是涉及了 Apache 与 Python 的集成。集成方式有直接集成(mod_python)，CGI，FastCGI, WSGI 和 uWSGI。本文主要是讲如何运行一个简单的 Python CGI。&lt;/p&gt;
&lt;h3&gt;直接集成(mod_python)&lt;/h3&gt;
&lt;p&gt;虽然由于多方面的安全因素，这已经成为历史了，但还是提一下。参见&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/index.php/Apache_HTTP_Server/mod_python"&gt;Apache HTTP Server/mod_python&lt;/a&gt;(它推荐使用 &lt;a href="https://wiki.archlinux.org/index.php/Apache_HTTP_Server/mod_wsgi"&gt;mod_wsgi&lt;/a&gt;, 以下该节相关内容摘在本链接，没有亲自尝试)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://modpython.org/"&gt;mod_python 官网&lt;/a&gt;(最后的更新是 2013-11-13)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;需要安装模块 mod_python.so，并在 &lt;code&gt;httpd.conf&lt;/code&gt; 加上相关的配置</description></item><item><title>开始再战 Python - 重新入门 - 工具篇</title><link>https://yanbin.blog/python-again-lightweight-tools/</link><pubDate>Sat, 03 Nov 2018 03:06:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/python-again-lightweight-tools/</guid><description>
&lt;p&gt;两年前想摸摸 Python 的门道，简要记录了一篇 &lt;a href="https://yanbin.blog/my-python-warm-up/"&gt;我的 Python 快速入门&lt;/a&gt;，当时只觉得那是一种与 C/Java 异样风格的编程语言，没领会到特别之处。如今对 Python 的感受就不一般了，因为是云服务，机器学习，人工知道大行其道的年代，所以更为急迫的想把它好好弄明白。&lt;/p&gt;
&lt;p&gt;Python 3 说是设计的比  Python 2 要合理的多，所以没有保持向后兼容，现在初学 Python 对于选择哪个版本的 Python 时没有任何犹豫了，当然拣最新的 Python 3 学了。因此各种命令行工具都得用有 3 标记的，如  &lt;code&gt;python3&lt;/code&gt;, &lt;code&gt;pip3&lt;/code&gt;, &lt;code&gt;pydoc3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;编程语言特别是脚本语言，简单验证或入门最需要一个 &lt;code&gt;REPL&lt;/code&gt; 工具，这里不论大型项目时最好选择全功能型的 IDE，像 PyCharm 这样的重型武器。在安装 Python 3 后，它自带了两个，分别是 &lt;code&gt;python3&lt;/code&gt; 和 &lt;code&gt;idle3&lt;/code&gt;(据说是 Integrated DeveLopment Environment 的缩写词, 可没见这么简陋的 IDE)&lt;/p&gt;
&lt;h3&gt;Python&lt;/h3&gt;
&lt;p&gt;命令 &lt;code&gt;python3&lt;/code&gt; 进到控制台的 Python 交互界面，Python 标准的 &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; 提示符。没有语法高亮，没有自动缩进，唯一高级点的功能就是 &lt;code&gt;tab&lt;/code&gt; 能对变量可以方法，属性的自动完成。</description></item><item><title>Kafka Producer 设置 Interceptor 来统计消息</title><link>https://yanbin.blog/kafka-producer-interceptor-record-statistics/</link><pubDate>Thu, 01 Nov 2018 01:33:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kafka-producer-interceptor-record-statistics/</guid><description>
&lt;p&gt;Kafka 消息的 Producer 在调用 &lt;code&gt;producer.send()&lt;/code&gt; 方法发送消息时会先把消息放到本地缓冲中，然后由  Kafka 网络线程从缓冲中提取消息再送到 Kafka 代理上去。本地缓冲区大小由 &lt;code&gt;buffer.memory&lt;/code&gt; 来配置，默认为 32M(32 * 1024 * 1024L)。如果发消息到网络慢于提交消息到缓冲区的话，缓冲区就可能会满就无法接受新的消息，这时候就要依照 &lt;code&gt;block.on.buffer.full&lt;/code&gt; 设置是否暂停还是抛出异常，默认为暂停 &lt;code&gt;producer.send()&lt;/code&gt;；暂停时间由 &lt;code&gt;max.block.ms&lt;/code&gt; 决定，默认为 60 秒。&lt;code&gt;producer.send()&lt;/code&gt; 返回一个 &lt;code&gt;Future&amp;lt;RecordMetadata&amp;gt;&lt;/code&gt;, 也就是每次调用 &lt;code&gt;send()&lt;/code&gt; 方法在缓冲区满后要等待 60 秒才能获得结果(异常)。&lt;/p&gt;
&lt;p&gt;这里的关系是 &lt;code&gt;send()&lt;/code&gt; --a--&amp;gt; &lt;code&gt;缓冲区&lt;/code&gt; --b--&amp;gt; &lt;code&gt;发送到 Kafka 代理&lt;/code&gt;，自然要在 &lt;code&gt;a&lt;/code&gt; 与 &lt;code&gt;b&lt;/code&gt; 之间进行流量控制，如果 &lt;code&gt;b&lt;/code&gt; 太慢，缓冲区满的话必须把 &lt;code&gt;a&lt;/code&gt; 放慢下来。如果能基于缓冲区已使用大小来放缓 &lt;code&gt;a&lt;/code&gt; 也是也行的，留待以后进行研究。本文提供另一种实现参考，为 Producer 配置一个 &lt;code&gt;Interceptor&lt;/code&gt; 能够大致统计多少消息提交到缓冲区，多少消息从缓冲区取出。&lt;/p&gt;
&lt;p&gt;Kafka 的所有配置项常量可以在这个页面 &lt;a href="https://kafka.apache.org/0100/javadoc/constant-values.html"&gt;https://kafka.apache.org/0100/javadoc/constant-values.html&lt;/a&gt; 找到。对 `interceptor.classes` 的解释是：可以为 Producer 配置一个或多个 Interceptor(需要实现 ProducerInterceptor)。另外 Consumer 也有自己的  Interceptor(实现 ConsumerInterceptor)。</description></item><item><title>Java 8 Map 中新增的方法使用记录</title><link>https://yanbin.blog/java-8-map-new-added-methods/</link><pubDate>Wed, 31 Oct 2018 00:13:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-map-new-added-methods/</guid><description>
&lt;p&gt;得益于 Java 8 的 default 方法特性，Java 8 对 Map 增加了不少实用的默认方法，像 &lt;code&gt;getOrDefault&lt;/code&gt;, &lt;code&gt;forEach&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;replaceAll&lt;/code&gt;, &lt;code&gt;putIfAbsent&lt;/code&gt;, &lt;code&gt;remove(key, value)&lt;/code&gt;, &lt;code&gt;computeIfPresent&lt;/code&gt;, &lt;code&gt;computeIfAbsent&lt;/code&gt;, &lt;code&gt;compute&lt;/code&gt; 和&lt;code&gt;merge&lt;/code&gt; 方法。另外与 Map 相关的 &lt;code&gt;Map.Entry&lt;/code&gt; 也新加了多个版本的 &lt;code&gt;comparingByKey&lt;/code&gt; 和 &lt;code&gt;comparingByValue&lt;/code&gt; 方法。&lt;/p&gt;
&lt;p&gt;为达到熟练运用上述除 &lt;code&gt;getOrDefault&lt;/code&gt; 和 &lt;code&gt;forEach&lt;/code&gt; 外的其他方法，有必要逐一体验一番，如何调用，返回值以及调用后的效果如何。看看每个方法不至于 Java 8 那么多年还总是  &lt;code&gt;if(map.containsKey(key))...&lt;/code&gt; 那样的老套操作。&lt;/p&gt;
&lt;p&gt;前注：Map 新增方法对  present 的判断是 map.containsKey(key) &amp;amp;&amp;amp; map.get(key) != null，简单就是  map.get(key) != null，也就是即使 key 存在，但对应的值为 null 的话也视为 absent。absent 就是 map.get(key) == null。&lt;/p&gt;
&lt;p&gt;不同 Map 实现对 key/value 是否能为 null 有不同的约束, HashMap, LinkedHashMap, key 和 value 都可以为 null 值，TreeMap 的 key 为不能为 null, 但 value 可以为 null, 而 Hashtable, ConcurrentMap 则 key 和 value 都不同为 null。一句话 absent/present 的判断是 map.get(key) 是否为 null。&lt;/p&gt;
&lt;p&gt;方法介绍的顺序是它们相对于本人的生疏程度而定的。每个方法介绍主要分两部分，参考实现代码与示例代码执行效果。参考实现代码摘自 JDK 官方的 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/Map.html"&gt;Map JavaDoc&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;getOrDefault 方法&lt;/h3&gt;
&lt;p&gt;本想忽略这个方法的测试，因为涉及到 key 存在，值为 null 的情况。当 key 不存在或相关联的值为 null 时，返回默认值，否则返回实际值。不要认为 key 存在时总是返回 map.get(key) 的值。&lt;/p&gt;
&lt;h4&gt;参考实现：&lt;/h4&gt;</description></item><item><title>Spring Boot 与 Logback 日志配置</title><link>https://yanbin.blog/springboot-work-with-logback/</link><pubDate>Mon, 08 Oct 2018 01:35:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springboot-work-with-logback/</guid><description>
&lt;p&gt;本文记录 SpringBoot 与 Logback 是如何工作的，即观察 SpringBoot 中 Logback  是怎么一步一步初始化的。用以测试的 SpringBoot 版本是 1.5.16, 而非最新的 SpringBoot 2。关于 SpringBoot 日志的官方文档在 &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html"&gt;Logging&lt;/a&gt;, 但不太详细或透彻。本文也不承诺说就理解得更有深度，只是为官方文档提供更多方面的参考。&lt;/p&gt;
&lt;p&gt;SpringBoot 默认使用 Slf4J + Logback 来记录日志，对于一个基本的依赖于&lt;/p&gt;
&lt;blockquote&gt;
&amp;lt;dependency&amp;gt;&lt;br /&gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;&lt;br /&gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
&amp;lt;/dependency&amp;gt;
&lt;/blockquote&gt;
&lt;p&gt;的 Spring Boot 项目，上面组件依赖了 spring-boot-starter-logging 组件，而该组件又引入了以下几个依赖&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;logback-classic:   依赖了 Slf4J&lt;/li&gt;
&lt;li&gt;jcl-over-slf4j&lt;/li&gt;
&lt;li&gt;jul-to-slf4j&lt;/li&gt;
&lt;li&gt;log4j-over-slf4j&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;相当于把其他的日志框架全桥接到了 Slf4J + Logback 上去了。</description></item><item><title>Amazon SQS 触发 AWS Lambda 及重试/DLQ</title><link>https://yanbin.blog/amazon-sqs-trigger-aws-lambda/</link><pubDate>Tue, 02 Oct 2018 00:48:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/amazon-sqs-trigger-aws-lambda/</guid><description>
&lt;p&gt;Amazon 在 2018 年 6 月份宣布可以设置用 SQS 来触发 Lambda，SQS 不再是单纯用于 ECS 服务中，或用于伸缩控制的。这儿就来亲自尝试一下用 SQS 驱动的 Lambda，以及要注意的要素。&lt;/p&gt;
&lt;p&gt;首先使用 Java 编写 Lambda 的话，AWS 在 com.amazonaws:aws-lambda-java-events:2.20 版本开始加入了 com.amazonaws.services.lambda.runtime.events.SQSEvent 类，可是这个版本的 aws-lambda-java-events 是有所限的，因为 &lt;code&gt;SQSEvent.SQSMessage&lt;/code&gt; 类是私有的，这就造成不能获取到 SQSEvent 中的记录数据。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;//下面的操作代码无法编译，因为 SQSEvent.SQSMessage 是私有的，不可访问&lt;br /&gt;
SQSEvent.SQSMessage sqs = sqsEvent.getRecords().get(0); &lt;br /&gt;
sqsEvent.getRecords().get(0).getBody();&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Java 使用 SQS 来驱动 Lambda 的话，至少需要 com.amazonaws:aws-lambda-java-events:2.2.1 版本，从此 SQSEvent.SQSMessage 变成 public 了。该版本是于  2018 年 6 月传到 Maven 官方中央仓库的，这就是那时才能真正用来写 Java 的 SQS 触发的 Lambda.&lt;/p&gt;
&lt;p&gt;同时此篇也是作为上文 &lt;a href="https://yanbin.blog/aws-lambda-work-with-dlq/"&gt;AWS Lambda 重试与死信队列(DLQ)&lt;/a&gt; 的一个很重要的补充。在此也会验证 SQS 触发的 Lambda 的重试机制以及 DLQ 相关的内容。</description></item><item><title>AWS Lambda 重试与死信队列(DLQ)</title><link>https://yanbin.blog/aws-lambda-work-with-dlq/</link><pubDate>Sun, 30 Sep 2018 23:46:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-lambda-work-with-dlq/</guid><description>
&lt;p&gt;AWS Lambda 允许设置 &lt;code&gt;Debugging and error handling&lt;/code&gt;, 在 Lambda 出现异常，达到最大的重试次数后，把以下信息放到选择的 SNS 或 SQS 主题作为死信队列(DLQ - Dead Letter Queue)，包括&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;原始 Lambda 接收到的消息(基于 SNS 和 SQS 消息的总大小，可能会被截取，本人猜测，尤其是 Kinesis 的消息会比较大)&lt;/li&gt;
&lt;li&gt;原始 Lambda 的 RequestId&lt;/li&gt;
&lt;li&gt;ErrorCode（三位数字的 HTTP 错误码）&lt;/li&gt;
&lt;li&gt;ErrorMessage, 即原 Lambda 抛出 Exception 的 getMessage() 信息，截取 1 KB 字符串&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;并且 Lambda 要使用 DLQ 的话还必须设置当前 Lambda 的 IAM role 有对于 SNS/SQS 主题相应的 &lt;code&gt;sns:Publish&lt;/code&gt; 和 &lt;code&gt;sqs:SendMessage&lt;/code&gt; 权限。&lt;/p&gt;
&lt;p&gt;AWS Lambda 基本重试规则：对于 Kinesis 消息会无限重试直至消息过期，对于 SNS 或 SQS 的消息出现异常后会再重试两次。参考：&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/retries-on-errors.html"&gt;AWS Lambda Retry Behavior&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;而在重试次数用完后仍然失败，并且设置了 DLQ 的话就会发送消息到 DLQ 中去。</description></item><item><title>跳过构造函数创建 Java 对象(测试)</title><link>https://yanbin.blog/create-java-instance-bypass-constructor/</link><pubDate>Tue, 21 Aug 2018 23:31:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/create-java-instance-bypass-constructor/</guid><description>
如果一个 Java 类在初始化时会有外部依赖，这就给单元测试创建它的实例时造成困难。当然被测试类可以改造为依赖全部构造时注入或创建实例后延迟注入，这里不考虑这种改造。&lt;/p&gt;
&lt;br/&gt;
可以参看我以前一篇类似的日志：&lt;a href="https://yanbin.blog/jmockit-mock-constructors/" target="_blank" rel="noopener noreferrer"&gt;使用 JMockit 来 mock 构造函数&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
来说下面的例子&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PriceInquiry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;priceInquiry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PriceInquiry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.........&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;priceInquiry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(....);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
假如上面的代码是不能改动的，并且在 &lt;code&gt;new PriceInquiry()&lt;/code&gt; 时依赖于网络环境，所以单机情况不能创建成功。也就使得测试时试图&lt;br/&gt;
&lt;blockquote&gt;
new OrderService();
&lt;/blockquote&gt;
会失败。并且试图用 Mockito 的 &lt;code&gt;@InjectMocks&lt;/code&gt; 也不行</description></item><item><title>如何快乐的使用 Java 8 的 Lambda</title><link>https://yanbin.blog/happy-with-java8-lambda/</link><pubDate>Wed, 08 Aug 2018 23:45:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/happy-with-java8-lambda/</guid><description>
&lt;p&gt;Java 8 的 Lambda 特性较之于先前的泛型加入更能鼓舞人心的，我对 Lambda 的理解是它得以让 Java 以函数式思维的方式来写代码。而写出的代码是否是函数式，并不单纯在包含了多少 Lambda 表达式，而在思维，要神似。&lt;/p&gt;
&lt;p&gt;实际中看过一些代码，为了 Lambda 表达式而 Lambda(函数式)，有一种少年不识愁滋味，为赋新词强说愁的味道。从而致使原本一个简单的方调用硬生生的要显式的用类如 &lt;code&gt;apply()&lt;/code&gt;, &lt;code&gt;accept(obj)&lt;/code&gt; 等形式。不仅造成代码可读性差，且可测试性也变坏了。&lt;/p&gt;
&lt;p&gt;为什么说的是快乐的使用 Java 8 的 Lambda 呢？我窃以为第一个念头声明 Lambda 表达式为实例/类变量(像本文第一段代码那样)，而不是方法的，一定会觉得如此使用方式很快乐的。所谓独乐乐，不如众乐乐；独乐乐，众不乐定然是更大的快乐; 更极致一些，不管什么时候必须是：我快乐，所以你也快乐。&lt;/p&gt;
&lt;p&gt;一方面也在于 Java 还没有进化到 JavaScript 或  Scala 那样的水平，JavaScript 的函数类型变量，不一定要用 &lt;code&gt;apply&lt;/code&gt; 或 &lt;code&gt;call&lt;/code&gt;, 直接括号就能实现方法调用。Scala 的函数类型用括号调用也会自动匹配到 &lt;code&gt;apply&lt;/code&gt; 或 &lt;code&gt;update&lt;/code&gt; 等方法上去。</description></item><item><title>Jackson 序列化忽略指定类型的属性</title><link>https://yanbin.blog/jackson-ignore-specified-field-type/</link><pubDate>Mon, 06 Aug 2018 23:52:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jackson-ignore-specified-field-type/</guid><description>
本文准确来讲是探讨如何用 Jackson 来序列化 Apache avro 对象，因为简单用 Jackson 来序列化 Apache avro 对象会报错。原因是序列化 &lt;code&gt;Schema getSchema()&lt;/code&gt; 时会报错，后面会讲到，需要序列化时忽略该属性。那么能不能在 &lt;code&gt;getSchema()&lt;/code&gt; 上加上 &lt;code&gt;@JsonIgnore&lt;/code&gt; 来忽略该属性呢？原理上是通的。不过手工修改的 avsc 生成的 Java 文件随时会因为重新编译而还原，所以不太具有实际可操作性，当然通过定制编译 avsc 用的模板文件来加入 &lt;code&gt;@JsonIgnore&lt;/code&gt; 是另一回事。&lt;br/&gt;&lt;br/&gt;
由于不能在要忽略的字段上添加 &lt;code&gt;JsonIgnore&lt;/code&gt; 来控制，而如果我们明确了要忽略的字段类型的话，是能够定制 Jackson 的  ObjectMapper  来屏蔽某个特定的类型。来看下面序列化 Apache avro 对象的例子：&lt;br/&gt;&lt;br/&gt;
假设我们有一个 Apache 的 Schema 文件 &lt;code&gt;user.avsc&lt;/code&gt;, 内容如下：</description></item><item><title>Java8 Optional 几个常见错误用法</title><link>https://yanbin.blog/java8-optional-several-common-incorrect-usages/</link><pubDate>Sun, 22 Jul 2018 23:23:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java8-optional-several-common-incorrect-usages/</guid><description>
&lt;p&gt;Java 8 引入的 Optional 类型，基本是把它当作 null 值优雅的处理方式。其实也不完全如此，Optional 在语义上更能体现有还是没有值。所以它不是设计来作为 null 的替代品，如果方法返回 null 值表达了二义性，没有结果或是执行中出现异常。&lt;/p&gt;
&lt;p&gt;在 Oracle  做  Java 语言工作的  Brian Goetz 在 Stack Overflow 回复  &lt;a href="https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555"&gt;Should Java 8 getters return optional type?&lt;/a&gt; 中讲述了引入  Optional 的主要动机。&lt;/p&gt;
&lt;blockquote&gt;
Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.
&lt;/blockquote&gt;
&lt;p&gt;说的是  Optional 提供了一个有限的机制让类库方法返回值清晰的表达有与没有值，避免很多时候 null 造成的错误。并非有了  Optional 就要完全杜绝 NullPointerException。&lt;/p&gt;
&lt;p&gt;在 Java 8 之前一个实践是方法返回集合或数组时，应返回空集合或数组表示没有元素; 而对于返回对象，只能用 null 来表示不存在，现在可以用  Optional 来表示这个意义。&lt;/p&gt;
&lt;p&gt;自 Java8 于  2014-03-18 发布后已 5 年有余，这里就列举几个我们在项目实践中使用 Optional 常见的几个用法。&lt;/p&gt;
&lt;h3&gt;Optional 类型作为字段或方法参数&lt;/h3&gt;
&lt;p&gt;这儿把 Optional  类型用为字段(类或实例变量)和方法参数放在一起来讲，是因为假如我们使用 IntelliJ IDEA 来写 Java 8 代码，IDEA 对于  Optional 作为字段和方法参数会给出同样的代码建议：</description></item><item><title>Java 9 - 说说响应式流</title><link>https://yanbin.blog/java-9-talk-reactive-stream/</link><pubDate>Tue, 03 Jul 2018 01:33:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-9-talk-reactive-stream/</guid><description>
&lt;p&gt;最初看到 Java 9 的这个新特性没太在意，及至重新关注到 Spring 5/Springboot 2 的响应式编程的时候才真正重视起 Reactive Streams(响应式流或反应式流)。应用响应式流的编程也就叫做响应式编程(Reactive Programming)，无论是翻译成反应式编程都有些令人摸不准头脑。与此对应的在 Web 设计方面有一个叫做响应式 Web 设计(Responsive web design)，两个词都译作响应式，却有些差别，大概是 Reactive 被译为反应的原因之一。&lt;/p&gt;
&lt;p&gt;通过这里对  Reactive Streams 的学习，主要目的是为了进一步掌握 Spring 5/Springboot 2 的响应式 MVC 作铺垫的，不至于猛然间见 Flux, Mono 而不知所措。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Functional_reactive_programming"&gt;函数式响应式编程&lt;/a&gt;概念最早来自于九十年代末，这也激发了微软的 &lt;a href="https://en.wikipedia.org/wiki/Erik_Meijer_(computer_scientist)"&gt;Erik Meijer&lt;/a&gt; 设计开发了 .NET 的  Rx(Reactive eXtension) 库，以及到后来 Netflix 的  RxJava 也与他有关系。Reactive Stream 更像是一种编程模式，致力于解决一个生产者产生一系列消息，一个或多个去消费它们的问题。两者的名词我们会用: producer-consumer(生产者-消费者), source/sink(水源/水槽, Akka Stream 用了这个概念), publisher-subscriber(发布者-订阅者)。&lt;/p&gt;
&lt;p&gt;既然 Reactive Stream 和 Java 8 引入的 Stream 都叫做流，它们之间有什么关系呢？有一点关系，Java 8 的 Stream 主要关注在流的过滤，映射，合并，而  Reactive Stream 更进一层，侧重的是流的产生与消费，即流在生产与消费者之间的协调。</description></item><item><title>相比于 Java 10 的 var, 更期待 Java 11 支持多行字符串</title><link>https://yanbin.blog/from-java-10-var-to-11-candicate-multiple-line-string/</link><pubDate>Sat, 16 Jun 2018 02:24:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/from-java-10-var-to-11-candicate-multiple-line-string/</guid><description>
&lt;p&gt;Java 六个月的版本迭代周期让我们突然间有些喘不过气来，又 Java 11 才会是一个长期支持版本，如果可能的话我们将会是从 Java 8 直接往 Java 11 跳去。在 Java 8 大行其道，裹足不前的当下，谈论着未来 Java 11 不确定的新特性，有一种看着别人家碗里的不过瘾，还要看着别人家锅里的感觉。&lt;/p&gt;
&lt;p&gt;本篇原本只是为了对 Java 11 潜在的原始字符串字面量(Raw String Literals)的哨探，同时考虑到 Java 10 仅有的一个语言层面的新特性 &lt;code&gt;var&lt;/code&gt; &lt;span style="text-decoration: underline;"&gt;局部变量类型推断&lt;/span&gt;，所以顺带介绍一下。&lt;/p&gt;
&lt;p&gt;我这儿把它称之为 &lt;strong&gt;var 局部变量类型推断&lt;/strong&gt;，说明了&lt;code&gt;var&lt;/code&gt; 类型推断并不适用于类或实例的变量，或方法的参数。而且 &lt;code&gt;var&lt;/code&gt; 也并未上升为一个  Java 的关键字，我们仍然可以用 &lt;code&gt;var&lt;/code&gt; 作为变量或方法名。&lt;/p&gt;
&lt;p&gt;对局部变量用 &lt;code&gt;var&lt;/code&gt; 进行类型推断的代码示例变化如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Java 10 以前&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Java 10 开始&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Java 10 以前&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Java 10 开始，推断为 int 类型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;都不需要引入&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;类&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt; 学了 &lt;code&gt;Scala&lt;/code&gt; 的语法，再重复一遍，&lt;code&gt;var&lt;/code&gt; 只能用于局部变量，这有一个好处就是局部变量的跨越性较小，不至于 &lt;code&gt;var&lt;/code&gt; 声明的变量阅读时只有编译器知道它的确切类型。&lt;code&gt;var&lt;/code&gt; 类型推断给代码书写上的变化就是对局部变量的任何类型声明都可以替代为 &lt;code&gt;var&lt;/code&gt;。</description></item><item><title>WordPress 在 Linux 下不能向 Gmail 邮箱发邮件的问题</title><link>https://yanbin.blog/wordpress-linux-cannot-send-email-to-gmail/</link><pubDate>Thu, 14 Jun 2018 00:24:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-linux-cannot-send-email-to-gmail/</guid><description>
一年前解决了 &lt;a href="https://yanbin.blog/wordpress-linux-cannot-send-out-email/"&gt;WordPress 在 Linux 下不能发送邮件的问题&lt;/a&gt;，后来有段时间很正常，我的网站接收邮件的邮箱是 Gmail 的。但是近来，网站上有留言时极少收到通知邮件，怀疑是 Debian 下的  &lt;code&gt;exim4&lt;/code&gt; 又不能正常工作了。但是试了如下的 PHP 脚本&lt;/p&gt;
&lt;blockquote&gt;
$ php -a&lt;br /&gt;
Interactive mode enabled&lt;br /&gt;
php&amp;gt; mail('&amp;lt;my_gmail_account&amp;gt;@gmail.com', 'test subject', 'test content');&lt;br /&gt;
php &amp;gt; exit
&lt;/blockquote&gt;
从命令行上并没有提示任何的错误，但是检查我的 Gmail 信箱，死活就是收不到邮件。即使直接用  &lt;code&gt;mail&lt;/code&gt; 命令&lt;br/&gt;
&lt;blockquote&gt;
echo Hello World | mail -v -s Test &amp;lt;my_gmail_account&amp;gt;@gmail.com
&lt;/blockquote&gt;
也是不行的。&lt;br/&gt;&lt;br/&gt;
查看日志文件 &lt;code&gt;/var/log/exim4/mainlog&lt;/code&gt;, 发现有下面的错误信息</description></item><item><title>Linux 下配置滚动日志之 rotatelogs</title><link>https://yanbin.blog/linux-config-log-ratation-rotatelogs/</link><pubDate>Tue, 12 Jun 2018 00:11:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-config-log-ratation-rotatelogs/</guid><description>
&lt;p&gt;上一篇 &lt;a href="https://yanbin.blog/linux-config-log-ratation-logrotate"&gt;Linux 下配置滚动日志之 logrotate&lt;/a&gt;, 介绍了定时服务 logrotate 的方式对日志进行滚动，删除旧归档。logrotate 是目前流行 Linux 发行版内置的定时服务，默认每日根据配置文件来滚动日志文件。那么它可能有一个弊端就是，如果每日增长的日志超大，同时会暴盘，那么就必须调整 logrotate 这个 cron 定时服务为每小时，或自定义的 cron 表达式来控制。&lt;/p&gt;
&lt;p&gt;而本文所介绍的 rotatelogs 工具(与 logrotate 名称太过相似) 是采用管道操作的方式来控制日志的滚动，可以基于实时监控的日志文件大小来滚动日志，也可以配置像 logrotate 来定时滚动日志。rotatelogs 是出自于 Apache HTTP Server 家族的，它被用于 Apache HTTP Server 的就错误日志，访问日志的滚动控制。&lt;/p&gt;
&lt;p&gt;类似于 rotatelogs 的工具不有一个老旧的 &lt;a href="http://oldzipsarchive.dreamhosters.com/apache/log_rotation/cronolog_faq.htm"&gt;cronolog&lt;/a&gt;，也是应用管道操作控制日志，它早已无人问津，最近更新在五年前，且功能很弱，只带滚动，不能清理旧归档，磁盘空间占用仍然是无上限。</description></item><item><title>本站已启用新域名 yanbin.blog</title><link>https://yanbin.blog/start-new-domain-name-yanbin-blog/</link><pubDate>Sun, 10 Jun 2018 01:31:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/start-new-domain-name-yanbin-blog/</guid><description>
&lt;p&gt;本站已启用新域名 yanbin.blog, 博客链接已更新为 &lt;a href="https://yanbin.blog"&gt;https://yanbin.blog&lt;/a&gt;,  完全脱离了主流域名后缀了。其实也是不得已而为之，毕竟  unmi.cc 相伴多年。在接下来的一个多月里新老域名都同时有效，已作 301 重定向，希望搜索引擎能尽快切换过来。届时 unmi.cc 将会失效，也许是一个月，也许是永远，又将为互联网创建不少死链接。&lt;/p&gt;
&lt;p&gt;本站自 2010 年独立创建以来，一直用的就是 unmi.cc 这个域名，从 bloghost.cn 上购买的。由于后来域名都是在 GoDaddy 管理的，所以想着域名 unmi.cc 转到一块进行管理。同时也是为了防备着国内互联网公司不光在主机服务上设限制，也可能会在域名上作起文章来。&lt;/p&gt;
&lt;p&gt;于是试图联系 bloghost.cn, 于是便是中国特色了，假若要从 bloghost.cn 买新服务，他们还算和气，但想从他们那儿转出域名，没门。看他们的网站还号称专业&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2018/start-new-domain-name-yanbin-blog/yanbin.blog-2-1.png"
width="448px"
/&gt;
&lt;p&gt;别一不小心反而给他们做了个广告，其实是真不值得信赖的。他们网站上的联系方式只有 E-mail 和几个 IM 聊天号，没有电话</description></item><item><title>Java 9 - 快速创建不可变集合</title><link>https://yanbin.blog/java-9-quick-create-immutable-collections/</link><pubDate>Wed, 06 Jun 2018 00:22:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-9-quick-create-immutable-collections/</guid><description>
&lt;p&gt;平台之所以谓之平台，以其能建立一个生态，并与之外围达成共赢。霸道点的平台也会反噬外围生态，像微软集成浏览器，媒体播放器。还有即将的 iOS 12 要把应用商店多是收费的 AR 皮尺放到它自己系统中来，走别人的路，让别人无路可走。从此众泰皮尺部的唯一的生产工具就会是人手一部能安装 iOS 12 iPhone 了。&lt;/p&gt;
&lt;p&gt;JDK 也不例外，Java 8 之前日期库的话 &lt;a href="http://www.joda.org/joda-time/"&gt;Joda-Time&lt;/a&gt; 是首要之选，Java 8 集成后应该是鲜有人问津。以往说到集合操作库，有两个选择，其一为 &lt;a href="https://commons.apache.org/proper/commons-collections/"&gt;
Apache Commons Collections&lt;/a&gt;，二为 Google 的 &lt;a href="https://github.com/google/guava/wiki"&gt;Guava&lt;/a&gt;，当然前者与后者竞争中也早已败下阵来，况且前者还受到 Java 8 的夹击。
而本文要说的可以说是 Java 9 把 Guava 中创建不可变集合的方式据为已用了，直截了当的说，凡是 Java 9 后有创建不可变集合的需求，只要用三大接口 &lt;code&gt;List&lt;/code&gt;， &lt;code&gt;Set&lt;/code&gt;，&lt;code&gt;Map&lt;/code&gt; 中的 &lt;code&gt;of(...)&lt;/code&gt; 方法就对了。&lt;/p&gt;
&lt;p&gt;Java 9 之前，当我们需要集合相关的操作，两个选择：&lt;/p&gt;</description></item><item><title>Linux 下配置滚动日志之 logrotate</title><link>https://yanbin.blog/linux-config-log-ratation-logrotate/</link><pubDate>Fri, 01 Jun 2018 02:07:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-config-log-ratation-logrotate/</guid><description>
&lt;p&gt;日志是个好东西，便于定位历史问题，但记录太多，不滚动，不除旧总暴盘的时候。如果是用日志框架输出的日志，像 Log4j 或 Logback 通过选择具有滚动特性的 Appender 就能实现日志的滚动，并删除旧的归档日志文件。但也有在程序当中难以控制的日志输出文件，这用的话必须采取事后补救措施，程序尽管往一个日志文件里写，由另一个程序来对该日志文件进行归档，清理操作。&lt;/p&gt;
&lt;p&gt;与此相关的工具，我们可以找到以下几个&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://linux.die.net/man/8/logrotate"&gt;logrotate&lt;/a&gt;, 如今的多数 Linux 发布版都自带了，感觉有一种主场优势。github 上 &lt;a href="https://github.com/logrotate/logrotate"&gt;logrotate/logrotate&lt;/a&gt; 仍活跃着&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.newsyslog.org/manual.html"&gt;newsyslog&lt;/a&gt;,  FreeBSD 和  Mac 系统自带，应该不常用。Mac OS 下可以看下配置文件 &lt;code&gt;/etc/newsyslog.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://oldzipsarchive.dreamhosters.com/apache/log_rotation/cronolog_faq.htm"&gt;cronolog&lt;/a&gt;, 原本的官网 www.cronolog.org 全是日文了，找到它的快照 &lt;a href="http://oldzipsarchive.dreamhosters.com/apache/log_rotation/cronolog_faq.htm"&gt;fleible web log rotation&lt;/a&gt;, github 上 &lt;a href="https://github.com/fordmason/cronolog"&gt;fordmason/cronolog&lt;/a&gt; 最近更新是五年前&lt;/li&gt;
&lt;li&gt;&lt;a href="https://httpd.apache.org/docs/2.4/programs/rotatelogs.html"&gt;rotatelogs&lt;/a&gt;, 出自于 Apache HTTP 项目, Apache HTTP server 用它滚动访问和错误日志&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本人最为推崇使用第一个工具 &lt;code&gt;logrotate&lt;/code&gt;, 因为多数 Linux 系统自带，不像 cronolog 和 rotatelogs 需要额外安装。它也有着更完备的功能，下面慢慢领略&lt;/p&gt;
&lt;h3&gt;logrotate 的工作机制&lt;/h3&gt;
&lt;p&gt;Linux 下默认有一个每日执行的 Cron Job，配置在 &lt;code&gt;/etc/cron.daily/logrotate&lt;/code&gt;，文件内容为(以 centos7 为例)</description></item><item><title>Java 9 线程栈遍历 API</title><link>https://yanbin.blog/java-stack-walking-api/</link><pubDate>Wed, 30 May 2018 01:29:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-stack-walking-api/</guid><description>
&lt;h3&gt;什么是线程栈&lt;/h3&gt;
继续纠缠 Java 9 的新特性，仍然是一个边角料，即 Java 9 增加了对线程栈遍历的 API。那么什么是线程栈，JVM 在创建每一个线程的同时都会创建一个私有的虚拟机栈，每一桢代表着一个方法调用，每次方法的调用与退出意味着压栈与出栈。每一桢上有局部变量，操作数常量引用等信息，这也是为什么局部变量是能最快被销毁的对象。过深的栈(比如过多的递归调用) 会出现我们程序员赖以生存的 StackOverflow。&lt;/p&gt;
&lt;br/&gt;
浅显些说，线程栈就是通常我们捕获到异常后，用 &lt;code&gt;e.printStackTrace()&lt;/code&gt; 看到自 main 方法追溯到当前方法的调用。例如：&lt;br/&gt;
&lt;blockquote&gt;
java.lang.RuntimeException: stack&lt;br /&gt;
    at cc.unmi.TestStackWalking.m2(TestStackWalking.java:15)&lt;br /&gt;
    at cc.unmi.TestStackWalking.m1(TestStackWalking.java:10)&lt;br /&gt;
    at cc.unmi.TestStackWalking.main(TestStackWalking.java:6)
&lt;/blockquote&gt;
调用层次是 main() 调用 m1(), m1() 调用 m2(), m2() 中的代码如下&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
上面输出的每一行就是一个栈桢，输出了当前类名，方法名，代码行号。</description></item><item><title>对 Java 9 把单个下划线作为关键字的猜想</title><link>https://yanbin.blog/java-9-single-underscore-keyword-thinking/</link><pubDate>Sun, 27 May 2018 12:27:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-9-single-underscore-keyword-thinking/</guid><description>
&lt;p&gt;我们知道 Java 的合法命名是以字母或下划线开头的字符串，当然，以前单个下划线 &lt;code&gt;_&lt;/code&gt; 也是一个合法的变量命名。但是自 Java 8 的第一个版本开始，单个下划线的变量名编译时会有警告&lt;/p&gt;
&lt;blockquote&gt;
int _ = 99;
&lt;/blockquote&gt;
&lt;p&gt;用 Java 8 编译时提示警告：&lt;/p&gt;
&lt;blockquote&gt;
Test.java:2: warning: '_' used as an identifier&lt;br /&gt;
    int _ = 99; &lt;br /&gt;
         ^&lt;br /&gt;
    (use of '_' as an identifier might not be supported in releases after Java SE 8)&lt;br /&gt;
1 warning
&lt;/blockquote&gt;
&lt;p&gt;这正是为 Java 9 作的预谋，以一个 LTS 版的 Java 8 作为过渡。因此，来到了 Java 9 后，单个下划线不再担当普通变量名的角色，变成了一个保留关键字，只说是另有任用。&lt;/p&gt;
&lt;p&gt;先来看下 Java 9 中的单个下划线变量名编译时的错误提示&lt;/p&gt;
&lt;blockquote&gt;
Test.java:2: error: as of release 9, '_' is a keyword, and may not be used as an identifier&lt;br /&gt;
    int _ = 99; &lt;br /&gt;
         ^&lt;br /&gt;
1 error
&lt;/blockquote&gt;
&lt;p&gt;到底单下划线会作为什么用途呢? 已发布的 Java 10 没有给出答案，正在演进中的 Java 11 也没有相关的信息。</description></item><item><title>Java 9 - 平台日志 API</title><link>https://yanbin.blog/java-9-platform-logging-api/</link><pubDate>Sat, 26 May 2018 12:19:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-9-platform-logging-api/</guid><description>
关于 Java 9 的新特性从某本书的最后一个说起：平台日志 API。个人没感觉这个有什么实质的用途，所谓的平台日志是指 JDK 自身代码，或者是 JVM 组件中的日志输出，而在自己应用程序代码中却不会去用这个平台日志 API。这个所谓的 Platform Logging API 名称的意义也就是在这里，平台用的，在诊断时用来观察 JDK 类或 JVM 中的日志输出，比如应该可以截获到  JVM 本地代码实现中的日志输出。对我们在项目中如何处理日志并不会有什么影响，该怎么还是怎么，不过了解多一点东西应该不会浪费脑容量的。&lt;br/&gt;&lt;br/&gt;
新加的平台日志体现在 &lt;code&gt;java.lang.System&lt;/code&gt; 中新加的几个方法和类&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2018/java-9-platform-logging-api/java9-platform-logging-1.png"
width="558px"
/&gt;
我们可以尝试着在代码使用一下它&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestLogging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello Java 9 Platform Logging API&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
输出如下&lt;br/&gt;
&lt;blockquote&gt;
May 26, 2018 10:56:51 AM cc.unmi.TestLogging main&lt;br /&gt;
INFO: Hello Java 9 Platform Logging API
&lt;/blockquote&gt;</description></item><item><title>该如何从 Java 8 升级到 Java 10</title><link>https://yanbin.blog/how-to-upgrade-from-java-8-to-10/</link><pubDate>Wed, 23 May 2018 02:18:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-to-upgrade-from-java-8-to-10/</guid><description>
Java 9 出来了很久，买的书《Java 9 Revealed - For Earyly Adoption and Migration》，说怎么迁移到 Java 9，可是突然间 Java 9 就无法通过正常渠道从 Oracle 官网下载了，这书还让不让人看。当然要看，因为尽管 Java 10 出来了，但实际的变化全压在 Java 9 这个版本上的，就当是通过 Java 10 来学习 Java 9 吧。&lt;/p&gt;
&lt;br/&gt;
本文随便说说 Java 8 之后的版本变迁，不涉及 Java 9 或是 Java 10 的具体新特性，并不能回答标题中的问题。作为一个不甘落后的 IT 从业人员，总是希望能紧跟技术(某一狭小特定领域的技术)的步伐。譬如说当从 Java 1.4 升级到 Java 1.5 之后，对范型也是跃跃欲试，无奈当时公司追求的是稳定压倒一切，不在服务器上升级 JDK，所以只能创造条件也要上。于是弄了个 &lt;a href="https://yanbin.blog/retrotranslator-jdk15-run-in-jvm14/"&gt;Retrotranslator让你用JDK1.5的特性写出的代码能在JVM1.4中运行&lt;/a&gt; 来适配。&lt;br/&gt;&lt;br/&gt;
后来的公司，也就是现在更为激进一些，来了 JDK 6，跟; JDK 7，跟; JDK 8, 继续跟; JDK 9 发布后，不跟了。不是不想跟，而是 Java 9 实在是变化有点大，模块化带来的不仅仅语言方面的改变，而是影响到如何组织，发布应用，这也就是为什么 jigsaw 雪藏多年的缘故。其实也可以对模块化不欲理会，但是单纯的把 Java 8 换成 Java 9 造成原来的项目不能正常构建的概率也比以往要高。&lt;br/&gt;&lt;br/&gt;
Java 8 是 2014 年 3 月发布，四后半后的 Java 9 在 2017 年 9 月发布，然而 2018 年 3 月 Java 10 就出来了。谁说 Oracle 在收购 Java 后就对它不作为了呢？Oracle 在 Java 9 之后开始了 6 个月的发布周期(见 &lt;a href="http://www.oracle.com/technetwork/java/javase/eol-135779.html"&gt;Oracle Java SE Support Roadmap&lt;/a&gt;)，这让人如何受得了，Oracle 自己也是。更何况我们都没来得急品尝 Java 9 的滋味，Oracle 自己就把 Java 9 的生命周期给结束了，进到 Java 9 的下载而面 &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html"&gt;Java SE 9 Downloads&lt;/a&gt;, 自动会导向到 Java SE 10 的下载页面，想要下载 Java SE 9 的话，只能到历史存档中去找 &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase9-3934878.html"&gt;Java SE 9 Archive Downloads&lt;/a&gt;。</description></item><item><title>使用 PostgreSQL 的 uuid 字段类型</title><link>https://yanbin.blog/use-postgresql-uuid-field-data-type/</link><pubDate>Mon, 21 May 2018 01:14:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-postgresql-uuid-field-data-type/</guid><description>
上一篇 &lt;a href="https://yanbin.blog/use-sql-server-uniqueidentifier-data-type/"&gt;使用 SQL Server 的 uniqueidentifier 字段类型&lt;/a&gt; 了解了 SQL Server 中如何使用 uniqueidentifier 字段类型后，现在来看下 PostgreSQL 中如何使用 uuid 字段类型。在 PostgreSQL 的字段类型是 &lt;code&gt;uuid&lt;/code&gt; 了，所以创建一个带有 &lt;code&gt;uuid&lt;/code&gt; 字段的表是&lt;/p&gt;
&lt;blockquote&gt;
CREATE TABLE customers (&lt;br /&gt;
    id uuid PRIMARY KEY,&lt;br /&gt;
    name VARCHAR(36)&lt;br /&gt;
);
&lt;/blockquote&gt;
我们这里设置 id 字段类型为 &lt;code&gt;uuid&lt;/code&gt;, 并且它是一个主键。也可以应用函数指定它的默认值，下面将会讲述到。&lt;br/&gt;&lt;br/&gt;
然后用 SQL 语句来向该表插入记录</description></item><item><title>使用 SQL Server 的 uniqueidentifier 字段类型</title><link>https://yanbin.blog/use-sql-server-uniqueidentifier-data-type/</link><pubDate>Fri, 18 May 2018 23:20:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-sql-server-uniqueidentifier-data-type/</guid><description>
SQL Server 自 2008 版起引入了 &lt;code&gt;uniqueidentifier&lt;/code&gt; 字段，它存储的是一个 UUID, 或者叫 GUID，内部存储为 16 个字节。SQL Server 可用两个函数来生成 &lt;code&gt;uniqueidentifier&lt;/code&gt;, 分别是 &lt;code&gt;NEWID()&lt;/code&gt; 和 &lt;code&gt;NEWSEQUENTIALID()&lt;/code&gt;, 后者只能用作字段的默认值。Java 也有一个 UUID 工具类 &lt;code&gt;java.uti.UUID&lt;/code&gt;, &lt;code&gt;UUID.randomUUID().toString()&lt;/code&gt; 生成一个随机的 UUID 字符串，在 &lt;code&gt;java.util.UUID&lt;/code&gt; 也是用两个 &lt;code&gt;long&lt;/code&gt; 字段表示内部状态。&lt;br/&gt;&lt;br/&gt;
SQL Server 的 &lt;code&gt;uniqueidentifier&lt;/code&gt; 类型字段表明了内部如何存储，在我们操作它时，它的外在表现形式都是一个固定格式 `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` 的字符串，不区分大小写的。&lt;br/&gt;&lt;br/&gt;
本文所使用的 SQL Server 是 2017 版，通过 Docker 来启动的&lt;br/&gt;
&lt;blockquote&gt;
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest
&lt;/blockquote&gt;
然后我们创建一个带有 &lt;code&gt;uniqueidentifier&lt;/code&gt; 类型字段的表</description></item><item><title>Mockito 也能 Mock final 类和 final 方法了</title><link>https://yanbin.blog/mockito-mock-final-class-final-method/</link><pubDate>Mon, 14 May 2018 22:58:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-mock-final-class-final-method/</guid><description>
以实际 Java 项目中的单元测试 Mock 框架基本是 Mockito 2 了，因为它有一个十分流畅的 API。Mockito  2也为 JUnit 5 配上了 MockitoExtension, 所以 JUnit 5 下使用 Mockito 2 的关节也打通了。但在我们享受 Mockito 2 便利的同时，与 JMockit 相比局限性就很明显，因为 Mockito 2 是通过创建匿名子类来进行 Mock 的，所以任何子类对父类无法突破的方面它都存在。譬如，final 类, final 方法, 私有方法, 静态方法, 构造函数都是无法通过子类型进行重写的。所以除非特别需要，在 Mockito 2 无法胜任时都求助于 JMockit，JMockit 借助于 javaagent 取得了 JVM 的高控制权才得已为所欲为。&lt;/p&gt;
&lt;br/&gt;
当 Mockito 来到了 2.1.0 版本，它也觉得不能对以上所有的限制置若罔闻, 首先带给我们的突破是它也可以 Mock final 类和 final 方法，虽然仍处于孵化器中，但毕竟是应用在单元测试中，能用就很不错了，只要以后不被拿走就行。这是官方对它的介绍 &lt;a href="https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods"&gt;Mock the unmockable: opt-in mocking of final classes/methods&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
下面我亲自操作一遍，并给出更全方位的测试样例</description></item><item><title>JUnit 5 使用 Mockito 2 进行测试</title><link>https://yanbin.blog/junit-5-work-with-mockito-2/</link><pubDate>Sun, 13 May 2018 17:11:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-5-work-with-mockito-2/</guid><description>
JUnit 5 刚出来那时，也就是第一个版本 5.0.0 时，还不能很好的支持 Mockito 的测试，因为 Mockito 没能跟得那么紧密。那时候 JUnit 5 只能试验性的提供了一个极不正式的 &lt;a href="https://github.com/junit-team/junit5-samples/blob/master/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java"&gt;com.example.mockito.MockitoExtension&lt;/a&gt;, 看那包名就知道不是来真的，所以决定再等。JUnit 5 不再原生支持 JUnit 4 的 Rule，一切都将是 Extension，那也是要求 Mockito 能够与之俱进。现在等来了，JUnit 5 进化到了 5.2.0, Mockito 也早已有了一个单独的模块 &lt;code&gt;mockito-junit-jupiter&lt;/code&gt; 来迎接它。&lt;/p&gt;
&lt;br/&gt;
在 Mockito 2.1.0 的 &lt;a href="https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#improvements"&gt;What's new in Mockito 2&lt;/a&gt; 中记述了 JUnit 5 为 Mockito 2 开发了一个 MockitoExtension。追溯到 Mockito 2 的 Release Notes, 我们发现 Mockito 2 官方最早引入 MockitoExtension 的版本是 2.16.3(2018-03023)。我对 Mockito 对 JUnit 5 支持的最新更新是从这个 Pull Request &lt;a href="https://github.com/mockito/mockito/pull/1221"&gt;MockitoExtension for JUnit5&lt;/a&gt; 得知的。&lt;br/&gt;&lt;br/&gt;
一句话讲就是现在的 Mockito 2 有原生态的 MockitoExtension 来支援 JUnit 5, 可以非常放心可靠的让 JUnit 5 和 Mockito 2 一起稳定工作。因此前面那个包名带 &lt;code&gt;example&lt;/code&gt; 字样的 MockitoExtension 链接也就无效了。</description></item><item><title>AWS S3 应用 KMS Key 进行服务端数据加密</title><link>https://yanbin.blog/aws-s3-server-side-data-encryption-with-kms-key/</link><pubDate>Sun, 13 May 2018 12:45:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-s3-server-side-data-encryption-with-kms-key/</guid><description>
当我们把数据搬上云端，为了保护敏感数据一定要对数据存储进行加密。而对于 S3 上的数据加密最简单莫过于启用服务端数据加密，可以是 AES-256 或 AWS-KMS。进行了服务端加密码的文件有什么不同呢？首先从 AWS S3 文件列表视图中看到的文件大小与原始文件大小是一样的，其实数据在 AWS 机房是加密存储了的，比如说直接在 AWS 服务器上拷出那些 S3 文件是不能理解的。&lt;/p&gt;
&lt;br/&gt;
本文涉及的内容包括：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;服务器端加密后的 S3 文件有何不同&lt;/li&gt;
&lt;li&gt;不启用 Bucket 的默认加密如何进行服务端数据加密(含 AWS CLI 和  Java 代码操作)&lt;/li&gt;
&lt;li&gt;启用及应用 Bucket 的默认加密&lt;/li&gt;
&lt;li&gt;上传与下载文件的 IAM role 需要的权限&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
&lt;h3&gt;服务器端加密后的 S3 文件有何不同&lt;/h3&gt;
首先看现象，后面才能确认我们的操作是否对数据真的进行了服务端加密。查看使用 AWS-KMS key 加密后的 S3 文件的 &lt;code&gt;Overview&lt;/code&gt;, 看到下面两个属性&lt;br/&gt;
&lt;blockquote&gt;
&lt;strong&gt;Server-side encryption&lt;/strong&gt;&lt;br /&gt;
AWS-KMS
&lt;strong&gt;KMS key ID&lt;/strong&gt;&lt;br /&gt;
arn:aws:kms:us-east-1:123456789012:key/e4ce859b-786b-4b8e-8b73-4968adb6e4ed
&lt;/blockquote&gt;
没加密的话, Server-side encryption 是 None, 并且没有 KMS key ID 属性。</description></item><item><title>Springfox 解决在单一资源操作多个方法实现时生成 Swagger 文档的问题</title><link>https://yanbin.blog/springfox-single-resource-operation-multiple-methods-swagger-documentation/</link><pubDate>Sun, 06 May 2018 01:06:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/springfox-single-resource-operation-multiple-methods-swagger-documentation/</guid><description>
在命名本文的标题都敲打了几分钟时间，问题很简单，然而用简短的一个标题完全描述出来却有点费事。在 Spring MVC 项目结合 Springfox 来生成 Swagger API 文档时，如果一个资源操作因为请求参数的不同而映射到多个 controller 方法，那么 Swagger 可能只能生成某一个 API 条目，其余都被忽略。至于为什么说是 "可能", 可能正好未遵循命名规范而躲过了这一劫。由此引出&lt;/p&gt;
&lt;h3&gt;我们的问题&lt;/h3&gt;
我们这里用了资源操作一词，它包含了两部分信息: 资源与操作，比如 &lt;code&gt;/users/{userId}&lt;/code&gt; 是资源，而发生在其上的 HTTP 各种方法，如 POST, GET, PUT, DELETE 等就是操作。而 Spring MVC 中允许我们针对不同的查询参数把相同的资源操作映射到不同的 controller 方法上，也是为了保持逻辑上更为清晰。&lt;br/&gt;&lt;br/&gt;
比如下面的例子路由配置的例子&lt;br/&gt;
&lt;blockquote&gt;
GET /users/{userId}     UserController.getUserInfo                                    //默认&lt;br /&gt;
GET /users/{userId}     UserController.getUserInfo                                   //当有 ?source=file 时&lt;br /&gt;
GET /users/{userId}     CloudUserController.getUserInfoFromCloud       //当有 ?source=cloud 时
&lt;/blockquote&gt;
看到上面资源与操作完全相同，仅仅因为 source 查询参数的不同而映射到三个 controller 方法。用代码体现如下图</description></item><item><title>一个最基础的 Spring 4 MVC Maven 项目</title><link>https://yanbin.blog/basic-spring-4-mvc-maven-project/</link><pubDate>Mon, 30 Apr 2018 08:35:12 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/basic-spring-4-mvc-maven-project/</guid><description>
这是一个最基本的 Spring 4 MVC 的 Maven 项目，非 SpringBoot 的，SpringBoot 由于有许多自动化配置特性，会更简单些。本例全部用 Java 代码作为配置，免除了创建 &lt;code&gt;web.xml&lt;/code&gt; 和如 &lt;code&gt;dispatcher-servlet.xml&lt;/code&gt; 这样的文件。本人更倾向于 Java 配置，它的优势在于能进行编译期检查，逻辑性也强，配置文件只是改动无需重新编译，都是要重启服务的; 关于使用 XML 配置文件的方式可参考文后的链接。&lt;br/&gt;&lt;br/&gt;
本文侧重于 Spring MVC 项目提供 RESTful JSON API, 因而静态 Web 内容提及较少。创建一个 Maven 项目的方式，可以直接创建一个 &lt;code&gt;pom.xml&lt;/code&gt; 文件，然后编辑它的内容，使用 IntelliJ IDEA 的话只需要选择导入为一个 Maven 项目就成，Eclipse 的话可能还要事先运行 &lt;code&gt;mvn eclipse:eclipse&lt;/code&gt; 初始化一下。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;项目结构布局&lt;/h3&gt;
&lt;a href="https://yanbin.blog/posts/2018/basic-spring-4-mvc-maven-project/spring-mvc-base-1.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2018/basic-spring-4-mvc-maven-project/spring-mvc-base-1-297x300.png"
width="297px"
/&gt;
&lt;/a&gt;
就是一个普通的 Maven 项目，稍稍不同的是 src/main 目录中除了 java 和 resources 之外，还有 webapp 目录，用于存放 web 静态文件或模板文件的。</description></item><item><title>AWS Batch 是如何向 Docker 容器传递参数</title><link>https://yanbin.blog/how-aws-batch-pass-parameters-to-docker-container/</link><pubDate>Sun, 01 Apr 2018 19:26:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-aws-batch-pass-parameters-to-docker-container/</guid><description>
AWS Batch 提供了简单有效的方式来运行 Docker 镜像，在单纯执行一个计算任务时比 ECS 使用起来要方便许多。AWS Batch
在提交任务时可以执行 &lt;code&gt;command&lt;/code&gt;, &lt;code&gt;environment&lt;/code&gt; 和 &lt;code&gt;parameters&lt;/code&gt;, 那么它将如何传递那些参数给 Docker 容器呢？&lt;/p&gt;
&lt;br/&gt;
首先看一下提交任务的脚本&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;aws batch submit-job &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; --job-name test-job &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --job-queue test-job-queue &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --job-definition test-job-definition &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; --container-overrides&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;command=echo,hello,environment=[{name=JAVA_OPTS,value=-Xmx4G}]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
注：aws cli 命令参数中有 &lt;code&gt;-&lt;/code&gt; 时需要用  --parameters='--name=Spring' 等号的格式&lt;br/&gt;&lt;br/&gt;
我们将用上面的脚本提交一个任务，然后用 &lt;code&gt;docker inspect &amp;lt;container-id&amp;gt;&lt;/code&gt; 观察 Docker 容器收到了什么参数&lt;br/&gt;&lt;br/&gt;
也没什么意外的，inspect 容器后我们看到</description></item><item><title>如何向 Docker 容器传递参数</title><link>https://yanbin.blog/pass-arguments-to-docker-container/</link><pubDate>Sat, 31 Mar 2018 04:15:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/pass-arguments-to-docker-container/</guid><description>
我们在运行 docker 镜像时希望能用下面的命令向容器传递命令行参数&lt;/p&gt;
&lt;blockquote&gt;
docker run &amp;lt;image-name&amp;gt; &amp;lt;command&amp;gt; arg1 arg2&lt;br /&gt;
docker run &amp;lt;image-name&amp;gt; arg1 arg2
&lt;/blockquote&gt;
其实只有第一种形式，紧随镜像名后那个总是一个命令，其后才是参数。如果要向 docker 容器传递参数时，Dockerfile 该如何写，这就有必要稍稍了解一下 Dockerfile 中  CMD 和 ENTRYPOINT 这两个指令，并且它们有 exec 和 shell 两种格式的写法。详情请见上篇 &lt;a href="https://yanbin.blog/dockerfile-difference-between-shell-exec-forms/"&gt;Dockerfile 中命令的两种书写方式的区别&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
对于一个 docker 镜像，我们可以这么来理解  ENTRYPOINT 与 CMD 的关系&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;如果没有定义 ENTRYPOINT， CMD 将作为它的 ENTRYPOINT&lt;/li&gt;
&lt;li&gt;定义了 ENTRYPOINT 的话，CMD 只为 ENTRYPOINT 提供参数&lt;/li&gt;
&lt;li&gt;CMD 可由 &lt;code&gt;docker run &amp;lt;image&amp;gt;&lt;/code&gt; 后的命令覆盖，同时覆盖参数&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
对于 #1 和  #2 更精致的理解是&lt;span style="color: #ff0000;"&gt;容器运行的最终入口由 ENTRYPOINT 和实际的 CMD 拼接而成&lt;/span&gt;。ENTRYPOINT 和 CMD 需转换为实际镜像中的 exec 格式来拼接，合并后的第一个元素是命令，其余是它的参数。</description></item><item><title>Dockerfile 中命令的两种书写方式的区别</title><link>https://yanbin.blog/dockerfile-difference-between-shell-exec-forms/</link><pubDate>Sat, 31 Mar 2018 02:17:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dockerfile-difference-between-shell-exec-forms/</guid><description>
最早的初衷是要研究一下运行 Docker 容器时如何向其传递参数，却冷不防掉入了另一个深渊，不得不关心起 Dockerfile 中命令(包括 RUN, CMD 和 ENTRYPOINT) 的两种不同写法上的区别。&lt;br/&gt;&lt;br/&gt;
所以呢，先要稍稍了解一下 Dockerfile 中 RUN, CMD, ENTRYPOINT 这三个指令&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;RUN 执行命令并创建新的镜像层，常用于安装软件包。可以多个，为避免创建过多的镜像层，我们尽量把命令合在一起，用分号或 &amp;amp;&amp;amp;。它与容器运行期无关。&lt;/li&gt;
&lt;li&gt;CMD 设置容器启动后默认执行的命令及其参数，但 CMD 能够在启动容器时被覆盖。多个 CMD 只有最后一个是有效的&lt;/li&gt;
&lt;li&gt;ENTRYPOINT 配置容器启动时运行的命令。多个  ENTRYPOINT 也是只有最后一个有效&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
关于以上三个命令的区别，这儿有篇文章讲得很清楚 &lt;a href="https://www.ibm.com/developerworks/community/blogs/132cfa78-44b0-4376-85d0-d3096cd30d3f/entry/RUN_vs_CMD_vs_ENTRYPOINT_%E6%AF%8F%E5%A4%A95%E5%88%86%E9%92%9F%E7%8E%A9%E8%BD%AC_Docker_%E5%AE%B9%E5%99%A8%E6%8A%80%E6%9C%AF_17?lang=en"&gt;RUN vs CMD vs ENTRYPOINT - 每天5分钟玩转 Docker 容器技术（17）&lt;/a&gt;，此处也照搬了些文字。&lt;br/&gt;&lt;br/&gt;
RUN, CMD 和  ENTRYPOINT 都支持两种写法，即 exec 和 shell 格式，见 &lt;a href="https://docs.docker.com/engine/reference/builder/#entrypoint"&gt;Dockerfile reference #ENTRYPOINT&lt;/a&gt; 对这两种方式的解释。RUN 只影响如何构建镜像，所以镜像中不保留 RUN 命令。CMD 和 ENTRYPOINT 都可以在运行容器时执行命令，这里不讲述它们间的区别，而要说的是它们所支持的 exec 和  shell 两种格式的写法。此篇以 ENTRYPOINT 为例说明两种格式的区别，CMD 类似。</description></item><item><title>AWS SQS 队列的 DLQ 设置与行为</title><link>https://yanbin.blog/aws-sqs-dead-letter-queue-setting-and-behavior/</link><pubDate>Thu, 29 Mar 2018 01:21:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-sqs-dead-letter-queue-setting-and-behavior/</guid><description>
和 Lambda 类似，AWS 的  SQS 队列也提供了 DLQ(Dead Letter Queue) 来支持重试功能，可以设定某一个消息在接收多次重新变得可见后进入到另一个 SQS 队列中。比如说队列 user-id-queue，设定了它的 Redrive Policy 为三次接收后转入到另一个 SQS 队列 user-id-dlq, 就会显示为&lt;/p&gt;
&lt;blockquote&gt;
&lt;strong&gt;Maximum Receives&lt;/strong&gt; 3&lt;br /&gt;
&lt;strong&gt;Dead Letter Queue&lt;/strong&gt; arn:aws:sqs:us-east-1:&amp;lt;account_id&amp;gt;:user-id-dlq
&lt;/blockquote&gt;
&lt;div&gt;在运用 SQS 的 DLQ 之前首先要理解 SQS 中消息的几个概念&lt;/div&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Message Available:  SQS 客户端可以获取到的消息, 即 Visible Messages&lt;/li&gt;
&lt;li&gt;Messages in Flight: 消息被 SQS 收取了之后，由 Available 转为 In Flight, 该状态的消息不能被客户端接收到&lt;/li&gt;
&lt;li&gt;Visibility Timeout:  消息停留在 In Flight 状态的时间, 如果在 Timeout 之前未删除这个消息，该消息重新变为 Available 状态&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
我们可以设置 SQS 队列的默认 Visibility Timeout 大小，也可以在代码中收取消息时指定这个值。&lt;br/&gt;&lt;br/&gt;
所以我们能够在集群环境中应用 SQS 的这个特性让多个节点同时监听单个 SQS 队列，基本上保证每个节点处理各自不同的消息。有一种例外就是：假设我们设置了 Visibility Timeout 是 30 秒，客户端 1 获取到消息后，消息变为 In Flight 状态，但 30 秒后仍然在处理过程中，此时消息回到Available 状态，客户端 2 也能获取到该消息，这也会造成单条消息的重复处理。解决的办法之一是适当延长 Visibility Timeout 的时间，给予第一个客户端更充分的处理时间。</description></item><item><title>unmi.cc 博客大事记</title><link>https://yanbin.blog/events-of-this-blog/</link><pubDate>Fri, 09 Mar 2018 23:39:32 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/events-of-this-blog/</guid><description>
&lt;p&gt;写下此篇流水纯粹是为了重拾那些零星的记忆，只能建立起一个模糊的脉络，找不回具体的时间点了，现在也是试着能否从某些日志里寻回些线索来。本篇日志最早创建于 2014 年 2 月份，中间偶有修改，却一直在草稿箱里静静的躺着。终于又时隔多年首次放出一篇非技术文出来。&lt;/p&gt;
&lt;p&gt;1. 开始的开始，&lt;strong&gt;2001&lt;/strong&gt; 年工作起，进入一个几乎完全陌生的程序世界。为了奋力从大海洋中汲取营养，大跃进的方式学习，并坚持在笔记本上面现在看来像是抄写一样的记笔记，不觉几年功夫，工工整整，满满的记载了四五本笔记本。还有看到那些好的东西也爱打印下来，其实打印出来的东西与它在网络上是一回事，没完整看的还是没看完，写下来了可不一样。这样记笔记的习惯其实也是工作之后的事情，上学期间最爱在课本上记东西。&lt;/p&gt;
&lt;p&gt;2. 到后来是网络的盛行，也是信息量的爆炸的时代，再系统再全的书籍也无法与 Google 相比了。所以大概是 &lt;strong&gt;2005&lt;/strong&gt; 年想到了在 QQ 空间里记了三五篇，一块娱乐之地上记录这些东西格调十分不搭。&lt;/p&gt;
&lt;p&gt;3. 找到了第一个真正意义上的 BSP，那就是 blogcn.com，它现在完全变样了。QQ 上的几篇只需要复制到编辑框保存就行，那应该是  &lt;strong&gt;2006&lt;/strong&gt; 年的事情。blogcn 是个综合性的 BSP，不好玩，代码高亮用工具保存成的带样式的 HTML 代码，好像也只在这里折腾了一年多时光，到  &lt;strong&gt;2007&lt;/strong&gt; 吧，继续寻租。</description></item><item><title>Groovy 的多重赋值和方法的多返回值</title><link>https://yanbin.blog/groovy-multiple-assignment-returns/</link><pubDate>Sun, 04 Mar 2018 00:32:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/groovy-multiple-assignment-returns/</guid><description>
追溯到刚开始学习 Groovy 还是在  2008 年，距今 2018 年有九年半余，曾记下几篇 &lt;a href="https://yanbin.blog/category/groovy/"&gt;Groovy&lt;/a&gt;
 的日志。那时学习 Groovy 并无明确的目的，只因它是脚本语言, 可用来快速验证 Java API。曾经 BeanShell 芸花一现，
JRuby 和 Jython 总是别人家的语言照搬而来的。而 Scala，Nashorn(jjs), JShell 更是后来的事，唯有 Groovy 写起来很亲切，完全不懂 Groovy 都没关系，直接上 Java。&lt;br/&gt;&lt;br/&gt;
现如今之所以重新勾搭上了 Groovy 是因为它仍然坚挺着，在 SoupUI(ReadyAPI) 和 Jenkins 中获得了重视，倒不是因为 Gradle。先前 Groovy 在 Spring 框架中的地位估计要被如今的 Kotlin 取而代之。&lt;br/&gt;&lt;br/&gt;
好了，回顾完后进入正题，关于 Groovy 如何进行多重赋值，以及延伸到方法返回多个值的情形。这里所说的多重赋值不是指用连等号来同时赋为一个值，&lt;br/&gt;&lt;br/&gt;
def a = b = c = 100   //不是说的这个&lt;br/&gt;&lt;br/&gt;
而是指同时对多个变量一步到位的赋成不同的值，要实现这个必须用到 List 类型。看下面一个基本例子(GroovyConsole)</description></item><item><title>Scala 自身类型(self-type) 解析</title><link>https://yanbin.blog/scala-self-type-understanding/</link><pubDate>Tue, 13 Feb 2018 00:33:59 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-self-type-understanding/</guid><description>
Scala 有一个自身类型(&lt;a href="https://docs.scala-lang.org/tour/self-types.html"&gt;self-type&lt;/a&gt;) 的东西，由来已久，居然今天才发现。如果一个类或 trait 指明了 self-type 类型(包括类和特质)，它的子类型或是对象也必须是相应的类型。它产生的效果与继承(或 mixin) 颇有几分相似。self-type 的语法是 &lt;code&gt;this 的别名: 某一类型(class 或 trait)&lt;/code&gt;, 直接看一个例子：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;Tweeter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;//这个表示 Tweeter 本身也必须是一个 User，它的子类型必须想办法符合这一要求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweetText&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;s&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$tweetText&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VerifiedTweeter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Tweeter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//Tweeter 要求这个类必须是一个 User, 所以需要继承自 User 类
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerifiedTweeter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Yanbin&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
上面 &lt;code&gt;this: User =&amp;gt;&lt;/code&gt; 中的 &lt;code&gt;this&lt;/code&gt; 只是一个别名，可以是 &lt;code&gt;self&lt;/code&gt; 或任何有效标识(如  &lt;code&gt;abc: User&lt;/code&gt;)，这里使用 &lt;code&gt;this&lt;/code&gt; 等于未指定别名，与原本的 this 重叠了。如果声明为非 &lt;code&gt;this&lt;/code&gt; 的话则可以这么用</description></item><item><title>Akka Actor: 从最简单的例子开始</title><link>https://yanbin.blog/akka-actor-start-with-easy-example/</link><pubDate>Sun, 11 Feb 2018 19:03:15 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/akka-actor-start-with-easy-example/</guid><description>
Akka 是什么？它提供了 JVM 上的 Actor 编程模型 -- 同时兼顾了并发与分布式。它由 Scala 编写的，替代了 Scala 本身的 Actor。Actor 视线程为重量级的资源，能够以少量的内存胜任更高的并发，类似的东西有纤程，协程。有一个数据对比是同样的 1GB 内存，可以创建 2.7M 个 Actor, 而线程只能创建 4096 个，仅供参考，当然 Java 也是会基于线程池来执行的。&lt;br/&gt;&lt;br/&gt;
Actor 增加了程序的灵活性，并减轻了复杂度(标准的赞美之辞)。&lt;br/&gt;&lt;br/&gt;
所谓 Action 编程模型兼顾并发与分布，是由于让你编程时可以不用考虑线程，线程配置成为部署的范畴; Actor 之间通信只能发送异步消息，Actor 可以分布在同一 JVM, 不同 JVM, 或是不同物理机器上。&lt;br/&gt;&lt;br/&gt;
因为 《Akka IN ACTION》中提供了第一个例子起点着实有点高，所以网上找来了一个了解 Akka Actor 的最简单例子，来自于 &lt;a href="https://alvinalexander.com/scala/simple-scala-akka-actor-examples-hello-world-actors"&gt;Simple Scala Akka Actor examples (Hello, world examples)&lt;/a&gt;。并非纯属翻译，主要是为了练手，所以不完全一致：</description></item><item><title>并发(Concurrent) 与并行(Parallel) 的区别</title><link>https://yanbin.blog/different-between-concurrent-and-parallel/</link><pubDate>Fri, 09 Feb 2018 01:21:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/different-between-concurrent-and-parallel/</guid><description>
&lt;p&gt;刚开始阅读 《Akka IN ACTION》这本书，刚开始是对 &lt;code&gt;Revolution&lt;/code&gt; 这个词翻译成中文是&lt;code&gt;革命&lt;/code&gt; 感到诧异，因为&lt;code&gt;革命&lt;/code&gt; 通俗来讲就是 &lt;code&gt;杀人&lt;/code&gt; 的意思。至于 &lt;code&gt;Revolution&lt;/code&gt; 英文解释不深究了，只是感叹何以颠覆性的变化就一定要杀人吗？&lt;/p&gt;
&lt;p&gt;也由此引出了编程中经常面对的 &lt;code&gt;Concurrent&lt;/code&gt;(名词为：Concurrency) 和 &lt;code&gt;Parallel&lt;/code&gt;(名词为：Parallelism) 这两个词，基本上是认为它们是同一个意思。其实不然，下面慢慢道来。&lt;/p&gt;
&lt;p&gt;如果从英文字典对它们的解释也没有多大区别，差不多都是说同是发生，但字面上 &lt;code&gt;Parallel&lt;/code&gt; 多了一个平行的意思。所以在中文上，在计算机领域我们约定的翻译是&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Concurrent(Concurrency)  --  并发&lt;/li&gt;
&lt;li&gt;Parallel(Parallelism)           --  并行&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
&lt;p&gt;比如在多线程环境中它们的区别具体体现在：&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;并发&lt;/strong&gt;&lt;/span&gt;：多个任务在同一个 CPU 核上按细分的时间片轮流(交替)执行，从逻辑上来看那些任务是同时执行。针对 CPU 内核来说，任务仍然是按细粒度的串行执行。也难怪在 Java 5 中新加的并发 API 的包名是 &lt;code&gt;java.uti.concurrent&lt;/code&gt;。</description></item><item><title>Java 8 根据属性值对列表去重</title><link>https://yanbin.blog/java-8-list-deduplication-with-lambda/</link><pubDate>Tue, 06 Feb 2018 21:39:55 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-list-deduplication-with-lambda/</guid><description>
&lt;p&gt;对列表的去重处理，Java 8 在 &lt;code&gt;Stream&lt;/code&gt; 接口上提供了类似于 SQL 语句那样的 &lt;code&gt;distinct()&lt;/code&gt; 方法，不过它也只能基于对象整体比较来去重，即通过 equals/hashCode 方法。&lt;code&gt;distinct&lt;/code&gt; 方法的功效与以往的 &lt;code&gt;new ArrayList(new HashSet(books))&lt;/code&gt; 差不多。用起来是&lt;/p&gt;
&lt;blockquote&gt;&lt;br/&gt;
&lt;p&gt;List&amp;lt;Book&amp;gt; unique = book.stream().distinct().collect(Collectors.toList())&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;并且这种去重方式需要在模型类中同时实现 equals 和 hashCode 方法。&lt;/p&gt;
&lt;p&gt;回到实际项目中来，我们很多时候的需求是要根据对象的某个属性来去重。比如接下来的一个实例，一个 books 列表中存在 ID 一样，name 却不同的 book, 我们认为这是重复的，所以需要根据 book 的 id 属性对行去重。在 collect 的时候用到的方法是 &lt;code&gt;collectinAndThen(...)&lt;/code&gt;, 下面是简单代码:</description></item><item><title>JVM 上的 Lisp 方言 Clojure 1.9</title><link>https://yanbin.blog/lisp-over-jvm-dialect-clojure-1-9/</link><pubDate>Sat, 03 Feb 2018 20:46:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lisp-over-jvm-dialect-clojure-1-9/</guid><description>
还是从 OSChina 网站上得知 Clojure 1.9 在 2017 年 12 月发布的，时值这么久才开始真正去了解一下这个新版本。距离上一个版本 1.8 的发布时间(2016 年 1 月), 大概两年才出一个版本，而且总的说来 Clojure 1.9 并没有带来多大惊喜。&lt;br/&gt;&lt;br/&gt;
唯一能带来点喜气的也就是 Clolure 有了自己的命令行工具了，再也无需寄身于 &lt;a href="https://leiningen.org/"&gt;Leiningen&lt;/a&gt;(一个 Clojure 构建工具，相当于 sbt 之于 Scala) 的篱笆之下了。以 Mac OS 平台为例，以前试图用 brew 来直接安装 &lt;code&gt;clojure&lt;/code&gt; 的时候会提示找不到 &lt;code&gt;clojure&lt;/code&gt;, 建议安装 &lt;code&gt;leiningen&lt;/code&gt;.&lt;br/&gt;
&lt;blockquote&gt;
$ brew install clojure
&lt;/blockquote&gt;
在 Clojure 1.9 出来之前，上面的命令会得到如下提示信息&lt;br/&gt;
&lt;blockquote&gt;
Error: No available formula with the name "clojure"&lt;br /&gt;
Clojure isn't really a program but a library managed as part of a&lt;br /&gt;
project and Leiningen is the user interface to that library.
To install Clojure you should install Leiningen:&lt;br /&gt;
brew install leiningen&lt;br /&gt;
and then follow the tutorial:&lt;br /&gt;
https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md
&lt;/blockquote&gt;
所以那时候不得不用</description></item><item><title>使用 Mockito 的 @InjectMocks 创建被测试类实例</title><link>https://yanbin.blog/mockito-injectmocks-initialize-tested-instance/</link><pubDate>Thu, 01 Feb 2018 23:26:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-injectmocks-initialize-tested-instance/</guid><description>
&lt;p&gt;初识 Mockito 这个测试框架后，我们要使用 Mock 的属性创建一个被测试类实例时，大概会下面这么纯手工来打造。&lt;/p&gt;
&lt;p&gt;假定类 &lt;code&gt;UserService&lt;/code&gt; 有一个属性 &lt;code&gt;UserDao userDao&lt;/code&gt;, 需要构造 &lt;code&gt;UserService&lt;/code&gt; 实例时 Mock 内部状态&lt;/p&gt;
&lt;blockquote&gt;
UserDao userDao = Mockito.mock(UserDao.class);&lt;br /&gt;
UserService testMe = new UserService(userDao);
&lt;/blockquote&gt;
&lt;p&gt;如此，userDao 的行为就可以自由模拟了，这种纯手工方式都不需要给测试类添加&lt;/p&gt;
&lt;blockquote&gt;
@RunWith(MockitoJunitRuner.class)&lt;br /&gt;
//或&lt;br /&gt;
MockitoAnnotations.initMocks(this);
&lt;/blockquote&gt;
&lt;p&gt;因为上面两句是给 Mockito 的注解使用的。&lt;/p&gt;
&lt;p&gt;如果所有的 Mock 对象全部通过手工来创建，那就不容易体现出 Mockito 的优越性出来。因此对于被测试对象的创建，Mock 属性的注入应该让 &lt;code&gt;@Mock&lt;/code&gt; 和 &lt;code&gt;@InjectMocks&lt;/code&gt; 这两个注解大显身手了。&lt;/p&gt;
&lt;p&gt;标注在实例变量上的 &lt;code&gt;@Mock&lt;/code&gt; 相当于是 &lt;code&gt;Mockito.mock(Class)&lt;/code&gt; 创建了一个 Mock 对象，而 &lt;code&gt;@InjectMock&lt;/code&gt; 标的实例会寻找到相应 Mock 属性想法构造出被测试类的实例。看下面的例子：</description></item><item><title>推送 Docker 镜像到 Amazon ECR 仓库</title><link>https://yanbin.blog/push-docker-image-to-amazon-ecr-repository/</link><pubDate>Tue, 30 Jan 2018 00:30:19 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/push-docker-image-to-amazon-ecr-repository/</guid><description>
Docker 镜像在未指定仓库时默认是从  &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; 拉取的。如果需向 Docker Hub 推送镜像的话还可用 &lt;code&gt;docker login&lt;/code&gt; 在交互中完成登陆 Docker Hub 的操作。&lt;code&gt;docker login&lt;/code&gt; 的命令格式是&lt;/p&gt;
&lt;blockquote&gt;
docker login [OPTIONS] [SERVER]
&lt;/blockquote&gt;
所以我们可以连接到任何的 Docker 镜像仓库，也可以是本机，但我们这里所要介绍的是如何推送镜像到 AWS 给我们提供的 Docker 镜像仓库(Amazon ECR - Amazon Elastic Container Registry)。每个帐号下都有自己独立的仓库，镜像推送到了  Amazon ECR 后我们能够很方便的在 ECS, Batch 服务中使用它，也可以从 ECR 拉取镜像到本地来。&lt;br/&gt;&lt;br/&gt;
首先我们来做一个运行 Spring Boot Web 的简单的 Docker 镜像，假定已用 &lt;code&gt;mvc pacakge&lt;/code&gt; 生成了一个可独立运行的 jar 包 &lt;code&gt;java-webapp-0.0.1-SNAPSHOT.jar&lt;/code&gt; 。该应用开启一个 Web 服务，访问 http://localhost:8080 显示一行字符串 &lt;code&gt;Hello World!&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
创建一个目录 aws-docker, 并把 &lt;code&gt;java-webapp-0.0.1-SNAPSHOT.jar&lt;/code&gt; 移入该目录，在其下创建 &lt;code&gt;Dockerfile&lt;/code&gt; 文件，文件目录结构如下：&lt;br/&gt;
&lt;blockquote&gt;
aws-docker&lt;br /&gt;
  ├── Dockerfile&lt;br /&gt;
  └── java-webapp-0.0.1-SNAPSHOT.jar
&lt;/blockquote&gt;</description></item><item><title>Bash/Zsh 下调用 Emacs/Vim 编辑当前命令</title><link>https://yanbin.blog/bash-zsh-call-emacs-vim-edit-current-command/</link><pubDate>Tue, 23 Jan 2018 01:32:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/bash-zsh-call-emacs-vim-edit-current-command/</guid><description>
&lt;h2&gt;Bash 环境&lt;/h2&gt;
&lt;p&gt;多数时候我们用的是 Bash, 比如个人的 Linux 不愿去定制，远程服务器的由不得你去定制，所以就从 Bash 说起。&lt;/p&gt;
&lt;h4&gt;默认键绑定 &lt;code&gt;emacs&lt;/code&gt;, 操作是 &lt;code&gt;ctrl-x, ctrl-e&lt;/code&gt;&lt;/h4&gt;&lt;br/&gt;
&lt;p&gt;在默认的 Bash  环境下，只要在命令行中按下 &lt;code&gt;ctrl-x, ctrl-e&lt;/code&gt; 就会把当前命令的内容调入到环境变量  &lt;code&gt;$EDITOR&lt;/code&gt; 指示的编辑器(默认为 emacs)去编辑，编辑后保存退出就会立即执行。&lt;/p&gt;
&lt;p&gt;如果未安装 &lt;code&gt;Emacs&lt;/code&gt; 编辑器，在按下 &lt;code&gt;ctrl-x, ctrl-e&lt;/code&gt; 会得到如下提示&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[vagrant@localhost ~]$&lt;br /&gt;
-bash: emacs: command not found&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果希望使用 &lt;code&gt;vi&lt;/code&gt; 来编辑当前命令，就需要设置 &lt;code&gt;EDITOR&lt;/code&gt; 环境变量，比如在 &lt;code&gt;.bashrc&lt;/code&gt; 中加入&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;export EDITOR=vi&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么在命令行中按下 &lt;code&gt;ctrl-x, ctrl-e&lt;/code&gt; 使用打开  &lt;code&gt;vi&lt;/code&gt; 来编辑当前命令。&lt;/p&gt;
&lt;p&gt;注：Emacs 要用 &lt;code&gt;ctrl-x, ctrl-c&lt;/code&gt;, 再回答 &lt;code&gt;y&lt;/code&gt;, 命令保存到临时文中; 而 vi 的相应操作是 &lt;code&gt;:wq&lt;/code&gt;, 至少这个操作上 vi 要简洁些。</description></item><item><title>Bash 下的 emacs 和 vi 键绑定</title><link>https://yanbin.blog/bash-emacs-vi-key-bindings/</link><pubDate>Mon, 22 Jan 2018 20:43:23 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/bash-emacs-vi-key-bindings/</guid><description>
&lt;p&gt;初衷只是为了记述一下在 Bash/Zsh 中怎么调用 Vi 编辑当前的命令，但一发挥便涉及到了 Bash/Zsh 的键绑定的话题，无法打住，只得另立新篇。这里也只说 Bash 的键绑定，不会有 Zsh 键绑定的内容。&lt;/p&gt;
&lt;p&gt;什么是 Bash 的键绑定(keybing) 呢? 就是在 Bash 中的快捷键方案，即相当于某个 IDE 的快捷键配置，或者叫 Keymap。比如说 IntelliJ IDEA 中可选择的 Keymap 有 Eclipse, Emacs, JBuilder, Mac OS X, Mac OS X 10.5+, NetBean, Visual Studio, 以满足不同使用者的习惯。&lt;/p&gt;
&lt;p&gt;相应的 Bash 也为我们提供了两种键绑定的方案，即 emacs(默认) 和 vi 键绑定类型。&lt;/p&gt;
&lt;h3&gt;Emacs 键绑定&lt;/h3&gt;
&lt;p&gt;我们大多数天天在 Bash 下无意识中使用着 Emacs 键绑定类型，即使可能从未用过 Emacs 本身。比如我们在 Bash 下的按键组合&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ctrl + a     跳到命令行的开始&lt;br /&gt;
ctrl + e     跳到命令行末尾&lt;br /&gt;
!!               重复最后一个命令&lt;br /&gt;
ctrl + l      清屏操作，类似于  clear 命令&lt;br /&gt;
ctrl + c      中断/杀掉当前运行的进程 (SIGINT)&lt;br /&gt;
ctrl + d     发送 EOF 标记，这会关掉当前的 shell (EXIT)&lt;br /&gt;
ctrl + z      发送 SIGTSTP 给当前任务，使其挂起送到后台。(所以如果 vi 未正常退出，而是按 ctrl + z 的话，vi 进程还呆在后台&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;它们都是来自于 Emacs 键绑定。是不是那么的熟悉啊？</description></item><item><title>Scala 中应用 Future 并发编程</title><link>https://yanbin.blog/scala-concurrent-with-future/</link><pubDate>Wed, 13 Dec 2017 01:14:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-concurrent-with-future/</guid><description>
&lt;p&gt;Java 世界里进行并发编程有 Future 和  ExecutorService(当成 ThreadPool 来对待)，及至 Java 8 引入了更为趁手的 CompletableFuture。那么使用 Scala 该如何进行并发编程呢？Scala 能毫无障碍的使用 Java 类库，所以完全可以用 Java 的 API 来使用线程池，只是那不太 Scala 罢了。&lt;/p&gt;
&lt;p&gt;既然是 Scala, 就尽量写成 Scala Style 吧，Scala 也有自己的 Future, 还有 Promise 呢? 至于 Promise 与 Future 多大区别，语义上似乎也差不多，只看到 Promise 中有一个 Future 的属性。如果想玩得高级一点的话就该把  Actor 弄起来，它算是所谓的纤程，多个  Actor 可跑在同一个线程中，当然启动要快，内存消耗少，还避免了上下文切换。&lt;/p&gt;
&lt;p&gt;这里还是先体验 Scala Future 的用法。Future 本身只是描述了一个任务，及将要获得的结果(或执行的动作)，因此那样一个任务也是要放到线程池中去执行。这和 Java 的 Future/ThreadPool 是一样的概念。稍有不同的是线程池的创建与使用，线程池的默认大小配置等。看个简单的应用示例，本次测试的 Scala 版本是 2.11.12，为了突出实际的线程池大小，我们把任务数设置为 20 个&lt;/p&gt;
&lt;h3&gt;Scala Future 并发编程体验&lt;/h3&gt;</description></item><item><title>实现 Amazon S3 数据(文件)分段上传</title><link>https://yanbin.blog/aws-s3-multipe-parts-upload/</link><pubDate>Thu, 07 Dec 2017 01:42:19 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-s3-multipe-parts-upload/</guid><description>
&lt;h3&gt;探索是否能以流式写数据到 S3&lt;/h3&gt;
&lt;p&gt;通常，在我们项目中用 Java 代码上传数据到 S3 是下面那样的操作&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();&lt;br /&gt;&lt;br/&gt;
s3Client.putObject("bucket_name", "s3key.txt", new ByteArrayInputStream("hello".getBytes()), new ObjectMetadata()); //ObjectMetadata 没什么特别的话可以为 null&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽然  putObject() 的第三个参数是一个流，但它是输入流, 并非输出流啊。也就是说在执行该方法时必须把所有待上传的上据全部准备在这个输入流中，所以这里就直接用一个 ByteArrayInputStream 来包裹需上入到 S3 的数据内容。&lt;/p&gt;
&lt;p&gt;当然 putObject() 也就无法像 FileObjectOutputStream 那样流式写入内容到文件中，因为 putObject() 前后都没有与 bucket 上那个文件上有联系。即使是用 PipedInputStream/PipedOutputStream  也不行, 比如说下面的代码</description></item><item><title>用 PreparedStatement 向 SqlServer 中一次性插入多条记录</title><link>https://yanbin.blog/sqlserver-insert-multiple-rows-onetime/</link><pubDate>Tue, 05 Dec 2017 01:56:44 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sqlserver-insert-multiple-rows-onetime/</guid><description>
标准 SQL 都提供了下面这种方式一条 INSERT INTO 语句插入多条记录&lt;/p&gt;
&lt;blockquote&gt;
&lt;p class="p1"&gt;&lt;span class="s1"&gt;INSERT&lt;/span&gt; &lt;span class="s1"&gt;INTO&lt;/span&gt; Customers(Id, Name, Age) &lt;span class="s1"&gt;VALUES&lt;/span&gt; (&lt;span class="s2"&gt;1&lt;/span&gt;, &lt;span class="s3"&gt;'Name1'&lt;/span&gt;, &lt;span class="s2"&gt;21&lt;/span&gt;.&lt;span class="s2"&gt;5&lt;/span&gt;), (&lt;span class="s2"&gt;2&lt;/span&gt;, &lt;span class="s3"&gt;'Name2'&lt;/span&gt;, &lt;span class="s2"&gt;32&lt;/span&gt;.&lt;span class="s2"&gt;3&lt;/span&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
VALUES 之后用括号列出每一条记录。但是在 Java 中想把上面的语句转换成 PreparedStatement  来插入多条记录时就有些问题。要么写成&lt;br/&gt;
&lt;blockquote&gt;
INSERT INTO Customers(Id, Name, Age) VALUES(?, ?, ?), (?, ?, ?), (?, ?, ?) ....
&lt;/blockquote&gt;
我们不知道 VALUES 后应该列多少个问号，而且  JDBC 对参数的个数是有限制的，最多 2000 个参数。如果根据字段个数来算一次添加多少条记录，那么这条 SQL 语句也是动态的，不能很好的作为 PreparedStatement 进行预编译。以一个表三个字段为例，2000 个参数下一次性最多能插入记录数 666 条，也可能由于输入是 666 条记录的任意数量，所以生成的语句非静态的。&lt;br/&gt;&lt;br/&gt;
这种方式与每次手动拼凑一个完全静态的 INSERT INTO 语句应该不会有太多的差别。&lt;br/&gt;&lt;br/&gt;
如果只是写成 &lt;br/&gt;
&lt;blockquote&gt;
INSERT INTO Customers(Id, Name, Age) VALUES(?, ?, ?)
&lt;/blockquote&gt;
然后试图进行下面的操作</description></item><item><title>体验 Scala 2.12 支持的 Java 8 风格(SAM) Lambda</title><link>https://yanbin.blog/scala-2-12-java-8-sam-lambda/</link><pubDate>Tue, 28 Nov 2017 23:27:34 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-2-12-java-8-sam-lambda/</guid><description>
&lt;p&gt;上一次关注 Scala 新版本特性还是在将近五年前，针对的是  Scala 2.10. 后来也一直在使用 Scala，基本上是 Scala 2.11，
但对 Scala 2.11 所带来的新特性基本无知，大约有个 Macro 功能，没什么机会用上，应用 sbt 时稍有接触。还是老句老话，
了解新特性最可靠的文档是每个版本的的 Release Notes, 比如
&lt;a href="https://github.com/scala/scala/releases/v2.12.0"&gt;Scala 2.12.0 Release Notes&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;其中 Scala 2.12 带来的主要特性在于对 Java 8 的充分支持：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scala 可以有方法实现的 trait 直接编译为带默认方法的 Java 接口&lt;/li&gt;
&lt;li&gt;Lambda 表达式无需生成相应的类，而是用到 &lt;code&gt;invokedynamic&lt;/code&gt; 字节码指令(这个是 Java 7 加进来的新指令)&lt;/li&gt;
&lt;li&gt;最方便的功能莫过于终于支持 Java  8 风格的 Lambda，即功能性接口的 SAM(Single Abstract Method)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Scala 的 Lambda 内部实现&lt;/h3&gt;
&lt;p&gt;这儿主要是体验 Scala 2.12 如何使用 Java 8 风格的 Lambda. 在 Scala 2.12 之前，Scala 对 Lambda 的支持是为你准备了一大堆的 trait 类，有&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Function0, Function1, ...... Function22 (接收多个参数，返回一个值)&lt;/li&gt;
&lt;li&gt;Product1, Product2, ...... Product22 (函数返回多个值，即 TupleX 时用的)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Spark 提交任务时 Invalid signature file digest 错误</title><link>https://yanbin.blog/spark-submit-invalid-signature-file-digest-error/</link><pubDate>Tue, 21 Nov 2017 00:07:07 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spark-submit-invalid-signature-file-digest-error/</guid><description>
&lt;p&gt;第一次用  &lt;code&gt;spark-submit&lt;/code&gt; 提交任务，是 Scala 的程序，命令格式是&lt;/p&gt;
&lt;blockquote&gt;
spark-submit --class &amp;lt;main-class&amp;gt; --master local[*] --name "My first Spark Job" spark-test-fat-1.0-SNAPSHOT.jar
&lt;/blockquote&gt;
&lt;p&gt;结果报出错误&lt;/p&gt;
&lt;blockquote&gt;
Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes&lt;br /&gt;
at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:330)&lt;br /&gt;
at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:263)&lt;br /&gt;
at java.util.jar.JarVerifier.processEntry(JarVerifier.java:318)&lt;br /&gt;
......&lt;br /&gt;
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:119)&lt;br /&gt;
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
&lt;/blockquote&gt;
&lt;p&gt;Google 查了下，是需要把 jar 文件中的 &lt;code&gt;META-INF/*.RSA META-INF/*.DSA META-INF/*.SF&lt;/code&gt; 文件清理掉。我们可以执行如下命令从现有的 jar 中除去&lt;/p&gt;
&lt;blockquote&gt;
zip -d &amp;lt;jar file name&amp;gt;.jar META-INF/*.RSA META-INF/*.DSA META-INF/*.SF
&lt;/blockquote&gt;
&lt;p&gt;是因为 jar 包中包含有这些文件才造成提交 Spark 时验证文件签名摘要时失败。</description></item><item><title>IntelliJ IDEA 中创建 Maven Scala 项目</title><link>https://yanbin.blog/intellij-idea-create-maven-scala-project/</link><pubDate>Sun, 19 Nov 2017 15:34:46 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/intellij-idea-create-maven-scala-project/</guid><description>
Scala 项目看家的构建工具当然是 SBT, 假如我们已习惯于 Maven, 想要用 Maven 来构建 Scala 项目该如何做呢？那首先要找到一个 Maven Scala 相应的 Archetype, 然后用命令
&lt;code&gt;mvn archetype:generate&lt;/code&gt; 或是在 IntelliJ IDEA 使用 Maven 项目创建向导来选择一个 Maven Scala Archetype。这里主要介绍 IntelliJ IDEA 中 Maven 向导创建 Scala 项目的方式。&lt;/p&gt;
&lt;br/&gt;
首先确保我们已安装了 IntelliJ IDEA 的 Maven 和  Scala 插件。插件中自带了 org.scala-tools.archetypes:scala-archetype-simple:1.2 的 Maven archetype,
 这是一个貌视 Scala 官方的 archetype。我们可以尝试基于它来创建一个 Maven Scala 项目。通过 IntelliJ IDEA 的菜单 &lt;code&gt;File/New/Project...&lt;/code&gt;,
 在弹出的窗口中选择 Maven/Create from archetype, 然后找到 &lt;a href="https://mvnrepository.com/artifact/org.scala-tools.archetypes/scala-archetype-simple/1.3"&gt;scala-archetype-simple&lt;/a&gt;,
 自带版本为 1.2, 当前最新版也不过 1.3, 那还是  2010 年建立，别提有多老了。</description></item><item><title>JMockit 中被 Mocked 的对象属性及方法的默认值</title><link>https://yanbin.blog/jmockit-mocked-object-default-field-method-values/</link><pubDate>Sun, 12 Nov 2017 01:03:40 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-mocked-object-default-field-method-values/</guid><description>
前脚研究完 &lt;a href="https://yanbin.blog/mockito-mocked-default-fields-method-returns"&gt;Mockito 中被 Mocked 的对象属性及方法的默认值&lt;/a&gt;, 虽然目今更多的是拥抱着 Mockito, 但总有时对 JMockit 也会挤眉弄眼，谁叫 JMockit 无所不能呢！被 Mockito 的 Mock 对象方法的默认返回值洗脑之后，进而觉察出 JMockit 应该有同样的实现方式。&lt;/p&gt;
&lt;br/&gt;
经过类似的测试，这里不详细列出测试过程，只是在基于前篇的测试中加入 JMockit 的依赖，最新版是 1.36。测试类 MyClassTest 中使用&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Mocked&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
来构造 MyClass 的 mock 对象 myClass, 其余代码是一样的。相关代码请前往上篇 &lt;a href="https://yanbin.blog/mockito-mocked-default-fields-method-returns"&gt;Mockito 中被 Mocked 的对象属性及方法的默认值&lt;/a&gt; 中找。 使用 JMockit 后跑出来的效果如下：</description></item><item><title>Mockito 中被 Mocked 的对象属性及方法的默认值</title><link>https://yanbin.blog/mockito-mocked-default-fields-method-returns/</link><pubDate>Sun, 12 Nov 2017 00:16:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-mocked-default-fields-method-returns/</guid><description>
&lt;p&gt;在 Java 测试中使用 Mockito 有段时日了，以前只是想当然的认为 Mock 的对象属性值和方法返回值都是依据同样的规则。基本类型是 0, 0.0, 或 false, 对象类型都是 null, Mock 对象的默认返回值也应该是一样的。直到最近有一天，有一个返回 &lt;code&gt;Optional&amp;lt;String&amp;gt;&lt;/code&gt; 类型的方法，由于忘记对该方法打桩，意外的发现它返回的不是 null, 而 &lt;code&gt;Optional.empty()&lt;/code&gt;, 因此才意识到此处定有蹊跷。着实有必要用代码验证一下 Mockito 是怎么决定属性及方法的各种返回类型的默认值的。&lt;/p&gt;
&lt;p&gt;此次测试所用的 Mockito 版本是 mockito-core-2.12.0.&lt;/p&gt;
&lt;p&gt;于是创建了下面一个类 MyClass 用于生成 Mock 对象，选取了一些典型的数据类型, 包括 int, Double, String, long[], Optional&amp;lt;String&amp;gt;, Collection&amp;lt;String&amp;gt;, Map&amp;lt;String, String&amp;gt;, 同时测试 Mock 对象默认的属性值与方法默认返回值。</description></item><item><title>记录一下 Spring 如何扫描注解的 Bean 与资源</title><link>https://yanbin.blog/how-spring-scan-beans-resources/</link><pubDate>Sat, 04 Nov 2017 22:55:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-spring-scan-beans-resources/</guid><description>
&lt;h3&gt;Spring 相关代码分析&lt;/h3&gt;
本文通过对 Spring 的源代码来理解它是如何扫描 Bean 与资源的，因为自己有一个类似的需求，想把一堆的配置文件丢到 resources 下某个目录中，在程序启动的时候能加载它们。因为文件名是不一定的，所以不能直接指定文件名来加载，通过对 Spring 扫描资源的理解后，可以在自己的代码中手工扫描那些配置文件，以后有任何新的配置文件只需要扔到相应的配置目录即可。&lt;br/&gt;&lt;br/&gt;
下面以一个最简单的 Spring Boot 项目为例，调试并观察源代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@ComponentScan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePackages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;cc.unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DemoApplication&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpringApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DemoApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
还是直奔主题吧，不一步一步的去探寻到底是哪个实现类去扫描资源的，用 Google 找到的是 &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.html"&gt;&lt;code&gt;ClassPathScanningCandidateComponentProvider&lt;/code&gt;&lt;/a&gt;, 因此直接在这个类的敏感位置上打上断点，比如它的构造函数</description></item><item><title>配置 VIM 英语字典</title><link>https://yanbin.blog/configure-vim-dictionary/</link><pubDate>Sun, 01 Oct 2017 23:08:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/configure-vim-dictionary/</guid><description>
&lt;p&gt;使用 Vim 官方网站的字典下载链接&lt;/p&gt;
&lt;h3&gt;下载字典：&lt;/h3&gt;
&lt;p&gt;下载页面：&lt;a href="http://www.vim.org/scripts/script.php?script_id=195"&gt;http://www.vim.org/scripts/script.php?script_id=195&lt;/a&gt;， 当前下载链接为 &lt;a href="http://www.vim.org/scripts/download_script.php?src_id=6351"&gt;http://www.vim.org/scripts/download_script.php?src_id=6351&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;解压缩所需字典文件&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;tar xzvf ~/Downloads/engspchk.targ.gz CVIMSYN/engspchk.dict&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;使用 Vim 整理成字典文件&lt;/h3&gt;
&lt;blockquote&gt;&lt;br/&gt;
&lt;p&gt;vim CVIMSYN/engspchk.dict&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;删除前面三行：&lt;code&gt;gg3dd&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;
删除行首 GoodWord单词: &lt;code&gt;:%s/^GoodWord\t//g&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;
整理成每个单词占一行：&lt;code&gt;%s/\t/^M/g&lt;/code&gt;,    &lt;code&gt;^M&lt;/code&gt; 的输入要用 &lt;code&gt;ctrl-v&lt;/code&gt; 再按回车键，也可以用 &lt;code&gt;ctrl-v-m&lt;/code&gt; 来输入 &lt;code&gt;^M&lt;/code&gt;.&lt;br /&gt;&lt;br/&gt;
删除最后一行：&lt;code&gt;Gdd&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;
排序：&lt;code&gt;:sort&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;现在的 engspchk.dict 中文件每一行只有一个单词，并且按字母排好了序的</description></item><item><title>使用插件 aspectj-maven-plugin 织入 AspectJ AOP</title><link>https://yanbin.blog/maven-plugin-aspectj-weaving/</link><pubDate>Sat, 30 Sep 2017 22:08:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maven-plugin-aspectj-weaving/</guid><description>
昨天刚刚侍弄完 &lt;a href="https://yanbin.blog/spring-intercept-method-based-on-annotation"&gt;Spring 下基于自定义注解拦截方法调用&lt;/a&gt;，
现在试下纯 AspectJ 的方式来打造，因为不是每一个项目都是 Spring。这次要推到 5 年前试验过用 javac 命令行编译的方式织入方面，
见 &lt;a href="https://yanbin.blog/aspectj-baseon-annotation-method"&gt;AspectJ 基于自定义的方法注解来拦截方法&lt;/a&gt;，
这次着重在用 &lt;a href="http://www.mojohaus.org/aspectj-maven-plugin/index.html"&gt;
aspectj-maven-plugin&lt;/a&gt; 插件的方法来织入 AspectJ 方面。&lt;br/&gt;&lt;br/&gt;
基本上代码还是昨天的，需求还是一样的:&lt;br/&gt;&lt;br/&gt;
被 @LogStartTime 注解的方法在进入该方法时记录当前时间在 ThreadLocal 中，并能根据 @LogStartTime 的属性值决定处理逻辑&lt;br/&gt;&lt;br/&gt;
因为 Java5+ 之后 AspectJ 可以写成 Java 类加注解的方式，*.aj 文件一般都没太大必要了，所以可以和 Spring AOP 共用一个 @Aspect 注解的方面代码 &lt;code&gt;MethodStartAspect&lt;/code&gt;。&lt;br/&gt;&lt;br/&gt;
我们将采用编译器织入，因此项目依赖只需要一个 &lt;code&gt;org.aspectj:aspectjrt:1.8.0&lt;/code&gt;, 它也不会引入别的组件。同样我们从 Main 方法和测试用例两方面来验证实现的效果，下面是整个测试项目的布局，以及依赖，除掉单元测试的其时就只需要一个 jar 包。</description></item><item><title>Spring 下基于自定义注解拦截方法调用</title><link>https://yanbin.blog/spring-intercept-method-based-on-annotation/</link><pubDate>Fri, 29 Sep 2017 23:46:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-intercept-method-based-on-annotation/</guid><description>
其实很多年前就做过如此的实验，一翻开自己的日志有关于 &lt;a href="https://www.google.com/search?q=aspectj+site%3Ayanbin.blog&amp;amp;oq=aspectj+site&amp;amp;aqs=chrome.0.69i59j69i57j0l3.7823j0j1&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8"&gt;aspectj site:yanbin.blog&lt;/a&gt;, 可以找到  2008 年写的日志。真是流光容易把人抛，红了樱桃，绿了巴蕉。只是那时候 Spring 刚步入 2.0, 才翻开强大 AOP 的篇章，还记得彼时只要是直接使用 AspectJ 就要写  *.aj 文件。而如今 Spring 都到 5.0 了，也就是一年前才重拾起 Spring, 这期间 AspectJ 早就可以不用 *.aj 文件，只需普通 Java 文件，加上 @Aspect 和 @Pointcut 之类的注解就行。&lt;/p&gt;
&lt;br/&gt;
本文内容与几年前写过的日志大体相差不大，再缀上一篇纯粹是个人笔记。这里不以 Spring 5.0 为例，仍然是最新的 4.3.11.RELEASE, 并且直接用 Spring, 而非选择  Spring Boot, 因为用了 Spring Boot 常常搞不清楚哪些是自动配置了的。原生的 Spring 可以使自己掌握一个 Spring AOP 的基本要素。&lt;br/&gt;&lt;br/&gt;
需求：@LogStartTime 注解的方法，在每次进入该方法时把当前时间写入 ThreadLocal 中去，被 @LogStartTime 注解的方法中随时可以获得进入方法的时间</description></item><item><title>学习并体验 JUnit 5 新特性</title><link>https://yanbin.blog/learn-junit-5-new-features/</link><pubDate>Mon, 25 Sep 2017 01:31:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/learn-junit-5-new-features/</guid><description>
上一篇 &lt;a href="junit-5-new-features-and-get-started/"&gt;JUnit 5 快速上手(从 JUnit 4 到 JUnit 5)&lt;/a&gt; 介绍了如何在一个项目中同时使用 JUnit 4 和 JUnit 5。现在来开始了解 JUnit 5 的新特性. 我们现在的项目基本是用 Maven 来管理依赖，在 Maven 项目中如何引入 JUnit 5 可以参考官方例子 &lt;a href="https://github.com/junit-team/junit5-samples/tree/master/junit5-maven-consumer"&gt;junit5-maven-consumer&lt;/a&gt;. 我们知道 JUnit 5 包括三个模块，不用 JUnit 4 的话只要 Platform 和 Jupiter, 而 Jupiter Maven 模块本身依赖于 JUnit Platform, 因此应用 JUnit 5 的项目 Maven 配置就是&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.junit.jupiter&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;junit-jupiter-engine&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这样在当前的 IntelliJ IDEA(2017.2.4) 可以执行 JUnit 5 的测试用例。但要让 Maven 找到 JUnit 5 的测试用例，还得在 &lt;code&gt;pom.xml&lt;/code&gt; 中加上</description></item><item><title>JUnit 中是以测试方法为一个独立的生命周期</title><link>https://yanbin.blog/junit-test-instance-lifecycle/</link><pubDate>Sun, 24 Sep 2017 23:22:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-test-instance-lifecycle/</guid><description>
&lt;p&gt;在研究 JUnit 5 新特性的时候，学习到其中有一节 &lt;a href="http://junit.org/junit5/docs/snapshot/user-guide/#writing-tests-test-instance-lifecycle"&gt;Test Instance Lifecycle&lt;/a&gt;, 才意识到对 JUnit 的理解一直存在一个误区，以为 JUnit 是以测试类为一个生命周期的，其实不然。不管是 JUnit 5 还是 JUnit 4 或更早的版本，JUnit 都是以测试方法为一个独立的生命周期。&lt;/p&gt;
&lt;p&gt;只是到了 JUnit 5 提供了方法来把生命周期由方法改为测试类，对于单个测试类可以使用注解 &lt;code&gt;@TestInstance(Lifecycle.PER_CLASS)&lt;/code&gt; 来指定用一个测试实例来跑所有的测试方法，这就意味着测试类中的成员变量只被初始化一次。&lt;code&gt;@TestInstance&lt;/code&gt; 的 Lifecycle 默认是 PER_METHOD, JUnit 4 就是 PER_METHOD, 而且是不能改的。如果在 JUnit 5 中改变为 PER_CLASS, 恐怕反而会出许多乱子，每个测试方法本就该是完全独立的。&lt;/p&gt;
&lt;p&gt;比如在同一个类中多个测试方法使用了同一个实例变量的情况下，总会用一个  &lt;code&gt;@After&lt;/code&gt; 方法来复位该实例变量，现在才知道那是多余的。像下面的代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculatorTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;</description></item><item><title>JUnit 5 快速上手(从 JUnit 4 到 JUnit 5)</title><link>https://yanbin.blog/junit-5-new-features-and-get-started/</link><pubDate>Mon, 18 Sep 2017 02:57:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-5-new-features-and-get-started/</guid><description>
&lt;p&gt;一直在关注 JUnit 5 的演进，自两年前首个 ALPHA 版后，经历了 6 的 Milestone, 3 个 RC 终于在 2017/09/10 正式发布了。其实还从未对其深究过，今天算是正式开始体验。&lt;/p&gt;
&lt;p&gt;不像以往的版本，JUnit 5 现在是三个模块的合体 JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;JUnit Platform: 运行测试的基础平台。还定义了开发测试框架的 &lt;a href="http://junit.org/junit5/docs/snapshot/api/org/junit/platform/engine/TestEngine.html"&gt;TestEngine&lt;/a&gt; API。并提供了命令行执行测试以及与 Gradle, Maven, JUnit4 Runner 的集成&lt;/li&gt;
&lt;li&gt;JUnit Jupiter: 包含了新的编程和扩展模型。它还提供了一个运行新型测试的 TestEngine 实现&lt;/li&gt;
&lt;li&gt;JUnit Vintage: 提供了一个让 JUnit Platform 运行 JUnit 3 和 JUnit 4 的 TestEngine 实现&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上三个模块分工还是很明确，因此&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从现有的 JUnit 4 项目步入到 JUnit 5 至少两 JUnit Platform 和  JUnit Vintage 两个&lt;/li&gt;
&lt;li&gt;建立全新项目可以只引入  JUnit Platform 和 JUnit Jupiter&lt;/li&gt;
&lt;li&gt;混合型当然是三个全部引入&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是由于 jar 包之间本身存在某种依赖关系，所以实际上 pom.xml 可以比想像的更简单</description></item><item><title>理解 Spring 定时任务的 fixedRate 和 fixedDelay 的区别</title><link>https://yanbin.blog/understand-spring-schedule-fixedrate-fixeddelay/</link><pubDate>Thu, 07 Sep 2017 03:04:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-spring-schedule-fixedrate-fixeddelay/</guid><description>
&lt;p&gt;用过  Spring 的 @EnableScheduling 的都知道，我们用三种形式来部署计划任务，即 @Scheduled 注解的 fixedRate(fixedRateString), fixedDelay(fixedDelayString), 以及 cron. cron 不在这里讨论的范畴。我们着重在如何理解 fixedRate 和 fixedDelay 的区别。&lt;/p&gt;
&lt;p&gt;在 Spring 的  &lt;a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html#fixedDelay--"&gt;Scheduled&lt;/a&gt; 注解的 JavaDoc 对此的解释很简单&lt;/p&gt;
&lt;blockquote&gt;
public abstract long fixedRate &lt;br /&gt;
Execute the annotated method with a fixed period in milliseconds between invocations.&lt;br /&gt;
&lt;br /&gt;
public abstract long fixedDelay &lt;br /&gt;
Execute the annotated method with a fixed period in milliseconds between the end of the last invocation and the start of the next.
&lt;/blockquote&gt;
&lt;p&gt;只是说是 fixedRate 任务两次执行时间间隔是任务的开始点，而 fixedDelay 的间隔是前次任务的结束与下次任务的开始。&lt;/p&gt;
&lt;p&gt;大致用示意字符串来表示如下(每个 T1, 或 T2 代表任务执行秒数(每次任务执行时间不定)，假定 fixedRate 或  fixedDelay 的值是 5 秒，用 W 表示等待的数)&lt;/p&gt;
&lt;p&gt;fixedRate:    T1.T1WWWT2.T2.T2WW.T3.T3.T3.T3.T3.T4.T4.T4.T4.T4.T4.T4T5T5WWWT6.T6........&lt;/p&gt;
&lt;p&gt;fixedDelay:  T1.T1.WWWWW.T2.T2.T2WWWWW.T3.T3.T3.T3.T3.WWWWW.T4.T4.T4.T4.T4.T4.T4.WWWWWT6.T6......&lt;/p&gt;
&lt;p&gt;一般来说能理解到上面两个场景已经差不多了，相比而言 fixedDelay 简单些，盯着上一次任务的屁股就行。</description></item><item><title>Terraform 进阶 - 部署 Lambda 并创建相关资源</title><link>https://yanbin.blog/terraform-deploy-lambda-create-resources/</link><pubDate>Mon, 28 Aug 2017 02:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/terraform-deploy-lambda-create-resources/</guid><description>
昨日刚刚体验了 Terraform 是一个什么鬼东西 &lt;a href="https://yanbin.blog/terraform-get-started-with-first-sample/"&gt;Terraform 使用 - 从最简单例子开始&lt;/a&gt;，今天再进一步。将来尝试的是使用 Terraform 来部署一个 Lambda 应用，并创建相关的资源。&lt;/p&gt;
&lt;br/&gt;
本例中的 Lambda 要由 Kinesis 来触发，并写数据到 S3 Bucket 中去，所以需要做的事情大致如下：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;创建 IAM Role, 该 Role 要能访问 S3, Kinesis 和 CloudWatch&lt;/li&gt;
&lt;li&gt;创建一个 Kinesis Stream (指定 Shard 数目)&lt;/li&gt;
&lt;li&gt;创建一个 S3 Bucket&lt;/li&gt;
&lt;li&gt;部署 Lambda (要指定能访问 S3 Bucket 的 Role, 并其他参数，如环境变量)&lt;/li&gt;
&lt;li&gt;设置 Lambda 的 Kinesis 触发器 (指定源 Kinesis Stream 和  batchSize)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
以下是 Lambda 的实现代码，从 Kinesis 读出字符串，逗号分割，第一部分作为 S3 Key, 第二部分作为文件内容写入到 S3 Bucket 中去。S3 Bucket 名称从环境变量中读取。</description></item><item><title>Java 反射有效的修改 final 属性值</title><link>https://yanbin.blog/java-reflection-modify-final-field/</link><pubDate>Sun, 27 Aug 2017 12:20:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-reflection-modify-final-field/</guid><description>
两年前写过一篇 &lt;a href="https://yanbin.blog/java-reflection-modify-final-field-value/"&gt;Java 反射修改 final 属性值&lt;/a&gt;, 在这里重新温习一下，假设有个类
&lt;br/&gt;
&lt;blockquote&gt;
class Person {&lt;br /&gt;
    public final String name = "Mike";&lt;br /&gt;
}
&lt;/blockquote&gt;
这里声明 name 为非静态的属性只是为了说明反射修改 final 属性无关乎静态不静态，静态只是表现在它是一个类属性，在一个类加载器空间只会有一份拷贝，仅此而已。&lt;br/&gt;&lt;br/&gt;
创建一个通用方法进行反射修改属性值&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;modify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newFieldValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getDeclaredField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifiersField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDeclaredField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;modifiers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifiersField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Field 的 modifiers 是私有的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifiersField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getModifiers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FINAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAccessible&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAccessible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newFieldValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
调用 modify(...) 方法试图修改 person 的 name 属性</description></item><item><title>Terraform 使用 - 从最简单例子开始</title><link>https://yanbin.blog/terraform-get-started-with-first-sample/</link><pubDate>Sat, 26 Aug 2017 13:42:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/terraform-get-started-with-first-sample/</guid><description>
&lt;a href="https://www.terraform.io"&gt;Terraform&lt;/a&gt; 是一个 IT 基础架构自动化编排工具，它的口号是 "Write, Plan, and create Infrastructure as Code", 基础架构即代码。具体的说就是可以用代码来管理维护 IT 资源，比如针对 AWS，我们可以用它创建，修改，删除 S3 Bucket, Lambda, EC2 实例，Kinesis， VPC 等各种资源。并且在真正运行之前可以看到执行计划(即干运行-dryrun)。由于状态保存到文件中，因此能够离线方式查看资源情况 -- 当然，前提是不要在 Terraform 之外对资源进行修改。&lt;/p&gt;
&lt;br/&gt;
Terraform 配置的状态除了能够保存在本地文件中，也可以保存到 &lt;a href="https://www.terraform.io/docs/backends/types/consul.html"&gt;Consul&lt;/a&gt;, &lt;a href="https://www.terraform.io/docs/backends/types/s3.html"&gt;S3&lt;/a&gt;, azure, http, swift 等处。&lt;br/&gt;&lt;br/&gt;
Terraform 是一个高度可扩展的工具，通过 Provider 来支持新的基础架构，AWS 不过为目前官方内建 68 个 &lt;a href="https://www.terraform.io/docs/providers/index.html"&gt;Providers&lt;/a&gt; 中的一个。其他能用 Terraform 的地方有 Alicloud(阿里云, 实名制备案才能用), Google Cloud, Heroku, Kubernetes, Microsoft Azure, MySQL, RabbitMQ, Docker 等等。愿意的话可以写自己的 Provider, 如搞个 Kafka 的话，用来管理 Topic 等的创建，维护工作。&lt;br/&gt;&lt;br/&gt;
Terraform 之前我们对 AWS 的操作用的是 awscli, 或 &lt;a href="https://serverless.com/framework/"&gt;Serverless&lt;/a&gt;。awscli 什么都能做，但它是无状态的，必须明确用不同的命令来创建，修改和删除。Serverless 不是用来管理基础架构的，用它创建  Lambda 时创建资源都是很麻烦的事。AWS 提供的 CloudFormation 才是与 Terraform 较类似的工具，但是看到用法就头疼。</description></item><item><title>Kafka 发布消息时如何选择 Partition</title><link>https://yanbin.blog/how-kafka-select-partition/</link><pubDate>Sun, 20 Aug 2017 02:04:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-kafka-select-partition/</guid><description>
本文旨在了解 Kafka 发送消息到有多个 Partition 的 Topic 时如何选择 Partition。或许多数人已经知道 Kafka 默认(当 key 为 null) 时采用 &lt;a href="https://en.wikipedia.org/wiki/Round-robin"&gt;Round-robin&lt;/a&gt; 策略，也就是雨露均沾，风水轮流转，实现类是 &lt;a href="https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/clients/producer/internals/DefaultPartitioner.java"&gt;DefaultPartitioner&lt;/a&gt;。但我们实际应用中为保持相关消息按序到，就必须送到指定的 Partition，方法可以有&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;指定 Partition 编号&lt;/li&gt;
&lt;li&gt;指定 Key&lt;/li&gt;
&lt;li&gt;自定义 Partitioner - 实现 org.apache.kafka.clients.producer.Partitioner, 并通过属性注册&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
还应考究当指定了 Key 或 Partition 编号发送消息后，后续消息 key 为 null 会选用哪个 Partition。最后再思考一个问题，Consumer 每次  poll 时是获得的消息列表是否只包含一个 Partition 源还是可以多个 Partiton 源。&lt;br/&gt;&lt;br/&gt;
为完成本次实验，可以本地搭建一个 Kafka 环境，参考 &lt;a href="https://yanbin.blog/initial-apache-kafka-environment/"&gt;简单搭建 Apache Kafka 分布式消息系统&lt;/a&gt;。待 Zookeeper 和 Kafka 正常启动后，我们用下面的命令创建一个 Partition 数量为 3 的 Topic &lt;code&gt;partition-test&lt;/code&gt;&lt;br/&gt;
&lt;blockquote&gt;
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic partition-test
&lt;/blockquote&gt;
验证一下该 Topic 的信息</description></item><item><title>谁说 HTTP GET 就不能通过 Body 来发送数据呢？</title><link>https://yanbin.blog/why-http-get-cannot-sent-data-with-reuqest-body/</link><pubDate>Sat, 19 Aug 2017 00:30:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/why-http-get-cannot-sent-data-with-reuqest-body/</guid><description>
当我们被问及 HTTP 的 GET 与 POST 两种请求方式的区别的时候，很多答案是说 GET 的数据须通过 URL 以 Query Parameter 来传送，而 POST 可以通过请求体来发送数据，所以因 URL 的受限，往往 GET 无法发送太多的字符。这个回答好比在启用了 HTTPS 时，GET 请求 URL 中的参数仍然是明文传输的一样。&lt;br/&gt;&lt;br/&gt;
GET 果真不能通过 Request Body 来传送数据吗？非也。如此想法多半是因循着网页中 form 的 method 属性只有 get 与 post 两种而来。因为把 form 的 method 设置为 post, 表单数据会放在 body 中，而 method 为 get(默认值) 时, 提交时浏览器会把表单中的字符拼接到 action 的 URL 后作为 query parameter 传送。于是乎就有了这么一种假像：HTTP GET 必须通过 URL 的查询参数来发送数据。&lt;br/&gt;&lt;br/&gt;
其实 HTTP 规范并未规定说 GET 就不能发送 body 数据，在 RFC &lt;a href="https://tools.ietf.org/html/rfc2616#section-9.3"&gt;GET&lt;/a&gt; 中只是说&lt;br/&gt;
&lt;blockquote&gt;
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
&lt;/blockquote&gt;
只是说 GET 意味着通过 URI 来识别资源。&lt;br/&gt;&lt;br/&gt;
我也是本着传统上对 GET 与 POST 区别的误解很多年，今天突然意识到 GET 应该可以使用 body, 况且 HTTP 本身是一个纯文本的协议。没有测试就没有 100% 的发言权，所以做了如下的测试</description></item><item><title>自定义类加载器动态加载 JDBC 驱动</title><link>https://yanbin.blog/custom-classload-dynamic-load-jdbc-driver/</link><pubDate>Mon, 14 Aug 2017 02:42:25 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/custom-classload-dynamic-load-jdbc-driver/</guid><description>
&lt;p&gt;我们可以用自定义的 URLClassLoader 从外部动态加载类，并使用它。但数据库驱动的管理类 DriverManager 却不比较苛刻，不承认非当前应用系统加载器加载的驱动类。见 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/sql/DriverManager.html"&gt;DriverManager&lt;/a&gt; 的 JavaDoc &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When the method &lt;code&gt;getConnection&lt;/code&gt; is called, the &lt;code&gt;DriverManager&lt;/code&gt; will attempt to locate a suitable driver from amongst those loaded at initialization and those loaded explicitly using the same classloader as the current applet or application&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于有有应用自定义类加载器加载数据库驱动类的需求时，就要对原 Driver 简单包装一下。继续往后会说介绍为什么要这么做。&lt;/p&gt;
&lt;p&gt;说明一下，DriverManager 能够根据 JDBC 连接字符串匹配到驱动类，所以一般来说都不需要显式调用 DriverManager.registerDriver() 方法。&lt;/p&gt;
&lt;p&gt;先看 DriverManager 在应用外部驱动类时会出现什么情况</description></item><item><title>再论机械式针对接口编程</title><link>https://yanbin.blog/rough-interface-based-programming/</link><pubDate>Thu, 10 Aug 2017 00:11:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rough-interface-based-programming/</guid><description>
&lt;p&gt;两月前论及到 &lt;a href="program-to-interface-and-agile/"&gt;针对接口编程及敏捷编程&lt;/a&gt;，如今再一次老调重提。面向对象有一个很重要设计原则：&lt;/p&gt;
&lt;blockquote&gt;
针对接口编程，而不是针对实现编程
&lt;/blockquote&gt;
&lt;p&gt;这一原则关键在于理解什么是接口，请参照前文提到的上一篇。因为 Java 语言中有 interface 这个概念，于是 Java 的 interface 便躺枪了，好好的针对接口编程在 Java 中就机械式的变成了针对 interface 的编程。&lt;/p&gt;
&lt;p&gt;以致于只要有实现的地方都可能变成像 UserDao.java(接口) 与 UserDaoImpl.java 成对出现，在以后的重构中基本上是为了实现而修改接口，这也进一步违背了针对接口编程的初衷，接口怎么一点也不稳固。&lt;/p&gt;
&lt;p&gt;或许有人认为先声明一个 interface，然后我们可能会有多种实现(为将来考虑，不过多数时候只会是单一实现)，这很像是 interface 存在的一个理由。可是当我们写下类似 UserDaoImpl 这个样的实现类名时直接宣告了该接口其实就只有一种实现，不然的话再加一个实现的话类名为哪般？UserDaoImpl2? 显然多种实现时 UserDaoImpl 需要重命名了。</description></item><item><title>Vimer 的福音：Mac 下 Caps + hjkl 作为方向键</title><link>https://yanbin.blog/vimer-mac-caps-hjkl-as-arrow-keys/</link><pubDate>Tue, 25 Jul 2017 03:13:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vimer-mac-caps-hjkl-as-arrow-keys/</guid><description>
本人所用的键盘是 &lt;a href="http://www.wasdkeyboards.com/index.php/products/mechanical-keyboard/wasd-vp3-61-key-custom-mechanical-keyboard.html"&gt;WASD VP3 61 Key&lt;/a&gt; 的 -- Poker 3 方案，可以不借助于软件直接键盘上设置为 Caps + hjkl 来作为方向键。Caps 跳线为 Fn, 并选择一个 Layer, 把原本 Fn + ijkl 的方向键组合键重新映射为 Fn + hjkl 即可。&lt;br/&gt;&lt;br/&gt;
办公室的 61 键的键盘借于朋友体验，所以又重新把 DAS 87 键摆了出来。由于习惯了 61 键的 Caps + hjkl 的方向键，在 87 键盘上每次按方向键时右手都要离开它的 Home Row，很是不爽。别说是 87 键，即便是有些键盘把方向键压缩在右 Shift 下方，都无法接受右手右下移去按方向键。&lt;br/&gt;&lt;br/&gt;
Caps 键没什么卵用(只会用 Caps 来输入大写字母的当我没说)，却常年占据着黄金位置(左手掌无需移动就能按到)，所以主意是如何把 Caps 另作他们，对于 Vimer 来说最理想的方向键是 Caps + hjkl。&lt;br/&gt;&lt;br/&gt;
Mac 下有个键盘映射的工具 Karabiner, 先前的版本 KeyRemap4MacBook 只支持到 Mac OS 10.8, 而 Karabiner 也只能支持 10.9 到 10.11。而我的 Mac 系统是 macOS Sierra 10.12, 有一个不完全特性的 Karabiner 叫做 &lt;a href="https://github.com/tekezo/Karabiner-Elements/blob/master/README.md"&gt;Karabiner-Elements&lt;/a&gt;, 它不能像 KeyRemap4MacBook 和 Karabiner 那样支持直接组合键的映射。因此在 macOS Sierra 一直未能把方向键映射成某个功能键(如 Caps) + hjkl，不得不用物理方向键。&lt;br/&gt;&lt;br/&gt;
有两种方法把 Caps + hjkl 映射为方向键&lt;br/&gt;&lt;br/&gt;</description></item><item><title>函数定义 Kotlin V.S. Scala</title><link>https://yanbin.blog/kotlin-function-definition-vs-scala/</link><pubDate>Sat, 10 Jun 2017 19:33:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kotlin-function-definition-vs-scala/</guid><description>
关注 Kotlin 已有段时日了，真是因为 Google 把它扶正而跑来跟风。因为进行想在 Java 与 Scala 间找一个折中的编程语言，
也就是 Kotlin。这是一篇好几月前列的我 &lt;a href="https://yanbin.blog/features-of-ideal-programming-language"&gt;想像中理想编程语言的几个特征&lt;/a&gt;，
琢磨来去当今也就 Kotlin 比较符合我的口味。很早就想买 《Kotlin IN ACTION》这本书，因那是 Kotlin 1.1 刚出，而出版的书只涵盖到了 Kotlin, 所以未出手。
看看再有一本好的那样的书估计也不是一时半会儿，所以今天还是把那本书弄到手了，至于 Kotlin 1.1 后的特性自个去补充。&lt;/p&gt;
&lt;br/&gt;
尽管书中未提及 Kotlin 语言的设计灵感来自于何种语言,  但我的直觉就是与 Scala 太多相似之处，但没有 Scala 简单，并揉合一些 Swift 的特性，因此我在阅读 《Kotlin IN ACTION》时更多的会和 Scala 相比较。&lt;br/&gt;&lt;br/&gt;
第一个主题是关于 Kotlin 函数的定义与约定。Kotlin 的基本定义格式与 Scala 是类似的&lt;br/&gt;
&lt;blockquote&gt;
//Kotlin&lt;br /&gt;
fun max(a: Int, b: Int): Int {&lt;br /&gt;
    return if (a &amp;gt; b) a else b&lt;br /&gt;
}
&lt;/blockquote&gt;
注：Kotlin 也像 Scala 一样，if 不再是一个控制语句，而是一个表达式，所以它是有返回值的。与  Java 有不同的是，Kotlin 的赋值语句是没有返回值的，不能用作 val b = (a = 3), 而 Scala 的赋值语句总是返回 Unit</description></item><item><title>针对接口编程及敏捷编程</title><link>https://yanbin.blog/program-to-interface-and-agile/</link><pubDate>Tue, 06 Jun 2017 10:00:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/program-to-interface-and-agile/</guid><description>
&lt;p&gt;本文发自于对平时编程习惯上的一些个人见解，还不至于牵扯到方法学的层面，尽管如此，也可能会招来许多不同的看法，只要是觉得经世致用就行。首先从耳熟能详的针对接口编程说起&lt;/p&gt;
&lt;h3&gt;是否总是针对接口编程&lt;/h3&gt;
&lt;p&gt;在初通软件设计时，针对接口编程这一理念似乎是宇宙真理(软件世界里并没有真理部)，而且对它的解释是&lt;/p&gt;
&lt;blockquote&gt;
具体类包含实现细节，而抽象类则只呈现概念
&lt;/blockquote&gt;
&lt;p&gt;当然很在理，也很权威。&lt;/p&gt;
&lt;p&gt;但针对接口的前提是什么呢？是在设计一个与外部系统交互的 API 情况下。比如要提供一个用户注册接口给外部，可以共同约定好接口为&lt;/p&gt;
&lt;blockquote&gt;
void register(String username, String password) throws RegistrationException;
&lt;/blockquote&gt;
&lt;p&gt;  并且这个接口应该是稳固的，然后各自去实现或完成调用细节，即使实现未完成调用端也可以通过 Mock 来进行单元测试。&lt;/p&gt;
&lt;p&gt;然而实际中对针对接口编程的理解很容易变为凡是非工具类，数据类都先声明一个接口挂单一实现类，类结构中就类似下面那样</description></item><item><title>Spring 定时任务(Schedule) 和线程</title><link>https://yanbin.blog/spring-schedule-runner-threads/</link><pubDate>Mon, 05 Jun 2017 01:02:12 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-schedule-runner-threads/</guid><description>
&lt;h3&gt;Spring 定时任务实例&lt;/h3&gt;
Spring 中使用定时任务很简单，只需要  @EnableScheudling 注解启用即可，并不要求是一个 Spring Mvc 的项目。对于一个 Spring Boot 项目，使用定时任务的简单方式如下：&lt;br/&gt;&lt;br/&gt;
pom.xml 中&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.5.3.RELEASE&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Application.java</description></item><item><title>使用 avro-tools, jq 查看 Apache Avro 序列化文件</title><link>https://yanbin.blog/avro-tools-jq-view-apache-avro-file/</link><pubDate>Sun, 04 Jun 2017 14:38:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/avro-tools-jq-view-apache-avro-file/</guid><description>
Apache Avro 是类似于 Google protobuf 那样的数据交换协议，但 Avro 可以用 JSON 格式来定义 Schema, 所以相比而言更容易上手。它也是 Hadoop, Kafka 所采用的交换格式。对于生成的 avro 序列化文件如果要编写代码来解读其中内容的话就太过于麻烦，Apache 给了我们一个便捷的工具来处理 Avro Schema 和数据。&lt;/p&gt;
&lt;br/&gt;
Java 版的 Avro Tools 可点击链接 &lt;a href="http://apache.mirrors.tds.net/avro/avro-1.8.2/java/avro-tools-1.8.2.jar"&gt;avro-tools-1.8.2.jar&lt;/a&gt; 下载，当前版本 1.8.2(发布于 2017/05/20)，执行命令是&lt;br/&gt;
&lt;blockquote&gt;
java -jar avro-tools-1.8.2.jar ..............
&lt;/blockquote&gt;
如果是 Mac 平台，还可以通过&lt;br/&gt;
&lt;blockquote&gt;
brew install avro-tools
&lt;/blockquote&gt;
来安装，执行命令就只是 &lt;code&gt;avro-tools&lt;/code&gt; 了。&lt;br/&gt;&lt;br/&gt;
在本文中还会用到一个 JSON 格式化高亮显示的工具 &lt;code&gt;jq&lt;/code&gt;, 在 Mac 下通过以下命令安装&lt;br/&gt;
&lt;blockquote&gt;
brew install jq
&lt;/blockquote&gt;
avro-tools 和 jq 已准备就绪，接下来演示下如何使用它们。&lt;br/&gt;</description></item><item><title>AWS Lambda 按序处理同一个 Kinesis Shard 中的消息</title><link>https://yanbin.blog/aws-lambda-consume-messages-from-one-share-in-series/</link><pubDate>Fri, 19 May 2017 02:56:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-lambda-consume-messages-from-one-share-in-series/</guid><description>
当 AWS Lambda 由 Kinesis 消息来触发时，一个 Kinesis Shard 会相应启动一个 Lambda 实例，比如说 Kinesis Stream 有 5 个 Shards, 那同时只会启动 5 个 Lambda 实例。那么把多条消息发送到同一个 Kinesis Shard 中去，这些消息会被如何消费呢？答案是按顺消息，不管这些消息是否被不同的 Lambda 实例处理。本文就是关于怎么去理解 &lt;a href="https://aws.amazon.com/lambda/faqs/"&gt;https://aws.amazon.com/lambda/faqs/&lt;/a&gt; 的下面那段话的内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;b&gt;Q: How does AWS Lambda process data from Amazon Kinesis streams and Amazon DynamoDB Streams?&lt;br /&gt;
AWS Lambda 如何处理来自于 Amazon Kinesis 和 DynamoDB 的数据&lt;/b&gt;
The Amazon Kinesis and DynamoDB Streams records sent to your AWS Lambda function are strictly serialized, per shard. This means that if you put two records in the same shard, Lambda guarantees that your Lambda function will be successfully invoked with the first record before it is invoked with the second record. If the invocation for one record times out, is throttled, or encounters any other error, Lambda will retry until it succeeds (or the record reaches its 24-hour expiration) before moving on to the next record. The ordering of records across different shards is not guaranteed, and processing of each shard happens in parallel.
从 Kinesis 和 DynamoDB 单个 Shard 上的记录会被 Lambda 严格的按序处理。这意味着如果你送两条记录到相同的 Shard, Lambda 将会保证第一条记录成功处理后才会处理第二条记录。假如处理第一条记录时超时，或超过资源使用上限，或碰到任何错误， Lambda 将会不断重试直到成功(或记录在 24 小时后过期), 而后才会去处理下一条记录。跨 Shard 的记录不保证到达顺序，且是并行处理多个 Shard 来的记录。
&lt;/blockquote&gt;
可以做几个试验，下面的代码可以保证消息总是被发送到同一个 Kinesis Shard，因为 PartitionKey 参数是一个常量</description></item><item><title>《Practical Vim》阅读笔记 (4)</title><link>https://yanbin.blog/practical-vim-2-notes-4/</link><pubDate>Mon, 15 May 2017 01:15:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/practical-vim-2-notes-4/</guid><description>
一路阅读完第五章直接跳跃到第十章有关宏的内容，宏的录制和回放功能是多数编辑器都会提供，但我却基本不用这一功能。在 Vim 中宏比点号(.) 更有效的处理重复性操作，既然作为一个 Vimer, 以重复操作为耻，因而十分有必要对它加以了解和掌握的，又是一个一分钟上手需一辈子来掌握的东西。&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/practical-vim-2-notes-4/vi-macro-recording.png"
width="360px"
/&gt;
1. 宏是录制在某个寄存器中，用 &lt;code&gt;q{register}&lt;/code&gt; 启动录制，例如 &lt;code&gt;qa&lt;/code&gt;, 然后执行一系操作，再次按 &lt;code&gt;q&lt;/code&gt; 结束录制，第一次回放用 &lt;code&gt;@{register}&lt;/code&gt;, 如 &lt;span style="color: #666666; font-family: monospace;"&gt;&lt;span style="background-color: #fafafa;"&gt;&lt;code&gt;@a&lt;/code&gt;,以后多次执行上一次宏回放可用 &lt;code&gt;@@&lt;/code&gt;.也可以支持数字,如 &lt;code&gt;10@a&lt;/code&gt;, 或 &lt;code&gt;10@@&lt;/code&gt;. &lt;code&gt;:reg a&lt;/code&gt; 可查看寄存器 &lt;code&gt;a&lt;/code&gt; 中宏的内容&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
2. 录制宏的一条金律是：确保每一条命令是可重复的。&lt;code&gt;h&lt;/code&gt;, &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;k&lt;/code&gt;, &lt;code&gt;l&lt;/code&gt;, 没什么意义，而 &lt;code&gt;0e&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;I&lt;/code&gt; 就是可重复的。当回放宏时遇到光标移动失败时, 如光标已在行首，宏中有 &lt;code&gt;h&lt;/code&gt; 操作，那么后续的操作被放弃，根据 &lt;code&gt;visualbell&lt;/code&gt; 配置可能有响声提示。宏的 motion 失败即放弃的特性让我们在重复执行宏时可以用一个足够大的数字，如 &lt;code&gt;200@@&lt;/code&gt;, 反正无副作用&lt;br/&gt;&lt;br/&gt;
3. 宏的序列执行与并发执行，当用 &lt;code&gt;5@a&lt;/code&gt; 执行宏 &lt;code&gt;"a   0f.r)w~j&lt;/code&gt;, 其实是把宏放在一个队列里，往下跳行执行，其他任何一行执行失败，后续行将得不到更新。如果针对宏 &lt;code&gt;"a   0f.r)w~&lt;/code&gt;, 对选择的多行执行 &lt;code&gt;:'&amp;lt;,'&amp;gt;normal @a&lt;/code&gt; 时效果就不一样了，它的意思是针对选择的多行单独执行宏，任何行的失败互不影响，所以它是并发执行的</description></item><item><title>AWS S3 Key 前缀分布优化数据请求的性能</title><link>https://yanbin.blog/aws-s3-key-prefix-distribution-performance/</link><pubDate>Fri, 12 May 2017 00:53:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-s3-key-prefix-distribution-performance/</guid><description>
很早就想写下这篇日志的，因为实际使用 AWS S3 来存取文件使用什么样的 Key 对性能的影响是极其大的。当然，如果你对 S3 的并发请求在 50 以内是无所谓的，要是并发要求很高的话，Key 的选择就变得至关重要的，不可不察。S3 Key 从第一个字符算起的任意长度子字符串都被称作前缀(prefix), 而对 S3 文件访问性能影响不在完整的 Key, 恰恰是那个前缀。&lt;br/&gt;&lt;br/&gt;
背景：我们最初在使用 S3 时，存储的文件的 Key 直接用了数据库的自增 ID，于是保存到 Bucket 中大概下面那样子的&lt;br/&gt;
&lt;blockquote&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;0.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;1.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;2.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;3.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;4.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;5.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;6.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;7.csv&lt;br /&gt;
examplebucket/&lt;span style="color: #ff0000;"&gt;1213485&lt;/span&gt;8.csv
&lt;/blockquote&gt;
Bucket 中有百万个文件，当初测试时 60 个左右的 Lambda 实例同时访问这个 Bucket 中不同的文件时，加载每个 S3 文件的时间大约在几百毫秒，然后并发上到 70， 80 后加载同样大小的 S3 文件的时间陡然增加到 10 秒以上，并发继续上到 100 以上直接导致众多 S3 的请求超时。后来了解到虽然一个 Bucket 中放多少个文件是没有限制的，而且官方文档说了文件多了并不影响访问的性能，但背后却有一个文件的分区存储机制，这个才是关键。&lt;br/&gt;&lt;br/&gt;
S3 的分区存储就像是硬盘分区，或文件分布在不同硬盘上的效果。试想一下，如果我们多个线程同时从一块硬盘上读取数据，每个线程需共同一个磁头来读取数据，性能就差; 但如果那些线程同时从不同的硬盘上读取各自的数据，那性能就大大提升了，它们互不干扰。在使用机械硬盘时我有过这样的体验，在同一个磁盘上拷贝文件比从一个磁盘拷贝到另一个磁盘要慢很多。</description></item><item><title>《Practical Vim》阅读笔记 (3)</title><link>https://yanbin.blog/practical-vim-2-notes-3/</link><pubDate>Tue, 09 May 2017 00:49:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/practical-vim-2-notes-3/</guid><description>
&lt;p&gt;1. 学习完前面的三大模式：正常模式，插入模式和可视模式后，现在步入了命令模式。&lt;code&gt;:&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt; 或 &lt;code&gt;?&lt;/code&gt;, 和 &lt;code&gt;&amp;lt;C-r&amp;gt;=&lt;/code&gt; 分别进入到 &lt;code&gt;Ex&lt;/code&gt; 命令，搜索和表达式，它们都被称作命令模式, 以前还常常把正常模式说成是命令模式。&lt;code&gt;:&lt;/code&gt; 后的是 &lt;code&gt;Ex&lt;/code&gt; 命令，是因为我们仍然沿袭了 Vim 的前身 Ex。我在 Mac 下输入命令 &lt;code&gt;ex&lt;/code&gt; 就会看到这个&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/practical-vim-2-notes-3/Ex-mode.png"
width="536px"
/&gt;
&lt;p&gt;进入到 Vim 的 Ex mode, 输入 &lt;code&gt;visual&lt;/code&gt; 命令就是正常的 Vim 界面。Vim 之于 Ex 就像 Gui 之于 Shell, Vim 的 Ex 命令可以做任何操作，见 &lt;code&gt;:h ex-cmd-index&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;2. 命令模式在 &lt;code&gt;:&lt;/code&gt; 提示符下输入时可以使用插入模式下的很多命令，如 &lt;code&gt;&amp;lt;C-w&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;C-u&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;C-k&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;C-v&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;C-r&amp;gt;{reg}&lt;/code&gt;, 甚至是 &lt;code&gt;&amp;lt;C-r&amp;gt;=&lt;/code&gt;，就是不能用 motion 命令，光标移得靠方向键&lt;/p&gt;
&lt;p&gt;3. 在阅读本章之前对 Vi 的命令模式只能用不觉明厉来形容，它对我的贡献仅仅是 &lt;code&gt;:wq&lt;/code&gt; 之类的，模式查找，简单替换，再就是执行一些未设置快捷键插件功能。也一直未理解替换怎么是 &lt;code&gt;%s/abc/dev&lt;/code&gt;, 只是依葫芦画瓢，现在终于可以在本章中理解那些命令了。从中真的能体会到 Ex 命令模式的乐趣，Ex 是不会把整个文件内容显示在屏幕上的, Ex 命令通常由 范围 + 命令 组成。&lt;/p&gt;
&lt;blockquote&gt;
:2           -- 光标跳到第一行，正常模式下可以 2G, 或 2gg&lt;br /&gt;
:print    -- 打印当前行的内容
&lt;/blockquote&gt;
&lt;p&gt;范围和命令一般会写在一块，范围可借用同样意义的特殊字符，默认操作当前行，因此下面一系列的命令就好理解了</description></item><item><title>Spring 项目中把属性或 SQL 语句写在 .xml 文件中</title><link>https://yanbin.blog/spring-properties-sql-in-xml-file/</link><pubDate>Wed, 03 May 2017 00:55:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-properties-sql-in-xml-file/</guid><description>
Spring 项目中把大量的 SQL 分散在 Java 代码中，无 Here Doc 的情况下用加号来连接写着实在是不爽，于是之前思考这个 &lt;a href="https://yanbin.blog/spring-external-sql-statements/"&gt;Spring 项目中把 SQL 语句写在 .sql 文件中&lt;/a&gt; --  把它们写在 *.sql 文件中，但是这个 *.sql 需要特定的格式来标识属性 Key&lt;br/&gt;
&lt;blockquote&gt;
--!select.user&lt;br /&gt;
select id, firstname, lastname, address
--!update.user&lt;br /&gt;
update ........
&lt;/blockquote&gt;
而且还需要一个额外的类 SqlPropertySourceFactory 来解析上面的 *.sql 文件, 识别出 select.user 是 Key, 紧接着后面的块是相应的属性值，用注解引用它时还有点额外的 factory 属性来配置，如&lt;br/&gt;
&lt;blockquote&gt;
@PropertySource(value = "classpath:sql/queries.sql", factory = SqlPropertySourceFactory.class)
&lt;/blockquote&gt;
所以一直在思考是否能够再简单些，是否能用一个自定义的注解，如&lt;br/&gt;
&lt;blockquote&gt;
@SqlPropertySource("classpath:sql/queries.sql")
&lt;/blockquote&gt;
捉摸了很久，似乎有点难度，不过再不断发掘的过程中找到了这个类 &lt;code&gt;org.springframework.core.io.support.PropertiesLoaderUtils&lt;/code&gt;, 有下面的代码片断</description></item><item><title>代码整洁之道(Clean Code) 笔记(三)</title><link>https://yanbin.blog/reading-clean-code-note-3/</link><pubDate>Mon, 24 Apr 2017 01:39:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/reading-clean-code-note-3/</guid><description>
&lt;h3&gt;第八章：边界&lt;/h3&gt;
&lt;p&gt;本章关于如何学习使用第三方组件&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第三组件或框架追求普适性，而使用者则想要集中满足特定的需求&lt;/li&gt;
&lt;li&gt;学习第三方组件首当其冲当然还是文档，其次重要的是它的单元测试，我甚至是把单元测试当作文档的一部分来看待。其实更重要的方法是学习性测试(learning tests), 通过测试来学习才是切实的体验，是一种精确的试验，我们甚至可以用自己的测试来验证第三方组件的新版本&lt;/li&gt;
&lt;li&gt;边界上会发生有趣的事，这就要求我们对它清晰的分割和定义，以避免我们的代码过多地了解第三方组件中的特定信息，依靠你能控制的东西好过依靠你控制不了的东西，免得日后受它控制。简而言之就是尽可能隔离边界，在边界改动时只需要修改一下适配器，对的适配&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;第九章：单元测试&lt;/h3&gt;
&lt;p&gt;这一章我自认为是很重要的，特别是正在采用测试驱动开发/设计(TDD) 的程序员来讲。充分，良好的单元测试是保证我对生产代码大刀阔斧的关键，这也就是 TDD 的最后一个 D 又能理解了 Design 的原由&lt;/p&gt;</description></item><item><title>JMockit 中捕获 mock 对象方法的调用参数</title><link>https://yanbin.blog/jmockit-capture-mock-call-arguments/</link><pubDate>Fri, 21 Apr 2017 01:02:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-capture-mock-call-arguments/</guid><description>
&lt;p&gt;&lt;/p&gt;三个月前写过一篇 &lt;a href="https://yanbin.blog/mockito-capture-method-paramters"&gt;Mockito 中捕获 mock 对象方法的调用参数&lt;/a&gt;，一般项目中 Mockito 不决求助于 JMockit, 同样的在 JMockit 也需对捕获被 Mock 的方法调用参数。当我们用 &lt;code&gt;new Expectations(){{}}&lt;/code&gt; 打桩并在后面断言了返回值，那就无需捕获参数来断言，匹配到了方法调用即证明传入的参数也是对的，如下面的代码所示&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserServiceTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Mocked&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;couldCallUserDaoToAddUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expectations&lt;/span&gt;&lt;span class="p"&gt;(){{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;123&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Yanbin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;123&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Yanbin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//这里断言成功也就证明了 userDao.findById(123) 方法被调用，参数必须是 123&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
但如果是未打桩的方法，或打桩是用的模糊参数(withInstanceOf(String.class)), 或是无返回值的方法就要事后对是否调用了某个方法以及传入什么参数的情况进行断言。</description></item><item><title>使用 Awaitility 测试异步代码</title><link>https://yanbin.blog/test-asyn-call-with-awaitility/</link><pubDate>Wed, 19 Apr 2017 00:30:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/test-asyn-call-with-awaitility/</guid><description>
对于同步方法的测试很简单，调用完后可立马检查执行状态; 而异步方法，由于我们无法确切的知道何时结束，因此以往的办法是用 &lt;code&gt;Thread.sleep(500)&lt;/code&gt; 来预估一个执行时间。然后通常我们估计的要长于实际的时间，这就很浪费，况且偶然的超过预估的等待时间也并不意味着代码有问题。还有 &lt;code&gt;sleep&lt;/code&gt; 方法还抛出一个检测异常 &lt;code&gt;InterruptedException&lt;/code&gt;, 一般会要对 &lt;code&gt;Thread.sleep(500)&lt;/code&gt; 作下简单包装。&lt;br/&gt;&lt;br/&gt;
于是今天要介绍的 &lt;a href="http://www.awaitility.org/"&gt;Awaitility&lt;/a&gt; 就应运而生了，专门针对异步方法的测试。它的官方文档在 &lt;a href="https://github.com/awaitility/awaitility/wiki/Usage"&gt;https://github.com/awaitility/awaitility/wiki/Usage&lt;/a&gt;。本文主要关注在 Java 8 环境下用 Lambda 的代码书写方式。Awaitlity 实际运行是以某种轮询的方式来检查是否达到某个运行状态，可设定最多，最少等待时间，或永久等待，或自定义轮询策略，之后就开始进行需要的断言，所以它可以尽可能的节省测试异步方法所需的时长。而不像 &lt;code&gt;Thread.sleep(500)&lt;/code&gt; 一路等到黑，并且没有回头路。&lt;br/&gt;&lt;br/&gt;
通常我会在项目中给 JUnit 配上三个最佳伴侣，它们是(按 &lt;code&gt;mvn dependency:tree&lt;/code&gt; 中的显示方式):&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;org.awaitility:awaitility:2.0.0:test&lt;/li&gt;
&lt;li&gt;org.assertj:assertj-core: version: 3.8.0:test&lt;/li&gt;
&lt;li&gt;org.mockito:mockito-core:2.7.22:test&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
当然如果项目中没有异步调用自然是不需要 Awaitility, 在我的项目中是基本不可能的。以上三种都追求 DSL，以流畅的方式进行愉快的测试。&lt;br/&gt;&lt;br/&gt;
现在来尝试下 Awaitility 的几种基本的用法，先假定有下面的代码 &lt;code&gt;UserService&lt;/code&gt;</description></item><item><title>代码整洁之道(Clean Code) 笔记(二)</title><link>https://yanbin.blog/reading-clean-code-note-2/</link><pubDate>Sun, 02 Apr 2017 23:44:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/reading-clean-code-note-2/</guid><description>
&lt;h3&gt;第四章：注释&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;别给糟糕的代码加注释 -- 重新写吧&lt;/li&gt;
&lt;li&gt;什么也不会比乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧，提供错误信息的注释更有破坏性&lt;/li&gt;
&lt;li&gt;若编程语方足够有表达力，或者我们长于用这些语方来表达意图，就不那么需要注释 -- 也许根本不需要&lt;/li&gt;
&lt;li&gt;注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败。注释总是一种失败&lt;/li&gt;
&lt;li&gt;不准确的注释要比没注释坏得多。它们满口胡言。代码，只有代码能忠实的告诉你它做的事&lt;/li&gt;
&lt;li&gt;好注释不多，如法律信息，警示等。我都不觉得 &lt;code&gt;TODO&lt;/code&gt; 是多好的东西&lt;/li&gt;
&lt;li&gt;大多数注释都是坏注释，甚至一些 IDE 自动生成的代码也加一堆的废话注释，分散阅读注意力。我也写过那种日志式注释，这本是版本控制系统干的事。&lt;/li&gt;
&lt;li&gt;很奇怪 IntelliJ IDEA 还在自动为新建的类加下创建者，代码是团队共有，时间长了根本就与作者无半毛关系。我经常碰到这种写有作者的代码哭笑不得，直接把原作者改了或删了也不太好，还不如把文件删了，新建一个没有作者注释的类&lt;/li&gt;
&lt;li&gt;直接把代码注释掉是种讨厌的做法，自己可能不会再启用，其他人以为很重要更不敢删。不用了最好果断删掉，版本控制系统会帮你记住历史。&lt;/li&gt;
&lt;li&gt;个人认为，注释会胡说八道，产品代码和测试代码不会撒谎。有功夫写注释还不如写好测试用例，测试需要维护，它本身就是最真实的文档。&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>使用 Mockito 修改私有属性</title><link>https://yanbin.blog/mockito-modify-private-field/</link><pubDate>Sat, 01 Apr 2017 10:59:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-modify-private-field/</guid><description>
修改私有属性来 Mock 可能不是一种很好的测试方式, 因为属性名是动态的，但有时不得已而为了，例如下面的代码：
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalApi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;findUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convertId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
测试时欲隔离对 ExternalApi 的外部依赖, 当然可以把它也作为构造函数的一个参数，这样创建 UserService 实例时就可以 Mock external 属性。不过 external 经常是不变的，所以作为方法参数的必要性也不大。这就希望能在构造出 UserService 之后对 external 私有属性进行 Mock 处理。&lt;br/&gt;&lt;br/&gt;
在 Mockito 1.x 和 2.x 下要使用不同的方式，分别使用到 Whitebox 和 FieldSetter 类，它们都来自于  &lt;code&gt;mockito.internal.util.reflection&lt;/code&gt; 包，可见 Mockito 打心底不推荐直接使用它们，但谁叫它们是 public 的呢。还有一种方式是使用 PowerMock + Mockito, 这是后话。</description></item><item><title>AWS Java Lambda 与环境变量</title><link>https://yanbin.blog/aws-java-lambda-environment-variable/</link><pubDate>Tue, 28 Mar 2017 00:15:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-java-lambda-environment-variable/</guid><description>
&lt;span style="color: #0000ff;"&gt;一句话概要：对 Lambda 环境变量的任何改动都会引起一次 Lambda 的冷启动，大可放心在 handleRequest(...) 方法外使用环境变量。&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
从 &lt;a href="http://unmi.cc/aws-java-lambda-keys/"&gt;AWS 上 Java Lambda 应用记要 中&lt;/a&gt;，我学到了 Lambda 的实例是跨请求共享的，所以为使用 Lambda 配置的环境变量时曾写出了下面&lt;strong&gt;复杂而多余&lt;/strong&gt;的  AWS  Lambda 代码:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Handler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SNSEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPoolSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getThreadPoolSizeFromEnv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExecutorService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threadPoolSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SNSEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;snsEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuredThreadPoolSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getThreadPoolSizeFromEnv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuredThreadPoolSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPoolSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPoolSize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuredThreadPoolSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threadPoolSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello Lambda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getThreadPoolSizeFromEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;threadpool_size&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;50&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
这段代码看起来很在理，既然 Lambda 实例是共享的，那么在必变环境变量之后就可能不会重新初始始化实例，所以在每次的请求方法中对比如果环境变量值改动了就重新用最新的配置值来初始化线程池。然而上面的代码结结实实是多余的，真是把 Lambda 想得太简单了，如果是很多环境变量岂不是逐一判断。</description></item><item><title>AWS Java Lambda 使用 Logback 记录日志</title><link>https://yanbin.blog/aws-java-lambda-use-logback/</link><pubDate>Sat, 18 Mar 2017 02:46:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-java-lambda-use-logback/</guid><description>
直接一句话：去掉 Log4J 的依赖，把  Slf4J, Logback, 和 log4j-over-slf4j 依赖加进来就行了，配置文件换成 logback.xml，这就完了，不要往下看了，都是些废话。&lt;/p&gt;
当我们用 &lt;a href="https://serverless.com/"&gt;Serverless&lt;/a&gt; 命令 &lt;code&gt;sls create -t aws-java-maven -p hello-lambda&lt;/code&gt; 创建的示例项目中直接用的是 Log4J 日志组件，而且也没用像  Slf4j, 或 Apache Common Logging 更上一层的通用日志框架。查看了几个 AWS 本身的组件 S3, SNS, 和 Kinesis 的 SDK, 它们内部是用的 Apache Common Logging 声明的日志变量&lt;br/&gt;
&lt;blockquote&gt;
import org.apache.commons.logging.LogFactory;&lt;br /&gt;
import org.apache.commons.logging.Logger;&lt;br /&gt;
&lt;br /&gt;
private static final Log log = LogFactory.getLog(AmazonKinesis.class)
&lt;/blockquote&gt;
而我们自己的组件中通用日志组件是 Slf4j, 底层实现为 Logback, 所以我们希望在 Lambda 中使用 Logback 来写日志。&lt;br/&gt;
选用一个通用日志框架总是明智之举，因为一个项目经常杂糅了多种日志实现，使用 Slf4J 或 Apache Common Logging 可以把它们(Log4J, Logback, 或更多)输出到共同的目的地，并且有统一的日志输出接口。而我们认为通用日志框架还是 Slf4J 要先进些，所以我们在 Java Lambda 中的日志方案是 Slf4J + Logback，还需要把 Log4J 的日志桥接到 Slf4J 上来，再经由 Logback 输出。&lt;br/&gt;&lt;br/&gt;
回到前面创建的 &lt;code&gt;hello-lambda&lt;/code&gt; 项目，看其中怎么用的 Log4J，先瞧瞧 &lt;code&gt;pom.xml&lt;/code&gt; 文件怎么引入的 Log4J, 它是间接通过一个 AWS 定义的 Log4J Appender 引入的</description></item><item><title>AWS 上 Java Lambda 应用记要</title><link>https://yanbin.blog/aws-java-lambda-keys/</link><pubDate>Thu, 16 Mar 2017 01:06:25 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aws-java-lambda-keys/</guid><description>
AWS 的 Lambda 给了那些不想自己管理 EC2 服务器和配置负载人员很大的便利，所以 Lambda 被描述为 Serverless。真正的只关注业务就行，怎么调度，同时有多少个实例运行交给亚马逊去处理就是了。运行 Lambda 的环境也是亚马逊内部的 EC2 服务器，镜像是 Amazon Linux, 所以如果想运行系统命令，那是 Linux 的。Lambda 支持多种语言 Node.js, Python, C#(.net core), 还有 Java 8，我们就选择了 Java 8, 一开始还担心它与别的语言比起来会多大劣势，其实不然。而且所谓的 Java 8, 并非单指 Java 语言，而是指 JVM 平台，所以也可以用 Scala, Clojure, Groovy, Kotlin 来写。&lt;br/&gt;&lt;br/&gt;
Java 与脚本语言如 Node.js, Python 相比给人一个明显的感觉是启动慢，还有人用统计数据来比划 &lt;a href="https://github.com/berezovskyi/lambda-test"&gt;AWS Lambda cold start(pseudeo-)benchmark&lt;/a&gt;. 不过真不用担心，人家说的是冷启动，也就发生在部署后第一次执行启动会比较慢。要是我们的 Lambda 经常被调用，或每天触发比较集中，Lambda 在任务到来之前处理待续状态，就不会有冷启动的耗时过程。或者是每次任务要执行 3 分钟左右，又何必在乎毫秒级的冷启动时间。&lt;br/&gt;&lt;br/&gt;
说到底就是别理会下面的数据&lt;br/&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;20ms startup time for Python ~ $0.041675&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;40ms startup time for Node.js ~ $0.08335&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;80ms startup time for Java ~ $0.1667&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Lambda 实例重用&lt;/h3&gt;
Java 的 Lambda 就是一个微服务，在首次触发时微服务冷启动有些慢，但一旦启动之后就可以用这个微服务实例接受后续的请求，只有在比较长的一段时间内未被触发 AWS 才会把这个微服务杀掉。</description></item><item><title>代码整洁之道(Clean Code) 笔记(一)</title><link>https://yanbin.blog/reading-clean-code-note-1/</link><pubDate>Tue, 07 Mar 2017 23:19:32 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/reading-clean-code-note-1/</guid><description>
&lt;h3&gt;第一章：整洁代码&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;代码即设计&lt;/li&gt;
&lt;li&gt;童子军军规：把露营地清理得比来时还干净，签入代码前是否已做重构&lt;/li&gt;
&lt;li&gt;你湎自行实践，且体验自己失败。你须观察他人的实践与失败&lt;/li&gt;
&lt;li&gt;勒布朗(LeBlanc) 法则：稍后等于永不 (Later equals never)&lt;/li&gt;
&lt;li&gt;赶上期限的唯一方法--做得快的唯一方法--就是始终尽可能保持代码整洁&lt;/li&gt;
&lt;li&gt;缺乏“代码感”的程序员，看混乱是混乱，无处着手。有“代码感”的程序员能从混乱中看出其他的可能与变化&lt;/li&gt;
&lt;li&gt;不好的代码引诱别人做没规矩的优化，搞出一堆混乱来&lt;/li&gt;
&lt;li&gt;整洁的代码只做好一件事，糟糕的代码想做太多事，它意图混乱，目的含混&lt;/li&gt;
&lt;li&gt;整洁的代码从不隐藏设计者的意图&lt;/li&gt;
&lt;li&gt;整洁的代码只提供一种而非多种做一件事的途径&lt;/li&gt;
&lt;li&gt;整洁的代码总是看起来像是某位特别在意它的人写的&lt;/li&gt;
&lt;li&gt;回放我们编码过程显示，多多数时间都是在滚动屏幕，浏览其他模块&lt;/li&gt;
&lt;li&gt;读与写花费时间的比例超过 10:1, 写新代码时，我们一直在读旧代码&lt;/li&gt;
&lt;li&gt;编写代码的难度，取决于读周边代码的难度&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>使用 JMockit 来 mock 构造函数</title><link>https://yanbin.blog/jmockit-mock-constructors/</link><pubDate>Tue, 28 Feb 2017 01:50:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-mock-constructors/</guid><description>
Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式，经常这也 Mock 不了，那也 Mock 不了，需要迁就于测试来调整实现代码，使得实现极不优雅。比如 Mockito 在 私有方法，final 方法，静态方法，final 类，构造方法面前统统的缴械了。powermock 虽然可作 Mockito 的伴侣来突破 Mockito 本身的一些局限，但是我一用它来 Mock 一个构造方法就出错&lt;/p&gt;
&lt;br/&gt;
&lt;blockquote&gt;
Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter
&lt;/blockquote&gt;
&lt;br/&gt;
原因是 Mockito 变化太快，powermock 跟不上它的步伐 -- &lt;a href="https://github.com/powermock/powermock/issues/684"&gt;https://github.com/powermock/powermock/issues/684&lt;/a&gt;，于是我只能止步。&lt;br/&gt;&lt;br/&gt;
不得已再祭出 JMockit 这号称(也确实是)一无所不能的大杀器，在此见识一下它怎么 Mock 构造函数的&lt;br/&gt;&lt;br/&gt;
本篇实例所使用的 JMockit 版本是 1.30, 当前最新版 1.31, 由于尚未被 Maven 中央仓库收录，所以暂用 1.30。在 pom.xml 中如下方式引入</description></item><item><title>JdbcTemplate 易被 Java 8 Lambda 带入的坑</title><link>https://yanbin.blog/jdbctemplate-java-8-lambda-trick/</link><pubDate>Sun, 26 Feb 2017 03:06:18 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbctemplate-java-8-lambda-trick/</guid><description>
&lt;p&gt;Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利，不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后，JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化，而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。&lt;/p&gt;
下面就是我们的一个样板代码，在我们的 &lt;code&gt;UserRespository&lt;/code&gt; 中有一个方法 findAll() 用于获得所有用户：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jdbcTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;select id, name from user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Java 的参数检查与断言 - Spring Assert</title><link>https://yanbin.blog/java-assertion-spring-assert/</link><pubDate>Sat, 25 Feb 2017 03:12:23 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-assertion-spring-assert/</guid><description>
&lt;p&gt;之前有一篇 &lt;a href="https://yanbin.blog/java-assertion-guava-preconditions/"&gt;Java 的参数检查与断言&lt;/a&gt; 介绍了在 Java 中如何应用 Guava 的 Preconditions 来进行参数检查与状态断言，原本是可以心无旁骛，专心的用它就行了，可是刚刚因琢磨我们使用 JdbcTemplate 存在的一大 Bug，阅读 Spring 的源代码时发现 spring-util 也提供了一个类似于 Gruva Preconditions 的工具类 -- &lt;a href="http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/Assert.html"&gt;Assert&lt;/a&gt;，它是自 Spring 1.1.2 开始就静静的躺在那儿了。&lt;/p&gt;
&lt;p&gt;所以现在要检查参数或状态断言时反而犯上了选择综合证，虽然内心还是偏向于 Guava Preconditions，但总之不那么坚决了，也不知到底是谁在重新发明着轮子。可以说 Spring Assert 与 Guava Preconditions 的功能基本一致，也是针对入口参数或中间运行结果的检查分别抛出 &lt;code&gt;IllegalArgumentException&lt;/code&gt; 和 &lt;code&gt;IllegalStateException&lt;/code&gt; . 下面一张图来了解它的所有方法。</description></item><item><title>Java 可变参 Object...objects 方法的陷进</title><link>https://yanbin.blog/java-varargs-method-error-prone/</link><pubDate>Thu, 23 Feb 2017 00:04:01 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-varargs-method-error-prone/</guid><description>
&lt;p&gt;Java 的可变参数(Varargs) 方法让我们调用起来很方便，不需要总是去构造一个数组来传递不定数量的参数，而且还可以作为方法的一个可选参数，如&lt;/p&gt;
&lt;blockquote&gt;
void foo(int id, String...name) {&lt;br /&gt;
    String yourName = name.length == 0 ? "Anonymous" : name[0];&lt;br /&gt;
        ......&lt;br /&gt;
}
&lt;/blockquote&gt;
&lt;p&gt;想要告诉名字就调用 &lt;code&gt;foo(1, "Yanbin")&lt;/code&gt;, 不想的话就用 &lt;code&gt;foo(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;但我们在使用 Java Varargs 时，当变参类型定义为 &lt;code&gt;Object...objects&lt;/code&gt; 时就要当心了，因为 Object 类型的包容性原因一不小心就可以掉到坑里去了，例如下面的方法&lt;/p&gt;
&lt;blockquote&gt;
void foo(Object...objects) {&lt;br /&gt;
     Arrays.stream(objects).forEach(System.out::println());&lt;br /&gt;
}
&lt;/blockquote&gt;
&lt;p&gt;当引用类型是 &lt;code&gt;Object[]&lt;/code&gt; 时调用没问题，下面代码调用可以得到预期的结果</description></item><item><title>Java 与 Python 通过 Apache Avro 交换数据</title><link>https://yanbin.blog/java-python-communicate-via-apache-avro/</link><pubDate>Mon, 20 Feb 2017 00:22:02 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-python-communicate-via-apache-avro/</guid><description>
最近转战到 Amazon 的云服务 AWS 上，考虑到在使用它的 Lambda 服务时 Python 应用有比较可观的启动速度，与之相比而言，Java 总是慢热型，还是一个内存大户。所以有想法 Lambda 函数用 Python 来写，来增强响应速度，而内部的应用仍然采用 Java, 于是就有了 Java 与 Python 的数据交换格式。使用 Kafka 的时候是用的 &lt;a href="https://avro.apache.org/"&gt;Apache Avro&lt;/a&gt;, 因此继续考察它。&lt;/p&gt;
&lt;br/&gt;
注意，本文的内容会有很大部份与前一篇 &lt;a href="https://yanbin.blog/apache-avro-serializing-deserializing/"&gt;Apache Avro 序列化与反序列化 (Java 实现)&lt;/a&gt; 雷同，不过再经一次的了应用，了解更深了。&lt;br/&gt;&lt;br/&gt;
在不同类型语言间进行数据交换，很容易会想到用 JSON 格式，那我们为什么还要用 Apache Avro 呢？通过接下来的内容，我们可以看到以下几点：&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Apache Avro 序列化的格式也是 JSON 的，Java 的 Avro 库依赖于 Jackson 库&lt;/li&gt;
&lt;li&gt;序列化数据库本身带有 Schema 定义的，方便于反序列化，特别是对于 Java 代码; 而 JavaScript 会表示多此一举&lt;/li&gt;
&lt;li&gt;自动支持序列化数据的压缩，在官方提供的库中，Java 可支持 &lt;code&gt;deflate&lt;/code&gt;, &lt;code&gt;snappy&lt;/code&gt;, &lt;code&gt;bzip2&lt;/code&gt;, 和 &lt;code&gt;xz&lt;/code&gt;. 其他语言中可能少些，如 Python 只支持 &lt;code&gt;deflate&lt;/code&gt;, 和 &lt;code&gt;snappy&lt;/code&gt;, 应该可扩充。序列化数据中 Schema 部分不被压缩&lt;/li&gt;
&lt;li&gt;天然支持序列化对象列表，这样在序列化数据中只需要一份 Schema，类似于数据库表 Schema 加上多记录行的表示方式。只用 Apache Avro 传输小对象的话，数据量比 JSON 事 JDK 序列化的数据要大。&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
Apache Avro 官方提供有 C, C++, C#, Java, PHP, Python 和 Ruby 的支持库，可在网上找到其他语种的类库，如 NodeJS, Go 的，等等。</description></item><item><title>Wordpress 在 Linux 下不能发送邮件的问题</title><link>https://yanbin.blog/wordpress-linux-cannot-send-out-email/</link><pubDate>Sat, 18 Feb 2017 01:31:58 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-linux-cannot-send-out-email/</guid><description>
&lt;p&gt;本博客迁移到现在的 VPS 上已是两年有余，在有评论到来时从来就没收到过提醒邮件，以前是放在租的主机上，所以那些都有人配置好了的。其实一直没把 Wordpress 未能发送邮件的问题放心上，自己收不到倒无所谓，但评论者接收邮件提醒算是件重要的事。&lt;/p&gt;
&lt;p&gt;今天一同事说我的博客无法进行评论，我一段时间以来只有一丝感觉为何这么久没人评论了，只想大家都不爱说话罢了，并未意思到博客本身的问题，也在此表示感谢。于是自己测试了下，登陆用户评论没问题，游客无法评论，最后查到是 &lt;code&gt;wpDiscuz&lt;/code&gt; 与 &lt;code&gt;Math Comment Spam Protection&lt;/code&gt; 插件起冲突了，于是把后者禁用了就没问题。至于评论提交后响应有些慢是由于不得不用的 &lt;code&gt;Akismet&lt;/code&gt; 插件的原因。&lt;/p&gt;
&lt;p&gt;为评论的问题既然登陆到了 VPS 上了，也顺便把两年来的邮件发送问题也诊断一下，其实做来很简单，就是当初配置 VPS 时少做了一步。当前所用的 Linux 发行版是  Debian，之前记录过一篇 &lt;a href="http://unmi.cc/debian-vps-for-wordpress/"&gt;Debian Linux VPS 为 WordPress 的系统准备&lt;/a&gt;，其中提到 WordPress 发送邮件用的是 exim4,&lt;/p&gt;
&lt;p&gt;所以第一步，修改 &lt;code&gt;/etc/exim4/update-exim4.conf.conf&lt;/code&gt;, &lt;code&gt;dc_eximconfig_configtype&lt;/code&gt; 的值由 '&lt;code&gt;local&lt;/code&gt;' 改成 '&lt;code&gt;internet&lt;/code&gt;'&lt;/p&gt;
&lt;blockquote&gt;
dc_eximconfig_configtype='internet'
&lt;/blockquote&gt;
&lt;p&gt;然后重启 exim4 服务，可用命令 &lt;code&gt;service exim4 restart&lt;/code&gt;.</description></item><item><title>一个简单的 Java 自动批处理队列</title><link>https://yanbin.blog/simple-java-auto-batch-queue/</link><pubDate>Fri, 17 Feb 2017 01:58:38 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/simple-java-auto-batch-queue/</guid><description>
实际中可能有这样的应用场景，得到一个记录不需要立即去处理它，而是等累积到一定数量时再批量处理它们。我们可以用一个计数器，来一个加一个，量大时一块处理，然后又重零开始计数。如果记录的来源单一还好办，要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。&lt;/p&gt;
&lt;br/&gt;
这里我编写了一个最简单的任务批处理的队列，构造了告诉它批处理数量，消费者，然后就只管往队列里添加记录，队列在满足条件时自动进行批处理。因为内部使用的是 &lt;code&gt;BlockingQuque&lt;/code&gt; 来存储记录，所以多线程往里同时添加记录也没关系，最后的未达到 &lt;code&gt;batchSize&lt;/code&gt;, 的那些记录可主动调用 &lt;code&gt;completeAll()&lt;/code&gt; 函数或在达到 timeout 后来触发批处理，并且结束队列内的循环线程。&lt;br/&gt;&lt;br/&gt;
注意: 多线程环境下往一个无线程保护的集合或结构中，如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失，而往有线程保护的目的地写东西就安全了，如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价，不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。&lt;br/&gt;&lt;br/&gt;
下面是 BatchQueue 的简单实现</description></item><item><title>Java8 Lambda 表达式与 Checked Exception</title><link>https://yanbin.blog/java8-lambda-and-checked-exception/</link><pubDate>Wed, 08 Feb 2017 23:54:38 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java8-lambda-and-checked-exception/</guid><description>
当我们在使用 Java 8 的 Lambda 表达式时，表达式内容需要抛出异常，也许还会想当然的让当前方法再往外抛来解决编译问题，如下面的代码
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/java8-lambda-and-checked-exception/java-lambda-exception-1.png"
width="383px"
/&gt;
让 &lt;code&gt;main()&lt;/code&gt; 方法抛出 &lt;code&gt;Exception&lt;/code&gt; 还是不解决决编译错误，仍然提示 "Unhandled exception: java.io.FileNotFoundException"。&lt;br/&gt;&lt;br/&gt;
因为我们可能保持着惯性思维，忽略了 Lambda 本身就是一个功能性接口方法的实现，所以把上面的代码还原为匿名类的方式&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Consumer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FileInputStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
那么对于上面那种情况应该如何处理呢？</description></item><item><title>Java 运行时如何获取泛型参数的类型</title><link>https://yanbin.blog/java-how-to-get-generic-type/</link><pubDate>Sun, 05 Feb 2017 00:57:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-how-to-get-generic-type/</guid><description>
在 Java 中对于下面最简单的泛型类&lt;br/&gt;
&lt;blockquote&gt;
class A&amp;lt;T&amp;gt; {&lt;br /&gt;
    public void foo() {&lt;br /&gt;
        //如何在此处获得运行时 T 的具体类型呢？&lt;br /&gt;
    }&lt;br /&gt;
}
&lt;/blockquote&gt;
设想我们使用时&lt;br/&gt;
&lt;blockquote&gt;
new A&amp;lt;String&amp;gt;().foo();
&lt;/blockquote&gt;
&lt;br/&gt;
是否能在 &lt;code&gt;foo()&lt;/code&gt; 方法中获得当前的类型是 String 呢？答案是否定的，不能。在 &lt;code&gt;foo()&lt;/code&gt; 方法中 this 引用给不出类型信息，&lt;code&gt;this.getClass()&lt;/code&gt; 就更不可能了，因为 Java 的泛型不等同于 C++ 的模板类，&lt;code&gt;this.getClass()&lt;/code&gt; 实例例是被所有的不同具体类型的 A 实例(new A&amp;lt;String&amp;gt;(), new A&amp;lt;Integer&amp;gt;() 等) 共享的，所以在字节码中类型会被擦除到上限。&lt;br/&gt;&lt;br/&gt;
我们可以在 IDE 的调试时看到这个泛型类的签名&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/java-how-to-get-generic-type/generic_signature-1.png"
width="399px"
/&gt;</description></item><item><title>《Practical Vim》阅读笔记 (2)</title><link>https://yanbin.blog/practical-vim-2-notes-2/</link><pubDate>Fri, 03 Feb 2017 01:11:31 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/practical-vim-2-notes-2/</guid><description>
&lt;p&gt;1. Vim 的模式: 通常我们说有三种模式，正常模式(Normal Mode), 插入模式(Inert Mode) 和可视模式(Visual Mode). 其实除此之外还有&lt;br /&gt;&lt;br/&gt;
    4) Operator-Pending Mode: 就是正常模式下按下操作(Operator) 指令(如 c, d 等) 后，等待输入移动(Motion) 指示时的模式。这时 Escape 或 Motion 指令后退回到正常模式&lt;br /&gt;&lt;br/&gt;
    5) Insert Normal Mode: 在插入模式时，按下 &lt;code&gt;&amp;lt;Ctrl-o&amp;gt;&lt;/code&gt; 组合键后进入该模式, 此时可接受一个 Action (Operator + Motion = Action), 比如 &lt;code&gt;3dd&lt;/code&gt;, 然后自动返回到插入模式。&lt;code&gt;&amp;lt;Esc&amp;gt;&lt;/code&gt; 也能从 Insert Normal Mode 返回到插入模式。这方便了插入模式只想执行一次指令操作时来回切换, Insert Normal Mode 在 Vim 的状态栏中显示为下图那样 - NORMAL --(insert) --&lt;br /&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/practical-vim-2-notes-2/vim-insert-normal.png"
width="181px"
/&gt;
&lt;p&gt;    6) 替换模式(Replace Mode)&lt;code&gt;-- REPLACE --&lt;/code&gt;，&lt;code&gt;R&lt;/code&gt; 会进到替换模式，随后输入往后覆盖，可尝试 &lt;code&gt;&amp;lt;Insert&amp;gt;&lt;/code&gt; 键;  &lt;code&gt;r{char}&lt;/code&gt; 和 &lt;code&gt;gr{char}&lt;/code&gt; 临时进入单字符的替换模式，替换完当前字符立即退到正常模式，也就是光标不往下走。&lt;br /&gt;&lt;br/&gt;
    7) 可视替换模式(Visual Replace Mode) &lt;code&gt;-- VREPLACE --&lt;/code&gt;, 用 &lt;code&gt;gR&lt;/code&gt; 进入该模式，在处理 &lt;code&gt;tab&lt;/code&gt; 字符时会更友好，还有别的好处吧，所以该书建议尽量用可视替换模式。&lt;br /&gt;&lt;br/&gt;
    8) 可视模式(Visual Mode)，这就复杂了。它有三个子模式:&lt;br /&gt;&lt;br/&gt;
      &lt;code&gt;character-wise Visual mode&lt;/code&gt; --  &lt;code&gt;-- VISUAL&lt;/code&gt; ;&lt;br /&gt;&lt;br/&gt;
      &lt;code&gt;line-wise Visual mode&lt;/code&gt; --  &lt;code&gt;-- VISUAL LINE --&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;
      &lt;code&gt;block-wise Visual mode&lt;/code&gt; -- &lt;code&gt;-- VISUAL BLOCK --&lt;/code&gt;&lt;br /&gt;&lt;br/&gt;
&lt;br /&gt;&lt;br/&gt;
    9) 选择模式(Select Mode)，可视模式下选择了内容后用 &lt;code&gt;&amp;lt;C-g&amp;gt;&lt;/code&gt; 可在两种模式下切换，状态栏 &lt;code&gt;-- VISUAL --&lt;/code&gt; 和 &lt;code&gt;-- SLECT --&lt;/code&gt; 的不同，还有选择模式下直接输入就会替换当前的内容，与其他编辑器的行为一样，而可视模式需要按 &lt;code&gt;c&lt;/code&gt; 来替换当前内容。</description></item><item><title>使用 Byte Buddy 运行时生成泛型子类</title><link>https://yanbin.blog/leverage-bytebuddy-generate-generic-subclass/</link><pubDate>Wed, 01 Feb 2017 22:59:54 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/leverage-bytebuddy-generate-generic-subclass/</guid><description>
在上一篇中尝试了 &lt;a href="https://yanbin.blog/leverage-javassist-generate-generic-subclass"&gt;使用 Javassist 运行时生成泛型子类&lt;/a&gt;，这里要用另一个更方便的字节码增加组件 &lt;a href="http://bytebuddy.net/#/"&gt;Byte Buddy&lt;/a&gt; 来实现类似的功能, 但代码上要直白一些。就是运用 Byte Buddy 在运行时生成一个类的子类，带泛型的，给类加上一个注解，可生成类文件或 Class 实例，不过这里更进一步，实现的方法是带参数的。&lt;br/&gt;&lt;br/&gt;
用 Byte Buddy 操作起来更简单，根本不需要接触任何字节码相关的，诸如常量池等概念。与 Javassist 相比，Byte Buddy 更为先进的是能生成的类文件都是可加载运行的，不像 Javassist 生成的类文件反编译出来是看起来是正常的，但一加载执行却不那回事。&lt;br/&gt;&lt;br/&gt;
本例所使用的 Byte Buddy 的版本是当前最新的 1.6.7，在 Maven 项目中用下面的方式引入依赖&lt;br/&gt;
&lt;blockquote&gt;
&amp;lt;dependency&amp;gt;&lt;br /&gt;
    &amp;lt;groupId&amp;gt;net.bytebuddy&amp;lt;/groupId&amp;gt;&lt;br /&gt;
    &amp;lt;artifactId&amp;gt;byte-buddy&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
    &amp;lt;version&amp;gt;1.6.7&amp;lt;/version&amp;gt;&lt;br /&gt;
&amp;lt;/dependency&amp;gt;
&lt;/blockquote&gt;
下面是几个需要在本例中用到的类定义</description></item><item><title>使用 Javassist 运行时生成泛型子类</title><link>https://yanbin.blog/leverage-javassist-generate-generic-subclass/</link><pubDate>Wed, 01 Feb 2017 01:30:35 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/leverage-javassist-generate-generic-subclass/</guid><description>
&lt;p&gt;越是复杂的项目希望使用者能愉快的编码的话，可能就要使用到字节码增强工具来暗地里做些手脚。
这方面的工具有 JDK 的 Instrumentation, &lt;a href="http://asm.ow2.org/"&gt;ASM&lt;/a&gt;,
&lt;a href="http://commons.apache.org/proper/commons-bcel/"&gt;BCEL&lt;/a&gt;, &lt;a href="https://github.com/cglib/cglib/wiki"&gt;CGLib&lt;/a&gt;, i
&lt;a href="http://jboss-javassist.github.io/javassist/"&gt;Javassist&lt;/a&gt;,
还有 &lt;a href="http://bytebuddy.net/#/"&gt;Byte Buddy&lt;/a&gt;. Javassist 和 Byte Buddy 更贴近我们编码中的概念，使用起来也简单，
而其他几个工具需要我们更多的了解字节码指令，以及常量池等概念。所以我着重去了解怎么运用 Javassist 和 Byte Buddy 来动态修改来生成类文件。&lt;/p&gt;
&lt;br/&gt;
所以本文是系列中的第一篇，旨在以一个 Javassist 的例子来了解它的基本使用方法。本例中在运行时动态生成一个类的子类，并且是泛型的，实现了一个方法，给类加上了一个注解，最终生成一个类文件。总之尽可能的让这个例子具有代表性，同时又需控制它的复杂性。最后通过加载类文件的方式来验证前面生成的类是否是正确的，也可以直接反编译生成的类文件来查看源代码，不过实际操作中我们可能会被反编译出来的源代码欺骗。&lt;br/&gt;&lt;br/&gt;
本例所使用的 Javassist 的版本是 3.21.0-GA, 是在一个 Maven 项目中测试的，所以 Maven 的依赖是&lt;br/&gt;
&lt;blockquote&gt;
&amp;lt;dependency&amp;gt;&lt;br /&gt;
    &amp;lt;groupId&amp;gt;org.javassist&amp;lt;/groupId&amp;gt;&lt;br /&gt;
    &amp;lt;artifactId&amp;gt;javassist&amp;lt;/artifactId&amp;gt;&lt;br /&gt;
    &amp;lt;version&amp;gt;3.21.0-GA&amp;lt;/version&amp;gt;&lt;br /&gt;
&amp;lt;/dependency&amp;gt;
&lt;/blockquote&gt;</description></item><item><title>Java 8 可重复注解的理解与应用</title><link>https://yanbin.blog/java8-repeatable-annotations/</link><pubDate>Mon, 16 Jan 2017 23:08:46 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java8-repeatable-annotations/</guid><description>
&lt;h2&gt;Java 8 之前如何重复使用注解&lt;/h2&gt;
在 Java 8 之前我们不能在一个类型重复使用同一个注解，例如 Spring 的注解 &lt;code&gt;@PropertySource&lt;/code&gt; 不能下面那样来引入多个属性文件&lt;/p&gt;
&lt;blockquote&gt;
@PropertySource("classpath:config.properties")&lt;br /&gt;
@PropertySource("file:application.properties")&lt;br /&gt;
public class MainApp {}
&lt;/blockquote&gt;
上面的代码无法在 Java 7 下通过编译，错误是: &lt;span style="color: #800000;"&gt;Duplicate annotation&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
于是我们在 Java 8 之前想到了一个方案来规避 Duplicate Annotation 的错误: 即声明一个新的 Annotation 来包裹 &lt;code&gt;@PropertySource&lt;/code&gt;, 如 &lt;code&gt;@PropertySources&lt;/code&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PropertySources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PropertySource&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
然后使用时两个注解齐上阵</description></item><item><title>Mockito 如何 mock 返回值为 void 的方法</title><link>https://yanbin.blog/mockito-how-to-mock-void-method/</link><pubDate>Mon, 16 Jan 2017 00:59:35 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-how-to-mock-void-method/</guid><description>
最初接触 Mockito 还思考并尝试过如何用它来 mock 返回值为 void 的方法，然而 Google 查找到的一般都会说用 &lt;code&gt;doThrow()&lt;/code&gt; 的办法&lt;/p&gt;
&lt;blockquote&gt;
doThrow(new RuntimeException()).when(mockObject).methodWithVoidReturn();
&lt;/blockquote&gt;
因为无法使用常规的 &lt;code&gt;when(mockObject.foo()).thenReturn(...)&lt;/code&gt; 的方法。&lt;br/&gt;&lt;br/&gt;
当时我就纳闷，为何我想 mock 一个返回值为 void 的方法，却是在模拟抛出一个异常，现在想来如果一个返回值为 void 的方法，为何要去 mock 这个方法呢？&lt;br/&gt;&lt;br/&gt;
回想一个我们要 mock 一个方法的意图是什么：&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;在特定输入参数的情况下期待需要的输出结果(返回值)&lt;/li&gt;
&lt;li&gt;在方法抛出某种类型异常调用者作出的反应&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
对于 void 返回值的方法，如果要验证有没有被调用过几次可以在事后用 &lt;code&gt;verify()&lt;/code&gt; 方法去断言。所以基本上对于 void 返回值的方法一般可不用去 mock 它，只需用  verify() 去验证，或者就是像前面一样模拟出现异常时的情况。&lt;br/&gt;&lt;br/&gt;
所以本文并不像是去直接回答标题所示的问题: Mockito 如何 mock  返回值为  void 的方法，而是如何应对 mock  对象的  void 方法</description></item><item><title>Mockito 中捕获 mock 对象方法的调用参数</title><link>https://yanbin.blog/mockito-capture-method-paramters/</link><pubDate>Sun, 15 Jan 2017 01:57:46 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mockito-capture-method-paramters/</guid><description>
Mockito 可以帮助我们创建 Mock 对象，mock 被调用的方法，断言调用次数，在方法参数不易确定的情况下还能帮我们捕获参数。下面是我们第一个问题:&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;为什么要捕获调用参数&lt;/h2&gt;
在被 mocker 方法调用参数明确的情况下可无需捕获参数，例如，下面的情景：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;dontCaptureArgument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mockito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserDao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Yanbin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//假如它的实现是 userDao.save(user)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userDao&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//断言了 userDao.save(user) 操作的还是 user 对象&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果 UserService 的 save(user) 最终操作的不是同一个对象，它的实现稍加变化如下</description></item><item><title>5 个最好的 Vim 速查卡 (Cheat Sheet)</title><link>https://yanbin.blog/5-best-vim-cheat-sheet/</link><pubDate>Sat, 14 Jan 2017 20:33:16 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/5-best-vim-cheat-sheet/</guid><description>
&lt;p&gt;Vim(Vi Improved) 早已替代了 Vi, 它存在于大多数的 Linux 发行版中。所以基本上 Vi 和 Vim 在你的系统中就是同一个程序，我用的 Mac, vi 命令就是一个指向到 vim 的链接&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ls -l $(which vi)&lt;br /&gt;
lrwxr-xr-x 1 root wheel 3 Sep 20 23:47 /usr/bin/vi -&amp;gt; vim&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;macOS Sierra 自带的 Vi/Vim 版本仍然是 7.4，我用 &lt;code&gt;brew install macvim&lt;/code&gt; 安装了最新版的 Vim 8.0, 由于只想启动 MacVim 控制台的 Vim,  所以把 vi/vim 命令链接到新版 Vim 上。&lt;/p&gt;</description></item><item><title>《Practical Vim》阅读笔记 (1)</title><link>https://yanbin.blog/practical-vim-2-notes-1/</link><pubDate>Fri, 06 Jan 2017 00:52:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/practical-vim-2-notes-1/</guid><description>
&lt;p&gt;Vim 的东西时而学一点，但很快又会忘记，就是最简单的 h, j, k, l 来移动光标都会有所迟疑，因为一直未强迫自己完全脱离方向键来使用 Vim，下个目标是 87 键的键盘都嫌多，打算入一个 61 键的 WSAD 键盘, HHKB 还是有些极端了。&lt;/p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2017/practical-vim-2-notes-1/WASD_VP3_61-Key.png"
width="741px"
/&gt;
&lt;p&gt;找到了一本学习 Vim 的好书 《Practical Vim》第二版，阅读时把对自己有用的东西仅当笔记记下来备忘，这个其实更应该记录在我的 Evernote 中作为私有笔记。重点的东西我在书中注解了，这里的笔记只能算是一个补充。</description></item><item><title>Vim 中 Java 代码自动完成 - vim-javacomplete2</title><link>https://yanbin.blog/vim-java-autocomplete-with-vim-javacomplete2/</link><pubDate>Wed, 04 Jan 2017 21:46:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vim-java-autocomplete-with-vim-javacomplete2/</guid><description>
用 Java 进行编码基本还是离不开 IntelliJ IDEA 或 Eclipse, 看别人完全用 Vim 进行 Javascript 项目编程很是眼红，估摸着能不能把 Vim 打造成一个更强的 Java IDE。语法高亮是不在话下，最主要是给它加上自动完成功能，不光对当前类，项目中的方法或变能能提示，而且必须像 Java IDE 那样理解所有的项目依赖。这就是今天试用的一个 Vim 插件 &lt;a href="https://github.com/artur-shaik/vim-javacomplete2"&gt;vim-javacomplete2&lt;/a&gt;，另种可能更好的方案 &lt;a href="https://github.com/Valloric/YouCompleteMe"&gt;YouCompleteMe&lt;/a&gt; + &lt;a href="http://eclim.org/"&gt;Eclim&lt;/a&gt; 还会再研究。&lt;/p&gt;
&lt;br/&gt;
提到 Vim 的自动完成功能，有必要了解 Vim 自带的提示功能&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;ctrl - n/p:  Vim 根据当前缓冲区的关键字来提示，像 Sublime 或 Visual Studio Code 中的关键字提示&lt;/li&gt;
&lt;li&gt;ctrl - x 进行自动自动完成模式，接着一些操作如 ctrl - l/n/t/i 完成类似于 ctrl - n/p 的操作; ctrl - k 能基于字典自动完成，完整按键是 ctrl - x ctrl -k&lt;/li&gt;
&lt;li&gt;ctrl - x ctrl - o, 这个单独拉出来，是使用 Vim 的 Omni Completion 功能来自动完成，因为将要用到的 vim-javacomplete2 就依赖于这个功能&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
除 YouCompleteMe 插件外，另外还两个 Vim 下的自动完成插件是 &lt;a href="https://github.com/Shougo/neocomplete.vim"&gt;NeoComplete&lt;/a&gt; 和 &lt;a href="https://github.com/ajh17/VimCompletesMe"&gt;VimCompleteMe&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
摘要部分算是说完了，现在开始体验 vim-javacomplete2 对 Java 项目的自动完成功能。它所有完成的代码提示不仅要支持基本的 Java 类库, 当前项目的类, 手动添加的 jar 包 ，还能支持 &lt;code&gt;maven&lt;/code&gt;, &lt;code&gt;gradle&lt;/code&gt; 和 Eclipse 的 &lt;code&gt;.classpath&lt;/code&gt; 文件中定义的 classpath, 这完全能应付我们实际中的项目了。实际运作也是一个 C/S 结构，这个插件会启动一个 javavi server, 用 &lt;a href="https://github.com/javaparser/javaparser"&gt;javaparser&lt;/a&gt; 来解析依赖, 然后 Vim 中用 omnifunc 经 socket 连接到 javavi server 获得提示列表的。</description></item><item><title>CompletableFuture 的并发性能研究</title><link>https://yanbin.blog/completablefuture-concurrent-performance/</link><pubDate>Thu, 29 Dec 2016 02:36:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/completablefuture-concurrent-performance/</guid><description>
今天继续探讨 &lt;code&gt;CompletableFuture&lt;/code&gt; 的特性，它并发时的性能如何呢？我们知道集合的 &lt;code&gt;stream()&lt;/code&gt; 后的操作是序列化进行的，&lt;code&gt;parallelStream()&lt;/code&gt;是能够并发执行的，而用 &lt;code&gt;CompletableFuture&lt;/code&gt; 可以更灵活的控制并发。&lt;/p&gt;
&lt;br/&gt;
我们先可以对比一下 parallelStream() 与 CompletableFuture 的性能差异&lt;br/&gt;&lt;br/&gt;
假设一个这样的耗时 1000 毫秒的计算任务&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getJob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InterruptedException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
分别用下面两个方法来测试，任务数可以通过参数来控制</description></item><item><title>理解 CompletableFuture 的任务与回调函数的线程</title><link>https://yanbin.blog/understand-completablefuture-task-callback-threads/</link><pubDate>Fri, 23 Dec 2016 02:21:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-completablefuture-task-callback-threads/</guid><description>
继续对 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html"&gt;CompletableFuture&lt;/a&gt; 的学习，本然依然不对它的众多方法的介绍，其实也不容易通过一篇述说完所有 CompletableFuture 的操作。此处重点了解下 CompletableFuture 几类操作时所使用的线程，CompletableFuture 的方法重点在它的静态方法以及实现自 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html"&gt;CompletionStage&lt;/a&gt; 接口的方法，如果是意图异步化编程，反而自我标榜的 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html"&gt;Future&lt;/a&gt; 中的方法用的少了。&lt;/p&gt;
&lt;br/&gt;
CompletableFuture 根据任务的主从关系为&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;提交任务的方法，如静态方法 supplyAsync(supplier[, executor]),  runAsync(runnable[, executor])&lt;/li&gt;
&lt;li&gt;回调函数，即对任务执行后所作出回应的方法，多数方法了，如 thenRun(action), thenRunAsync(action[, executor]), whenComplete(action), whenCompleteAsync(action[, executor]) 等&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
根据执行方法可分为同步与异步方法，任务都是要被异步执行，所以提交任务的方法都是异步的。而对任务作出回应的方法很多分为两个版本，如&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;同步方法，如 thenRun(action), whenComplete(action)&lt;/li&gt;
&lt;li&gt;异步方法，如 thenRunAsync(action[, executor]), whenCompleteAsync(action[, executor]), 异步方法可以传入线程池，否则用默认的&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
因此所要理解的 CompletableFuture 的线程会涉及到任务与回调函数所使用的线程。</description></item><item><title>Java 8 CompletableFuture 浅入</title><link>https://yanbin.blog/java-8-completablefuture-brief-touch/</link><pubDate>Fri, 23 Dec 2016 01:50:09 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-completablefuture-brief-touch/</guid><description>
Java 1.5 有了 &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html"&gt;Future&lt;/a&gt;, 可谓是跨了一大步，继而 Java 1.8 新加入一个 Future 的实现&lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html"&gt; CompletableFuture&lt;/a&gt;, 从此线程与线程之间可以愉快的对话了。最初两个线程间的协调我采用过 Object 的  &lt;code&gt;wait()&lt;/code&gt; 和 &lt;code&gt;notify()&lt;/code&gt; , Thread 的 &lt;code&gt;join()&lt;/code&gt; 方法，那可算是很低级的 API 了，是否很多 Java 程序都不知道它们的存在，或根本没用过它们。&lt;/p&gt;
&lt;br/&gt;
如果是简单的等待所有线程完成可使用 Java 1.5 的 &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html"&gt;CountDownLatch&lt;/a&gt;, 这里有一篇介绍 &lt;a href="http://unmi.cc/countdownlatch-threads/"&gt;CountDownLatch 协调线程&lt;/a&gt;, 就是实现的 waitAll(threads) 功能。而 Java 8 的 &lt;code&gt;CompletableFuture&lt;/code&gt; 的功能就多去，可简单使用它实现异步方法。虽说 &lt;code&gt;CompletableFuture&lt;/code&gt; 实现了 &lt;code&gt;Future&lt;/code&gt; 接口，但它多数方法源自于 &lt;code&gt;CompletionStage&lt;/code&gt;, 所以还里氏代换，用 &lt;code&gt;Future&lt;/code&gt; 来引用 &lt;code&gt;CompletableFuture&lt;/code&gt; 实例就很牵强了; 这也是为什么 PlayFramework 自 2.5 开始直接暴露的类型是 &lt;code&gt;CompletionStage&lt;/code&gt; 而非其他两个。&lt;br/&gt;&lt;br/&gt;
顾名思义，CompletableFuture 代表着一个 Future 完成后该干点什么，具体大致有：&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Future 完成后执行动作，或求取下一个 Future 的值。then...&lt;/li&gt;
&lt;li&gt;多个 Future 的协调; 同时完成该怎么，其中一个完成该如何。allOf, anyOf&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
有时候可以把 Future 想像成与线程是一一对应的。</description></item><item><title>巩固 Java Future 的使用</title><link>https://yanbin.blog/consolidate-use-of-java-future/</link><pubDate>Thu, 22 Dec 2016 00:13:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/consolidate-use-of-java-future/</guid><description>
&lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html"&gt;Future&lt;/a&gt; 还是一 Java 1.5 带进来的产物，但过去那么多年实际代码中却很少有直接接触, 大约它多是隐匿在各种现成框架中默默的为我们服务。Future 本身不代表着多线程，而是代表着需异步计算的结果, 将来的一个期待，至于后来真正的结果不可知。在此之前想要获得一个 &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html"&gt;Runnable&lt;/a&gt; 在其他线程中的计算结果颇费波折，有了 Future 加之它身后的 &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html"&gt;Callable&lt;/a&gt; 一切就变得简单了。&lt;/p&gt;
&lt;br/&gt;
对比一下 Java 1.5 前后的下面几个概念&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Callable 相当于之前的 Runnable, 只是 Callable 是有返回值的&lt;/li&gt;
&lt;li&gt;ExecuteService.submit(callable): Future 就类似于之前的 Thread(runnable)&lt;br /&gt;
只是前者 submit 后立即执行，通过 get() 获得结果，后者用 start() 方法启动，runnable 是没有结果的。如果你也不想关心 Future 的结果也能 ExecuteService.submit(runnable)&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
只有 callable 被提交(到线程池) 后返回的 Future 才可能会有结果, 所以下面的代码永远等不到结果&lt;br/&gt;
&lt;blockquote&gt;
Future&lt;String&gt; future = new FutureTask&lt;&gt;(() -&gt; "Never");&lt;br /&gt;
String result = future.get();
&lt;/blockquote&gt;
&lt;br/&gt;
最容易理解的 Future 基本使用代码如下：</description></item><item><title>Hibernate 映射枚举(Enum) 类型的属性</title><link>https://yanbin.blog/hibernate-map-enum-type-field/</link><pubDate>Wed, 14 Dec 2016 01:24:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-map-enum-type-field/</guid><description>
在数据库中我们一般用整数或字符串来表示枚举值(有些数据库(如 MySQL)本身带有枚举类型), 而在使用 Hibernate 时实体对象中也用 Integer 或 String 来表示枚举就不那么友好了。试想来我们这样定义实体对象的两个属性&lt;br/&gt;
&lt;blockquote&gt;
@Entity&lt;br /&gt;
public class User {&lt;br /&gt;
  ....
  public Integer type;  //0: Individual 类型，1: Company 类型&lt;br /&gt;
  public String gender;  //可取值 Male 和 Female&lt;br /&gt;
}
&lt;/blockquote&gt;
这样的定义很不严谨，type 和 gender 理论上可取任何值，这会造成表中数据的混乱。其实 Hibernate 在 Java 实体对象中是可以直接用枚举类型与数据库中的整数或字符串映射，需用到 &lt;code&gt;@Enumerated&lt;/code&gt; 注解，用法如下：</description></item><item><title>Java 的参数检查与断言 - Guava Preconditions</title><link>https://yanbin.blog/java-assertion-guava-preconditions/</link><pubDate>Tue, 13 Dec 2016 00:11:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-assertion-guava-preconditions/</guid><description>
在前一篇 &lt;a href="https://yanbin.blog/scala-parameter-check-and-assertion/"&gt;Scala 的参数检查与断言: require, assert, assume 和 ensuring&lt;/a&gt;，捉摸 Scala 的断言时提到了 JDK 内置对断言的粗略支持，也就是 &lt;code&gt;assert&lt;/code&gt; 语句，并且默认该特性是被关掉，需 &lt;code&gt;-ea&lt;/code&gt; 开启。&lt;/p&gt;
&lt;blockquote&gt;
assert object != null;&lt;br /&gt;
assert object != null : "object can't be null";
&lt;/blockquote&gt;
还进一步接触了 Scala 的 &lt;code&gt;Predef&lt;/code&gt; 方法 &lt;code&gt;require&lt;/code&gt;, &lt;code&gt;assert&lt;/code&gt;, &lt;code&gt;assume&lt;/code&gt;, 和 &lt;code&gt;ensuring&lt;/code&gt; 是怎么检验参数与断言运算结果的，Scala 的这些方法在校验失败时相应的抛出 &lt;code&gt;IllegalArgumentException&lt;/code&gt; 和  &lt;code&gt;AssertionError&lt;/code&gt; 异常。&lt;br/&gt;&lt;br/&gt;
JDK  7 引入了 &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html"&gt;Objects&lt;/a&gt; 工具类，它的三个 &lt;code&gt;T requireNotNull(T object)&lt;/code&gt; 方法能对参数进行 null 值检查，null 时抛出 &lt;code&gt;NullPointerException&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2016/java-assertion-guava-preconditions/Java-Objects-requireNotNull.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2016/java-assertion-guava-preconditions/Java-Objects-requireNotNull-800x119.png"
width="800px"
/&gt;
&lt;/a&gt;</description></item><item><title>Scala 的参数检查与断言: require, assert, assume 和 ensuring</title><link>https://yanbin.blog/scala-parameter-check-and-assertion/</link><pubDate>Mon, 12 Dec 2016 13:40:29 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-parameter-check-and-assertion/</guid><description>
似乎 C/C++ 的编程人员相比于 Java 更偏爱于断言，JDK 1.4 才开始引入 assert 的支持，但默认是关闭的，需要用 &lt;code&gt;-ea&lt;/code&gt; 编译选项打开，否则代码中的 &lt;code&gt;assert&lt;/code&gt; 语句全被忽略，一般会在单元测试中开启该选项。简单回顾一下 JDK 自带的断言，它用两种写法&lt;br/&gt;
&lt;blockquote&gt;
assert object != null;&lt;br /&gt;
assert object != null : "object can't be null";
&lt;/blockquote&gt;
第一个参数是个 bool 值，断言失败只会笼统的抛出&lt;code&gt;java.lang.AssertionError&lt;/code&gt; 异常，并不区分是在检验方法参数还是中间运算结果。严谨来说我们会希望参数检查不通过时抛出 &lt;code&gt;java.lang.IllegalArgumentException&lt;/code&gt;; 而中间运算结果的断言不过希望抛出 &lt;code&gt;java.lang.AssertionError&lt;/code&gt;, 最好是 &lt;code&gt;java.lang.IllegalStateException&lt;/code&gt;。&lt;br/&gt;&lt;br/&gt;
很多时候我们不会去使用 &lt;code&gt;-ea&lt;/code&gt; 编译选项，也就是主动放弃了 JDK 本身的断言功能。介于两个因素(不同的断言错误和默认的断言选项关闭)，Scala 为我们提供了更方便的参数检查与断言方法，它们来自于 &lt;a href="http://www.scala-lang.org/api/rc2/scala/Predef$.html"&gt;Predef&lt;/a&gt;, 其所定义的方法可以直接使用&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2016/scala-parameter-check-and-assertion/Scala-Predef-Require.png"
width="606px"
/&gt;</description></item><item><title>SQL Server 和 HSQLDB 中使用 merge into 完成 saveOrUpdate 操作</title><link>https://yanbin.blog/sql-server-and-hsqldb-use-merge-into-do-saveorupdate/</link><pubDate>Sun, 11 Dec 2016 16:18:02 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sql-server-and-hsqldb-use-merge-into-do-saveorupdate/</guid><description>
当我们调用 Hibernate 的  saveOrUpdate() 或 JPA 的 save() 方法的 Hibernate 实现时，都会做两步操作：1）按 ID 查询记录是否已存在，2）不存在插入新记录，存在则更新原记录。这种两步操作其实可以在 SQL Server 和 HSQLDB 中一条语句完成，这就是本文要介绍的 &lt;code&gt;merge into&lt;/code&gt; 语句。感觉到用数据库自己的特性，并且一条语句会比 &lt;code&gt;saveOrUpdate()&lt;/code&gt; 两步操作性能要好，还需实测。&lt;br/&gt;&lt;br/&gt;
之所以把 SQL Server 和 HSQLDB 扯到一块来讲，是因为我们在实际项目中的单元测试是基于 HSQLDB 内存数据库的。&lt;code&gt;merge into&lt;/code&gt; 如其名所示，它应该是给予我们便利的去根据把一个表中符合条件的记录合并到另一个表中去。我们这里只利用它的这特性去实现类似 Hibernate 的 &lt;code&gt;saveOrUpdate()&lt;/code&gt; 操作。&lt;br/&gt;&lt;br/&gt;
假设我们有一个简单的表&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果指 id 的记录已存在更新原来记录的 name 和  address, 不存在则插入新记录</description></item><item><title>Spring 如何初始化泛型类实例</title><link>https://yanbin.blog/how-spring-initialize-generic-object/</link><pubDate>Fri, 09 Dec 2016 01:20:05 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-spring-initialize-generic-object/</guid><description>
在 Java 中对于泛型类型，比如这样简单的类定义&lt;/p&gt;
&lt;blockquote&gt;
class Processor&amp;lt;T&amp;gt; {}
&lt;/blockquote&gt;
如果直接初始化时要指定具体类型的话，我们可以这么写&lt;br/&gt;
&lt;blockquote&gt;
Processor&amp;lt;String&amp;gt; processor = new Processor&amp;lt;&amp;gt;();  //Java 7 及以上版本
&lt;/blockquote&gt;
&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;Spring 对基本泛型的初始化&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
如果我们要用 Spring 容器来初始化这个类，比如给上面那个类加个 @Named 注解&lt;br/&gt;
&lt;blockquote&gt;
@Named&lt;br /&gt;
class Processor&amp;lt;T&amp;gt; {&lt;br /&gt;
}
&lt;/blockquote&gt;
这时候我们通过 &lt;code&gt;beanFactory.getBean(Processor.class)&lt;/code&gt; 得到的是一个什么样的实例呢？Spring 怎么知道要指定什么具体类型呢？很简单，任何不确定的情况都是 Object。所以通过容器得到的  &lt;code&gt;Processor&lt;/code&gt; 实例相当于用下面代码构造出来的&lt;br/&gt;
&lt;blockquote&gt;
Processor processor = new Processor();  //更准确来讲是 Processor&amp;lt;Object&amp;gt; processor = new Processor&amp;lt;&amp;gt;();
&lt;/blockquote&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;再进一步&lt;/strong&gt;&lt;/span&gt;，对于有上限约束的泛型定义，Spring 才如何应对呢？像</description></item><item><title>Spring 项目中把 SQL 语句写在 .sql 文件中</title><link>https://yanbin.blog/spring-external-sql-statements/</link><pubDate>Sat, 12 Nov 2016 02:49:18 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-external-sql-statements/</guid><description>
我们在使用 JDBC 时, 如果把所有的 SQL 语句全写在 Java 文件中, 由于 Java 不支持 Here Document, 多行字符串要么用加号, 要么用 Java 8 的 &lt;code&gt;String.join()&lt;/code&gt; 方法来连接, 同时不能对 SQL 语句进行语法加亮, 所以这样的 SQL 字符串阅读性很差. 别说为何不用 Hibernate 之类的而不直接写原始的 SQL 语句, 在操作复杂的系统时还是会用到 JdbcTemplate 吧.&lt;/p&gt;
&lt;br/&gt;
所以我们希望能把 SQL 语句写在单独的 &lt;code&gt;*.sql&lt;/code&gt; 文件里, 这样很多编辑器就能语法高亮显示, 或在输入时还能得到智能提示. 有种办法是把 &lt;code&gt;*.sql&lt;/code&gt; 用作为属性文件, 那么在其中定义多行的 SQL 语句时就得这样&lt;br/&gt;
&lt;blockquote&gt;
select.user=select id, firstname, lastname, address \&lt;br /&gt;
  from users \&lt;br /&gt;
  where id=?
&lt;/blockquote&gt;
加载后就能用 &lt;code&gt;getProperty("select.user")&lt;/code&gt; 来引用相应的语句了. 属性文件的换行与 Bash  一样, 也是用  &lt;code&gt;\&lt;/code&gt;, 但如此, 则 &lt;code&gt;*.sql&lt;/code&gt; 并非一个纯粹的 SQL 文件, 不能正确的进行语法加亮, 一旦写上 SQL 的注释 &lt;code&gt;--&lt;/code&gt; 就更是在添乱了.&lt;br/&gt;&lt;br/&gt;
所以我们的第二个方案是: 首先 &lt;code&gt;*.sql&lt;/code&gt; 就该是一个真正的  SQL 文件, 而不是伪装的属性文件, 为了能在程序中引用每一条 SQL 语句, 我们该如何表示各自的 Key 呢? 这里的灵感仍然是来自于 Linux Shell, 在 Linux Shell 中指定执行环境的用了特殊的注释方式 &lt;code&gt;#!&lt;/code&gt;, 如&lt;br/&gt;
&lt;blockquote&gt;
#!/bin/bash&lt;br /&gt;
#!/usr/bin/env python
&lt;/blockquote&gt;</description></item><item><title>Java 单元测试如何断言(检查)控制台输出</title><link>https://yanbin.blog/java-unit-test-assert-console-output/</link><pubDate>Tue, 18 Oct 2016 00:09:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-unit-test-assert-console-output/</guid><description>
&lt;p&gt;关于在 JUnit 单元测试中如何断言某个函数的控制台输出已是我一个长久的问题. 虽然有控制台输出的函数有了副作用, 不能称之为一个纯函数, 在讲求函数式编程的今天, 纯函数是最好测试的, 所谓的 Data In, Data Out. 但总还是有这样的需求, 比如自己实现的某个日志框架的 Appender, 需要验证它向控制台的输出内容.&lt;/p&gt;
&lt;p&gt;我先前在项目中的办法是, 先把把标准输出定向到一个 &lt;code&gt;ByteArrayOutputStream&lt;/code&gt; 中去, 完后把这个流转成字符串来断言它的内容, 最后恢复标准输出为 &lt;code&gt;System.out&lt;/code&gt;, 代码如下:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ByteArrayOutputStream output = new ByteArrayOutputStream();&lt;br /&gt;
System.setOut(new PrintStream(output));&lt;/p&gt;
&lt;p&gt;System.out.print("Hello");&lt;br /&gt;
&lt;br /&gt;
assertThat(output.toString(), is("Hello");&lt;br /&gt;
System.setOut(System.out);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样也能完成任务, 本质也是对的, 但稍显复杂了些. 今天读 &lt;code&gt;Spring in Action&lt;/code&gt; 一书, 发现它用了 &lt;code&gt;StandardOutputStreamLog&lt;/code&gt; 这个 JUnit 的 &lt;code&gt;@Rule&lt;/code&gt;, 来自于 &lt;a href="http://stefanbirkner.github.io/system-rules/"&gt;System Rules&lt;/a&gt;. 其实 &lt;code&gt;StandardOutputStreamLog&lt;/code&gt; 类已不推荐使用, 取而代之的是 &lt;a href="http://stefanbirkner.github.io/system-rules/#SystemErrAndOutRule"&gt;SystemOutRule&lt;/a&gt;, 所以应用 &lt;code&gt;SystemOutRule&lt;/code&gt; 来断言控制台输出的测试方法就是</description></item><item><title>Kafka 生产消费 Avro 序列化数据</title><link>https://yanbin.blog/kafka-produce-consume-avro-data/</link><pubDate>Sun, 16 Oct 2016 15:53:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kafka-produce-consume-avro-data/</guid><description>
本文实践了如何连接 Kafka 生产和消费 Avro 序列化格式的数据, 不能像 NgAgo-gDNA 那样, 为保证实验内容及结果的可重复性, 文中所用的各中间件和组件版本如下:&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Apache Kafka: &lt;a href="https://www.apache.org/dyn/closer.cgi?path=/kafka/0.10.0.1/kafka_2.11-0.10.0.1.tgz"&gt;kafka_2.11-0.10.0.1&lt;/a&gt;, 这个版本在初始始化生产者消费者的属性与之前版本有所不同.&lt;/li&gt;
&lt;li&gt;kafka-clients: Java API 客户端, 版本为  0.10.0.1&lt;/li&gt;
&lt;li&gt;Apache Avro: 1.8.1. 关于 Avro 序列化的内容可参见 &lt;a href="https://yanbin.blog/apache-avro-serializing-deserializing"&gt;Apache Avro 序列化与反序列化 (Java 实现)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Java 8&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
Apache Kafka 消息系统设计为可以传输字符串, 二进制等数据, 但直接用于传输生产消费两端都能理解的对象数据会更友好.  所以我们这里用 Avro 的 Schema  来定义要传输的数据格式, 通信时采用自定义的序列化和反序列化类进行对象与字节数组间的转换.&lt;br/&gt;&lt;br/&gt;
以下是整个实验过程&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;本地启动 Apache Kafka 服务&lt;/h3&gt;
请参考 &lt;a href="https://yanbin.blog/initial-apache-kafka-environment"&gt;简单搭建 Apache Kafka 分布式消息系统&lt;/a&gt; 启动 ZooKeeper 和 Kafka 即可. 程序运行会自动创建相应的主题. 启动后 Kafka 开启了本地的 9092 端口, 程序中只需要连接这个端口, 不用管 ZooKeeper 的  2181 端口.</description></item><item><title>Apache Avro 序列化与反序列化 (Java 实现)</title><link>https://yanbin.blog/apache-avro-serializing-deserializing/</link><pubDate>Sat, 15 Oct 2016 16:54:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-avro-serializing-deserializing/</guid><description>
像两个人交流一样要找一个互相能理解的语言, 在国内为普通话, 跑国外多用英语相通, 两个进程间通信也需要找一个大家都能理解的数据格式. 简单的如 JSON, XML, 那是自我描述性格式, XML 有 Schema 定义, 但尚无正式的 &lt;a href="http://json-schema.org/"&gt;JSON Schema&lt;/a&gt; 规范. 在讲求效率的场合, 纯文本式的数据交换格式无法满足要求, 于是有二进制的 Google Protobuf 和 Apache Avro. 在 Apache 的生态像 Hadoop, Kafka 中自然是选用 Avro.&lt;br/&gt;&lt;br/&gt;
Avro 支持多种语言, 如 C, C++, C#, Java, PHP, Python 和 Ruby. 它使用 JSON 来定义 Schema, 通过工具可以由 Schema 生成相应语言的数据对象, 比如 Java 的  avro-tools.jar. 这样可以在跨进程跨语言透明的实现为对象交换.&lt;br/&gt;&lt;br/&gt;
本文体验 Java 环境中 Avro 数据格式的序列化与反序列化.&lt;br/&gt;&lt;br/&gt;
Avro Schema 文件就是数据生产和消费端的通信协议; 我们可以由 Schema 生成相应的 Java 对象, 然后以具体的 Java 对象交换, 或者不生成 Java 对象而纯粹以 &lt;code&gt;GenericRecord&lt;/code&gt; 交互. 为操作数据的简单, 我们通常采用前一种方式, 即生成具体数据传输对象.</description></item><item><title>Giter8 -- 把项目布局模板放到 GitHub 上</title><link>https://yanbin.blog/giter8-put-project-layout-on-github/</link><pubDate>Fri, 14 Oct 2016 23:08:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/giter8-put-project-layout-on-github/</guid><description>
因为学习或做些小 Demo, 会临时建立一个项目, 项目的布局也常有类似, 不想每次为一个 Maven 项目而执行 &lt;code&gt;mkdir -p example/src/{main,test}/{java,resources}&lt;/code&gt;, 或是通过 IDE 来创建, 于是萌生了把自己常用的项目模板放到 GitHub 上的想法. 我们当然可以把直接在 GitHub 上创建一个个项目模板仓库, 想用时只要 &lt;code&gt;git clone&lt;/code&gt; 下来, 但克隆的总是与 GitHub 上相应的仓库有关联.&lt;/p&gt;
&lt;br/&gt;
Google 了, 有不少方法能建立项目基本框架, 如&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://maven.apache.org/guides/mini/guide-creating-archetypes.html"&gt;创建 Maven 项目骨架&lt;/a&gt;,  &lt;code&gt;mvn archetype:generate -DarchetypeGroupId=.....&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/sbt-np-create-folders-build-sbt/"&gt;sbt 的 np 插件可以快速生成项目目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;YEOMAN 也有自己的 &lt;a href="http://yeoman.io/generators/"&gt;Generator&lt;/a&gt;, 很强大也复杂&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
再就是现在要介绍的, 比较适合于我的口味的 &lt;a href="http://www.foundweekends.org/giter8/"&gt;Giter8&lt;/a&gt;, 简单实用, 定义模板更是轻松自如. Giter8 是构建在 &lt;a href="http://www.scala-sbt.org/0.13/docs/Setup.html"&gt;sbt launcher&lt;/a&gt; 之上的用于从 GitHub 或其他任何 Git 仓库中攫取项目模板的命令行工具. 模板定义简单, 支持变量的提示输入. 下面是安装, 使用, 以及建立自己的模板:</description></item><item><title>如何定制 Spring Boot 的 Banner</title><link>https://yanbin.blog/customize-spring-boot-banner/</link><pubDate>Wed, 12 Oct 2016 23:07:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/customize-spring-boot-banner/</guid><description>
当我们启动一个 Spring Boot 的 Hello World 程序, 可以看到 Spring 会在控制台下输出一段 ASCII 字符组成的 Spring 字样, 像这个图中的样子,
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2016/customize-spring-boot-banner/spring-boot-banner.png"
width="302px"
/&gt;
并且 ":: Spring Boot ::" 是绿色显示, 版本号灰色. 如果每个 Spring Boot 做的应用都用默认的 Banner 就不好玩了, 无外乎只在声明我正在用 Spring Boot. 好在 Spring 提供了多种方式让我们定制自己的 Banner.&lt;br/&gt;&lt;br/&gt;
官方文档: &lt;a href="http://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/htmlsingle/#boot-features-banner"&gt;http://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/htmlsingle/#boot-features-banner&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
初始 Banner 的代码是 &lt;a href="https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/SpringApplicationBannerPrinter.java"&gt;SpringApplicationBannerPrinter&lt;/a&gt; 类.&lt;br/&gt;&lt;br/&gt;
Spring Boot 默认寻找 Banner 的顺序是:&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;依次在 Classpath 下找 文件 &lt;code&gt;banner.gif&lt;/code&gt;, &lt;code&gt;banner.jpg&lt;/code&gt;, 和 &lt;code&gt;banner.png&lt;/code&gt;, 先找到谁就用谁&lt;/li&gt;
&lt;li&gt;继续 Classpath 下找 &lt;code&gt;banner.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;上面都没有找到的话, 用默认的 &lt;a href="https://github.com/spring-projects/spring-boot/blob/master/spring-boot/src/main/java/org/springframework/boot/SpringBootBanner.java"&gt;SpringBootBanner&lt;/a&gt;, 就是我们最常见到的那个&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>简单搭建 Apache Kafka 分布式消息系统</title><link>https://yanbin.blog/initial-apache-kafka-environment/</link><pubDate>Thu, 06 Oct 2016 16:12:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/initial-apache-kafka-environment/</guid><description>
&lt;p&gt;早先都是用的基于 JMS 规范的消息系统, 像 ActiveMQ, IBM MQSeries 等. 随着互联网的发展, 大约是要适应当今大数据, 高可用性, 高效的需求, 于是诞生了 &lt;a href="http://kafka.apache.org/"&gt;Apache Kafka&lt;/a&gt; 这一新时代的分布式消息系统. Apache Kafka 也是发布-订阅式的消息系统, 用 Scala 语言写的, 它最初由 LinedIn 开发并贡献到 Apache 基金会.&lt;/p&gt;
&lt;p&gt;Kafka 的集群实质是依赖于 ZooKeeper 的集群来协同管理, 所以这里可以参照之前的 &lt;a href="https://yanbin.blog/zookeeper-fast-get-started/"&gt;ZooKeeper 快速搭建与体验&lt;/a&gt; 来搭建一个 ZooKeeper 集群(其实这是一个伪集群, 实际产品中应该把 ZooKeeper 集群分布在不同的机器上).&lt;/p&gt;
&lt;p&gt;本文主要是参考官方的 &lt;a href="http://kafka.apache.org/quickstart"&gt;Kafka Quickstart&lt;/a&gt; 来快速体验 Kafka 消息系统, 下载的 Kafka 自带了 ZooKeeper, 默认只启动了一个  ZooKeeper 节点. 如需 ZooKeeper 集群可以不依赖于 Kafka 自带的 ZooKeeper 而单独搭建.&lt;/p&gt;
&lt;p&gt;下面开始演示建立一个最简单的 Kafka 系统</description></item><item><title>小试 Spring Boot - Spring MVC</title><link>https://yanbin.blog/try-spring-boot-spring-mvc/</link><pubDate>Thu, 06 Oct 2016 00:05:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/try-spring-boot-spring-mvc/</guid><description>
前阵想试下 Spring MVC 4 有了些什么新特性, 可真正用 Maven 在 IDE 中建立一个项目并不那么容易. Spring 当初在笑 EJB 的笨重时如今把自己也搞大了, 继而出台了一个 &lt;a href="http://projects.spring.io/spring-boot/"&gt;Spring Boot&lt;/a&gt; 来响应微服务的号召.&lt;/p&gt;
&lt;br/&gt;
Spring Boot 的出世可以大大提升使用 Spring 框架时的开发效率. Spring 尽量简化 Spring 项目的配置, 一个 &lt;code&gt;mvn package&lt;/code&gt; 就轻轻松的把一个 Web 项目打成一个  &lt;code&gt;fat jar&lt;/code&gt;, 运行 &lt;code&gt;java -jar spring-boot-sample-1.0-SNAPSHOT.jar&lt;/code&gt; 就能通过内嵌的 Tomcat 或 Jetty 来启动一个 Web 应用了, 更别提怎么应对普通控制台应用了.&lt;br/&gt;&lt;br/&gt;
现在就来体验一下 Spring Boot 做一个 Spring MVC 项目有多简单, 我们仍然是建立一个 Maven 项目, 最简单的 &lt;code&gt;pom.xml&lt;/code&gt; 文件内容如下:</description></item><item><title>ZooKeeper 快速搭建与体验</title><link>https://yanbin.blog/zookeeper-fast-get-started/</link><pubDate>Tue, 04 Oct 2016 01:36:38 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/zookeeper-fast-get-started/</guid><description>
Apache &lt;a href="https://zookeeper.apache.org/"&gt;ZooKeeper&lt;/a&gt; 是一个面向分布式应用程序的高性能协调服务器, 至于它的具体介绍像能提供命名和配置管理、同步和组服务等, 请自个 Google. 虽然对 ZooKeeper 早有耳闻, 也只最近因为项目中有 &lt;a href="http://kafka.apache.org/"&gt;Kafka&lt;/a&gt; 内部用到了 ZooKeeper, 所以才促使我着手去了解一下 ZooKeeper 为何物. ZooKeeper 脱胎于著名的项目 Hadoop, 它也像 &lt;a href="https://hazelcast.org/"&gt;Hazelcast&lt;/a&gt; 那样未采用严格意义的 Master-Slave 的集群方式, 而是动态选出 Leader, 这避免了单点故障的问题.&lt;br/&gt;&lt;br/&gt;
既然是集群, 实际应用是会在不同的机器上启动服务, 现如今有了 docker 很容易用它来测试多主机环境, 用 &lt;code&gt;docker search zookeeper&lt;/code&gt; 就知道了. 本实例中暂不涉及 docker, 而是在同一个系统中用多个端口来启动三个 ZooKeeper 实例进行测试. 下面以 Mac/Linux 为例:&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;下载安装 ZooKeeper&lt;/h3&gt;
到 &lt;a href="https://zookeeper.apache.org/releases.html#download"&gt;https://zookeeper.apache.org/releases.html#download&lt;/a&gt; 下载, 写下此文时的最新稳定版是 &lt;a id="file-link" tabindex="0" href="http://mirrors.gigenet.com/apache/zookeeper/stable/zookeeper-3.4.9.tar.gz"&gt;zookeeper-3.4.9.tar.gz&lt;/a&gt;, 下载后解压到某处, 为方便起见把解压后的 zookeeper-3.4.9/bin 加到系统环境变量 $PATH 中, 以后方便任何时候运行 zkServer.sh, zkCli.sh 这样的命令.</description></item><item><title>Hazelcast 介绍与使用(整理)</title><link>https://yanbin.blog/hazelcast-introduction/</link><pubDate>Mon, 26 Sep 2016 22:51:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hazelcast-introduction/</guid><description>
要用到 Hazelcast 这个东西用作分布式缓存, 网上搜索了下发现这篇文章对我理解 Hazelcast 那种无主从之分, 避免了单点故障很有帮助, Hazelcast 的数据分布方式很有点像磁盘阵列 RAID 1, RAID0+1 的影子. 基本上在一个节点出现故障的情况下是不会影响数据访问的.&lt;br/&gt;&lt;br/&gt;
下面这个系列讲的很详细:&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://my.oschina.net/chkui/blog/729698"&gt;Hazelcast集群服务(1)——Hazelcast介绍&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://my.oschina.net/chkui/blog/732408"&gt;Hazelcast集群服务(2)——Hazelcast基本配置&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://my.oschina.net/chkui/blog/736892"&gt;Hazelcast集群服务(3)——集群功能详解&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://my.oschina.net/chkui/blog/746496"&gt;Hazelcast集群服务(4)——分布式Map&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;&lt;br/&gt;
Hazelcast 是一个开源的可嵌入式数据网格（社区版免费，企业版收费）。你可以把它看做是内存数据库，不过它与 Redis 等内存数据库又有些不同。项目地址：&lt;a title="http://hazelcast.org/" href="http://hazelcast.org/"&gt;http://hazelcast.org/&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
Hazelcast 使得 Java 程序员更容易开发分布式计算系统，提供了很多 Java 接口的分布式实现，如：Map, Queue, Topic, ExecutorService, Lock, 以及 JCache 等。它以一个 JAR 包的形式提供服务，只依赖于 Java，并且提供 Java, C/C++, .NET 以及 REST 客户端，因此十分容易使用。&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;如何存储数据&lt;/h2&gt;
Hazelcast 服务之间是端对端的，没有主从之分，因此也不存在单点故障。集群中所有的节点都存储等量的数据以及进行等量的计算。&lt;br/&gt;&lt;br/&gt;
Hazelcast 缺省情况下把数据分为 271 个区。这个值可配置于系统属性&lt;em&gt; &lt;code&gt;hazelcast.partition.count。&lt;/code&gt;&lt;/em&gt; 对于一个给定的键，在经过序列号、哈希并对分区总数取模之后能得到此键对应的分区号。所有的分区等量的分布与集群中所有的节点中，每个分区对应的备份也同样分布在集群中。</description></item><item><title>使用 Java8 Optional 的正确姿势</title><link>https://yanbin.blog/proper-ways-of-using-java8-optional/</link><pubDate>Mon, 05 Sep 2016 01:52:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/proper-ways-of-using-java8-optional/</guid><description>
我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;......&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPresent&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
那么不得不说我们的思维仍然是在原地踏步, 只是本能的认为它不过是 User 实例的包装, 这与我们之前写成&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.....&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
实质上是没有任何分别. 这就是我们将要讲到的使用好 Java 8 Optional 类型的正确姿势.&lt;br/&gt;&lt;br/&gt;
在里约奥运之时, 新闻一再提起五星红旗有问题, 可是我怎么看都看不出来有什么问题, 后来才道是小星星膜拜中央的姿势不对. 因此我们千万也别对自己习以为常的事情觉得理所当然, 丝毫不会觉得有何不妥, 换句话说也就是当我们切换到 Java 8 的 Optional 时, 不能继承性的对待过往 null 时的那种思维, 应该掌握好新的, 正确的使用 Java 8 Optional 的正确姿势.</description></item><item><title>.NET Core 上的 Web -- ASP.NET Core</title><link>https://yanbin.blog/try-asp-net-core/</link><pubDate>Sat, 03 Sep 2016 00:16:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/try-asp-net-core/</guid><description>
.NET Core 上不光可以做控制台的程序, 还可也实现 AST.NET 的 Web 应用, 而且是自带服务器的那种. 像 NodeJS, Spring Boot, Netty 那种非容器型的嵌入式的 Web Server, 非常适合于做微服务应用. 谁说 ASP.NET 就一定要部署到 IIS 上呢?&lt;/p&gt;
&lt;br/&gt;
本文参考 &lt;a href="https://docs.asp.net/en/latest/getting-started.html"&gt;https://docs.asp.net/en/latest/getting-started.html&lt;/a&gt; 而来, 基本步骤是一致的&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;1. 安装 .NET Core&lt;/h3&gt;
参考上一篇 &lt;a href="https://yanbin.blog/net-core-console-hello-world/"&gt;.NET Core 上手体验 Hello World&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;2. 创建 .NET Core 项目&lt;/h3&gt;
&lt;blockquote&gt;
mkdir appnetcoreapp&lt;br /&gt;
cd aspnetcoreapp&lt;br /&gt;
dotnet new
&lt;/blockquote&gt;
&lt;h3&gt;3. 更新 &lt;code&gt;project.json&lt;/code&gt; 引入 Kestrel HTTP server 依赖&lt;/h3&gt;</description></item><item><title>.NET Core 上手体验 Hello World</title><link>https://yanbin.blog/net-core-console-hello-world/</link><pubDate>Fri, 02 Sep 2016 23:13:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/net-core-console-hello-world/</guid><description>
别人开源是种奉献精神, 微软突然把 .Net Framework 也开源, 人们看到的却是一种野心, 现在 .NET Core 开源还真正跨平台了, 真是雄心壮志啊.&lt;/p&gt;
&lt;br/&gt;
本人 Java 多年, 但不可否认 C# 是一个很好的语言, 我认为单纯从语言上来说要优于 Java 语言, 所以 .NET Core 搭上 C# 力量更是不能小觑, 不得不摸索一下.&lt;br/&gt;&lt;br/&gt;
之前虽有浅尝 .NET Core 的 Hello World,   以及最简单的 ASP.NET 应用, 但未有成文, 在此再作演练一番以备忘.&lt;br/&gt;&lt;br/&gt;
&lt;h2&gt;安装 .NET Core&lt;/h2&gt;
本人所用平台为 Mac, 所以当打开 .NET Core 的首页面 &lt;a href="https://www.microsoft.com/net/core"&gt;https://www.microsoft.com/net/core&lt;/a&gt; 时直接激活了 Mac 标签, 在这个页面我们知道 .NET Core 支持 Windows, Linux, Mac; 还有现成的 Docker 镜像拉下即用. .NET Core 当前版本是 1.0, 可以照着官方向导来做, 不过我倾向于全控制台来完成. 首先你的 Mac 得先有个 &lt;code&gt;brew&lt;/code&gt;,  现在用 &lt;code&gt;port&lt;/code&gt; 管理应用比较少了吧.&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ brew update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;$ brew cask install dotnet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
说白了, &lt;span style="color: #ff0000;"&gt;&lt;strong&gt;安装 .NET Core 只需要用 &lt;code&gt;brew cask install dotnet&lt;/code&gt;&lt;/strong&gt;&lt;/span&gt;, 第一条命令没事都应该运行一下的, 发现有可升级的就运行一下 &lt;code&gt;brew upgrade&lt;/code&gt;.&lt;br/&gt;&lt;br/&gt;
&lt;code&gt;brew cask install dotnet&lt;/code&gt; 会检测是否有 &lt;code&gt;openssl&lt;/code&gt;, 没有的话自动安装, 接着帮你下载 dotnet 安装包装上:</description></item><item><title>Clojure REPL 连接远程会话</title><link>https://yanbin.blog/clojure-lein-repl-connect-session/</link><pubDate>Wed, 27 Jul 2016 02:13:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/clojure-lein-repl-connect-session/</guid><description>
学习 Clojure 一般是用 &lt;code&gt;lein repl&lt;/code&gt; 启动控制台, 每次启动 &lt;code&gt;lein repl&lt;/code&gt; 都会发现它打开了一个端口, 例如&lt;/p&gt;
&lt;blockquote&gt;
➜ ~ lein repl&lt;br /&gt;
nREPL server started on port 57212 on host 127.0.0.1 - nrepl://127.0.0.1:57212&lt;br /&gt;
REPL-y 0.3.7, nREPL 0.2.12&lt;br /&gt;
Clojure 1.8.0&lt;br /&gt;
...........
&lt;/blockquote&gt;
一直不清楚上面显示的 &lt;code&gt;nrepl://127.0.0.1:57212&lt;/code&gt; 该作何用, 而且端口号是随机的, 这其中定有文章.&lt;br/&gt;&lt;br/&gt;
幸好有 Google 帮忙, 查一查, 然后才意识到何不运行  &lt;code&gt;lein help repl&lt;/code&gt; 来看看 &lt;code&gt;repl&lt;/code&gt; 中到底有何玄机, 原来是:&lt;br/&gt;&lt;br/&gt;
&amp;lt;none&amp;gt; -&amp;gt; :start     &lt;code&gt;lein repl&lt;/code&gt; 默认的行为, 见下. 未指定 :host 或 :port , :host 默认为 127.0.0.1,  :port 为随机&lt;br /&gt;
:start [:host host] [:port port] &lt;strong&gt;原来  &lt;code&gt;lein repl&lt;/code&gt; 设计为为 CS 结构&lt;/strong&gt;. :start 会启动一个 nREPL  server, 并且立即启动一个 client 连接上它. :host 默认为 127.0.0.1, :port 默认为随机的&lt;br /&gt;
:headless [:host host] [:port port]   只启动 nREPL server, 等待别人来连接它, 相同的默认 :host, :port 规则.  也就是它不会进到 Clojure 控制台&lt;br /&gt;
:connect [dest]   连接一个 nREPL server. 目标服务器的指定有三种方式: HTTPS(S) URL, host:port, 或 port</description></item><item><title>Scala 中置, 前置, 后置操作符</title><link>https://yanbin.blog/scala-infix-prefix-postfix-operators/</link><pubDate>Wed, 20 Jul 2016 02:21:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-infix-prefix-postfix-operators/</guid><description>
&lt;p&gt;拟此篇以温习 Scala 对方法调用上的一些约定. 标题中说是关于操作符的事, 其实 Scala 像有了访问方法和属性的一致性原则一样, 可以说操作符与方法更是统一的,
或者说只有方法调用. 此处所称的操作符只不过是 Scala 对无参(prrameterless), 或只有一个参数的方法, 和特殊的四个
&lt;code&gt;unary_+&lt;/code&gt;, &lt;code&gt;unary_-&lt;/code&gt;, &lt;code&gt;unary_!&lt;/code&gt;, &lt;code&gt;unary_~&lt;/code&gt; 方法的便捷的调用约定格式.&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #0000ff; font-size: 12pt;"&gt;&lt;strong&gt;一. 中置操作符(对只有一个参数方法的调用约定, &lt;code&gt;a plus b&lt;/code&gt;)&lt;/strong&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;blockquote&gt;
case class MyNumber(value: Int) {&lt;br /&gt;
  def +(that: MyNumber) = MyNumber(this.value + that.value)&lt;br /&gt;
}
&lt;/blockquote&gt;
&lt;p&gt;调用方式&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;MyNumber&lt;span class="o"&gt;(&lt;/span&gt;10&lt;span class="o"&gt;)&lt;/span&gt;.+&lt;span class="o"&gt;(&lt;/span&gt;MyNumber&lt;span class="o"&gt;(&lt;/span&gt;20&lt;span class="o"&gt;))&lt;/span&gt; //标准调用格式
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;MyNumber&lt;span class="o"&gt;(&lt;/span&gt;10&lt;span class="o"&gt;)&lt;/span&gt; + MyNumber&lt;span class="o"&gt;(&lt;/span&gt;20&lt;span class="o"&gt;)&lt;/span&gt; //只有一个参数时, 不用点, 不用括号&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;第一行是用点语法的标准方法调用格式, &lt;strong&gt;Scala 在当方法只有一个参数时, 可以省略点, 以及括号&lt;/strong&gt;,
因此可写为上面第二行种的格式. 所以方法 &lt;code&gt;+&lt;/code&gt; 就化身为了中置操作符了.</description></item><item><title>如何直接运行 Clojure 脚本文件</title><link>https://yanbin.blog/run-clojure-script-file/</link><pubDate>Tue, 19 Jul 2016 01:03:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/run-clojure-script-file/</guid><description>
对于大多数的脚本编程语言来说, 提供有现成的分别进入控制台与执行脚本文件的命令. 例如 Scala, Python 默认进入控制台(REPL), 接文件路径为参数则执行脚本文件. 还有分别进入控制台和执行脚本的命令是: irb 与 ruby, groovsh 与 groovy, php -a 与 php, perl -de1 和 perl. 可以 Clojure 本身就没有 clojure 这样的命令. 当我们试图在 Mac 下用 brew install clojure 安装时, 得到的提示是没有 clojure, 应该用 &lt;code&gt;brew install leiningen&lt;/code&gt; 去安装 leiningen, 它是一个类似于 Scala sbt 的工具.&lt;/p&gt;
&lt;br/&gt;
所以启动 Clojure REPL 的命令就是 &lt;code&gt;lein repl&lt;/code&gt;, 其实还有一个办法来启动 Clojure 的控制台, 因为 Clojure 也是构筑于 JVM 之上的, 所以也能像启 Groovy/Scala 一样通过  &lt;code&gt;java&lt;/code&gt; 指令加载 jar 文件来启动. 去官网 &lt;a href="http://clojure.org/"&gt;http://clojure.org/&lt;/a&gt; 下载 Clojure 安装包(例如: ), 解压, 假定它的 jar 文件是 &lt;code&gt;~/Developers/clojure-1.8.0/clojure-1.8.0.jar&lt;/code&gt;, 那么也可以用命令 &lt;code&gt;java -jar ~/Developers/clojure-1.8.0/clojure-1.8.0.jar&lt;/code&gt; 进到 Clojure 控制台.&lt;br/&gt;&lt;br/&gt;
进到 Clojure 的提示符 &lt;code&gt;user=&amp;gt;&lt;/code&gt; 下就可以测试 Clojure 代码了, 那么如何加载一个写在 &lt;code&gt;clj&lt;/code&gt; 文件里的代码呢? 我们可以在 Clojure 控制台下用方法 &lt;code&gt;load-file&lt;/code&gt;. 假定 ~/hello.clj 文件的内容是</description></item><item><title>想像中理想编程语言的几个特征</title><link>https://yanbin.blog/features-of-ideal-programming-language/</link><pubDate>Tue, 12 Jul 2016 23:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/features-of-ideal-programming-language/</guid><description>
&lt;p&gt;代码写多了, 总希望能从繁琐的代码中挣脱出来, 编程语言的设计应为快速解决问题为目的. 不断重复的表达式应有相应的语法糖加以简化.&lt;/p&gt;
&lt;p&gt;函数式语言设计之初不被人重视, 进入到多核化之后我也渐渐从面向对象转入到了函数式的阵营.&lt;/p&gt;
&lt;p&gt;学习设计模式时 Java Swing 堪称各种模式的典范, 但是设计的后用起来顺手又能如何, 不过 Java Swing 的性能更是硬伤, 所以有了 SWT 那种更接近于本地化的组件库.&lt;/p&gt;
&lt;p&gt;编程语言有两种哲学: 条条大路通罗马 与 一条大路罗马. 前者可以让语言更灵活, 但却容易走上一条不归路, 譬如 Scala; 后者让你专注于解决实际的问题, 像 Python 的语法非常简单.&lt;/p&gt;
&lt;p&gt;填满了博客中文章概要显示中的内容, 可以说一说我希望中的语言的几个特征, 主要关注语法:</description></item><item><title>Java 语言的几个缺陷之六: 无字符串插值</title><link>https://yanbin.blog/java-language-defect-no-string-interpolation/</link><pubDate>Tue, 12 Jul 2016 02:07:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-no-string-interpolation/</guid><description>
Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样
&lt;br/&gt;
&lt;blockquote&gt;
bash-3.2$ name=Yanbin&lt;br /&gt;
bash-3.2$ echo "Hello $name"&lt;br /&gt;
Hello Yanbin
&lt;/blockquote&gt;
Java 中不支持类似的方式&lt;br/&gt;
&lt;blockquote&gt;
String name = "Yanbin";&lt;br /&gt;
String greeting = "Hello $name";  //Java 中无法把 $name 替换为 name 变量值 "Yanbin"
&lt;/blockquote&gt;
而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写&lt;br/&gt;
&lt;blockquote&gt;
String.format("Hello %s", "World");&lt;br /&gt;
String.format("%1$s %1$s %s %s %2$s", "aa", 10);    //aa aa aa 10 10
&lt;/blockquote&gt;
我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是 &lt;code&gt;System.out.printf()&lt;/code&gt;. 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如 &lt;code&gt;"Hello %s".format("World")&lt;/code&gt; 会容易读些, Scala 的字符串实例就有 &lt;code&gt;format&lt;/code&gt; 方法了.</description></item><item><title>Java 语言的几个缺陷之五: 多返回值问题</title><link>https://yanbin.blog/java-language-defect-no-multiple-returns/</link><pubDate>Sun, 10 Jul 2016 02:10:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-no-multiple-returns/</guid><description>
希望 Java 能支持动态对象(匿名对象) 的特性是源于想要 Java 方法能优雅的返回多个值. 目前如果希望 Java 方法返回多个值的做法有返回一个自定义对象, 数组或列表, 或 Map. 这种需求多发生在私有方法上, 但目前的解决办法有如下弊端:&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;如果用自定义类来作为返回类型的话, 会使得类过于杂乱, 而且这些自定义类的复用性不高&lt;/li&gt;
&lt;li&gt;数组或列表有太强的顺序依赖, 没有属性名告知每个位置上值的意义, 而且类型都必须为 Object&lt;/li&gt;
&lt;li&gt;返回 Map 的, 在维护 Key 上容易出错(或须为 Key 定义很多字符串常量), 且类型也是为 Object.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
说了上面许多, 各位可能还不定清楚我想要的是什么, 其实就是类似于 C# 的匿名对象(或曰动态对象), 看一段 C#  的方法返回一个匿名对象的例子(.NET3.5 开始引入匿名对象, .NET 4.0 后匿名对象可以作为函数值)</description></item><item><title>Java 语言的几个缺陷之四: 过时的 JavaBean</title><link>https://yanbin.blog/java-language-defect-4-outdated-javabean/</link><pubDate>Sat, 09 Jul 2016 03:35:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-4-outdated-javabean/</guid><description>
曾几何时在业务分层结构中的 VO 或 DTO 层充斥着无数的标准 JavaBean 类, 那些碍手脚的 getter/setter 方法简值不忍直视. 或许 JavaBean 设定规范的用意是当某些属性为只读时不提供 setter 方法, 而实际使用时, 因 getter/setter 都同时具备, 那么 JavaBean 的所有私有属性又何异于公有属性呢. &lt;/p&gt;
&lt;br/&gt;
更别说对于某些形式的属性名, 若属性名为 &lt;code&gt;xCoordinate&lt;/code&gt;  时, 它所对应的 getter 方法分别是 &lt;code&gt;getxCoordinate()&lt;/code&gt;,一般的 IDE 都会为它自动生成 &lt;code&gt;getXCoordinate()&lt;/code&gt; 方法, 这是错误的.  实际上 &lt;code&gt;getXCoordinate()&lt;/code&gt; 对应的属性名是 &lt;code&gt;XCoordinate&lt;/code&gt;. &lt;br/&gt;&lt;br/&gt;
所以 Play Framework 1 以及 Play Framework 2.4.6 之前的版本采用了字节码增强的技术, 实现了像 Objective-C 的 @property 的特性, 即只要声明公有属性, 编译器为该属性生成默认的 getter/setter 方法, 您也可以手工去覆盖个别默认的 getter/setter 方法.&lt;br/&gt;&lt;br/&gt;
因此在 Play Framework 中书写的的  model 类就只需要属性了, 像&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
就这么简单, 想像一下如果我们为一个众多属性的 model 类补全所有的 getter/setter 方法读起来有多恐怖.</description></item><item><title>Java 语言的几个缺陷之三: 不支持 var 类型推断</title><link>https://yanbin.blog/java-language-defect-3-not-support-var-type-infer/</link><pubDate>Sat, 09 Jul 2016 02:26:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-3-not-support-var-type-infer/</guid><description>
&lt;p&gt;虽说 Java 的支持泛型以及近期 Lambda  表达式的加入, 在对类型进行推断上已经很强大了, 但在类型声明的时候仍然略显冗余,
最主要的一点是 Java 不能像 Scala 那样在声明变量有赋值的情况下进行类型推断. 我们先来看下 Java  已经为我们所进行的改进:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//刚引入泛型时我们是这样&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//后来变成这样了, 可以钻石符号推断, Java 7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//根据 list.addAll() 上下文推断要创建的类型是 new ArrayList&amp;lt;String&amp;gt;(), Java 8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;dodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Foo:dodo()&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//Java 8 Lambda 可以根据 Lambda 表达式的签名推断出接口类型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dodo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在 Java 7 的泛型方法其实也是可以通过声明类型与方法参数来推断要返回的具体类型的.</description></item><item><title>Java 语言的几个缺陷之二: equals() 比较字符串</title><link>https://yanbin.blog/java-language-defect-2-equals-compare-strings/</link><pubDate>Sat, 09 Jul 2016 00:50:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-2-equals-compare-strings/</guid><description>
对于面向对象的语言不知道除了 Java 还有没别的语言会拿怎么比较两个字符串相等频频作为面试题来考. 原本是在编程语言中两个字符串内容是否相等时用 &lt;code&gt;==&lt;/code&gt; 比较时却可能是不对的. 在 Java 中
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;ab&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ab&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;ab&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;new String(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;) //false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; == String.value(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;) //true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;new String(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;).equals(new String(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)) //true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;new String(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;).intern() == new String(&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;).intern() //true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在 Java 中明明看到两个字符串内容一样用 &lt;code&gt;==&lt;/code&gt; 进行比较多数时候不是你想要的结果, 只有用 &lt;code&gt;equals()&lt;/code&gt; 方法才是王道.  使用 Java 的字符串必须了解它内部是怎么存储的. 比于上面的结果我不作细说, 主要涉及到字符串常量池及内部状态, &lt;code&gt;==&lt;/code&gt; 比较引用, &lt;code&gt;equals()&lt;/code&gt; 比较内容.&lt;br/&gt;&lt;br/&gt;
Java 还常常对 &lt;code&gt;equals&lt;/code&gt; 比较字符串津津乐道, 而我仍然认为它是语言设计上的一个缺陷, 所以 JVM 上的其他编程语言如 Groovy, Scala 纷纷倒勾, 无一不是用&lt;code&gt;==&lt;/code&gt; 来比较字符串的内容, 它们也提供字符串引用的比较, 但多少人实际关心两个字符串的引用是否相同呢, 反正字符串设计的是 Immutable 的.&lt;br/&gt;&lt;br/&gt;
若说是因为 Java 不支持操作符的重载, 但可以像 Scala, Groovy 那样在编译器上下功夫的. 最终我想依然是受累于 100% 源代码与二进制的兼容性, 改进的话会造成早先代码的行为错乱.</description></item><item><title>Java 语言的几个缺陷之一: 无 Here Document</title><link>https://yanbin.blog/java-language-defect-1-no-here-document/</link><pubDate>Fri, 08 Jul 2016 23:43:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-language-defect-1-no-here-document/</guid><description>
Java  语言由于一直要保持源代码及二进制的向后兼容, 所以尽管语法上有了很大的演进, 但有些东西仍然无法触及.  不像有些新生代的语言设计时可以博采众长, 或者像 Scala 那种语言向后兼容性的要求没这么苛刻, Scala 只要保持主, 次版本相同时的兼容性. Java 则不同, 1.0 的代码或字节码放到 1.8 下还要能跑.
&lt;br/&gt;
实际应用中在多个语言切换时, 很自然的会对其他语言与 Java 进行横向对比. 感觉 Java 语言有几个缺陷, 不在此篇中一一列举, 只把第一个不足之处道来.&lt;br/&gt;&lt;br/&gt;
Java 没有 Here Document 的支持, Here Document 又称 heredoc, hereis, here-string, here-script, 再通俗点讲就是多行字符串(multiline string). 在 Java 中如果要定义多行字符串, 需要连串的加号及换行符(\n), 如下&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;This is a\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;multiline\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;string content&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
曾经为了寻求 Java 对 Here Document 的支持, 尝试过 Java 的  APT(Java Annotation Processing Tool), 定义注解 @HereDocument 使用注释, 编译时带 &lt;code&gt;-processor&lt;/code&gt; 参数, 但是注释中的格式很容易被 IDE 给自动格式化掉, 见 &lt;a href="java-implement-here-document/"&gt;Java 的多行字符串 Here Document 的实现&lt;/a&gt;. 所以这种尝试很快就放弃了, 因为项目中用 Java 和  Scala 混合编程, 真正需要 Here Document 的地方直接用 Scala 代码就行了.</description></item><item><title>Clojure 快速突击(续)</title><link>https://yanbin.blog/clojure-get-started-1/</link><pubDate>Fri, 01 Jul 2016 23:57:23 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/clojure-get-started-1/</guid><description>
Clojure 确实要比 Python 语义上庞大, 所以无法尽量在一篇之中收纳进来, 只得另立新篇, 也许还会有第三篇笔记. 现在开始学习函数定义&lt;/p&gt;
&lt;h3&gt;&lt;span style="color: #0000ff;"&gt;函数定义&lt;/span&gt;&lt;/h3&gt;&lt;br/&gt;
函数用  &lt;code&gt;defn&lt;/code&gt; 宏来定义, 函数名与参数列表之间可选的字符串是函数的注释, 相当于 Python 的函数体中第一个字符串,  这个字符串能被 &lt;code&gt;(doc func-name)&lt;/code&gt;  列出来, Python 中是用 &lt;code&gt;dir(fun_name&lt;/code&gt; 显示函数帮助. 不需要 return 关键字, 和 Groovy/Scala 一样最后一个表达式的值为函数的返回值, 所以函数总是有返回值(或为 nil).&lt;br/&gt;&lt;br/&gt;
和 C 语言一样, 函数必须先定义再使用, 否则要用 &lt;code&gt;(declare function-names)&lt;/code&gt; 提前声明, 下面代码是在 Clojure 的 REPL 中执行的&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-clojure" data-lang="clojure"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;user=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;say-hello-to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;say hello to some&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, &amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;user/say-hello-to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;user=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;doc &lt;/span&gt;&lt;span class="nv"&gt;say-hello-to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;-------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;user/say-hello-to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;say&lt;/span&gt; &lt;span class="nv"&gt;hello&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;some&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;user=&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
提一下 Clojure 的命名规则, 变量和函数名用中划线连接的小写单词. &lt;code&gt;defn-&lt;/code&gt; 定义的函数是私有, 只对当前名字空间可见, 比如上面的  user 名字空间. 有点像 Python 的下划线变量或函数的可见性约定.</description></item><item><title>Clojure 快速突击</title><link>https://yanbin.blog/clojure-get-started/</link><pubDate>Thu, 30 Jun 2016 23:34:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/clojure-get-started/</guid><description>
自己所学过的编程语言基本是 C 风格的, 给自己定下的目标是要学习下 Python, Swift 和 &lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt;. 正如之前的 &lt;a href="http://gloveangels.com/my-python-warm-up/"&gt;我的 Python 快速入门&lt;/a&gt; 那样的几分钟入门, 这里记录下 Clojure 的快速上手过程.&lt;/p&gt;
&lt;br/&gt;
为什么是 Clojure, 因为它是 Lisp 的一个方言, 个人觉得有必要拓展一下不同的语言风格与思维方式, 就像当初接触 Objective-C 的 [person sayHello] 的方法调用有点不好理解一样, 其实把它还原为面向对象的本质是向 person 发送 sayHello 消息就简单了.&lt;br/&gt;
&lt;blockquote&gt;
编程语方不仅仅是一种技术, 它更是一种思维习惯
&lt;/blockquote&gt;
希望通过 Clojure 这样的语言来感受另样的思维方式. Clojure 是运行在 JVM 之上的函数式 List 方言. Clojure 乍一看, 基本就是一个括号语言, 它的语法更能体现操作/函数为中心. &lt;span style="color: #0000ff;"&gt;Clojure 的圆括号兼具 C 风格语言的圆括号(参数列表), 分号(分隔语句), 以及大括号(限定作用域) 的功能&lt;/span&gt;. (1 + 2 + 3 +4) 只用写成 (+ 1 2 3 4)。&lt;br/&gt;&lt;br/&gt;
因为  Clojure 是构筑在 JVM 之上, 所以可以从 &lt;a href="http://clojure.org/community/downloads"&gt;http://clojure.org/community/downloads&lt;/a&gt; 下载 clojure 的 jar 包, 然后&lt;br/&gt;
&lt;blockquote&gt;
java -jar clojure-&lt;span style="color: #800000;"&gt;1.8.0&lt;/span&gt;.jar
&lt;/blockquote&gt;
就能进到 Clojure 的 REPL(read-eval-print-loop) 控制台了, 就可以开始体验 Clojure 的代码 &lt;code&gt;user=&amp;gt; (+ 1 2 3)&lt;/code&gt; ,如果要运行一个已经写好的 Clojure 文件, 如 hello.clj, 就要用 &lt;code&gt;java -jar clojure-1.8.0.jar hello.clj&lt;/code&gt; 来执行. 为方便可以建立一个脚本  &lt;code&gt;clj&lt;/code&gt;, 内容为&lt;br/&gt;
&lt;blockquote&gt;
java -jar /path/clojure.jar $1
&lt;/blockquote&gt;</description></item><item><title>我的 Python 快速入门</title><link>https://yanbin.blog/my-python-warm-up/</link><pubDate>Mon, 27 Jun 2016 02:09:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/my-python-warm-up/</guid><description>
本文只是我个人的掌握 Python 的快速入门笔记, 所以混乱不堪, 并不适合于每一个想要学习 Python 的读者&lt;br/&gt;&lt;br/&gt;
Python 命令进到它的 shell, ctrl+d 或 exit() 退出 python. help(str) 可以查看 str 函数的帮助, q 退出帮助. 对象的方法可用 dir 来查看, &lt;code&gt;dir([])&lt;/code&gt;, &lt;code&gt;dir("")&lt;/code&gt;, 进而 &lt;code&gt;help([].append)&lt;/code&gt;, &lt;code&gt;help(dir([]))&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
Python 是用严格的缩进来格式化代码块的, &lt;a href="http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/"&gt;Google 的 Python 代码规范&lt;/a&gt;是用 4 个空格来缩进. Google 建议 Java 是用两个空格.&lt;br/&gt;&lt;br/&gt;
Python 是动态类型的, 所以可以 a = 1; a = "string" 随意赋值为不同类型. Python 也能用分号把多条语句写在同一行里, 但基本没人用分号的.&lt;br /&gt;
Python 的基本类型有 整数, 长整数, 浮点数和复数, 以及字符串&lt;br/&gt;&lt;br/&gt;
字符串可以用单引号和双引号, 它们像 Javascript, 是完全一样的&lt;br /&gt;
''' 或 "”” 三引号的字符串是 here doc, 多行字符串&lt;br /&gt;
转义符也是用 \, 如 ''What\’s your name\n?&lt;br /&gt;
自然字符串: 即不转义, 用 R 或 r 来指定, 如 r"Newlines are indicated by \n”, 会输出 "\n"" 字面值. 可用于书写正则表达式&lt;br /&gt;
放在一起的字符串就会被 Python 自动连接, 如 print ''What\'s' ''your name?’, 输出为 "What’s your name?”&lt;br/&gt;&lt;br/&gt;
Python 的命名规则有几个必须知道的: 类名和 Java 一样; 模块, 方法, 变量名用小写字母下划线分隔, 常量用大写加下划线. 单或双下划线开头是特殊用途. 命名规则请参考 &lt;a href="https://google.github.io/styleguide/pyguide.html?showone=Naming#Naming"&gt;Google Python Style Guide#Naming&lt;/a&gt;&lt;br /&gt;
Python 是纯面向对象的, 任何东西都是对象, 函数也是&lt;br/&gt;&lt;br/&gt;
Python 可以用 \ 来连接语句行, 像 Bash 一样, 如</description></item><item><title>Docker for Mac 公测版试用</title><link>https://yanbin.blog/docker-for-mac-public-beta/</link><pubDate>Tue, 21 Jun 2016 01:12:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/docker-for-mac-public-beta/</guid><description>
伴随着 2016 年 &lt;a href="http://2016.dockercon.com/"&gt;Docker Conference&lt;/a&gt; 的进行, Docker for Mac 和 Windows 的版本终于完全公测开始了. 之前阅读 &lt;a href="https://blog.docker.com/2016/03/docker-for-mac-windows-beta/"&gt;Docker for Mac Windows Beta: the simple way to use Docker on your laptop&lt;/a&gt; 里的介绍, 想要安装的必须在 &lt;a href="https://beta.docker.com/"&gt;https://beta.docker.com/&lt;/a&gt;  收到获准才能下载.&lt;/p&gt;
&lt;br/&gt;
Docker for Mac and Windows 可能让我们在 Mac 或 Windows 下脱离 Virtual Box 虚拟机, 像在 Linux 下一样跑 Docker. 因为不存在了 Virtual Box 这一中间层, 端口重定向就变成了一件很简单的事情了. 再也不用像原来那样 &lt;code&gt;Localhost:80 -&amp;gt; VirtualBox-Default:1080 -&amp;gt; Container:80&lt;/code&gt;, 而直接就是 &lt;code&gt;Localhost:80 -&amp;gt; Container:80&lt;/code&gt;.&lt;br/&gt;&lt;br/&gt;
也不再需要用 &lt;code&gt;eval $(docker-machine env default)&lt;/code&gt; 来设置 &lt;code&gt;DOCKER_HOST&lt;/code&gt; 等环境变量了.&lt;br/&gt;&lt;br/&gt;
目前的 Beta 版是 Version 1.12.0-rc2-beta16 (build: 9493) f615be9fb245904fbdf1aa0cad251d418c869428&lt;br/&gt;&lt;br/&gt;
本人趁手的是 Mac, 所以大概讲述如何安装 Docker for Mac. 首先我们可以尽情的卸载掉已安装的 Virtual Box, Docker-Machine 和 Docker 了, 然后从 &lt;a href="https://docs.docker.com/docker-for-mac/"&gt;https://docs.docker.com/docker-for-mac/&lt;/a&gt; 下载 &lt;a href="https://download.docker.com/mac/beta/Docker.dmg"&gt;Docker for Mac&lt;/a&gt;. 它是一个 dmg 文件, 想安装普通的 Mac 应用那样拖拽到 /Applications 目录,</description></item><item><title>Vim 中标签(tab) 操作</title><link>https://yanbin.blog/vim-operate-tabs/</link><pubDate>Sun, 05 Jun 2016 19:20:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vim-operate-tabs/</guid><description>
&lt;p&gt;Vim 自 7.0 开始支持 tab 页了，这就像很多数文本编辑器那样方便在多文件中切换，而不是只能使用 Buffer 暗地里来回切。默认时标签上显示 tab 序号加上当前打开的文件名。&lt;/p&gt;
&lt;p&gt;注：Visual Studio Code 1.1.1 目前尚不支持 tab 功能。&lt;/p&gt;
&lt;p&gt;Vim 中关于 Tab 的操作命令如下：&lt;/p&gt;
&lt;p&gt;vim -p file1 file2 file3....   在多标签中打开多个文件&lt;/p&gt;
&lt;p&gt;:tabe[dit] 或  :tabnew          在当前标签后打开新的标签&lt;/p&gt;
&lt;p&gt;:tabn[ext] 或 &lt;span style="color: #0000ff;"&gt;gt&lt;/span&gt;         切换到下一个 tab&lt;/p&gt;
&lt;p&gt;:tabp[revious] 或 &lt;span style="color: #0000ff;"&gt;gT&lt;/span&gt;   切换到上一个 tab&lt;/p&gt;
&lt;p&gt;:tabn [N]    切换到第 N 个 tab&lt;/p&gt;
&lt;p&gt;:tabfir[st]   切换到第 1 个 tab&lt;/p&gt;&lt;p&gt;:tabl[ast]    切换到最后一个 tab</description></item><item><title>JMockit 如何 Mock 部分方法/属性</title><link>https://yanbin.blog/jmockit-partial-mock/</link><pubDate>Tue, 17 May 2016 23:32:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-partial-mock/</guid><description>
&lt;p&gt;现在的 JMockit 已经偷偷升级到了 1.23 版了，在 JVM 上的 Mock 工具中就数它最无敌了，因为它抢夺了最佳控制点  --javaagent，
可以说它是无所不能的。一般我们使用 JMockit 是通过两种方式，&lt;code&gt;new MockUp&lt;/code&gt; 和 &lt;code&gt;new Expectations&lt;/code&gt;.
JMock 不仅能够 Mock 类的所有方法，还能部分 Mock -- 这个是 &lt;code&gt;new Expectations&lt;/code&gt; 的默认行为。
所以这里我们来看下在使用 &lt;code&gt;new Expectations&lt;/code&gt; 的情况下如何对类的部份静态方法或部分实例方法进行 Mock。&lt;/p&gt;
&lt;p&gt;大致表述一下，共分为三种情况&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;针对类进行 Mock, 只有录制的静态方法被 Mock 住，其他的静态方法或实例方法都会调用实际实现&lt;/li&gt;
&lt;li&gt;针对某一实例进行 Mock，只在调用该实例已录制的方法才被 Mock 住，静态方法或新建实例调用任何方法都是实际实现&lt;/li&gt;
&lt;li&gt;针对类进行 Mock，但录制的是一个实例方法，那么该实例或任何新建实例在调用该录制方法时都会被 Mock 住&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不知道上面在说什么，本来就是空洞无凭，所以还是下实例，假定要测试下面这个类，或者说是测试使用到下面类的其他类</description></item><item><title>sbt 最简单的带输入任务 inputTask</title><link>https://yanbin.blog/sbt-simplest-inputtask-sample/</link><pubDate>Tue, 17 May 2016 00:57:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-simplest-inputtask-sample/</guid><description>
&lt;p&gt;在 sbt 中我们可以定义 settingKey, taskKey 和 inputKey. inputKey 接收输入的任务更具灵活性，虽然在 sbt 中 taskKey 和  inputKey 数量比例为 25:1，但仍然不可忽视了 inputKey 的贡献。&lt;/p&gt;
&lt;p&gt;起初在阅读 sbt 关于 inputKey 的资料时，一不小心就被带入到 Parser 上去了。其实还不如开门见山，先跳过 Parser 部份，示范 inputKey 任务中直接处理用户的原生输入。&lt;/p&gt;
&lt;p&gt;这里有两个最简单的 sbt inputKey 的示例&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #000080;"&gt;&lt;strong&gt;一. 命令后非空格起全部输入当作一个字符串&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;complete.DefaultParsers._&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;demo1&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputKey&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Demo1 inputTask&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;demo1&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trimmed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkString&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;trimmed() 方法能去除了两边的空格，如果是 token(any.* map(_.mkString)).parse 则两边空格都会算上，在 &lt;code&gt;demo1 abc &lt;/code&gt; 的 abc 两边的空格也都会算在输入参数里
&lt;/p&gt;
&lt;blockquote&gt;
&amp;gt; demo1 abc "abc"&lt;br /&gt;abc "abc"
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;span style="color: #ff0000;"&gt;sbt  的  input task  参数居然默认是紧接着命令开始的，如上面如果输入是 &lt;code&gt;demo1xxx&lt;/code&gt;,
demo1 任务接收到的参数是 &lt;code&gt;xxx&lt;/code&gt;. 如果同时也定义了另一个任务是 &lt;code&gt;demo1x&lt;/code&gt;, 那么输入 &lt;code&gt;demo1xx&lt;/code&gt; 就会执行
&lt;code&gt;demo1x&lt;/code&gt; 任务，参数是 &lt;code&gt;xx&lt;/code&gt;. 从后往前进行匹配任务名。这个还有点奇怪，因为我以前任何时候都是自然的把命令与参数用空格分开，
它却可以把命令与参数写在一起。&lt;/span&gt;
&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;二. 像调用 main 方法一样传入参数&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Scala 和 Java 的集合类型相互转换</title><link>https://yanbin.blog/scala-java-collections-cast/</link><pubDate>Sun, 08 May 2016 00:15:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-java-collections-cast/</guid><description>
在 Scala 和 Java 混合编程时免不了需要进行集合类型在两种语言间相互转换，更多的是在 Scala 调用 Java 的方法时把 Scala 的集合转型为 Java 的集合。典型场景是：&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orderIds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;......&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
上面定义的 Java 方法，如果要在 Scala 中调用它，不考虑两种语言的集体类型转换的话，可以直接传入 Java 代码要求的类型，像这样&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;orderIds&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orderIds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SJ001&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderIds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这样当然可以，但不能享受到 Scala 语言中集合使用的便利性，如快捷的构造，丰富的怪异的方法(++, ::, ## 等)。所以希望此时 Scala 中调用 &lt;code&gt;process()&lt;/code&gt;  能接近这种方法&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;SJ001&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;SJ002&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
特别是当 Java 接受一个 java.util.Map 时，能在 Scala 里直接传入 &lt;code&gt;Map("key1" -&amp;gt; "value1",  "key2" -&amp;gt; "value2")&lt;/code&gt; 就方便许多。&lt;br/&gt;&lt;br/&gt;
用方法来完成 Scala 和 Java 间对应集合类型的转换当然没问题，但别忘了 Scala 还支持隐式转换，那就是只要在 Scala 代码中引入 &lt;a href="http://www.scala-lang.org/api/2.8.0/scala/collection/JavaConversions$.html"&gt;collection.JavaConversions._&lt;/a&gt; 对于上面的方法在 Scala 中就可以直接传入 Scala 的 List() 了。也就是</description></item><item><title>让 Scala 测试方法名中的空格不再显示为 $u0020</title><link>https://yanbin.blog/scala-space-in-test-method-name-not-shows-u0020/</link><pubDate>Sat, 07 May 2016 00:02:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-space-in-test-method-name-not-shows-u0020/</guid><description>
&lt;p&gt;在 Scala 中借助于斜撇号我们可以使用关键字或任何字符为作为变量或方法名，例如下面的方法都是合法的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;scala&amp;gt; def `if`: Unit = {}&lt;br /&gt;
if: Unit&lt;/p&gt;
&lt;p&gt;scala&amp;gt; def `我是谁?`: Unit = {}&lt;br /&gt;
我是谁$qmark: Unit&lt;/p&gt;
&lt;p&gt;scala&amp;gt; def `just do it`: Unit = {}&lt;br /&gt;
just$u0020do$u0020it: Unit&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对了，Scala 2.11.6 在显示有些字符时会进行编码，像上面的 ? 和 空格。而用 2.11.7 的 Scala  控制台下居然原样显示，不编码(这是在进一步试难时发现的)。&lt;/p&gt;
&lt;p&gt;我们在用 Scala 写 JUnit 风格的单元测试, 由于 JUnit 不支持像 TestNG 那样在方法名上加描述，所以方法名必须完成自我描述。依照严格的方法命名的话，势必要用驼峰方式或下划线把单词分开，如&lt;/p&gt;
&lt;blockquote&gt;
ef getOneHunderIfSixtyPlusFouty: Unit = { ... }&lt;br /&gt;def get_one_hunder_if_sixty_plus_forty: Unit = { ... }
&lt;/blockquote&gt;
&lt;p&gt;注：如果用 Scala 写 Spec 测试代码另当别论，因为有些情况下必须用 JUnit 风格(就用 JMockit 时)&lt;/p&gt;</description></item><item><title>Google Guice 轻装上阵</title><link>https://yanbin.blog/google-guice-hello-world/</link><pubDate>Tue, 26 Apr 2016 23:24:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/google-guice-hello-world/</guid><description>
&lt;p&gt;PlayFramework 已全面向 &lt;a href="https://github.com/google/guice"&gt;Google Guice&lt;/a&gt; 演进, 由于我们的项目是从 Play 2.0 开始搭建,
现虽已升级到了 2.4.6, 但对 Guice 的还没抱得那么的紧, 有点积重而慢步前挪. Google Guice 也是一个依赖注入(DI) 容器, 据说它比 Spring 快了很多很多,
是轻量级项目或框架首选, Google 出品俱是精品.&lt;/p&gt;&lt;p&gt;既然它是轻量级的 DI, 那么必须呈上一个最轻量级的入门, 官方的
&lt;a href="https://github.com/google/guice/wiki/GettingStarted"&gt;Get Started&lt;/a&gt; 文档其实啰嗦了点, 对于 BillingService 依赖的
CreditCardProcess, TransactionLog, 和 Receipt 都未有交待, 算不上一个完整项目. 所以我这里尽量简化, 又不偏离一个最简单的项目模型.
那就是下面那个例子, 它实现了在不同时间发出不同的问候.&lt;/p&gt;&lt;p&gt;项目需要依赖 Guice 及它的子依赖, 这得看你是用什么来管理依赖的, 依照 
&lt;a href="http://mvnrepository.com/artifact/com.google.inject/guice/4.0"&gt;http://mvnrepository.com/artifact/com.google.inject/guice/4.0&lt;/a&gt;
根据实际情况加上依赖配置, 前面这个链接默认为 sbt 的, 我还真偏爱了 sbt, 若是 Maven 就配置 Maven 的 pom.xml 吧. Guice 的引入带上以下几个依赖包&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;   +-com.google.inject:guice:4.0&lt;br /&gt;
     +-aopalliance:aopalliance:1.0&lt;br /&gt;
     +-com.google.guava:guava:16.0.1&lt;br /&gt;
     +-javax.inject:javax.inject:1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Guice 目前是 4.0.</description></item><item><title>sbt 任务间的依赖</title><link>https://yanbin.blog/sbt-task-dependency/</link><pubDate>Sat, 23 Apr 2016 00:08:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-task-dependency/</guid><description>
项目中的 &lt;code&gt;build.sbt&lt;/code&gt; 中发现定义任务时有 &lt;code&gt;task2 &amp;lt;&amp;lt;= task1 map {...}&lt;/code&gt; 这样任务依赖的写法, 这个  &lt;code&gt;&amp;lt;&amp;lt;=&lt;/code&gt; 方法有点晦涩难懂, 读过 &lt;code&gt;sbt in action&lt;/code&gt; 之后才知道这是 sbt 0.12 或之前的做法, sbt 0.13 之后不这么用了, 直接访问下 task1.value 就行, 因此前面可改写为 &lt;code&gt;task2 := {task1.value; ...}&lt;/code&gt;, 这也使得定义任务依赖时与普通任务一致风格了. 新的写法得益于 Scala 2.10 的宏特性, 后面还会讲到.&lt;/p&gt;
&lt;br/&gt;
对于依赖于多个任务的情况, 在 sbt 0.13 前面分别是这样的, 假定有三个 Task(task1, task2, 和 task3)&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskKey&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;task 1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskKey&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;task 2&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;task3&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskKey&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;task 3&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果 task3 依赖于 task1 和 task 2&lt;br/&gt;&lt;br/&gt;
sbt 0.13 前后版本中的写法分别是&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//sbt 0.12 及更早版本, 当然 sbt 0.13 中也能这样写
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//http://www.scala-sbt.org/0.12.2/docs/Detailed-Topics/Tasks.html#dependencies
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;task3&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//task1, task2 并发执行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//sbt 0.13 后, 推荐这种新写法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;task3&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt; &lt;span class="n"&gt;也是并发执行&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
像任务的 &lt;code&gt;.value&lt;/code&gt; 属性其实是一个宏定义, 源码</description></item><item><title>ScalaTest + Selenium 集成测试</title><link>https://yanbin.blog/scalatest-selenium-integration-test-with-browsers/</link><pubDate>Wed, 20 Apr 2016 02:28:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scalatest-selenium-integration-test-with-browsers/</guid><description>
在 Play 1 和 Play 2 中都内置了 Selenium 集成测试工具, 这里自己尝试自己单独测试用 &lt;a href="http://www.scalatest.org/"&gt;ScalaTest&lt;/a&gt; + &lt;a href="http://www.seleniumhq.org/"&gt;Selenium&lt;/a&gt; 来做简单的集成测试. Selenium 可以支持内置的无界面 Java 实现的浏览器, 也可以用外部浏览器, 如 Safari, Firefox, Chrome, IE, Opera 或移动设备的浏览器. 使用不同外部浏览的方式或用插件(Safari/Firefox 等) 或是像借助于 Chromium 来驾驭 Chrome 浏览器.&lt;/p&gt;
&lt;br/&gt;
而我们这里要用的 ScalaTest 2.2.6 在包 &lt;a href="http://doc.scalatest.org/2.2.6/index.html#org.scalatest.selenium.package"&gt;org.scalatest.selenium&lt;/a&gt; 有以下几个特质 Chrome, Driver, Firefox, HtmlUnit, InternetExplorer, Page, Safari 和  WebBrowser. 由此可以看出 ScalaTest 默认支持的浏览器. 若单论 Selenium 本身, 它可强大的暂时超乎我的想像, 可以自建服务器, 选择浏览器分发测试任务. 我们知道使用 Selenium 的好处是不光可以像通常那样断言页面静态文本, 还能执行 Javascript 脚本, 所以可断言动态内容.&lt;br/&gt;&lt;br/&gt;
本人开发环境为 Mac OS, 可以成功让 Selenium 测试跑在内置 Java 浏览器, 和外置的  Safari, Chrome 浏览器中, Firefox 的插件未安装成功.&lt;br/&gt;&lt;br/&gt;
让事实说话, 仍然让一个最简单的例子自己说话, 创建的是一个 sbt 项目, 尽量把目录最简化, 只有 &lt;code&gt;build.sbt&lt;/code&gt; 文件和 &lt;code&gt;test/IntegrationTest.scala&lt;/code&gt; 测试代码, 内容分别为</description></item><item><title>建立 Play 2 框架一样的目录布局</title><link>https://yanbin.blog/layout-like-playframework-2/</link><pubDate>Fri, 15 Apr 2016 23:34:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/layout-like-playframework-2/</guid><description>
&lt;p&gt;sbt 项目继承并扩展了 Maven 的默认项目布局, 加入了 Scala 代码的支持, 所以目录如 Shell 命令 &lt;code&gt;mkdir -p src/{main,test}/{java,scala,resources}&lt;/code&gt; 生成的目录结构, 即&lt;/p&gt;
&lt;blockquote&gt;
.&lt;br /&gt;
└── src&lt;br /&gt;
    ├── main&lt;br /&gt;
    │   ├── java&lt;br /&gt;
    │   ├── resources&lt;br /&gt;
    │   └── scala&lt;br /&gt;
    └── test&lt;br /&gt;
        ├── java&lt;br /&gt;
        ├── resources&lt;br /&gt;
        └── scala
&lt;/blockquote&gt;
&lt;br/&gt;
这个目录目录虽然很清晰, 但把 Java 和 Scala 代码拆在两处没多大必要, 其次是层次多了点. 因使用 Play Framework 时日有点久了, 比较习惯于 Play 2 改造后的项目布局. 我们启动到 Play 2 的 activator(其实就是加入了定制的 sbt) 控制台, 用命令看它的目录布局</description></item><item><title>sbt 中单元测试并发执行</title><link>https://yanbin.blog/sbt-tests-execute-in-parallel/</link><pubDate>Wed, 13 Apr 2016 00:46:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-tests-execute-in-parallel/</guid><description>
此次研究的目的原本是要使得 Play Framwork 2 中单元测试能够并发执行, 包括 JUnit 和 Spec 的测试用例, Play 2 的 activator 就是一个 sbt 的包装. 开发中发现我们 Play 2 中的单元测试是按序执行的, 实际上 sbt 下测试用例默认是并发执行的. 之所以 Play 2 的单元测试是按序的, 是因为 activator 设置了把 sbt 的两个属性 &lt;code&gt;fork in Test := true&lt;/code&gt; 和 &lt;code&gt;parallelExecution in Test := false&lt;/code&gt;, 见 &lt;a href="https://github.com/playframework/playframework/blob/master/framework/src/sbt-plugin/src/main/scala/play/sbt/PlaySettings.scala"&gt;PlaySettings.scala&lt;/a&gt;, 它们默认分别为 false 和 true. 这使得默认设置下 Play 2 中的所有测试无法并发执行.&lt;/p&gt;
&lt;br/&gt;
sbt 默认的 fork 是 false, Play 2 改为 true 之后便可以使用 &lt;code&gt;javaOptions in Test := "-Dkey1=value1"&lt;/code&gt; (注: 如果 fork 为 false 的话, javaOptions 将无效.) 往单元测试中参数了, 这也是为什么在 Play 2 的单元测试中无法获得启动 sbt 时(像 sbt -Dkey1=value) 的参数, 不同一个 JVM 啊.&lt;br/&gt;&lt;br/&gt;
那是不把 Play 2 的 &lt;code&gt;fork in Test&lt;/code&gt; 和 &lt;code&gt;parallelExecution in Test&lt;/code&gt; 分别改回成 false 和 true 就可以让测试用例并发执行了呢? 答案是 Yes. 但我们得相信 Play 2 把它们预设为 true 和 false 是有它的用意的, 比如集成测试的每个用例都会开启本地的 3333 端口, 如果让两个集成测试同时执行将会造成端口冲突. 细致说来, Play 2 其实是懒政, 只管一刀切而让所有测试按序执行而影响了效率, 如能利用好 sbt 的测试分组机制是可以达到测试的并发执行的.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;这里引出 sbt 执行测试的几个机制:&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
1) sbt 总是对测试进行分组, 默认时所有的测试都包含在 &amp;lt;default&amp;gt; 组中, 可用 &lt;code&gt;show testGrouping&lt;/code&gt; 查看, 如&lt;br/&gt;</description></item><item><title>查看 sbt 项目的依赖关系树</title><link>https://yanbin.blog/show-sbt-dependency-tree/</link><pubDate>Tue, 05 Apr 2016 00:40:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/show-sbt-dependency-tree/</guid><description>
sbt 是借助于 ivy 来管理项目依赖, 像 Maven 项目中可以用 &lt;code&gt;dependency:tree&lt;/code&gt; 来显示依赖树, 那么对于 sbt 项目该如何查看项目依赖关系呢? 本文提及了三种方式来显示项目依赖, 它们是 Shell 脚本, 自定义 sbt 任务, 和 sbt-dependency-plugin 方式. 最后一个办法使得我们也能用 dependencyTree 显示出 Maven 的  dependency:tree 效果来, 还有更酷的的.&lt;/p&gt;
&lt;blockquote&gt;
&amp;gt; dependencyTree&lt;br /&gt;
[info] default:test_2.10:0.1-SNAPSHOT [S]&lt;br /&gt;
[info]   +-ch.qos.logback:logback-classic:1.0.13&lt;br /&gt;
[info]     +-ch.qos.logback:logback-core:1.0.13&lt;br /&gt;
[info]     +-org.slf4j:slf4j-api:1.7.5&lt;br /&gt;
[info]&lt;br /&gt;
[success] Total time: 0 s, completed Apr 5, 2016 12:29:53 AM
&lt;/blockquote&gt;
下面是探索的全部过程.&lt;br/&gt;&lt;br/&gt;
通过 sbt 控制台的 tab  自动完成或用 &lt;code&gt;help .*[Dd]ependenc.*&lt;/code&gt; 命令再进一步过滤出与依赖比较接近 sbt 控制台任务</description></item><item><title>Java 构建工具及 sbt 最快速体验</title><link>https://yanbin.blog/java-built-tools-sbt-simplist-project/</link><pubDate>Thu, 31 Mar 2016 02:51:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-built-tools-sbt-simplist-project/</guid><description>
应对 Java 项目, 我们大概有以下几个自动化构建工具:&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://ant.apache.org/"&gt;Ant&lt;/a&gt; -- XML 化跨平台批处理, 配置文件 build.xml, 执行的是 target&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maven.apache.org/"&gt;Maven&lt;/a&gt; -- 开始&lt;a href="https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html"&gt;标准化目录布局&lt;/a&gt;, 基于项目对象模型, 配置文件 pom.xml, 执行的是 phase/goal&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt;  -- 使用 Maven 默认布局, Groove 语言铸就, 配置是 groovy 语法的 build.gradle 文件, 执行的是 task&lt;/li&gt;
&lt;li&gt;&lt;a href="http://buildr.apache.org/"&gt;Buildr&lt;/a&gt;  -- 默认也是 Maven 目录布局, Java 世界被 Ruby 插手, 配置是 ruby 语法的 buildfile 文件, 也是 task&lt;/li&gt;
&lt;li&gt;&lt;a href="http://leiningen.org/"&gt;Leiningen&lt;/a&gt; -- 也采用 Maven 目录布局, Clojure 写的, 可用于构建 Java 和 Clojure 项目, 配置文件是 Clojure 语法的 prject.clj, 基于 task&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.scala-sbt.org/"&gt;sbt&lt;/a&gt;  -- 默认 Maven 目录布局, Scala(Simple) Build Tool, Scala 写的, 构建 Java 和 Scala 项目, 配置是 Scala 语法的 build.sbt, 基于 task. 交互式控制台.&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
Ant 让我们摆脱了对系统平台的依赖, 终于不同人构建的工件是一样的了, 曾经它就是昭示着敏捷. 除了 Ant 需要我们定义所有的 target 外, 其他构建都内置了基本足够用的 task, 而且也都采用了业界接受的 Maven 目录布局. 也是从 Maven 开始引入了项目依赖管理, 所以 Maven 才是里程碑式的.&lt;br/&gt;&lt;br/&gt;
在我们搜索 Java 第三方依赖时常常进到类似这样的页面 &lt;a href="http://mvnrepository.com/artifact/com.google.guava/guava/19.0"&gt;http://mvnrepository.com/artifact/com.google.guava/guava/19.0&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2016/java-built-tools-sbt-simplist-project/java-dependencies.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2016/java-built-tools-sbt-simplist-project/java-dependencies-800x219.png"
width="600px"
/&gt;
&lt;/a&gt;</description></item><item><title>Mac OS X 下安装使用 Docker (新)</title><link>https://yanbin.blog/mac-os-x-experience-docker-new-2/</link><pubDate>Sun, 27 Mar 2016 18:14:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-os-x-experience-docker-new-2/</guid><description>
&lt;p&gt;两年前的一篇&lt;a href="https://yanbin.blog/mac-os-x-experience-docker"&gt;Mac OS X 下安装使用 Docker&lt;/a&gt;
安装时还是用的 boot2docker, 如今进化到了在 Mac OS X 下用 Docker Toolbox, 而且命令也由 boot2docker 换成了 docker-machine.
当然由于是非 Linux 系统, 所以 Mac OS X 仍然需要借助于 VirtualBox 中的 Linux 虚拟机作为桥梁,
Docker Toolbox 创建的虚拟机名是 default (boot2docker 创建的虚拟机名是 boot2docker-vm) 就是这一桥梁,
我们称之为 DOCKER_HOST. 文中的 default 虚拟机指的就是这个 DOCKER_HOST.&lt;/p&gt;&lt;p&gt;现在来看下安装步骤及体验,
因为我系统中已安装好了 VirtualBox, 所以这一步骤等会就省略了.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一. 安装 docker 和  docker-machine&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #0000ff;"&gt;官方的指南是通过下载  DockerToolbox
来安装 docker, docker-machine 和其他辅助工具. 但如果你偏于极客, 并不习惯于图形界面来安装的话, 那么安装 docker 和 docker-machine 就只要下面两个指令&lt;/span&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;brew install docker
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;brew install docker-machine&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span style="color: #0000ff;"&gt;如果你已选择了使用上面的两条命令来安装 docker, docker-machine 的话, 那么请跳到下一步.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;如果你热爱图形界面来安装应用的话, 可以到页面 &lt;a href="https://www.docker.com/products/docker-toolbox"&gt;https://www.docker.com/products/docker-toolbox&lt;/a&gt;
中下载 Mac 的版本的 &lt;a href="https://github.com/docker/toolbox/releases/download/v1.10.3/DockerToolbox-1.10.3.pkg"&gt;DockerToolbox-1.10.3.pkg&lt;/a&gt;, 当前版本是 1.10.3&lt;/p&gt;
&lt;div style="text-align: center;"&gt;
&lt;a href="https://yanbin.blog/posts/2016/mac-os-x-experience-docker-new-2/Docker-Toolbox-Installer.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="inline post-image"
src="https://yanbin.blog/posts/2016/mac-os-x-experience-docker-new-2/Docker-Toolbox-Installer-300x212.png"
width="400px"
/&gt;
&lt;/a&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;a href="https://yanbin.blog/posts/2016/mac-os-x-experience-docker-new-2/Docker-Toolbox-includings.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="alignright post-image"
src="https://yanbin.blog/posts/2016/mac-os-x-experience-docker-new-2/Docker-Toolbox-includings-800x565.png"
width="400px"
/&gt;
&lt;/a&gt;
&lt;/div&gt;</description></item><item><title>拆分 Playframework 2 的 routes 为多个文件</title><link>https://yanbin.blog/split-playframework-2-routes-into-mutiple-files/</link><pubDate>Mon, 22 Feb 2016 23:33:39 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/split-playframework-2-routes-into-mutiple-files/</guid><description>
我们用 Playframework 2 时，当 routes 中太多的路由配置时，我们可能会考虑把它们归类分布到多个文件中去。比如按 API 或用途分，有些是 RESTful API，有些是 Web 页面的，对于这种情景，我们可以由以下几个文件来组织：&lt;/p&gt;
&lt;br/&gt;
1. routes 文件，这个仍然是充当入口&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;-&amp;gt; / general.Routes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;-&amp;gt; /api api.Routes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;-&amp;gt; /web web.Routes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这里穿插着来解释下，&lt;code&gt;-&amp;gt;&lt;/code&gt; 是固定写法，表示要去别外寻找了，紧接着的 /, /api, 和 /web 是分类路由的上下文了，例如，访问 api.routes 中定义的 /customers 的完整 API 路径就是 /test/customers。最后一部分是全类名，并非指别的路由文件的名称，像文件 general.routes 编译后会生成 general 包下生成  Routes.scala 文件，即类为 general.Routes.</description></item><item><title>如何轻松理解排序函数</title><link>https://yanbin.blog/understand-sort-function/</link><pubDate>Fri, 01 Jan 2016 03:06:47 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-sort-function/</guid><description>
&lt;p&gt;这是我自最早接触 Javascript 的排序之日起一直萦绕在脑海中的问题。比如一个简单的 Javascript 数组排序&lt;/p&gt;
&lt;blockquote&gt;
[3, 2, 4, 6, 5].sort(function(e1, e2) {&lt;br /&gt;
    return e1 - e2;&lt;br /&gt;
});
&lt;/blockquote&gt;
&lt;p&gt;当然我那时候还不会用 JSON 和无名函数来写上面的代码。
反正经常是搞不清楚想要升序或降序时，是应该 return e1 - e2 还是 return e2 - e1 ?，没把握就试一下，总是二选一。&lt;/p&gt;
&lt;p&gt;现在许多语言对集合排序都是使用排序函数的方式，总不想每次都琢磨不定，每次都去试。需要 TDD 时测试用例必须覆盖排序用例，
但还是希望以后书写排序函数时心里有个底。当然，比如在 Java 中对集合进行排序我们可以深入到源代码来理解排序函数是如何影响元素顺序的。&lt;/p&gt;
&lt;p&gt;简单通俗点，是否有一种浅显的方式来理解排序函数呢？待我把答案慢慢叙来，就我个人理解就两个原则：</description></item><item><title>Java 的 fork-join 框架实例备忘</title><link>https://yanbin.blog/java-fork-join-framework-memo/</link><pubDate>Tue, 29 Dec 2015 00:17:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-fork-join-framework-memo/</guid><description>
Java 7 首次引入了 fork/join 框架，但一直未曾直接尝试. 而且基本上也很少在实际项目中直接写 fork-join 的代码，在我们使用第三方组件时倒是间接会接触到 fork/join 框架。譬如 Akka 的 fork-join-executor, sbt 执行测试用例时也是默认 fork/join 并发执行。fork-join 可以帮助我们把计算任务粒度细化，并更有效的利用多 CPU 内核。&lt;br/&gt;&lt;br/&gt;
fork-join 与 map-reduce 有些相妨，在 Java 7 时代我其实是忽视了它的存在。目今正在了解 Java 8 的  parallelStream 时，因为它的底层实现也是 fork/join, 所以有兴致去稍加体验一下。fork/join 的算法简单来讲就是递归对半去细化计算任务，及到不能细化时由多内核(线程)去计算被拆分的任务，最后反方向把结果汇总。&lt;br/&gt;&lt;br/&gt;
下面是从 《Java 8 IN ACTION》中截的一个说明 fork/join 的处理过程</description></item><item><title>Java 8 的 groupingBy 产生空的 Map 分组</title><link>https://yanbin.blog/java-8-groupingby-empty-map-group/</link><pubDate>Fri, 25 Dec 2015 22:25:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-groupingby-empty-map-group/</guid><description>
前面一篇 &lt;a href="https://yanbin.blog/java-8-groupingby-howto-generate-empty-map/" target="_blank" rel="noopener"&gt;Java 8 的 groupingBy 能否产生空的 Map 分组&lt;/a&gt; 是提出来的思考，本篇就是上一篇的答案。&lt;/p&gt;
&lt;br/&gt;
由于在 Java 8 中用 Collectors.groupingBy 对 List 进行分组时每个组里都必须存在元素，也就是&lt;br/&gt;
&lt;blockquote&gt;
Stream&amp;lt;Person&amp;gt; stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));&lt;br /&gt;
System.out.println(stream.collect(Collectors.groupingBy(person -&amp;gt; person.gender)));
&lt;/blockquote&gt;
只能得到结果&lt;br/&gt;
&lt;blockquote&gt;
{male=[Tom, Jerry]}
&lt;/blockquote&gt;
而无法表示存在其他 gender 的可能性，并且 female=[] 的情况，即想要结果&lt;br/&gt;
&lt;blockquote&gt;
{male=[Tom, Jerry], female=[]}
&lt;/blockquote&gt;
如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现，如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口，有五个接口方法. 事成之后我们写</description></item><item><title>Java 8 的 groupingBy 能否产生空的 Map 分组</title><link>https://yanbin.blog/java-8-groupingby-howto-generate-empty-map/</link><pubDate>Fri, 25 Dec 2015 14:14:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-groupingby-howto-generate-empty-map/</guid><description>
我们在 Java 8 之前用 for-loop 对 List 进行分组的时候，可能会要求产生空的分组。例如对 List&amp;lt;Person&amp;gt; 按性能进行分组，即使给定的 List&amp;lt;Person&amp;gt; 中全是 male, 我们也想得到 Map 包含两个 Key&lt;/p&gt;
&lt;blockquote&gt;
{male=[person1, person2], female=[]}
&lt;/blockquote&gt;
而不只是&lt;br/&gt;
&lt;blockquote&gt;
{male=[person1, person2]}
&lt;/blockquote&gt;
这两种表示略有区别，第一种方式暗示着有另一种可能性。看看 for-loop 如何对 List&amp;lt;Person&amp;gt; 进行分组。&lt;br/&gt;&lt;br/&gt;
类 Person 代码</description></item><item><title>Java 8 返回集合中第一个匹配的元素</title><link>https://yanbin.blog/java-8-return-the-first-match-element/</link><pubDate>Thu, 17 Dec 2015 01:10:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-return-the-first-match-element/</guid><description>
在 Java 8 之前如果我们要找到集合中第一个匹配元素，要使用外部循环，如下面方法 findFirstMatch() 如果找到一个大于 3 的数字立即返回它，否则返回 null&lt;/p&gt;
&lt;blockquote&gt;
  public Integer findFirstMatch() {&lt;br /&gt;
    List&amp;lt;Integer&amp;gt; integers = Arrays.asList(1, 4, 2, 5, 6, 3);&lt;br /&gt;
    for(int i: integers) {&lt;br /&gt;
      if(i &amp;gt; 3) return i;&lt;br /&gt;
    }&lt;br /&gt;
    return null;&lt;br /&gt;
  }
&lt;/blockquote&gt;
因为在 for 循环中找到第一个大于 3 的数字是 4, 并且立即返回，所以不管集合 integers 再大，也不会遍历整个集合。&lt;br/&gt;&lt;br/&gt;
注：不要纠结于上面示例方法的实际用途，实际上集体和匹配条件都该通过参数传入方法的，这里只作演示循环。&lt;br/&gt;&lt;br/&gt;
那么我们来到 Java 8 之后用 Stream API 该如何实现，翻遍了 Stream API, 能过滤元素的操作也就是 filter 方法，于是尝试这样的写法</description></item><item><title>Java 8 Lambda 捕获外部变量</title><link>https://yanbin.blog/java-8-lambda-capture-outer-variables/</link><pubDate>Fri, 11 Dec 2015 23:30:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-lambda-capture-outer-variables/</guid><description>
可能会把捕获外部变量的 Lambda 表达式称为闭包，那么 Java 8 的 Lambda 可以捕获什么变量呢？&lt;/p&gt;
&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;捕获实例或静态变量是没有限制的(可认为是通过 final 类型的局部变量 this 来引用前两者)&lt;/li&gt;
&lt;li&gt;捕获的局部变量必须显式的声明为 &lt;em&gt;final&lt;/em&gt; 或实际效果的的 &lt;em&gt;final&lt;/em&gt; 类型&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
回顾一下我们在 Java 8 之前，匿名类中如果要访问局部变量的话，那个局部变量必须显式的声明为  final，例如下面的代码放在 Java 7 中是编译不过的&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2015/java-8-lambda-capture-outer-variables/java7-annymous-capture-local-variable.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2015/java-8-lambda-capture-outer-variables/java7-annymous-capture-local-variable-800x358.png"
width="500px"
/&gt;
&lt;/a&gt;
Java 7 要求 version 这个局部变量必须是 final  类型的，否则在匿名类中不可引用。</description></item><item><title>Java 8 Stream 如何还原为集合</title><link>https://yanbin.blog/java-8-stream-to-collection/</link><pubDate>Tue, 08 Dec 2015 02:29:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-stream-to-collection/</guid><description>
由于 Java 对集合的函数式操作并非原生态的，必须对得到的 stream() 进行过滤等操作，之后还是一个 stream()，一般我们最后返回给调用者需还原为相应的集合。这无法与 Scala 的 &lt;code&gt;for ... yield&lt;/code&gt; 操作相比。例如下面在使用 Stream API 过滤获得所有大于 3 的数字之后，方法的返回值还应该还原为 List&amp;lt;Integer&amp;gt;, 这个需求非常自然
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arrays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;greaterThan3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamOfInteger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamOfInteger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ToIntegerList&lt;/span&gt;&lt;span class="p"&gt;......;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
我们这儿的问题就是如何把上面的 streamOfInteger 转换为 List&amp;lt;Integer&amp;gt;, 有以下几种办法</description></item><item><title>Java 8 Lambda 写法与简化</title><link>https://yanbin.blog/java-8-lambda-simple-ways/</link><pubDate>Mon, 07 Dec 2015 23:06:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-lambda-simple-ways/</guid><description>
Java 8 的 Lambda 表达式的实现方式还是基于已有的字节码指令，由 Lambda 表达式的方法签名结合上下文并经由 SAM 推断出正确的类型来。Java 8 的 Lambda 完整书写格式是&lt;/p&gt;
&lt;br/&gt;
&lt;code&gt;(type parameter1 [type parameter2, ...type parametern]) -&amp;gt; { statements here }&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
这种标准格式所表示的就是方法签名。&lt;br/&gt;&lt;br/&gt;
虽不及其他语言的 Lambda 表达式，像 Swift, Scala 可省略参数部分，可用默认参数名 &lt;code&gt;$0&lt;/code&gt;, &lt;code&gt;$1&lt;/code&gt;, 或 &lt;code&gt;_&lt;/code&gt;, 但 Java 8 的 Lambda 还是可以进行酌情简化&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;参数类型总是可省略   --     &lt;code&gt;(x, y) -&amp;gt; { x + y; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;参数为一个时，参数括号可省略  --    &lt;code&gt;x -&amp;gt; { System.out.println(x); }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;语句为一条时，可省略大括号, 并且语句后不要分号 --  &lt;code&gt;x -&amp;gt; System.out.println(x)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;上面更进一步，如果是单条 return 语句，必须把 return 关键字去掉  --  &lt;code&gt;x -&amp;gt; "Hello " + x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;就差一点，参数部分总是不能省，无参必须写成 &lt;code&gt;() -&amp;gt; System.out.println("hi")&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Java 8 中若要近似的实现无参数部分写法，那就是方法引用了 -- &lt;code&gt;System.out::println&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Mac OS X 下不能 brew update 的问题</title><link>https://yanbin.blog/mac-os-x-issue-of-brew-update/</link><pubDate>Tue, 01 Dec 2015 11:47:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-os-x-issue-of-brew-update/</guid><description>
&lt;p&gt;我在 Mac OS X 下执行 brew update 出错&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #800000;"&gt;➜   brew update&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;error: Your local changes to the following files would be overwritten by merge:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/abcde.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/acpica.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    ............&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/pypy.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/For&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;error: The following untracked working tree files would be overwritten by merge:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Aliases/actor-framework&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/aescrypt-packetizer.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    ............&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/stlink.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;    Library/Formula/with-readline.rb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;Please move or remove them before you can merge.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;Aborting&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #800000;"&gt;Error: Failure while executing: git pull -q origin refs/heads/master:refs/remotes/origin/master&lt;/span&gt;</description></item><item><title>Swift 学习笔记(对象和类)</title><link>https://yanbin.blog/swift-learning-class-object/</link><pubDate>Mon, 30 Nov 2015 23:24:38 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-class-object/</guid><description>
&lt;p&gt;类的最基本特性就是封装, Swift 的类可以想见就是把 let 的常量, var 的变量, func 的方法写在 class 定义里。
在此之上就涉及变、常量的初始，实例的初始化。Swift 的类定义与 Java 相比还是有些特别的地方，如属性不存在默认值，
必须在构造函数中初始化; 构造函数还是沿用了 Objective-C 的方式，名称都是 init; 实例化时不需要 new 等等。
&lt;/p&gt;
&lt;p&gt;Swift 的类终于不用像 Objective-C 那样接口定义和实现分开来写了。&lt;/p&gt;&lt;p&gt;下面尽量用一个类声明来说明 Swift 类和对象更多的问题</description></item><item><title>Swift 学习笔记(@autoclosure 与 call-by-name)</title><link>https://yanbin.blog/swift-learning-autoclosure-call-by-name/</link><pubDate>Tue, 24 Nov 2015 23:24:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-autoclosure-call-by-name/</guid><description>
咋一眼看到 Swift @autoclosure 自动闭包便不由自主的联想到 Scala 的 Call-By-Name 求值策略。既然主要话题是关于 Swift 的，那还是 Swift 的 @autoclosure 先入为主，下面例子中的 &lt;code&gt;greet(name: () -&amp;gt; String)&lt;/code&gt; 接收一个闭包作为参数&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello &lt;/span&gt;&lt;span class="si"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;())&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;({()&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Swift&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;//标准&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Swift&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;//简化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Swift&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;//再简化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Swift&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;//因为 greet 只有一个参数，因而可以这样写&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
对 &lt;code&gt;greet(_:)&lt;/code&gt; 的函数调用不断简化，最后简化为 &lt;code&gt;greet{"Swift}&lt;/code&gt;。总而言之大括号是甩不了了，有大括号的围绕才叫闭包。&lt;br/&gt;&lt;br/&gt;
同时我们依据闭包的简化规则倒退发现：给任何一个表达式两边加上大括号 {}，就构成了一个闭包，它是无参数的闭包，返回值可有可无。例如 {1+2}, {foo(10)+5}, {print("Hello")}, {array.removeAtIndex(0)} 等等&lt;br/&gt;&lt;br/&gt;
至此，我们还未死心，思考能不能对 greet{"Swift"} 的写法进一步简化，这就要请出 @autoclosure 隆重登场, 对 greet() 的 name 参数前加上  @autoclosure, 就是</description></item><item><title>Swift 学习笔记(闭包)</title><link>https://yanbin.blog/swift-learning-closues/</link><pubDate>Tue, 24 Nov 2015 00:49:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-closues/</guid><description>
谈到函数式编程，恐怕最不能放过的就是闭包。闭包的定义总是不那么清晰，好像每种语言都有小许的差别。通常说的是可以捕获(访问)外部变量语句块，Swift 的闭包类似于 Objective-C 的 Block 或其他语言的 Lambda 表达式。所以闭包同 Lambda 基本上是同义词。&lt;/p&gt;
&lt;br/&gt;
现在还没完， Swift 认为全局函数(有名字的，不能捕获外部变量)和嵌套函数(有名字，可捕获外部变量)也认为是特殊的闭包。闭包表达式(无名字的闭)才是真正意义上的闭包，它用最简洁的方式来书写一个函数。因此它尽了最大的可能的作了约定性的简化，例如参数与返回类型的推断，return 的省略，无参或只有一个参数的简化。&lt;br/&gt;&lt;br/&gt;
闭包的语法形式&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;   { (&lt;code&gt;parameters&lt;/code&gt;) -&amp;gt; &lt;code&gt;return&lt;/code&gt; &lt;code&gt;type&lt;/code&gt; in&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;        &lt;code&gt;statements&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;    }&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
闭包中可用常量参数(let)，变量参数(var), inout 参数，甚至是可变个数参数，但是不能用默认参数。说白了，闭包也就是无名函数的另一种写法。</description></item><item><title>Swift 学习笔记(函数第一等)</title><link>https://yanbin.blog/swift-learning-function-first-class/</link><pubDate>Mon, 23 Nov 2015 02:04:26 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-function-first-class/</guid><description>
正好前段时间在发力理解函数式编程，学习 Swift 至今就想看看 Swift 对函数式编程的支持如何。从变量声明用 let, 以及函数参数的 let 和 var 都非常适合于函数式编程，它们对实例保护的很好。let 和 var 类型的函数参数保证了函数内部操作的是传入参数的深拷贝。&lt;/p&gt;
&lt;br/&gt;
在 Objective-C 中要调用一个函数，除了通过对像为媒介，还能 perform 一个  selector, selector 其实也是定位函数的方式，这更像是反射。其实 C/C++ 的函数指针概念更不错，用于直接定位函数。&lt;br/&gt;&lt;br/&gt;
所以进一步，函数要成为第一等公民，它本身可以很容易的作为参数传入或作为返回值，即能构成高阶函数。这在 Swift 也是没问题的，我们可以定义函数类型的变量，例如：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;addTwoInts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;_&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;addFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addTwoInts&lt;/span&gt; &lt;span class="c1"&gt;//addTwoInts 函数类型表示 (Int, Int) -&amp;gt; Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;add&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addTwoInts&lt;/span&gt; &lt;span class="c1"&gt;//自动推断出 add 函数的类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:()-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Void&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Swift 学习笔记(函数)</title><link>https://yanbin.blog/swift-learning-function/</link><pubDate>Mon, 23 Nov 2015 00:43:15 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-function/</guid><description>
由于 Swift 出自于名门 Apple，与 Objective-C 共同服务于 iOS/Mac OS X 平台，因而看得出它更像是脱胎于 Objective-C。现在来学习它的函数相关的知识，Swift 函数定义有些像 Scala，只是用 func  替代了 def, 返回类型的指定用的是 &lt;code&gt;-&amp;gt;&lt;/code&gt; 而不是 &lt;code&gt;=&lt;/code&gt;, 格式如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Hello &lt;/span&gt;&lt;span class="si"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s"&gt;, today is &lt;/span&gt;&lt;span class="si"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Tuesday&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
当然函数无参数就是空括号(不能省略空括号)，无返回值的话就无需 &lt;code&gt;-&amp;gt;&lt;/code&gt; 指定类型了，比如 &lt;span style="color: #800000;"&gt;func greet() { print("Hello")}&lt;/span&gt;，从形式上看不出是否有副作用(纯函数)&lt;br/&gt;&lt;br/&gt;
调用函数时和 Objective-C 是一样的，除第一个参数不需要指定形参名，其他参数必须指定形参名，并且顺序是要和函数定义时完全一样的。我们知道在 Objective-C 中一般约定把第一次参数包含在方法名中，如</description></item><item><title>Swift 学习笔记(基本语法)</title><link>https://yanbin.blog/swift-learning-basic-syntax/</link><pubDate>Sun, 22 Nov 2015 21:33:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-learning-basic-syntax/</guid><description>
&lt;div&gt;因为是本人学习 Swift 的笔记，是基于所掌握的其他语言来认识 Swift 的，所以并非详尽的 Swift ABC，也就显得凌乱不堪。&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;不会自动转型，强型转型的方式是 String(变量值), 括号不是括住类型&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;total: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;  &lt;span class="c1"&gt;//这是错的，必须写成 “total: &amp;#34; + String(number), 直接常量的话 let message = “total:&amp;#34; + String(123)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;let 为常量, var 为变量, 类型的指定方式与  Scala 一样，如果不让它自动推断或无法推断时，这样指定类型&lt;br/&gt;
&lt;div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Double&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Swift 语言 Server 端开发资料收集</title><link>https://yanbin.blog/swift-server-side-development/</link><pubDate>Wed, 18 Nov 2015 23:01:23 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swift-server-side-development/</guid><description>
苹果的 Swift 语言第二版预计在今年底开源，届时还可在 Linux 下使用 Swift 语言。试想一下此语言用在服务端 Web 开发也该是个很好的方向，需要实现对  HTTP 协议的支持。&lt;/p&gt;
&lt;br/&gt;
Swift 本身就可以做 Shell 脚本用，所以以 CGI 的方式运行是没问题的，没有第三方相关的框架，可以自己完全输出响应头和体。&lt;br/&gt;&lt;br/&gt;
我们可以把 Swift 集成到像 Apache 或 Nginx 的 Web 服务器中，要做的工作可能多些，应该要用到 C/C++ 来写 Swift 支持模块。想下我们用这种 URL http://localhost/service/customer.swift 来访问 swift 实现的后台页面。&lt;br/&gt;&lt;br/&gt;
或者也可以让 Swift 代码像 NodeJs 一样运行，自己启动 HTTP Server。幸运的是我们已经有了 &lt;a href="https://github.com/glock45/swifter" target="_blank" rel="noopener"&gt;Swifter&lt;/a&gt; 和 &lt;a href="https://github.com/izqui/Taylor" target="_blank" rel="noopener"&gt;Taylor&lt;/a&gt;，再配之以纯 Swift 的模板实现 GRMustache, 简单开发不在话下。&lt;br/&gt;&lt;br/&gt;
一旦该语言流行开了，特别是能用在 Linux 平台下，总会有人去实现出对 HTTP 协议的支持，再配之以模板，让 Swift 像 PHP 一样在 Apache 中运行就不难了。</description></item><item><title>sbt 项目通过 sbt-aspectj-plugin 使用 AspectJ</title><link>https://yanbin.blog/sbt-project-sbt-aspectj-plugin-use-aspectj/</link><pubDate>Tue, 17 Nov 2015 23:28:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-project-sbt-aspectj-plugin-use-aspectj/</guid><description>
Java 在运用面向方向编程时，依照  &lt;a href="https://eclipse.org/aspectj/" target="_blank" rel="noopener"&gt;AspectJ&lt;/a&gt; 的语法自己书写 *.aj 文件可以得到尽可能大的控制能力。如果是一个 &lt;a href="http://www.scala-sbt.org/" target="_blank" rel="noopener"&gt;sbt&lt;/a&gt; 的项目，有一个 &lt;a href="https://github.com/sbt/sbt-aspectj" target="_blank" rel="noopener"&gt;sbt-aspectj-plugin&lt;/a&gt; 插件可以帮上我们的忙。那么如何应用这个插件呢？ 该插件首页面告诉我们要在 &lt;code&gt;project/plugins.sbt&lt;/code&gt; 中加上下面这句话&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;addSbtPlugin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;com.typesafe.sbt&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;sbt-aspectj&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;0.10.4&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
其他就是参考例子 &lt;a href="https://github.com/sbt/sbt-aspectj/tree/v0.10.4/src/sbt-test"&gt;runnable sample projects&lt;/a&gt;，然而这几个例子并非那么直白。所以还是自己做一个最简单的例子来体验 sbt 项目如何使用 AspectJ.</description></item><item><title>走进函数式编程 (Becomming Functional) (递归)</title><link>https://yanbin.blog/becomming-functional-4/</link><pubDate>Fri, 13 Nov 2015 00:32:37 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/becomming-functional-4/</guid><description>
阅读完递归这一章我仍然无法理解递归为何被作为函数式编程的一个特性。只是递归有时候会简洁，但没弄好却更让人犯迷糊，用循环的话就四平八稳，总是比较好理解。当然有些情况用循环去思考确实难于处理。递归方法内部调用方法本身通常也比较慢，因为需要频繁的保存现场，创建栈帧，恢复现场，所以受内存限制，递归到达一定深度容易导致堆栈溢出。&lt;/p&gt;
&lt;br/&gt;
章中还说到递归的一个优点在于它只需处理输入的值，而循环则需关注整个集合本身。这个该如何理解呢？在每一次递归调用的时候传入的都是当前需要被处理的数据。&lt;br/&gt;&lt;br/&gt;
先来比较下循环变换成递归的一个简单示例，用 Scala 实现</description></item><item><title>Java 8 的泛型增强</title><link>https://yanbin.blog/java-8-generic-enhance/</link><pubDate>Wed, 11 Nov 2015 22:16:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-8-generic-enhance/</guid><description>
Java 5 引入了泛型，这是一次重大的改进，从此集合中的东西不需要每次显式的去转型。不过 Java 5 还不具备类型推断的能力，所以声明泛型必须写成&lt;/p&gt;
&lt;br/&gt;
List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();&lt;br/&gt;&lt;br/&gt;
一直到 Java 6 也是如此。自 Java 7 起泛型增强为可根据声明类型进行推断，所以 Java 7 中可以这么写&lt;br/&gt;&lt;br/&gt;
List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();  //&amp;lt;&amp;gt; 中的参数可省略，如果类型参数多, 或多层嵌套时很省事&lt;br /&gt;
或&lt;br /&gt;
List&amp;lt;String&amp;gt; list = Collections.emptyList(); //见  &lt;a href="https://yanbin.blog/java-generic-auto-infer-by-declare-type/"&gt;Java 泛型 -- 依据声明的变量类型自动推断&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
Java 8 开始对泛型类型推断又进一步增强：可根据方法上下文进行推断，例如下面的代码在 Java 7 下编译不过&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;根据&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;上下文推断要创建的类型是&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>走进函数式编程 (Becomming Functional) (不可变性)</title><link>https://yanbin.blog/becomming-functional-3/</link><pubDate>Wed, 11 Nov 2015 00:45:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/becomming-functional-3/</guid><description>
&lt;p&gt;不可变性(Immutable--不是不让谁变性) 在设计模式中似乎不那么显眼，但是它在函数式编程却起着举足轻重的意义了。我们都知道 Java 的 String 是设计为 Immutable 的，对 String 改变都会产生新的 String 实例，这有助于共享常量池中字符串与并发。&lt;/p&gt;&lt;p&gt;这里再回顾上一篇关于纯虚函数(输入决定输出，无副作用函数，非 C++ 中可重载无 Body 的函数)，由此可知，Immutable 也有助于实现纯虚函数，因为一个不可变实例被传入到某个函数中，该实例的内部状态是无法被修改的，也就是说该函数是无副作用的。&lt;/p&gt;&lt;p&gt;先来感受一下可变类型在并发环境下的一个弊端，看个例子，可变的 Customer 类有三个属性 name, email 和  age, 它还有一个更新方法 updateNameAndEmail()</description></item><item><title>走进函数式编程 (Becomming Functional) (2)</title><link>https://yanbin.blog/becomming-functional-2/</link><pubDate>Tue, 06 Oct 2015 02:34:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/becomming-functional-2/</guid><description>
函数式编程第二式，&lt;strong&gt;纯函数 (Pure Functions)&lt;/strong&gt;. 何谓纯函数，纯函数就是数据库的函数一样没有副作用，不修改对象的内部状态，或者说只进行计算。给定什么输入，永远得到相同的输出，即&lt;strong&gt;输出只依赖于输入&lt;/strong&gt;。&lt;/p&gt;
&lt;br/&gt;
纯函数有什么好处呢？极其容易测试，只需要给定一组输入，对输出进行断言。如果是带副作用的方法，它修改了某个私有的属性，很难进行状态判定。&lt;br/&gt;&lt;br/&gt;
Scala 基于统一访问原则，属性与方法不需要那么明晰，所以属性与方法名是不能重名的。它对有无副作用存在这么一个约定：&lt;br/&gt;&lt;br/&gt;
空括号方法 (empty-paren method), 如 def width() {...} 它是有副作的。这样调用 obj.width()&lt;br /&gt;
无参方法 (parameterless method), 如 def width {...}, 它是无副作的。这样调用 obj.width, 这和使用属性一致形式&lt;br/&gt;&lt;br/&gt;
Java 也有类似的约定，get 开头的或 getter 方法往往是无副作用的。要是有人偏偏在 getName() 方法里修改了对像的属性而引入了 Bug，那只会让人唏嘘不已。&lt;br/&gt;&lt;br/&gt;
方法的副作用一般有哪些呢？&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;输出内容到屏幕&lt;/li&gt;
&lt;li&gt;写数据到文件或数据库&lt;/li&gt;
&lt;li&gt;修改了对象的属性&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>走进函数式编程 (Becomming Functional) (1)</title><link>https://yanbin.blog/becomming-functional-1/</link><pubDate>Fri, 02 Oct 2015 22:51:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/becomming-functional-1/</guid><description>
本人正在阅读 《&lt;a href="http://shop.oreilly.com/product/0636920030331.do" target="_blank" rel="noopener"&gt;Becoming Functional&lt;/a&gt;》这本书，且对 Scala 的使用经验已有数年，所以品读的同时更是对头脑中函数编程的概念进行重新整理。函数编程并非一定要诸多语言特性的支持，它是一种不同的思维方式；比如说我们公司项目如今还是用的 Java 7，但我们一直以函数式思维来书写我们的代码。我们正在从 Java 7 升级到 Java 8，待到 Java 8 时代码行文肯定要比现在简练的多，但函数式编程思维未变。&lt;/p&gt;
&lt;br/&gt;
下面是函数式编程基本概念&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;First-class functions: 函数是第一类型&lt;/li&gt;
&lt;li&gt;Pure functions:  纯函数，无边界效应，输出依赖于输入，易测试。像数据库的函数而非存储过程&lt;/li&gt;
&lt;li&gt;Recursion: 递归，Scala 强调尾递归优化，避免坠入 StackOverflow&lt;/li&gt;
&lt;li&gt;Immutable variables: 这在 OO 里也是一种不错的模式，它与 Pure function 也是相辅的。可能是首先映入函数式编程思维的概念，它不关乎并发性能，解决了并发冲突&lt;/li&gt;
&lt;li&gt;Nostrict evaluation: 即变量值的赖加载，变量不到用时不初始化。在 Java 只能用方法来模拟实现&lt;/li&gt;
&lt;li&gt;Statements: 表达式优于控制结构，语句可以有返回值的，如 val a = if (condition) 1 else 2&lt;/li&gt;
&lt;li&gt;Pattern Matching: 模式匹配，不光是通常对数值或字符串的 switch/case, 还能应用到任何对象的匹配，进行类型检查或从对象中提取元素&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
《&lt;a href="http://shop.oreilly.com/product/0636920030331.do" target="_blank" rel="noopener"&gt;Becoming Functional&lt;/a&gt;》逐章对上面七大概念进行讲解.&lt;br/&gt;&lt;br/&gt;
First-class functions: 函数是第一类型</description></item><item><title>早先为 Jackson 写的 Json-Path 支持</title><link>https://yanbin.blog/jackson-json-path-support-before/</link><pubDate>Thu, 20 Aug 2015 22:31:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jackson-json-path-support-before/</guid><description>
今天才发现 Jackson 其实是支持 Json-Path 的，但以前一直不知道，关键是 Jackson 的文档也没见提到。所以很久以前是自己给 Jackson 写了一个简陋的 Json-Path 支持类，这个是为测试代码用的，所以基本够用。想要更全面的功能可使用 &lt;a href="https://github.com/jayway/JsonPath" target="_blank" rel="noopener"&gt;https://github.com/jayway/JsonPath&lt;/a&gt; 这个项目。对于 Jackson 中如何使用 Json-Path, 我还会进一步研究下。&lt;/p&gt;
&lt;br/&gt;
我写的 RichJsonNode 类是一个 Scala 版本，最早也写过一个 Java 版本，还有一个基于 GSON 的 Java 版本的。这里只贴出 Scala 版源码。支持的基本方法及语法如下：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;a/b, a/b[2], a/b[1]/c, a[1]/b, $[0]&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;selectNode(path), selectString(path), selectInt(path), selectDouble(path), selectBollean(path), hasField(path) 和  arrayLength(path)&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
源码如下：</description></item><item><title>Scala 如何测试异常</title><link>https://yanbin.blog/scala-how-to-test-exception/</link><pubDate>Thu, 20 Aug 2015 21:42:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-how-to-test-exception/</guid><description>
几年前整理过一篇 &lt;a href="https://yanbin.blog/junit-4-how-to-test-exceptions/"&gt;JUnit 4 如何正确测试异常&lt;/a&gt;，类似的问题：如何在 Scala 中测试异常呢？因 Scala 可以完全采用 JUnit 测试用例的风格，所以当然可以运用 Java 的三种方式来测试异常，即&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;try { 待测试代码; fail() } catch(某种异常) {断言}&lt;/li&gt;
&lt;li&gt;@Test(expected = Exception.class)&lt;/li&gt;
&lt;li&gt;@Rule&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
回到 Scala 中来，我并不那么情愿在 Scala 中使用 JUnit 的 @Test def testMethod() 这样的测试方法风格，而多少采用 BDD 或者叫更 DSL 的风格。&lt;br/&gt;&lt;br/&gt;
那看看我们 Scala 有些什么独到的异常测试方法&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff; font-size: 12pt;"&gt;&lt;strong&gt;一： intercept&lt;/strong&gt;&lt;/span&gt;</description></item><item><title>自定义 Jackson 注解与禁用某一特定的注解</title><link>https://yanbin.blog/customize-jackson-annotation-and-disable-specific-annotation/</link><pubDate>Mon, 03 Aug 2015 15:06:09 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/customize-jackson-annotation-and-disable-specific-annotation/</guid><description>
Jackson 是 &lt;a href="http://www.playframewok.com" target="_blank" rel="noopener"&gt;Playfrmework&lt;/a&gt; 2 中默认的 JSON 处理框架，先前是  GSON，JSON 是 Playframework 中的第一等公民，可见 Jackson 在 Playframewok 中的重要地位。Jackson 提供了一系列的&lt;a href="https://github.com/FasterXML/jackson-annotations" target="_blank" rel="noopener"&gt;注解&lt;/a&gt;可用，像 @JsonIgnore, @JsonProperty, @JsonUnwrapped, @JsonFilter 等。人的需求总是很难得到满足，所以免不了还是要定义自己的注解。比如有这样一个需求，JavaBean 中被 @MaskField(这个即将成为我第一个自定义的注解) 标记的属性或 getter 方法，总是输出为 &lt;code&gt;******&lt;/code&gt;, 无此标记的属性或方法输出原始值。&lt;/p&gt;
我尝试过 @JsonFilter  或是单纯的自定义  JsonSerializer, 并不怎么如意。本人最终的实现方式涉及到&lt;br/&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;@JacksonAnnotationsInside -- 用来创建自己的 @MaskField 注解&lt;/li&gt;
&lt;li&gt;JsonSerializer  -- 被 @MaskField 标记的字段采用自定义的 JsonSerializer 来序列化&lt;/li&gt;
&lt;li&gt;JacksonAnnotationIntrospector  -- 禁用某一特定的注解，这样可以在做任意时候启用或禁用 @MaskField</description></item><item><title>Java 接口常量反模式及如何定义 Java 常量</title><link>https://yanbin.blog/java-constant-interface-anti-pattern/</link><pubDate>Mon, 13 Jul 2015 16:13:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-constant-interface-anti-pattern/</guid><description>
&lt;p&gt;初学 Java 的人很不经意间就会把常量定义在接口中，大概唯一的理由是接口不能实例化，而使用接口中定义的常量也是不用附着在实例上的。
这主要还是 JDK 本身给我们做了很多这样的榜样,
如  &lt;a href="http://docs.oracle.com/javase/7/docs/api/java/io/ObjectStreamConstants.html" target="_blank"&gt;java.io.ObjectStreamConstans&lt;/a&gt;，
多是出现在 Enum 类型到来之前。&lt;/p&gt;
&lt;p&gt;其实 Java 的接口常量是一种反模式，理由如下：&lt;/p&gt;
&lt;p&gt;1. 接口是不能阻止被实现或继承的， 也就是说子接口或实现中是能够覆盖掉常量的定义(重名)，这样通过父，子接口(或实现) 去引用常量是可能不一致的&lt;br /&gt;
2. 同样的，由于被实现或继承，造成在继承树中可以用大量的接口, 类 或实例去引用 同一个常量，从而造成接口中定义的常量污染了命名空间。(Java 编译器竟然允许使用实例去引用类变量)&lt;br /&gt;
3. 接口暗含的意思是：它是需被实现的，代表着一种类型，它的公有成员是要被暴露的 API。而在接口中定义的常量说不上是 API
&lt;/p&gt;
&lt;p&gt;4. 这点有些重复，Java 允许通过子类去引用父类中定义的常量，各级对像实例去引用父类的常量，所以这会造成相当的混乱不堪。定义的常量不能保证单一的引用方式。&lt;/p&gt;
&lt;p&gt;参见： &lt;a href="http://blog.csdn.net/hosterzhu/article/details/9833665" target="_blank"&gt;Effective java 第 19 条: 接口只用于定义类型&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;既然接口中不适于定义常量，那么该在何处为常量安家呢？接口为 实现/继承 而生，如果放在类中，并且这个类是 final，且封闭掉构造方法就行。
于是我们先前的接口常量定义</description></item><item><title>fish 2.2.0 (July 12, 2015) 支持 vi 模式</title><link>https://yanbin.blog/fish-2-2-0-july-12-2015-support-vi-mode/</link><pubDate>Mon, 13 Jul 2015 11:07:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fish-2-2-0-july-12-2015-support-vi-mode/</guid><description>
&lt;p&gt;随着 Mac 下终端的使用日益增多，系统默认的 bash 已经满足不了需求了，于是有了更为强劲的
&lt;a href="http://fishshell.com/" target="_blank"&gt;fish&lt;/a&gt; 和
&lt;a href="http://www.zsh.org/" target="_blank"&gt;zsh&lt;/a&gt;，以及它们各自的强心剂
&lt;a href="https://github.com/oh-my-fish/oh-my-fish" target="_blank"&gt;Oh-My-Fish&lt;/a&gt; 和
&lt;a href="http://ohmyz.sh/" target="_blank"&gt;Oh-My-Zsh&lt;/a&gt;. 我的选择是 Fish 和 Oh-My-Fish。&lt;/p&gt;
&lt;p&gt;到目前为止，最新的 fish 2.2.0 于 2015 年 7 月 12 日发布，Release notes 如下 &lt;a href="http://fishshell.com/release_notes.html" target="_blank"&gt;http://fishshell.com/release_notes.html&lt;/a&gt;，其中显著改变有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Abbreviations: the new `abbr` command allows for interactively-expanded abbreviations, allowing quick access to frequently-used commands (&lt;a href="https://github.com/fish-shell/fish-shell/issues/731"&gt;#731&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Vi mode: run `fish_vi_mode` to switch fish into the key bindings and prompt familiar to users of the Vi editor (&lt;a href="https://github.com/fish-shell/fish-shell/issues/65"&gt;#65&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;New inline and interactive pager, which will be familiar to users of zsh (&lt;a href="https://github.com/fish-shell/fish-shell/issues/291"&gt;#291&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Underlying architectural changes: the `fishd` universal variable server has been removed as it was a source of many bugs and security problems. Notably, old fish sessions will not be able to communicate universal variable changes with new fish sessions. For best results, restart all running instances of `fish`.&lt;/li&gt;
&lt;li&gt;The web-based configuration tool has been redesigned, featuring a prompt theme chooser and other improvements.&lt;/li&gt;
&lt;li&gt;New German, Brazilian Portuguese, and Chinese translations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我对第二点比较感兴趣，即增加了 vi 模式，在 fish 下运行 &lt;code&gt;fish_vi_mode&lt;/code&gt; 命令，
或者在 &lt;code&gt;~/.config/fish/config.fish&lt;/code&gt; 中加上 &lt;code&gt;fish_vi_mode&lt;/code&gt; 便自动进入 vi 模式。</description></item><item><title>通过反编译字节码来理解 Java 枚举</title><link>https://yanbin.blog/understand-java-enum-with-bytecode/</link><pubDate>Wed, 01 Jul 2015 16:27:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-enum-with-bytecode/</guid><description>
&lt;p&gt;枚举的声明很简单, 像 &lt;code&gt;enum Gender { Male, Female }&lt;/code&gt;, 其余事情就是 Java 编译器帮我们干的了，所以 enum 也就是一块语法糖。有了枚举确实是很方便，避免了传统常量的无范围性。那么编译器到底在后面做了什么呢？以及理解了这个之后我们可以怎么去使用 Java 的枚举, 下面就从这个例子说起：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;
public enum Gender {&lt;br /&gt;
    Male,&lt;br /&gt;
    Female&lt;br /&gt;
}
&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;把上面的编译成 Gender.class, 然后用  javap -c Gender 反编译出来就是</description></item><item><title>Linux 输入输出重定向, &amp;&gt;file, 2&gt;&amp;1, 1&gt;&amp;2 等</title><link>https://yanbin.blog/linux-input-output-redirection/</link><pubDate>Fri, 26 Jun 2015 13:28:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-input-output-redirection/</guid><description>
我们无论是在写批处理还是 Linux 的 Shell 都常用到 &amp;gt;, &amp;gt;&amp;gt; 或 &amp;lt;，这是输入输出重定向。特别是 Linux 的 Shell 常见到  2&amp;gt;&amp;amp;1 这样的写法，这是在干什么呢？这里就来了解下 Linux 下的输入输出重定向的一些来龙去脉。&lt;/p&gt;
&lt;br/&gt;
在 Linux 下几乎一切都号称是文件，标准输入、输出也不例外，它们是叫做 fd (File Descriptor) 文件描述符。这里我们关注三个东西&lt;br/&gt;&lt;br/&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;名称&lt;/td&gt;
&lt;td&gt;代码&lt;/td&gt;
&lt;td&gt;操作符&lt;/td&gt;
&lt;td&gt;Java中表示&lt;/td&gt;
&lt;td&gt;Linux 下文件描述符（Debian 为例)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标准输入(stdin)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&amp;lt; 或 &amp;lt;&amp;lt;&lt;/td&gt;
&lt;td&gt;System.in&lt;/td&gt;
&lt;td&gt;/dev/stdin -&amp;gt; /proc/self/fd/0 -&amp;gt; /dev/pts/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标准输出(stdout)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&amp;gt;, &amp;gt;&amp;gt;, 1&amp;gt; 或 1&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;System.out&lt;/td&gt;
&lt;td&gt;/dev/stdout -&amp;gt; /proc/self/fd/1 -&amp;gt; /dev/pts/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标准错误输出(stderr)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&amp;gt; 或 2&amp;gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;System.err&lt;/td&gt;
&lt;td&gt;/dev/stderr -&amp;gt; /proc/self/fd/2 -&amp;gt; /dev/pts/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
从以上表格我们可以理解 0, 1 和 2 分别是什么东西了，它们的输入源或输出目的地默认都是屏幕。&lt;br/&gt;&lt;br/&gt;
下面不作系统解释输入, 输出重定向的完整使用，只说明一些常见的例子：</description></item><item><title>Java 反射修改 final 属性值</title><link>https://yanbin.blog/java-reflection-modify-final-field-value/</link><pubDate>Thu, 25 Jun 2015 16:47:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-reflection-modify-final-field-value/</guid><description>
使用过 Java 反射的大多都知道, 想要修改某个类或对象的私有变量的值的话, 在调用 set 设置新值之前执行一下 setAccessible(true) 即可。这样利用的 Java 的反射就能绕过 private 的限制 ，不再有 IllegalAccessException 异常了。这是一个 trick, 调用 Java 的私有方法也能这么做，有些人或许或这样来测试 Java 私有方法。&lt;/p&gt;
&lt;br/&gt;
提前说一句：在修改 final 型值时，要特别留意它的常量值本身是否被编译器优化内联到某处，否则你会看到虽然没什么异常，但取出的还是原来的值。后面会稍为深入的讲到。&lt;br/&gt;&lt;br/&gt;
例如下面是一段完整的代码, 由于调用了 setAccessiable(true), 所以能成功把 OneCity 的私有属性 name 的值改为 "Shenzhen"：</description></item><item><title>Bash 的常用语法，控制结构</title><link>https://yanbin.blog/bash-common-syntax-control-structures/</link><pubDate>Mon, 18 May 2015 04:02:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/bash-common-syntax-control-structures/</guid><description>
最近玩弄 Jenkins 较多，构建服务器基本是 Mac OS 和 Linux，虽说有许多现成插件可用，但不敢不说 &lt;strong&gt;Execute Shell&lt;/strong&gt; 这个东西都是即开即用，方便而灵活的。因此不断的要和 Shell 打交道，真正通用的的 Shell 自然是 Bash，在 Mac OS 下可发现自带了 zsh, ksh 和 tcsh，考虑到 Linux 还是 Bash 吧。&lt;/p&gt;
&lt;br/&gt;
本人工作时用的是 Fish Shell，目前相当的脚本语言都可用来写 Shell 脚本，如 PHP, Ruby, Python, Perl，NodeJs 等，只是 Bash 的地位总难被替代，借句话说 Bash 的是拿来 “用” 的，而像 Ruby, Python 等做 Shell 是拿来 “编” 的，再就是 Bash 与 Linux 命令的亲缘性更强。&lt;br/&gt;&lt;br/&gt;
我也只会在实在用 Bash 太难于表达的时候才用其他脚本语言，如处理日期的运算，因 Mac OS 的 date 和 Linux 的 date 命令差异较大，不得已会选择有较强类型的 Ruby 等。&lt;br/&gt;&lt;br/&gt;
既然 Bash 要作为一个日常语言，那不妨作个备忘录，记录下那些常用的语法，控制结构。&lt;br/&gt;&lt;br/&gt;
&lt;span style="font-size: 12pt;"&gt;&lt;strong&gt;1. if 条件语句&lt;/strong&gt;&lt;/span&gt;</description></item><item><title>优秀程序设计的18大原则[转]</title><link>https://yanbin.blog/the-principles-of-good-programming/</link><pubDate>Wed, 18 Mar 2015 11:27:09 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/the-principles-of-good-programming/</guid><description>
好的编程原则跟好的系统设计原则和技术实施原则有着密切的联系。下面的这些编程原则在过去的这些年里让我成为了一名优秀的程序员，我相信，这些原则对任何一个开发人员来说，都能让他的编程能力大幅度的提高，能让他开发出可维护性更强、缺陷更少的程序。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1. 不要自我重复(DRY - Don't repeat yourself)&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
这也许是在编程开发这最最基本的一个信条，就是要告诉你不要出现重复的代码。我们很多的编程结构之所以存在，就是为了帮助我们消除重复(例如，循环语句，函数，类，等等)。一旦程序里开始有重复现象的出现(例如很长的表达式、一大堆的语句，但都是为了表达相同的概念)，你就需要对代码进行一次新的提炼，抽象。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2. 提炼原则(Abstraction Principle)&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
跟“不要自我重复原则”相关，这一原则是说“程序中任何一段具有功能性的代码在源代码文件中应该唯一的存在。”&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;3. 保持简单(KISS - Keep it simple, stupid!)&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
简单化(避免复杂)永远都应该是你的头等目标。简单的程序让你写起来容易，产生的bug更少，更容易维护修改。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;4. 不要开发你目前用不到的功能(Avoid Creating a YAGNI - You aren't going to need it)&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
除非你真正需要用到它，否则不要轻易加上那些乱七八糟用不到的功能。</description></item><item><title>NodeJS 的 Web 服务也可以监听在 sock 文件</title><link>https://yanbin.blog/nodejs-web-server-listen-on-domain-socket/</link><pubDate>Sun, 25 Jan 2015 22:30:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nodejs-web-server-listen-on-domain-socket/</guid><description>
像 NodeJS 写的 TCP 服务可以监听在某个 sock 文件(Domain Socket) 上，它的 HTTP 服务也能这么干。虽然作为 HTTP 服务连接某个 sock 文件的意义不大，所以这里只算是一个纯粹的尝试。&lt;br/&gt;
&lt;br/&gt;
TCP 服务是这样写&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;net&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;received: &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/tmp/node_tcp.sock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
连接上面那个 '/tmp/node_tcp.sock'&lt;br/&gt;
&lt;blockquote&gt;
✗ telnet /tmp/node_tcp.sock&lt;br/&gt;
Trying /tmp/node_tcp.sock...&lt;br/&gt;
Connected to (null).&lt;br/&gt;
Escape character is '^]'.&lt;br/&gt;
Hello World!&lt;br/&gt;
received: Hello World!&lt;/blockquote&gt;
准确说来本文应该是 NodeJS 的 TCP 和 HTTP 监听 Domain Socket 文件。</description></item><item><title>使用 NodeJS 框架 NW.js 编写桌面应用入门</title><link>https://yanbin.blog/nodejs-nw-js-gui-app-hello-world/</link><pubDate>Thu, 22 Jan 2015 02:51:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nodejs-nw-js-gui-app-hello-world/</guid><description>
前阵子还在琢磨于 Atom Shell 和 Node-Webkit 间如何作个选择。基于 Atom Shell 有点像是站在了 Node-Webkit 的肩膀上的原因，感觉 Atom Shell  会有些优势，所以首先体验了下 &lt;a title="使用 NodeJS 框架 Atom Shell 编写桌面应用入门" href="http://unmi.cc/nodejs-atom-shell-gui-app-hello-world/" target="_blank" rel="noopener noreferrer"&gt;使用 NodeJS 框架 Atom Shell 编写桌面应用入门&lt;/a&gt;。至于它们实现上是否合并了 Context 对我目前来说还不清楚会有什么影响。&lt;/p&gt;
&lt;br/&gt;
正当我要安下心来的时候，先前那个 Node-Webkit 最近几日来了个华丽变身，以更为简洁明快的名字 &lt;a href="http://nwjs.io/" target="_blank" rel="noopener noreferrer"&gt;NW.js&lt;/a&gt; 横空出世，并且与 NodeJS 的另一分支 &lt;a href="https://iojs.org/" target="_blank" rel="noopener noreferrer"&gt;io.js&lt;/a&gt; 更热乎。新的名字似乎有意在含糊它与 Webkit 之间的关系。对于 NW.js 的这一激动人心之举, 难免心起涟漪，顿绝不能只是路过，而况它还是出自国人之手，自豪感总有的。&lt;br/&gt;&lt;br/&gt;
于是决心一试，略玩之后发现, 特别是开发阶段可显示 toolbar -- 浏览任意 html, 前进/后退/刷新，并能用 Developer Tools 调试 UI 和 JavaScript 的特性。这才令我有些爱不释手，尚不知 Atom Shell 是否有类似的功能。&lt;br/&gt;&lt;br/&gt;
开始随我体验 NW.js 的 Hello World 程序，这里包含了外部 js 和 css 文件，仍是以 Mac OS X 平台为例，其他平台类似。&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;1. 前提条件&lt;/strong&gt;&lt;/span&gt;，安装 NW.js&lt;br/&gt;&lt;br/&gt;
$ &lt;span style="color: #800000;"&gt;npm install nw -g&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
nw 模块就安装在 /usr/local/lib/node_modules/nw 目录下了，以后可以直接用 nw 命令来测试程序。</description></item><item><title>使用 NodeJS 框架 Atom Shell 编写桌面应用入门</title><link>https://yanbin.blog/nodejs-atom-shell-gui-app-hello-world/</link><pubDate>Thu, 15 Jan 2015 00:43:31 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nodejs-atom-shell-gui-app-hello-world/</guid><description>
NodeJS 使前后台语言上得到了统一，给 “不就是做网站的” 那些人带来了福音，其基于事件驱动的处理机制更是在并发/CPU 密集型计算上大展身手。&lt;/p&gt;
&lt;br/&gt;
虽然我不怎么做 UI 程序，但对于每一种语言却爱关心它做 GUI 程序是否得心应手。像 Java, Groovy, Scala 等写桌面程序也不怎么样，JavaFX 似乎显现出苗头来，因为 JavaFX 也支持 CSS 了，但布局仍然老套。&lt;br/&gt;&lt;br/&gt;
HTML + CSS + JS 做页面布局等样式控制有种与身俱来的优越感，于是 NodeJS 的世界里便产生了  &lt;a href="https://github.com/nwjs/nw.js" target="_blank" rel="noopener"&gt;Node-Webkit&lt;/a&gt;(已更名为 NW.js) 和 &lt;a href="https://github.com/atom/atom-shell" target="_blank" rel="noopener"&gt;Atom Shell&lt;/a&gt;，它们各自的代表作有 &lt;a href="http://lighttable.com/" target="_blank" rel="noopener"&gt;LightTable&lt;/a&gt; 和堪与 Sublime 相媲美的 &lt;a href="https://atom.io/" target="_blank" rel="noopener"&gt;Atom&lt;/a&gt; 编辑器。&lt;br/&gt;&lt;br/&gt;
Node-Webkit(NW.js) 有两个context，node context 和 web context, 应用入口是一个 html 文件。而在  Atom Shell 只维持一个context，入口是一个 JS 文件，浏览器的启动控制要亲自动手。&lt;br/&gt;&lt;br/&gt;
完了基本的介绍后，回到原点，来看个 Atom Shell 的 Hello World 程序例子。</description></item><item><title>Java NIO2(AIO) 进行文件异步读取</title><link>https://yanbin.blog/java-nio2-aio-readfile/</link><pubDate>Mon, 12 Jan 2015 17:33:25 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-nio2-aio-readfile/</guid><description>
在  NodeJS 中进行异步操作很简单，而 Java 到了  7 开始才支持异步的 IO 操作。虽然之前的版本有引入非阻塞 IO，但编码中还不易体现出它的优越性。亮一下 NodeJS 用异步 IO 的例子：&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;var fs = require('fs');&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;fs.readFile('Test.scala', 'utf-8', function(err, data){&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;  if( !err ) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;    console.log(data);&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;  }&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;});&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
console.log('continue doing other thins');&lt;br/&gt;&lt;br/&gt;
执行输出是&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;continue doing other things&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;CONTENT FROM FILE Test.scala&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
对的，理想中的异步操作就是，传递回调函数来读取文件，读取完成后招待回调，且不阻塞主线程。&lt;br/&gt;&lt;br/&gt;
在 Java 8 之前，因为没有 Lambda 支持只能应用内部类的方式。JDK 提供了以下异步 Channel 来实现异步操作&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;AsynchronousFileChannel&lt;/strong&gt;</description></item><item><title>三种方式告知泛型类或泛型方法具体类型</title><link>https://yanbin.blog/three-ways-tell-generic-concrete-type/</link><pubDate>Wed, 07 Jan 2015 12:28:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/three-ways-tell-generic-concrete-type/</guid><description>
在上一篇中写道 &lt;a href="https://yanbin.blog/java-generic-auto-infer-by-declare-type/" target="_blank" rel="noopener"&gt;Java 泛型 -- 依据声明的变量类型自动推断&lt;/a&gt;，主要是说明了通过声明类型告知泛型方法具体类型，其后有个小结三种方式告知泛型类或泛型方法具体类型，在此重列如下：&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;一. 具体类型写在两尖括号中&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
1. List&amp;lt;String&amp;gt; list&lt;br /&gt;
2. new HashMap&amp;lt;String, String&amp;gt;&lt;br /&gt;
3. instance.&amp;lt;String&amp;gt;foo()   //如方法 &lt;span style="color: #800000;"&gt;&amp;lt;T&amp;gt; T foo() { return (T) obj; }&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;二. 变量声明类型指示具体类型&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
1. List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;&amp;gt;()   // 这个例子应该可以列在这里，List&amp;lt;String&amp;gt; 指示了具体类型是 String, 所以后只需要空 &amp;lt;&amp;gt;，JDK7 or later&lt;br /&gt;
2. String s = obj.foo()   //比如方法是 &lt;span style="color: #800000;"&gt;&amp;lt;T&amp;gt; T foo() { return (T) obj; }&lt;/span&gt;, 由于前面的 String s 声明，所以不必写成 String s = instance.&amp;lt;String&amp;gt;foo()&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;三. 实参类型指示具体类型&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
1. instance.foo(String.class)  //方法为 &amp;lt;T&amp;gt; T foo(Class&amp;lt;T&amp;gt; type) { return (T) obj; }, instance.foo(String.class) 返回的就是字符串值&lt;br /&gt;
2. instance.foo("abc")  //方法为 &lt;span style="color: #800000;"&gt;&amp;lt;T&amp;gt; foo(T value) { return (T) obj; }&lt;/span&gt;, instance.foo("abc") 返回的就是字符串值</description></item><item><title>Java 泛型 -- 依据声明的变量类型自动推断</title><link>https://yanbin.blog/java-generic-auto-infer-by-declare-type/</link><pubDate>Mon, 05 Jan 2015 18:25:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-generic-auto-infer-by-declare-type/</guid><description>
Java 泛型在调用方法操作时应用具体参数还是很好理解的，比如
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Map&amp;lt;String, Integer&amp;gt; map = new HashMap&amp;lt;&amp;gt;(); &lt;/span&gt; //钻石操作符是 JDK 1.7 引入的&lt;br/&gt;&lt;br/&gt;
后续的 put 操作调用的具体方法就是&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Integer put(String key, Integer value)&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
因为  put 方法的原型是&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;V put(K key, V value)&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
上面的代码我们是看到了 &amp;lt;&amp;gt;，所以知道是泛型调用。有时候并不需要 &amp;lt;&amp;gt;，但实际上也是进行的泛型调用，那就是 Java  可以依据变量声明类型来作特化调用 -- 应用具体参数类型。&lt;br/&gt;&lt;br/&gt;
例如：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;List&amp;lt;String&amp;gt; list = Collections.emptyList();  //虽不见 &amp;lt;&amp;gt;, 但同样是泛型调用，类型为 String&lt;/span&gt;</description></item><item><title>Vi 中的光标位置操作</title><link>https://yanbin.blog/vi-cursor-location-move/</link><pubDate>Tue, 11 Nov 2014 03:34:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-cursor-location-move/</guid><description>
&lt;p&gt;Vi 中的光标操作是其他众多命令，如(d-delete, c-change, yy) 的基础。它犹如汇编中的寄存器/寻址方式，成为了许多 Vi 的初学者望而却步的一主要原因，特别是在某些系统版本的 Vi 中无法正常使用方向键移动光标的情况。那么好的习惯还是请试着用 h, j, k, l 来当方向键，对于键盘控来说，把手从字母上移动到方向键上去都是在浪费时间。&lt;/p&gt;
&lt;p&gt;本来是想直接记录一下在 Vi 中怎么进行删除、修改操作的，然而却发现 Vi 的光标操作是无法逾越的一道沟。在这里，我会试着去理解每一个按键的更自然的意义，现在开始拉清单</description></item><item><title>Vim 中使用被选择文本以 Shell 命令执行</title><link>https://yanbin.blog/vim-execute-selection-as-shell/</link><pubDate>Mon, 10 Nov 2014 01:58:37 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vim-execute-selection-as-shell/</guid><description>
&lt;p&gt;在 Vi 中如果想选取文本作为 Shell 命令来执行该如何做呢？ 想法的背景是想在 Vi 中编辑 Shell 命令，主要是对参数的修改，然后直接选择执行，而不希望复制再贴到 Terminal 下看结果。方法其实很简单，用不着使用 &lt;a href="http://code.hootsuite.com/vimshell/" target="_blank" rel="noopener noreferrer"&gt;VimShell&lt;/a&gt;，关键这个插件的安装还依赖于另两个插件。方法就是：&lt;/p&gt;
&lt;p&gt;1. 可视模式选择文本，选择技巧, 按 v 逐字选择，j 从光标位置到行尾，最有用的莫过于大 V，以行为单位选择，所以按一下大 V 直接选中当前行&lt;br /&gt;&lt;br/&gt;
2. Shift+; 进到 :'&amp;lt;,'&amp;gt; 提示符&lt;br /&gt;&lt;br/&gt;
3. 输入 w !sh, 回车即可，要用 fish 就用  w !fish&lt;/p&gt;
&lt;p&gt;效果&lt;/p&gt;
&lt;a href="https://yanbin.blog/posts/2014/vim-execute-selection-as-shell/vi-selection-shell.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/vim-execute-selection-as-shell/vi-selection-shell-800x174.png"
width="800px"
/&gt;
&lt;/a&gt;
&lt;p&gt;&lt;span style="color: #808080;"&gt;原理(&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;这个表述其实不怎么准确&lt;/strong&gt;&lt;/span&gt;)：其实进行的是一个管道操作，把选择的文本内容输送给 sh 执行，相当于&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #808080;"&gt;echo "echo 'Hello world'" | sh&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;如果想把执行结果输出到文件或是剪贴板, 就可借题发挥下了，比如</description></item><item><title>Vi 使用 Vundle 管理插件执行 PluginInstall 出错</title><link>https://yanbin.blog/vi-vundle-plugininstall-errors/</link><pubDate>Mon, 10 Nov 2014 01:07:29 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-vundle-plugininstall-errors/</guid><description>
&lt;p&gt;Vi 有了 Vundle 这么一个总管(插件管理器)，安装插件着实太省事了，再也不需要自己把 *.vim 文件放在各处。只需要做一次&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #800000;"&gt;$ git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;编辑好 &lt;code&gt;~/.vimrc&lt;/code&gt; 文件，装什么每次在这个文件中加上 &lt;code&gt;Plugin 'scrooloose/nerdtree'&lt;/code&gt;，运行 &lt;code&gt;:PluginInstall&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;我第一次体验执行 &lt;code&gt;vim +PluginInstall +qall&lt;/code&gt; (和在 Vi 中执行 &lt;code&gt;:PluginInstall&lt;/code&gt; 同一效果), 总是得到类似这样的错误&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #800000;"&gt; Error detected while processing function vundle#installer#new..&amp;lt;SNR&amp;gt;46_process..vundle#installer#run..vundle#installe&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;r#install..&amp;lt;SNR&amp;gt;46_sync..&amp;lt;SNR&amp;gt;46_make_sync_command..&amp;lt;SNR&amp;gt;46_get_current_origin_url..&amp;lt;SNR&amp;gt;46_system:&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;line    1:&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;E484: Can't open file /var/folders/xz/vqv039517flcxtqzrq_jjy1xqzfzc0/T/v0rQ2fl/1&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;Error detected while processing function vundle#installer#new..&amp;lt;SNR&amp;gt;46_process..vundle#installer#run..vundle#installe&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;r#install..&amp;lt;SNR&amp;gt;46_sync:</description></item><item><title>Vi 寄存器操作，及使用系统剪贴板</title><link>https://yanbin.blog/vi-registers-system-clipboard/</link><pubDate>Sun, 09 Nov 2014 21:26:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-registers-system-clipboard/</guid><description>
使用 Vi 不得不提它强大的寄存器功能，一般操作系统只支持单例的剪贴板操作，而 Vi 借助于寄存器，相当于有几十个剪贴板可以使用。Vi 中针对内容的操作会记入到寄存器中，包括复制，粘贴，删除，以及只读的寄存器和与系统剪贴板交换内容的寄存器。本文最初目的是怎么与系统剪贴板交换内容。&lt;br/&gt;
&lt;br/&gt;
在 Vi 中使用 :reg (:registers 或 :dis/:display) 命令显示寄存器中的内容&lt;br/&gt;
&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2014/vi-registers-system-clipboard/vi-registers.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/vi-registers-system-clipboard/vi-registers-800x397.png"
width="800px"
/&gt;
&lt;/a&gt;
&lt;br/&gt;
主要寄存器&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Vi Buffer 的基本操作</title><link>https://yanbin.blog/vi-buffer-basic-operation/</link><pubDate>Sun, 09 Nov 2014 19:59:48 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-buffer-basic-operation/</guid><description>
在 vi 中打开过多个文件时，每个文件都是存放在各自的 buffer  中，可以用命令 ls (buffers 或 files，三个命令中拣你最喜欢的一个即可，ls 输入方便，buffers/files 更表意) 显示所有的 buffer。显示出来的结果如下：&lt;br/&gt;
&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2014/vi-buffer-basic-operation/vi-list-buffers.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/vi-buffer-basic-operation/vi-list-buffers-800x118.png"
width="800px"
/&gt;
&lt;/a&gt;
&lt;br/&gt;
可以看到&lt;br/&gt;
&lt;br/&gt;
每个 Buffer  的编号，分别是 1, 4, 5, 6&lt;br/&gt;
% 表示的是当前窗口中的 Buffer&lt;br/&gt;
+ 表该 Buffer  中的内容有修改&lt;br/&gt;
Buffer 中的文件名称</description></item><item><title>Scala 函数式编程学习视频 -- 例子：平方根和牛顿方法</title><link>https://yanbin.blog/scala-functional-programming-example-square-roots-with-newtons-method/</link><pubDate>Sun, 28 Sep 2014 13:09:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-example-square-roots-with-newtons-method/</guid><description>
该视频演示了如何用牛顿方法，递归，算平均值不断趋近的方法获得平方根。&lt;/p&gt;
&lt;br/&gt;
&lt;a href="https://yanbin.blog/posts/2014/scala-functional-programming-example-square-roots-with-newtons-method/newton-root-squart.png" target="_blank" rel="noreferrer"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/scala-functional-programming-example-square-roots-with-newtons-method/newton-root-squart-800x368.png"
width="600px"
/&gt;
&lt;/a&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 条件和值定义</title><link>https://yanbin.blog/scala-functional-programming-conditionals-value-definitions/</link><pubDate>Sat, 27 Sep 2014 15:50:55 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-conditionals-value-definitions/</guid><description>
Scala 的 if 和 Java 中的用法差不多，但它在 Scala 是个表达式而不仅是条语句，它是有返回值，这让我们用起来很方便。在 Java 中我们一般先声明一个变量，在 if 语句中不同条件下赋不同的值。Scala 中就一条赋值表达式&lt;br/&gt;
&lt;br/&gt;
val a = if (x == 5) 100 else if(x == 10) 200 else 300&lt;br/&gt;
&lt;br/&gt;
&lt;code&gt;val&lt;/code&gt; 关键字是用来定义值的，&lt;code&gt;value&lt;/code&gt; 的缩写，就是 Java 中的常量 public static final&lt;br/&gt;
&lt;br/&gt;
这里也同样涉及到了 call-by-name, call-by-value，到 Scala 控制台下试下&lt;br/&gt;
&lt;blockquote&gt;
scala&amp;gt; val a = (1 to 1000000000).foldLeft(0){(a,b)=&amp;gt;a+b} //定义时就要算出 foldLeft 的值，所以需要耗费些时间&lt;br/&gt;
a: Int = -243309312&lt;br/&gt;
&lt;br/&gt;
scala&amp;gt; def b = (1 to 1000000000).foldLeft(0){(a,b)=&amp;gt;a+b} //定义时很快，每次使用 b 时计算，其实就是定义一个方法，应该好理解&lt;br/&gt;
b: Int
&lt;/blockquote&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 求值策略和终止</title><link>https://yanbin.blog/scala-functional-programming-evaluation-strategies-termination/</link><pubDate>Sat, 27 Sep 2014 14:46:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-evaluation-strategies-termination/</guid><description>
本视频继续讲了 CBN(Call By Name) 和 CBV(Call By Value)，它们就是一种求值策略。一般的 Scala 是采用 call-by-value，当函数的参数是使用的 &lt;code&gt;=&amp;gt;&lt;/code&gt; 来声明参数，就是 call-by-name, =&amp;gt; 是 Lambda 用的符号，y 有点像是个函数参数。&lt;code&gt;=&amp;gt; 就是告诉这个参数会采用 call-by-name&lt;/code&gt;。看个例子：&lt;br/&gt;
&lt;blockquote&gt;def constOne(x: Int, y: =&amp;gt; Int) =1 //函数返回一个常数，x, y 分别是 CBV, CBN&lt;br/&gt;
constOne(23, (1 to 1000000000).foldLeft(0){(a,b)=&amp;gt;a+b}) //CBN, 把 foldLeft 表达式传给 constOne，希望在 constOne 中计算，所以很快&lt;br/&gt;
constOne((1 to 1000000000).foldLeft(0){(a,b)=&amp;gt;a+b}, 23) //CBV, 计算出 foldLeft 后值传给 constOne，所以很慢&lt;/blockquote&gt;
上面的 constOne(x: Int, y: =&amp;gt; Int) = 1 用 scalac 编译后再 javap -c 反编译看到的是：&lt;br/&gt;
&lt;blockquote&gt;public int constOne(int, scala.Function0&amp;lt;java.lang.Object&amp;gt;);&lt;br/&gt;
Code:&lt;br/&gt;
0: iconst_1&lt;br/&gt;
1: ireturn&lt;/blockquote&gt;</description></item><item><title>Java 解析 XML 时如何屏蔽掉 “[Fatal Error]” 的输出</title><link>https://yanbin.blog/java-parse-xml-suppress-fatal-error-output/</link><pubDate>Fri, 26 Sep 2014 01:05:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-parse-xml-suppress-fatal-error-output/</guid><description>
我们在用 Java 解析 XML，当文档不是一个合法的 XML 时，可能会收到 &lt;code&gt;[Fatal Error]&lt;/code&gt; 的控制台输出，即使把整个代码都 catch 住，仍然不能抑制住 [Fatal Error] 的信息输出。比如常见到这样的输出：&lt;br/&gt;
&lt;blockquote&gt;[Fatal Error] :1:1: Content is not allowed in prolog.&lt;/blockquote&gt;
为什么不能禁掉它呢，本来 catch 了异常对程序已经有了很好的保护，想眼不见心不烦，但还是避之不及。&lt;br/&gt;
&lt;br/&gt;
因为，因为这个 XML 解析器用 &lt;code&gt;System.error.print()&lt;/code&gt; 输出来了，当然你可以用 System.setErr(PrintStream) 重定向掉错误输出，但不现实，波及面太大。我们需要找到源头，首先交代解决方案就是覆盖掉默认的 ErrorHandler。&lt;br/&gt;
&lt;br/&gt;
看下这段 XML 解析代码：</description></item><item><title>Scala 函数式编程学习视频 -- 编程元素</title><link>https://yanbin.blog/scala-functional-programming-elements/</link><pubDate>Sun, 21 Sep 2014 10:44:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-elements/</guid><description>
进到 Scala 的 REPL (Read-Eval-Print-Loop) 有两种方式，scala 或 sbt console。函数式的任务就是求值(reduction steps)，Scala 把表达式和方法进行了统一，Scala 的变量和函数也应作一致性思考。Substitution Model, no side effects, λ-calculus -&amp;gt; immutable。 Call-by-name vs call-by-value ，是调用者还是被调用者来对参数求值的问题。val 定义值时，会做 call-by-value 操作，def 则会做 call-by-name 操作，或者可以说是静态与动态求值。&lt;br/&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/scala-functional-programming-elements/scala-call-by-name-or-value.png"
width="500px"
/&gt;
&lt;br/&gt;
要指定函数参数 y 以 Call-By-Name 传递，要这么定义 &lt;code&gt;def test(x: Int, y: =&amp;gt; Int) = x * x&lt;/code&gt;，这样就避免了 test(3, 2*4) 调用时 &lt;code&gt;2*4&lt;/code&gt; 被求值，这里把 y 转换成了函数参数 &lt;code&gt;y: =&amp;gt; Int&lt;/code&gt;。&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 编程范式</title><link>https://yanbin.blog/scala-functional-programming-paradigms/</link><pubDate>Sat, 20 Sep 2014 17:44:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-paradigms/</guid><description>
开始真正进入 Scala 编程的世界。&lt;br/&gt;
&lt;br/&gt;
范式是一个比较抽象的概念，可以理解规范和模式，此视频让我们了解编程语言范式分类。我们原来接触过指令式，过程式及一直广泛应用的面向对象式编程，面向方面不算数。Scala 让我们进入到了函数式编程，不像 Haskell, Scala 把面向对象与函数式编程很多的连接了起来。进到 Scala 的世界，一定要时常性的用函数式编程思维武装自己的头脑，并且在多核的时代尽可能的 &lt;span style="color: #000080;"&gt;&lt;strong&gt;Immutable&lt;/strong&gt;&lt;/span&gt;。&lt;br/&gt;
&lt;br/&gt;
有狭义和广义函数式编程语言之分，狭义函数式没有变量，没有赋值操作，没有循环等指令式控制结构--代表用 Pure List, XSLT 等；广义函数式更宽松，尽量用 immutable, 函数是第一等公民，像 Scala, Clojure 等。&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 总目</title><link>https://yanbin.blog/functional-programming-principles-in-scala-video-catalog/</link><pubDate>Sat, 20 Sep 2014 17:01:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/functional-programming-principles-in-scala-video-catalog/</guid><description>
&lt;p&gt;Coursera 上的 Functional Programming Principle in Scala 视频教程，合成了英文字幕，应该没有多大必要性翻译成中文字幕，本人觉得大家的英语都比我强，因为它是我永久的，历史的伤口。&lt;/p&gt;
&lt;p&gt;该课程共有七周的课时，算下来每天十至二十多分钟的时间。&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 12pt;"&gt;&lt;strong&gt;开始教程&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style="list-style-type: circle;"&gt;
&lt;li&gt;&lt;a title="Scala 函数式编程学习视频 -- 课程介绍" href="https://yanbin.blog/scala-functional-programming-course-introduction/" target="_blank"&gt;课程介绍&lt;/a&gt; (2:44)&lt;/li&gt;
&lt;li&gt;&lt;a title="Scala 函数式编程学习视频 -- Linux 下工具设置" href="https://yanbin.blog/scala-functional-programming-linux-tools/" target="_blank"&gt;Linux 下工具设置&lt;/a&gt; (12:24)&lt;/li&gt;
&lt;li&gt;&lt;a title="Scala 函数式编程学习视频 -- Mac OS X 下工具设置" href="https://yanbin.blog/scala-functional-programming-mac-os-x-tools/"&gt;Mac OS X 下工具安装&lt;/a&gt; (12:17)&lt;/li&gt;
&lt;li&gt;&lt;a title="Scala 函数式编程学习视频 -- Windows 下工具设置" href="https://yanbin.blog/scala-functional-programming-windows-tools/" target="_blank"&gt;Windows 下工具安装&lt;/a&gt; (10:37)&lt;/li&gt;
&lt;li&gt;&lt;a title="Scala 函数式编程学习视频 -- 指导: 编程作业" href="https://yanbin.blog/scala-functional-programming-assignments/" target="_blank"&gt;指导: 编程作业&lt;/a&gt; (8:47)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: 12pt;"&gt;第一周：函数和求值&lt;/span&gt;&lt;/strong&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 指导: 编程作业</title><link>https://yanbin.blog/scala-functional-programming-assignments/</link><pubDate>Sat, 20 Sep 2014 16:58:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-assignments/</guid><description>
基本上每个阶段过后都有一个不小的练习，本视频告诉我们怎么下载练习并导入到 Scala IDE，可在 SBT 下提交代码，不在 Cousera 上学习的话，也没法提交。可以从这里把那几个练习下载过来试试身手。&lt;br/&gt;
&lt;br/&gt;
有兴趣的同学可以从下面的链接下载红练习项目：&lt;br/&gt;
&lt;blockquote&gt;
&lt;a href="
/posts/2014/scala-functional-programming-assignments/example.zip
"&gt;example&lt;/a&gt;
   &lt;a href="
/posts/2014/scala-functional-programming-assignments/recfun.zip
"&gt;recfun&lt;/a&gt;
   &lt;a href="
/posts/2014/scala-functional-programming-assignments/funsets.zip
"&gt;funsets&lt;/a&gt;
  &lt;a href="
/posts/2014/scala-functional-programming-assignments/objsets.zip
"&gt;objsets&lt;/a&gt;
   &lt;a href="
/posts/2014/scala-functional-programming-assignments/patmat.zip
"&gt;patmat&lt;/a&gt;
  &lt;a href="
/posts/2014/scala-functional-programming-assignments/forcomp.zip
"&gt;forcomp&lt;/a&gt;
  &lt;a href="
/posts/2014/scala-functional-programming-assignments/streams.zip
"&gt;streams&lt;/a&gt;
&lt;/blockquote&gt;</description></item><item><title>Scala 函数式编程学习视频 -- Windows 下工具设置</title><link>https://yanbin.blog/scala-functional-programming-windows-tools/</link><pubDate>Sat, 20 Sep 2014 12:15:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-windows-tools/</guid><description>
Windows 无疑还是最大众化的个人操作系统了，很少很少人在 Windows 下用 Open-JDK。&lt;br/&gt;
&lt;br/&gt;
本视频示例了在 Windows 下 JDK, SBT 的安装，需要设置相关的 PATH 环境变量。以及下载安接 Scala SDK IDE (集成了 Scala SDK, Scala 插件的 Eclipse，这样就不用单独安装那三个玩意儿)。&lt;br/&gt;
&lt;br/&gt;
接着在 Scala IDE 中演示了 Scala HelloWorld 程序，最后就是 Scala WorkSheet，比 Scala 命令行下的 REPL 更强大的交互式工具，比 Swift 还早的 Playground.</description></item><item><title>Scala 函数式编程学习视频 -- Mac OS X 下工具设置</title><link>https://yanbin.blog/scala-functional-programming-mac-os-x-tools/</link><pubDate>Sat, 20 Sep 2014 10:33:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-mac-os-x-tools/</guid><description>
随着苹果在 iPhone, iPad 以及 Apple Watch 上的造势，Mac OS X 也越来越流行起来。我们公司最近购置了不少的 Mac 设备，本人在 Apple 平台上工作已有三年了，对 Windows 系统感觉陌生了。&lt;br/&gt;
&lt;br/&gt;
本视频示例了在 Mac OS X 下的 JDK, SBT 安装，一般 Mac OS X 随机安装了 JDK，若没有的话执行 java -version 会带领你安装 JDK。需要设置 SBT 的 PATH。以及如何下载安接 Scala SDK IDE (集成了 Scala SDK, Scala 插件的 Eclipse，这样就不用单独安装那三个玩意儿)。&lt;br/&gt;
&lt;blockquote&gt;如果想要安装更高版本的 JDK 也是要到 Oracle 去下载，还需设置新 JDK bin PATH 覆盖现有的。&lt;/blockquote&gt;
接着在 Scala IDE 中演示了 Scala HelloWorld 程序，最后就是 Scala WorkSheet，比 Scala 命令行下的 REPL 更强大的交互式工具，比 Swift 还早的 Playground.&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Scala 函数式编程学习视频 -- Linux 下工具设置</title><link>https://yanbin.blog/scala-functional-programming-linux-tools/</link><pubDate>Sat, 20 Sep 2014 10:11:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-linux-tools/</guid><description>
不清楚现在用 Linux 桌面做编程的有多少了，主要由 Ubuntu 和  RedHat 来支撑了。Mac OS X 倒是越发流行起来。&lt;br/&gt;
&lt;br/&gt;
本视频示例了在 Ubuntu 上安装 JDK, SBT -- 下载，解压，设置 PATH 的过程。以及如何下载安接 Scala SDK IDE (集成了 Scala SDK, Scala 插件的 Eclipse，这样就不用单独安装那三个玩意儿)。&lt;br/&gt;
&lt;br/&gt;
接着在 Scala IDE 中演示了 Scala HelloWorld 程序，最后就是 Scala WorkSheet，比 Scala 命令行下的 REPL 更强大的交互式工具，比 Swift 还早的 Playground.&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Scala 函数式编程学习视频 -- 课程介绍</title><link>https://yanbin.blog/scala-functional-programming-course-introduction/</link><pubDate>Sat, 20 Sep 2014 08:51:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-functional-programming-course-introduction/</guid><description>
Functional Programming Principles in Scala 课程介绍及课程安排，视频中会提些小问题，会在后面的视频中看到答案。
其后三个视频将会涉及到 Scala 环境的设置，包括 Windows, Mac OS X 和 Linux 下的工具设置。我们将接触到 SBT(Simple Build Tool)，
其实是名不符实，SBT 是我用过最不简单的构建工具。我也觉得 SBT 叫做 Scala Build Tool 才合适，可以选择 Gradle，不过本课程是用 SBT。
IDE 的话基本就是 Eclipse, IDEA 二分天下。&lt;br/&gt;
&lt;br/&gt;
挂上了英文字幕，你要在官方学完这套课程还能得到一个认证，形式而已。&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>配置 Scala 的 vim 编辑环境</title><link>https://yanbin.blog/config-vim-word-with-scala/</link><pubDate>Thu, 11 Sep 2014 21:18:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/config-vim-word-with-scala/</guid><description>
能在 Eclipse 或是 IDEA 中写 Scala 程序当然是不错的，但有时候还是有必要在更轻量级的文本编辑器中编辑，在 Unix 族系统中首推 vi。要让更好的编辑 Scala, 最主要就是让 vi 支持 Scala 的语法高亮。&lt;br/&gt;&lt;br/&gt;
有现成的 vi 插件，&lt;a href="https://github.com/scala/scala-dist/tree/master/tool-support/src/vim" target="_blank" rel="noopener noreferrer"&gt;https://github.com/scala/scala-dist/tree/master/tool-support/src/vim&lt;/a&gt;。下载下来三个目录 indent, syntax 和 ftdetect 及文件放在 &lt;code&gt;~/.vim&lt;/code&gt; 目录下.&lt;br/&gt;&lt;br/&gt;
然后到 vim 中命令 &lt;code&gt;syntax on&lt;/code&gt; 打开语法高亮就能识别 *.scala 文件高亮显示 Scala 代码了。应该把 &lt;code&gt;syn on&lt;/code&gt; 加到 vim 的初始化脚本  &lt;code&gt;~/.vimrc&lt;/code&gt; 文件中去。&lt;br/&gt;&lt;br/&gt;
更为自动的一点是有人把下载支持文件做成了一句语 bash 脚本</description></item><item><title>Play2.3 自定义模板类型 -- Java 版</title><link>https://yanbin.blog/play2-3-custom-view-template-java/</link><pubDate>Sun, 07 Sep 2014 01:05:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-3-custom-view-template-java/</guid><description>
在上一篇 &lt;a title="Play2 自定义模板类型 (Java&amp;amp;Scala)" href="https://yanbin.blog/play2-custom-template-type-java-scala/" target="_blank" rel="noopener"&gt;Play2 自定义模板类型 (Java&amp;amp;Scala)&lt;/a&gt;，是基于 Play2.2 怎么自定义 Json 模板类型，分别用 Java 和 Scala 实现。从 Play2.3 开始，模板明确了是用 &lt;a href="https://github.com/spray/twirl" target="_blank" rel="noopener"&gt;Twirl&lt;/a&gt;，所以构建文件上的配置略有不同，并且模板编译出的源文件位置也不一样，Play2.2 前生成的模板源文件在 &lt;code&gt;target/scala-2.10/src_managed/main/views&lt;/code&gt; 目录，现在是生成在 &lt;code&gt;target/twirl/main/views&lt;/code&gt; 目录。&lt;/p&gt;
&lt;br/&gt;
在 Play2.3 中仍然是默认只支持 html, txt, xml, js 四种类型的模板，见 &lt;a href="https://github.com/playframework/twirl/blob/master/sbt-twirl/src/main/scala/play/twirl/sbt/SbtTwirl.scala" target="_blank" rel="noopener"&gt;SbtTwirl&lt;/a&gt;。我们这里还是以增加 Json 模板支持为例，且只介绍用 Java 的方式。因为 Play2 尽管可以用 Java 来编写应用，但实现部份基本是 Scala，所以如果用 Scala 来进行扩展相对来说来比用 Java 简单些。&lt;br/&gt;&lt;br/&gt;
Play2.3 官方的自定义模板的文档 &lt;a href="https://www.playframework.com/documentation/2.3.x/JavaCustomTemplateFormat" target="_blank" rel="noopener"&gt;Adding support for a custom format to the template engine&lt;/a&gt; 有些出入，似乎还未来得急更新，以实操为证。&lt;br/&gt;&lt;br/&gt;
还是从构建文件开始</description></item><item><title>Scala + JUnit 怎么使用 @Rule</title><link>https://yanbin.blog/scala-junit-how-to-rule/</link><pubDate>Sat, 06 Sep 2014 20:42:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-junit-how-to-rule/</guid><description>
JUnit 是个很著名的飞行模式测试框架，即使到了 Scala 中还是免不了要用 JUnit Style 的测试方式，基于 Spec 的方式并不处处行得通，比如想要在 Scala 中使用 JMockit 框架时。&lt;br/&gt;
&lt;br/&gt;
JUnit 提供给我们有两个扩展点，&lt;a href="http://junit.org/javadoc/4.11/org/junit/runner/Runner.html" target="_blank"&gt;Runner&lt;/a&gt; 和 &lt;a href="http://junit.org/javadoc/4.11/org/junit/Rule.html" target="_blank"&gt;Rule&lt;/a&gt;, Runner 扩展点一般被各种框架劫持了，自己搞个 &lt;code&gt;@RunWith(SomeRunner.class)&lt;/code&gt; 可能让你无法在测试中应用框架。于是剩下了 Rule 是个更自由的扩展点，这里不讲述怎么定制自己的 Rule，而是怎么用它，怎么在 Scala 中用它。之前的一篇 &lt;a href="http://unmi.cc/junit-4-how-to-test-exceptions/" target="_blank"&gt;JUnit 4 如何正确测试异常&lt;/a&gt; 中使用了 &lt;code&gt;ExpectedException&lt;/code&gt; 这个 Rule。&lt;br/&gt;
&lt;br/&gt;
Rule 的要求是: Annotates fields that reference rules or methods that return a rule. A field must be public, not static, and a subtype of &lt;a title="interface in org.junit.rules" href="http://junit.org/javadoc/4.11/org/junit/rules/TestRule.html"&gt;&lt;code&gt;TestRule&lt;/code&gt;&lt;/a&gt; (preferred) or &lt;a title="interface in org.junit.rules" href="http://junit.org/javadoc/4.11/org/junit/rules/MethodRule.html"&gt;&lt;code&gt;MethodRule&lt;/code&gt;&lt;/a&gt;. A method must be public, not static, and must return a subtype of &lt;a title="interface in org.junit.rules" href="http://junit.org/javadoc/4.11/org/junit/rules/TestRule.html"&gt;&lt;code&gt;TestRule&lt;/code&gt;&lt;/a&gt; (preferred) or &lt;a title="interface in org.junit.rules" href="http://junit.org/javadoc/4.11/org/junit/rules/MethodRule.html"&gt;&lt;code&gt;MethodRule&lt;/code&gt;&lt;/a&gt;. 属性或方法必须是 public 非静态的，它们的类型或返回类型必须分别是 &lt;a href="http://junit.org/javadoc/4.11/org/junit/rules/TestRule.html" target="_blank"&gt;TestRule&lt;/a&gt; 和 &lt;a href="http://junit.org/javadoc/4.11/org/junit/rules/MethodRule.html" target="_blank"&gt;MethedRule&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
这里尝试以 Java 的方式使用另一个 Rule，&lt;a href="http://junit.org/javadoc/4.11/org/junit/rules/TestName.html" target="_blank"&gt;TestName&lt;/a&gt;, 可以得到当前测试方法的名称</description></item><item><title>Play2 自定义模板类型 (Java&amp;Scala)</title><link>https://yanbin.blog/play2-custom-template-type-java-scala/</link><pubDate>Wed, 03 Sep 2014 10:36:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-custom-template-type-java-scala/</guid><description>
Play2 默认支持的模板类型是 html, txt, xml 和 js，不在这些支持之列的模板文件即使放到 app/views 目录中，也不会被编译的。如果要支持自定义的模板类型就要些定制了，这比 Play1 复杂些。模板的定制包括在 Build.scala 或 build.sbt 中加上 templatesTypes 配置，并需创建 BufferedContent 和 Format 实现类。下面以增加 json 模板类型为例，兼顾 Scala 和 Java 的实现类，是基于 Play2.2 的，在 Play2.3 中又略有不同。&lt;br/&gt;&lt;br/&gt;
官方有相关的文档，参考：&lt;a href="http://www.playframework.com/documentation/2.2.x/ScalaCustomTemplateFormat" target="_blank" rel="noopener"&gt;Custom formats on Scala&lt;/a&gt;, &lt;a href="http://www.playframework.com/documentation/2.2.x/JavaCustomTemplateFormat" target="_blank" rel="noopener"&gt;Custom formats on Java&lt;/a&gt; 和 &lt;a href="https://github.com/playframework/playframework/blob/2.2.x/framework/src/play/src/main/scala/play/api/templates/Templates.scala" target="_blank" rel="noopener"&gt;模板定义参考&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;
在较新一些的 2.2 的 &lt;a href="https://searchcode.com/codesearch/view/2451189/#l-210" target="_blank" rel="noopener"&gt;PlaySettings&lt;/a&gt; 中，可以看到&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;templatesTypes&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;html&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;play.api.templates.HtmlFormat&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;txt&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;play.api.templates.TxtFormat&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;xml&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;play.api.templates.XmlFormat&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;js&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;play.api.templates.JavaScriptFormat&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
弄清了上面的原理后，开始我们的步骤&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;第一步：修改项目构建文件&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
在构建文件 build.sbt 或 Build.scala 中增加下面的内容作为项目的 setting</description></item><item><title>如果要给 RESTful 每种 HTTP Method 类型指示一种颜色</title><link>https://yanbin.blog/restful-colors-for-http-methods/</link><pubDate>Tue, 02 Sep 2014 00:20:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/restful-colors-for-http-methods/</guid><description>
&lt;p&gt;RESTful 时需要考虑每种 HTTP Method 操作的业务含义，再也不是 GET 使用 URL, POST 提交表单这样简单的区别。&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html" target="_blank" rel="noopener"&gt;http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html&lt;/a&gt; 定义有 GET, POST, PUT, HEAD, DELETE, PATCH, OPTIONS, TRACE, CONNECT 九种类型的 HTTP Method。&lt;/p&gt;
&lt;p&gt;关于它们可能针对的业务含义就不多说了，而是如果要对每一种操作方法赋予一个颜色应该怎么去对应呢，比如一般 GET 不会有边际效应的操作可以选择绿色，或者蓝色。幸好我也不用多想，这里参考了 &lt;a href="http://swagger.wordnik.com/" target="_blank" rel="noopener"&gt;Swagger&lt;/a&gt; 的颜色方案，它支持除 OPTIONS, TRACE 和 CONNECT 之外的颜色配置，其实我也没用过这两种方法。&lt;/p&gt;
&lt;p&gt;从 Play2 Swagger 中截图如下：</description></item><item><title>Java 的匿名类初始化块中如何访问同名参数?</title><link>https://yanbin.blog/java-anonymous-class-access-same-name-parameter/</link><pubDate>Tue, 26 Aug 2014 23:54:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-anonymous-class-access-same-name-parameter/</guid><description>
因为写过类似下面的一段代码来实始化一个匿名实例&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cc.unmi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;buildTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(){{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//希望把 buildTest() 方法参数中的 name 赋给 this.name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 仍然是 null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
看上面的 &lt;code&gt;buildTest()&lt;/code&gt; 方法中的 &lt;code&gt;this.name = name&lt;/code&gt; 希望能把方法参数 final String name 中的 name 值赋值给 this.name, 但是无效，this.name = name 是在把自己赋给自己。</description></item><item><title>代码中如何获得 Java 方法的形式参数名</title><link>https://yanbin.blog/retrieve-java-method-formal-parameter-names/</link><pubDate>Sun, 24 Aug 2014 14:07:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/retrieve-java-method-formal-parameter-names/</guid><description>
对于一个 Java 方法 foo(int id, String name); 我们如何能在代码中获得形式参数名 id 和 name 呢？&lt;/p&gt;
&lt;br/&gt;
我们知道通过反射 API &lt;code&gt;Method.getGenericParameterTypes()&lt;/code&gt; 可以获得方法的参数类型，但是对于参数名一般就是 arg0, arg1, arg2 ..., 因为 Java 编译时把形式参数名擦除了。所以对完全擦除了形式参数名的字节码应该是没办法了，但我们自己写的类还是有能力去管控的。&lt;br/&gt;&lt;br/&gt;
对于自己写的类，有两种办法获得形式参数名，分别是&lt;br/&gt;&lt;br/&gt;
1) Java8 的 -parameters 编译参数，然后用 Java8 新引入的反射 API &lt;a href="http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html" target="_blank" rel="noopener"&gt;Parameter&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
我们先在 Java8 下运行下面的代码</description></item><item><title>Mac OS X 下吾所倾爱的三个程序员编辑器</title><link>https://yanbin.blog/mac-os-x-my-favourite-3-text-editors/</link><pubDate>Tue, 22 Jul 2014 01:11:02 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-os-x-my-favourite-3-text-editors/</guid><description>
作为编程，讲救效率自然是用各种 IDE，诸如 Idea, Eclipse, NetBeans 等。但平时总也离不开一款趁手的纯文本编辑器，如阅读各种语言的代码，简单编辑。我就经常会用 Sublime 这类工具的列编辑功能，奇怪于为何 IDE 们鲜有提供列编辑的特性的。&lt;br/&gt;
&lt;br/&gt;
在 Mac 下的 TextEdit 其实是一款富文本编辑器，且未给编程带来任何便利。根据本人从开始扑向 Mac OS X 平台起到现在，选择过三款程序编辑器，它们分别是 TextMate, Sublime 和 Atom，前两为付费的，最后一个是免费的。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: 12pt;"&gt;1. &lt;a href="http://macromates.com/" target="_blank"&gt;TextMate&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
最早听说很多人喜欢用它写 Ruby 程序，也不知是不是真的. TextMate 以其卓越的 Bundle 们驰行于天下。只是最近几年来它却裹足不前了，似乎失去了前进的方向，一直稳定在 1.5.11 版本上，2.0 已经 alpha 很久了. 其间好像思考过开源，出现在了 GitHub 上 &lt;a href="https://github.com/textmate/textmate" target="_blank"&gt;https://github.com/textmate/textmate&lt;/a&gt;，少有维护。</description></item><item><title>Java 和 Scala 调用变参的方式</title><link>https://yanbin.blog/java-scala-call-varargs-method/</link><pubDate>Mon, 21 Jul 2014 00:11:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-scala-call-varargs-method/</guid><description>
Java 和 Scala 都支持变参方法, 写在最后的位置上，最基本的调用方式也都是一样的，一个个罗列过去。也可以传入数组参数，因为变参本质上就是一个数组，就是把 ... 开始位置到最后一个参数都收纳到数组中去，所以变参之所以要放在最后的位置上，且一个方法中最多只能有一个变参类型。&lt;/p&gt;
&lt;br/&gt;
这里主要是对比 Scala 对变参方法的调用，基本调用法当然是没问题的，但是在传入数组作为变参的参数列表与 Java 相对时就稍有变化了。&lt;br/&gt;&lt;br/&gt;
另外提一下，如果想传入 List 作为变参列表，而不是整体作为变参的第一个元素就是调用集合的 toArray() 方法转换成一个数组传入。&lt;br/&gt;&lt;br/&gt;
下面看 Java 中对变参方法的调用，参数列表和数组&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JavaVarArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;e&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; : &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>为 Jackson 自定义序列化对象的 JSON 格式</title><link>https://yanbin.blog/jackson-custom-serialize-json-format/</link><pubDate>Thu, 12 Jun 2014 00:58:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jackson-custom-serialize-json-format/</guid><description>
伴随着 Play1, 我们原来使用的 JSON 库是 Gson. 回忆下 Gson 是怎么自定义序列化对象的 JSON 格式，大概是这样子的&lt;/p&gt;
&lt;br/&gt;
GsonBuilder()..registerTypeHierarchyAdapter(Cat.class, new Cat());&lt;br/&gt;&lt;br/&gt;
然后 Cat 需要实现 JsonSerializer 的 serialize() 方法。&lt;br/&gt;&lt;br/&gt;
来到了 Play2 中，JSON 库变成了 Jackson，那么 Jackson 该如何为对象自定义 JSON 格式呢？&lt;br/&gt;&lt;br/&gt;
例如，默认时 Jackson 对 Map 类型输出的是一个 JSON 对象&lt;br/&gt;&lt;br/&gt;
Map("key1"-&amp;gt;"value1", "key2"-&amp;gt;"value2")     转换成 JSON 是 {"key1":"value1", "key2":"value2"}&lt;br/&gt;&lt;br/&gt;
当为适应某些客户端，对于 LinkedHashMap 类型，我们想要输出的是一个有序的 JSON 数组： [{"key1":"value1"},{"key2":"value2"}]&lt;br/&gt;&lt;br/&gt;
我们就应该自定义某些 Map 的序列化格式，实现方法有两种，addSerializer 和 @JsonSerialize，不管哪种方式都需事先具体化 JsonSerializer 类，并实现它的 serialize 抽象方法&lt;br/&gt;&lt;br/&gt;
所以我先来实现一个能序列化 Map 的 JsonArrayMapSerializer 类</description></item><item><title>sbt 中自定义的 Task</title><link>https://yanbin.blog/sbt-customize-task/</link><pubDate>Wed, 28 May 2014 01:39:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-customize-task/</guid><description>
sbt, 又是一种自动化构建工具，意为 Simple Build Tool，目前还难副其名，它管理依赖也是用的 Ivy。Scala 相关的项目一般会用它，如 Play2，所以需要来研究下它怎么自定义任务。&lt;/p&gt;
&lt;br/&gt;
sbt 项目可以用 build.sbt 或 project/Build.scala 来定义项目，build.sbt 里写些简单的 settings 表达式，而 Build.scala 就强大，可以写 val, object 和方法定义。而且单个 Build.scala 可以定义多个项目，build.sbt 只用来定义当前项目。build.sbt 和 project/Build.scala 能同时存在，它们的内容会编译到一块。还有一个全局的 build.sbt 文件 &lt;code&gt;~/.sbt/build.sbt&lt;/code&gt;，这里控制所有的项目。&lt;br/&gt;&lt;br/&gt;
所以我们分别看在 project/Build.scala 和 build.sbt 中如何定义自己的 task&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;首先看 project/Build.scala&lt;/strong&gt;&lt;/span&gt;，分别由两行完成&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;mytask&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskKey&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;This is my customized task&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mytask&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Execute customized task&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
完整的一个 project/Build.scala 如下</description></item><item><title>logback.xml 给变量指定默认值</title><link>https://yanbin.blog/logback-xml-variable-with-default-value/</link><pubDate>Mon, 19 May 2014 20:15:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/logback-xml-variable-with-default-value/</guid><description>
随着通用日志组件转入 Slf4j，logback 也变成了默认的日志实现，像 log4j 一样，logback.xml 中也可以使用系统属性或环境变量，如 ${catalina.home}。在 log4j.properties 中，如果变量在系统属性和环境变量中找不到的话默认为 "" 空字符串，而到了 logback.xml 中如果某个变量找不到默认就是 "变量名_IS_UNDEFINED" 了，这样就比较奇怪了。&lt;br/&gt;&lt;br/&gt;
那如何在没有配置 catalina.home 系统属性或环境变量时设置一个默认值呢，例如，没有 catalina.home  时取值为 "."，这时值日志文件的路径就是&lt;br/&gt;&lt;br/&gt;
./logs/unmi-%d{yyyy-MM-dd}.log&lt;br/&gt;&lt;br/&gt;
了，也就是生成中当前目录下的 logs 子目录中，这样算是很友好的方式。下面就来分析怎么一步步找到答案的，没耐心或是只求结果的话，直接滚屏到下面就行。&lt;br/&gt;&lt;br/&gt;
我们的问题是，对于下面那样的 logback.xml 配置：</description></item><item><title>Mac OS X 下安装使用 Docker</title><link>https://yanbin.blog/mac-os-x-experience-docker/</link><pubDate>Fri, 16 May 2014 03:30:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-os-x-experience-docker/</guid><description>
&lt;span style="color: #ff0000; font-size: 12pt;"&gt;前注: 欢迎进到本页, 本篇写时较早, 而 Mac OS X 下 Docker 早不建议用 boot2docker 了, 取而代之的是 docker-machine. 请点击链接 &lt;a style="color: #ff0000;" href="http://unmi.cc/mac-os-x-experience-docker-new-2/"&gt;Mac OS X 下安装使用 Docker (新) &lt;/a&gt;查看最新安装过程与体验&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
云主机可以选择操作系统镜像快速创建主机，这比虚拟机更便捷了，我们本地也可以这么做了，因为有了 &lt;a href="https://www.docker.io/" target="_blank" rel="noopener"&gt;Docker&lt;/a&gt; 这个东西。它依赖于 LXC(Linux Container)，能从网络上获得配置好的 Linux 镜像，非常容易在隔离的系统中运行自己的应用。也因为它的底层核心是个 LXC，所以在 Mac OS X 下需要在 VirtualBox 中跑一个精小的 LXC(这里是一个 &lt;a href="http://tinycorelinux.net"&gt;Tiny Core Linux&lt;/a&gt;，完全在内存中运行，个头只约 24MB，启动时间小于 5 秒的 &lt;a href="https://github.com/boot2docker/boot2docker" target="_blank" rel="noopener"&gt;boot2docker&lt;/a&gt;) 虚拟机，构建在 VirtualBox 中。以后的通信过程就是 docker --&amp;gt; boot2docker --&amp;gt; container，端口或磁盘映射也是遵照这一关系。&lt;br/&gt;&lt;br/&gt;
理解了上面的关系，开始说说 Docker 安装过程&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. 安装 &lt;a href="https://www.virtualbox.org/" target="_blank" rel="noopener"&gt;VirtualBox&lt;/a&gt;&lt;/strong&gt;, 不多讲, 因要在它当中创建一个 boot2docker-vm 虚拟机&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;2. 安装 boot2docker&lt;/strong&gt;&lt;br/&gt;
&lt;blockquote&gt;brew install boot2docker&lt;/blockquote&gt;
&lt;span style="color: #c0c0c0;"&gt;你也可以手工安装</description></item><item><title>Play2 中使用自定义的路由器文件 routes</title><link>https://yanbin.blog/play2-use-customized-routes-file/</link><pubDate>Thu, 08 May 2014 01:32:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-use-customized-routes-file/</guid><description>
用过 PlayFramework 的都知道默认的路由器文件是 conf/routes，Play2 可以定义自己的 routes 文件。在默认的 application.conf 中有这么一段注释&lt;/p&gt;
&lt;br/&gt;
&lt;blockquote&gt;
# Router&lt;br /&gt;
# ~~~~~&lt;br /&gt;
# Define the Router object to use for this application.&lt;br /&gt;
# This router will be looked up first when the application is starting up,&lt;br /&gt;
# so make sure this is the entry point.&lt;br /&gt;
# Furthermore, it's assumed your route file is named properly.&lt;br /&gt;
# So for an application router like `conf/my.application.Router`,&lt;br /&gt;
# you may need to define a router file `my.application.routes`.&lt;br /&gt;
# Default to Routes in the root package (and `conf/routes`)&lt;br /&gt;
# application.router=my.application.Routes
&lt;/blockquote&gt;
&lt;br/&gt;
也就是通过 application.router 可以定义自己的 routes 文件。上面的解释很容易把人搞混，问题在于何处是文件路径，何处是类路径，至少写着的 'conf/my.applicaton.Router 就是在混淆视听。对于上面的解释要明白下面几点</description></item><item><title>Play1 直接调用 Action 方法，不作 302 跳转</title><link>https://yanbin.blog/playframework-1-invoke-action-directly-no-302/</link><pubDate>Fri, 02 May 2014 02:12:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/playframework-1-invoke-action-directly-no-302/</guid><description>
用过 PlayFramework 的同学们应该都知道，Action 方法间的调用是进行的  302 重定向操作。&lt;br/&gt;&lt;br/&gt;
简单例子说明一下，当基于下面的 r1, r2 路由配置时，如果 Application.f1() 方法中调用了 f2() 方法，实际运作是 f1() 在调用 f2() 时，会先反向出 f2() 方法对应的路由  GET /r2，然后向 /r2 发出的一个 302 跳转.&lt;br/&gt;&lt;br/&gt;
上面也算是绕个弯形成了对 f2() 方法的调用，这也是非常合理，在 Action 中很容易理解的。&lt;br/&gt;
&lt;blockquote&gt;
GET /r1 Application.f1&lt;br /&gt;
GET /r2 Application.f2&lt;br /&gt;
GET /r3 Application.f3
&lt;/blockquote&gt;
&lt;br/&gt;
为什么说会反向出 f2() 方法对应的路由，可以反证一下。&lt;br/&gt;&lt;br/&gt;
例如说在 f1() 中调用了一个&lt;code&gt; public static void f4()&lt;/code&gt; 方法，但是 f4() 并未出现在 routes 配置中，也就是 f4() 没有对应的路由配置，我们将会看到这样一个异常</description></item><item><title>Mac OS X 下开发 Android 程序时使用 USB 连真机调试</title><link>https://yanbin.blog/mac-osx-dev-android-usb/</link><pubDate>Wed, 30 Apr 2014 23:47:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-osx-dev-android-usb/</guid><description>
本人很早一篇 &lt;a title="Mac OS X 下开发 Android 程序时使用 WiFi ADB 连真机调试" href="https://yanbin.blog/mac-osx-dev-android-wifi-adb" target="_blank"&gt;Mac OS X 下开发 Android 程序时使用 WiFi ADB 连真机调试&lt;/a&gt;，手机端需要连接  WiFi, Root 权限开启 ADB 端口，电脑上执行 adb connect 命令来让 Mac 连接 Android 真机进行调试，其实用不着那么麻烦。&lt;br/&gt;
&lt;br/&gt;
即使是现在新版的 Andorid SDK Manager 里的 &lt;code&gt;Google USB Drive  Not compatible with Mac OS X&lt;/code&gt;，并不是说不支持 USB 连接 Android 手机，而是在 Mac 下根本用不着安装这个驱动，有些知名手机直接 USB 连上 Mac 就能被 DDMS 发现。&lt;br/&gt;
&lt;br/&gt;
而我在用一个小米的手机在连机测试，不被 DDMS 识别，解决办法是把小米手机的 Vendor ID 加到文件 &lt;code&gt;~/.android/adb_usb.ini&lt;/code&gt; 中去就行。&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://developer.android.com/tools/device.html" target="_blank"&gt;Using Hardware Devices&lt;/a&gt; 里有很多手机品牌商的 Vendor ID，没有小米的，不急，手机连上 Mac 从设备信息里查相应的 USB 连接端口上能找到。</description></item><item><title>用 grunt-contrib-connect 构建实时预览开发环境</title><link>https://yanbin.blog/grunt-contrib-connect-build-livereload-dev-env/</link><pubDate>Wed, 30 Apr 2014 02:16:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/grunt-contrib-connect-build-livereload-dev-env/</guid><description>
本文基本是参照着 &lt;a href="http://nya.io/Node.js/grunt-livereload-for-realtime-preview/" target="_blank" rel="noopener"&gt;用Grunt与livereload构建实时预览的开发环境&lt;/a&gt; 实操了一遍，直接实现能实时预览文件列表，内容页面。不用刷新页面了，这比以前开发网页程序都简单。&lt;/p&gt;
&lt;br/&gt;
这里要用到的 Grunt 插件有&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://github.com/gruntjs/grunt-contrib-connect" target="_blank" rel="noopener"&gt;grunt-contrib-connect&lt;/a&gt;, 用来充当一个静态文件服务器，本身集成了 livereload 功能&lt;br /&gt;
&lt;a href="https://github.com/gruntjs/grunt-contrib-watch" target="_blank" rel="noopener"&gt;grunt-contrib-watch&lt;/a&gt;, 监视文件的改变，然后执行指定任务，这里用来刷新 &lt;code&gt;grunt serve&lt;/code&gt; 打开的页面&lt;br/&gt;&lt;br/&gt;
以下是个辅助的插件&lt;br/&gt;&lt;br/&gt;
&lt;a href="https://github.com/sindresorhus/load-grunt-tasks" target="_blank" rel="noopener"&gt;load-grunt-tasks&lt;/a&gt;, 省事的插件，有了这个可以不用写一堆的 &lt;code&gt;grunt.loadNpmTasks('xxx')&lt;/code&gt;，再多的任务只需要写一个 &lt;code&gt;require('load-grunt-tasks')(grunt)&lt;/code&gt;。&lt;br/&gt;&lt;br/&gt;
参考的文档中提到了 &lt;a href="https://www.npmjs.org/package/time-grunt" target="_blank" rel="noopener"&gt;time-grunt&lt;/a&gt; 插件，可用来显示每一个任务所花的时间和百分比，由于此示例中基本就 watch 任务占了百分百的时间。&lt;br/&gt;&lt;br/&gt;
下面是 Grunt 项目的两个基本的文件</description></item><item><title>Chrome 扩展开发，定制多功能框(omnibox)</title><link>https://yanbin.blog/chrome-extensions-development-omnibox/</link><pubDate>Tue, 29 Apr 2014 02:05:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/chrome-extensions-development-omnibox/</guid><description>
原来一直想着用完整的 Web 应用或 GUI 来做些增强效率的工具集，其实我们每天打交道的浏览器，进行下扩展就能好好的利用。粗略对比了下 Chrome 和 Firefox 的插件系统，Chrome 的插件开发应该要比 Firefox 的简单，无需引用什么新的概念，像 XUL。&lt;/p&gt;
&lt;br/&gt;
例如，想实现一下 omnibox，即 Chrome 的全功能框(地址栏)，想要地址栏进入自定的 Unmi 模式，输入关键字让 google 去本站查询，或是输入 tag, category 直接进入本站的相关 tag, category 日志列表，在当前浏览器的 Tab 中打开目的页面。&lt;br/&gt;&lt;br/&gt;
比如在地址栏上输入关键字 "u&amp;gt;", 然后按空格或 Tab 键就进入到 Unmi 模式，如图&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/chrome-extensions-development-omnibox/unmi_omnibox.png"
width="500px"
/&gt;
下面是完整开发步骤</description></item><item><title>AngularJS HelloWorld</title><link>https://yanbin.blog/angularjs-helloworld/</link><pubDate>Thu, 24 Apr 2014 01:53:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/angularjs-helloworld/</guid><description>
经历过一个使用 &lt;a href="http://backbonejs.org/" target="_blank" rel="noopener"&gt;Backbone.js&lt;/a&gt; 的项目，把 Backbone.js 也弄得七七八八了，关键是它比较容易理解。最近接触一个使用了 &lt;a href="https://angularjs.org/" target="_blank" rel="noopener"&gt;AngularJS&lt;/a&gt; 的项目，乍一看那可是两头雾水，它是一个 MVW(Model - View - Whatever) 型的 Javascript 框架。它与 Backbone.js 相比门槛就要高些了，我的第一感觉。一般都说 MVC，AngularJS 引入个 W(Whatever) 概念来意味就更深远了。&lt;/p&gt;
&lt;br/&gt;
AngularJS 抛却了传统的 Javascript 的操作理念，不再是 Dom 上绑定事件，触发事件，操作 Dom 这样的思维。它在 HTML 代码中的表现就是那些 ng-* 属性，以及 {{...}} 括起来的 AngularJS 表达式。一个简单的操作甚至不需要写一行 Javascript 代码，在 AngularJS 的首页就有几个基本的例子，我体验了类似第一个例子，大概是这样子的</description></item><item><title>Java 设计之初为何就不让用 == 比较两字符串呢？</title><link>https://yanbin.blog/java-why-cannot-compare-strings-with/</link><pubDate>Thu, 24 Apr 2014 00:14:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-why-cannot-compare-strings-with/</guid><description>
Java 的字符串值比较不能用 == 号这个设计不知道最初是怎么考虑的，它最大的贡献无疑是滋生了一个长久未衰的面试题，加之连 Code Review 都可能被忽略掉的 Bug。本来两个字符串用等号相比较是最自然而然的做法，然而它却是要迫使我们相信想当然很可能是错误的那样一个道理。&lt;br/&gt;
&lt;br/&gt;
我一直认为 Java 的字符串比较值不能用 == 而必须用 equals() 方法是个不恰当的设计，这从其他种种语言的现实做法(人家都用 == 比较值)就知道。&lt;br/&gt;
&lt;br/&gt;
猜想一下 Java 为何要这样对待字符串，可能 Java 又想类型全部对象化，同时考虑到方便性，仍然保留了 int, short, boolean 等原始类型，它们是可以用 == 比较值，其他真正的对象类型用 equals() 方法比较也是无可厚非的。这时候夹缝中的字符串却被为难到了，它那么的常用，还常以字面量的面目出现，它更该是个基本类型，而实为对象类型，因此不被认可用 == 直接比较值， 而选择了用 equals() 方法来比较字符串值。&lt;br/&gt;
&lt;br/&gt;
而另一方面，由于字符串是多例的，所以有些情况下又更令人迷惑，比如下面的种种情况</description></item><item><title>JMockit 如何 mock 异常</title><link>https://yanbin.blog/jmockit-how-to-mock-excepction/</link><pubDate>Tue, 22 Apr 2014 23:51:40 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-how-to-mock-excepction/</guid><description>
2014-07-26 修改本文&lt;/p&gt;
&lt;br/&gt;
后来发现用 JMockit 来 mock 异常根本没有之前文中描述的那么复杂，其实还是在那个 result 上，给它赋个异常实例就轻而易举的解决了，只需如此&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expectations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NetworkException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;No IPAddress &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
原文可不用看下去了。&lt;br/&gt;
&lt;hr /&gt;&lt;br/&gt;
做过几篇 JMockit 使用 Expectations 来 Mock 方法，私有方法，私有属性的的日志，今天工作上突然有个需求是要 Mock 异常。现在再也不能为了跑个单元测试而去拔下网线了，也不该人为的去制造其他混乱来测试。开始是想能不能用 Expectations 来 Mock 异常，尚未发现相关的属性可以设置，没有类似 result 那样的属性，比如想像中有个 exception/throwable 属性：</description></item><item><title>初始化 Gradle 工程目录</title><link>https://yanbin.blog/initialize-gradle-project-layout/</link><pubDate>Mon, 21 Apr 2014 23:37:55 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/initialize-gradle-project-layout/</guid><description>
最近重新在 Eclipse 中打开旧的 Maven 项目，总有些什么错误，备受折磨。期间试手了 Ant+Ivy, 现今试用了下 Gradle，感觉不错，它应该才是我真想要的，Maven 差不多该扔到一边去了。&lt;/p&gt;
&lt;br/&gt;
Gradle 像 sbt 构建工具一样，也不会帮我们生成默认的目录布局，这些要自己来做。这不是什么麻烦事，因为只需做一次，可预先写好初始化脚本，幸好本人已习惯 shell 操作。&lt;br/&gt;&lt;br/&gt;
假设我们有这样的 &lt;code&gt;build.gradle&lt;/code&gt; 文件&lt;br/&gt;
&lt;blockquote&gt;
apply plugin: 'java'&lt;br /&gt;
apply plugin: 'war'    //有这行时我们需要创建 src/main/webapp 目录&lt;br /&gt;
apply plugin: 'eclipse'
&lt;/blockquote&gt;
我们执行 &lt;code&gt;gradle eclipse&lt;/code&gt; 后是不会生成&lt;code&gt;src&lt;/code&gt; 目录的，工程下的源文件目录是需要自己来生成的。Gradle 参考 Maven 的标准目录布局，即&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;src
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; ├── main
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; │   ├── java
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; │   ├── resources
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; │   └── webapp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; └── test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; ├── java
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; └── resources&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
我们有两种办法初始化目录布局</description></item><item><title>基于 Groovy 的自动化构建工具 Gradle 入门</title><link>https://yanbin.blog/groovy-based-auto-builder-gradle-get-started/</link><pubDate>Sun, 20 Apr 2014 23:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/groovy-based-auto-builder-gradle-get-started/</guid><description>
本人工作之初没有使用自动化构建，后来敏捷了，开始使用 Ant - 完全面向过程的定义步骤，不进行依赖管理。再发展到 Maven，面向对象的方式管理工程，有了依赖的管理，JAR 包统一从中央仓库获得，保存在本地库。同时产生了 Ivy + Ant，让 Ant 也能管理依赖，不过 JAR 包依赖仓库也是用 Maven 的。&lt;/p&gt;
&lt;br/&gt;
前面都是用 XML 来配置的，虽说 XML 的标签描述性也很强，但阅读性还是有些牵强，并且 Maven 和 Ant+Ivy 都需要 IDE 的支持，比如在 Eclipse 中有专门 &lt;strong&gt;Maven Dependencies&lt;/strong&gt; 或 &lt;strong&gt;ivy.xml [*]&lt;/strong&gt; 中列出项目的依赖包，这里常会出现些问题。&lt;br/&gt;&lt;br/&gt;
而我今天想尝试使用的是基于 Groovy 的 Gradle，只要一个 build.gradle 文件，实质是一个 groovy 文件，由于可以 DSL，所以 Gradle 比 XML 更写意，更主要的是 Gradle 项目不需要 IDE 的特别支持，&lt;a href="#maven_ivy"&gt;见后面的图&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
先要安装 Gradle，在我的 Mac 系统下我是用 &lt;code&gt;brew install gradle&lt;/code&gt; 安装的，不同平台下可能用 yum, apt-get，安装后执行 &lt;code&gt;gradle&lt;/code&gt; 命令验证。&lt;br/&gt;&lt;br/&gt;
接着按以下步骤：&lt;br/&gt;&lt;br/&gt;
&lt;span style="background-color: #ffffff; color: #0000ff; font-size: 12pt;"&gt;&lt;strong&gt;1. 创建项目目录&lt;/strong&gt;&lt;/span&gt; testgradle，并进入到新创建的目录&lt;br/&gt;
&lt;blockquote&gt;
mkdir testgradle&lt;br /&gt;
cd testgradle</description></item><item><title>Grunt 运行 Jasmine 测试: grunt-contrib-jasmine-实例</title><link>https://yanbin.blog/grunt-run-jasmine-specs-with-grunt-contrib-jasmine-example/</link><pubDate>Sat, 19 Apr 2014 03:46:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/grunt-run-jasmine-specs-with-grunt-contrib-jasmine-example/</guid><description>
&lt;p&gt;个人感受，TDD 是一种很好的开发方式，而如今一惯不怎么写测试用例的 JavaScript 代码也有了许多优秀的测试框架，如 Jasmine，Qunit，JsTestDriver，JSUnit，Mocha等。Jasmine(茉莉花--该不会是敏感词吧) 是 BDD，行为驱动开发的测试，典型的例子就是 given-when-then 这样的描述，即基于 Spec 的测试。&lt;/p&gt;
&lt;p&gt;Grunt 是 Node.js 生态圈中的构建工具，正火着呢。一说到 Node.js，初接触它的人初印象大概是 &lt;code&gt;require('http').createServer(...).listen(1337, 'localhost')&lt;/code&gt;,  这样的例子反而让人无所适从，其实 Node.js 的能耐大着呢。&lt;/p&gt;
&lt;p&gt;关于 Grunt 的简单使用可以参考之前的一篇：&lt;a href="https://yanbin.blog/grunt-nodejs-based-build-tool/" target="_blank"&gt;Grunt - 基于 node.js 构建工具之初体验&lt;/a&gt;，这篇足够有助于我们对 Grunt 的大局的理解。&lt;/p&gt;
&lt;p&gt;Grunt 能与 Jasmine 的结合有赖于 &lt;a href="https://github.com/gruntjs/grunt-contrib-jasmine" target="_blank"&gt;grunt-contrib-jasmine&lt;/a&gt;
这条红线，再加上 &lt;a href="http://phantomjs.org" target="_blank"&gt;PhantomJS&lt;/a&gt;, 使可以完全脱离浏览器而在控制台下跟测试用例了。
最快速的体验 grunt-contrib-jasmine, 可从 github 上克隆一个例子项目来，当然假设你已安装好 Node.js, Grunt, PhantomJS 这些工具</description></item><item><title>WordPress 升级到了 3.9，语法高亮按钮插件更新</title><link>https://yanbin.blog/upgrade-wordpress-3-9-with-syntax-button/</link><pubDate>Thu, 17 Apr 2014 02:41:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/upgrade-wordpress-3-9-with-syntax-button/</guid><description>
WordPress 终于又迎来一次次版本号的升级，带来几个便捷之处，如直接拖拽图片进编辑器中，随意缩放旋转。同时把 TinyMCE 从 4.0.21.1 版，这直接造成了我之前的那个添加代码的自定义工具按钮歇菜了，弹不出选项来。见 &lt;a title="语法加亮插件 SyntaxHighlighter 的好伴侣，轻便的 TinyMCE 工具按钮" href="http://unmi.cc/unmi-syntax-button/" target="_blank" rel="noopener"&gt;语法加亮插件 SyntaxHighlighter 的好伴侣，轻便的 TinyMCE 工具按钮&lt;/a&gt; 中描述的那个自定义插件。所以要把该插件升级下，使之兼容 TinyMCE 4，TinyMCE 从 3 到 4 的改变还真不小，界面上看到多了行菜单条，它的 API 变得大了去。&lt;/p&gt;
&lt;br/&gt;
本文参考了：&lt;br/&gt;&lt;br/&gt;
1. &lt;a href="http://www.tinymce.com/wiki.php/API3:method.tinymce.Plugin.createControl" target="_blank" rel="noopener"&gt;http://www.tinymce.com/wiki.php/API3:method.tinymce.Plugin.createControl&lt;/a&gt;&lt;br /&gt;
2. &lt;a href="http://www.tinymce.com/wiki.php/api4:class.tinymce.Plugin" target="_blank" rel="noopener"&gt;http://www.tinymce.com/wiki.php/api4:class.tinymce.Plugin&lt;/a&gt;&lt;br /&gt;
3. &lt;a href="http://www.tinymce.com/tryit/menubutton.php" target="_blank" rel="noopener"&gt;http://www.tinymce.com/tryit/menubutton.php&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
现在改成了一个带三角的 MenuButton，如图：</description></item><item><title>PlayFramework 1 自定义标签 -- FastTags</title><link>https://yanbin.blog/playframework-1-custom-tag-fast-tags/</link><pubDate>Mon, 14 Apr 2014 22:10:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/playframework-1-custom-tag-fast-tags/</guid><description>
最早是用 HTML 来自定义标签，现在觉得 HTML 写有关逻辑的代码就有点不伦不类了，HTML 里着重是显示代码。前有一篇 
&lt;a title="PlayFramework 1 自定义标签 -- Java 对象扩展" href="https://yanbin.blog/playframework-1-custom-tag-java-object-extensions/" target="_blank" rel="noopener"&gt;
PlayFramework 1 模板应用 -- Java 对象扩展
&lt;/a&gt; 学习了对 Java 对象扩展的方式，如果不是基于已有对象类型进行方法扩展来进行调用，就可以自定义 FastTags 的方式。&lt;/p&gt;
&lt;br/&gt;
Java 对象扩展的使用是 ${obj.abc()}, FastTags 标签是 #{abc}...${/abc}。&lt;br/&gt;&lt;br/&gt;
FastTags 标签类继承自 &lt;code&gt;play.templates.FastTags&lt;/code&gt;，标签对应方法的原型是&lt;br/&gt;
&lt;blockquote&gt;
public static void _tagName(Map&amp;lt;?, ?&amp;gt; args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine)
&lt;/blockquote&gt;
&lt;br/&gt;
这些都是得益约定优于配置，下面来几个例子，分别说明默认参数，命名参数，及多参数，标签体的处理。</description></item><item><title>PlayFramework 1 模板应用 -- Java 对象扩展</title><link>https://yanbin.blog/playframework-1-template-java-object-extensions/</link><pubDate>Mon, 14 Apr 2014 19:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/playframework-1-template-java-object-extensions/</guid><description>
涉及到页面显示的问题，用自定义标签总能够能事不少，即使是最原始的自定义 JSP 标签也有人乐此不疲，进化到  Play 中的自定义标签数得上很轻量级的实现，简单的只需要一小页文档 &lt;a href="http://www.playframework.com/documentation/1.2.7/templates" target="_blank" rel="noopener"&gt;The template engine &lt;/a&gt;就足矣。&lt;/p&gt;
&lt;br/&gt;
概括起来 Play1 支持三种方式自定义标签: &lt;code&gt;HTML 文件方式&lt;/code&gt;, &lt;code&gt;自定义 FastTags&lt;/code&gt;, &lt;code&gt;Java 对象扩展方法&lt;/code&gt;. 前二者为面向过程的方式，第三种方式为面向对象的方式，是在往某种数据类型追加一个方法。&lt;br/&gt;&lt;br/&gt;
确切的说针对 Java 对象的方法的扩展并不能称之为自定义标签。&lt;br/&gt;&lt;br/&gt;
一个这样的场景，股票价格变化值要显示在页面上，根据正，负，零，再加上不同的区域四种条件分别显示为不同的颜色，比如美国分别为绿色，红色和黑色(注: 美国股市显示的颜色正好与中国相反)。假设 priceChange 是 Integer 类型，我们就可以定义对 Integer 对象的扩展，所在类必须继承自 JavaExtensions 类：</description></item><item><title>Grunt - 基于 node.js 构建工具之初体验</title><link>https://yanbin.blog/grunt-nodejs-based-build-tool/</link><pubDate>Mon, 31 Mar 2014 03:04:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/grunt-nodejs-based-build-tool/</guid><description>
自动化构建工具真是缭乱纷呈，最早的 make, nmake, 到 Ant, NAnt, 进化到面向对象的 Maven, Gradle，由 Scala 而起的 SBT, SBuild，再就是今天要说的由 node.js 建立起的生态
&lt;a href="http://gruntjs.com" target="_blank"&gt;Grunt&lt;/a&gt;。
Grunt 可以做什么呢，理论上只要有相应的&lt;a href="http://gruntjs.com/plugins" target="_blank"&gt;插件&lt;/a&gt;支持，它可以应对任何构建任务，但它更为有力的表现是在前端方面。&lt;br/&gt;
&lt;br/&gt;
Grunt 是运行在 node.js 环境，由 npm 来安装，所以首先要安装 node.js, npm, 它们俩的安装不多说，我这里的版本分别是：&lt;br/&gt;
&lt;blockquote&gt;
unmi@localhost ~&amp;gt; node --version&lt;br/&gt;
v0.10.24&lt;br/&gt;
unmi@localhost ~&amp;gt; npm --version&lt;br/&gt;
1.3.21
&lt;/blockquote&gt;
运行 Grunt 还要安装 grunt-cli, 使用命令 npm install -g grunt-cli，安装后我机器上 Grunt 的版本是</description></item><item><title>Apache 配置 SSL(HTTPS) 并整合 Tomcat</title><link>https://yanbin.blog/apache-ssl-tomcat/</link><pubDate>Sun, 30 Mar 2014 00:42:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-ssl-tomcat/</guid><description>
我们在 Tomcat 中可以开启 SSL，用 HTTPS 来访问，见前一篇
&lt;a title="快速启用 Tomcat 的 HTTPS 协议访问" href="https://yanbin.blog/enable-tomcat-https-protocol/" target="_blank"&gt;快速启用 Tomcat 的 HTTPS 协议访问&lt;/a&gt;，
不过更接近实际的应用是 Tomcat 只担当 Servlet 容器，HTTPS 协议部份，甚至是静态页面是交给 Apache 的处理，Apache 与 Tomcat 之间有一个通道。
当然前端用 F5 那类负载均衡设备就另当别论了。&lt;br/&gt;
&lt;br/&gt;
这里实践一下怎么开启 Apache 的 HTTPS，并与 Tomcat 进行整合的操作。平台是 Mac OS X, Apache2, Tomat8，其他平台或不同版本的应用软件配置类似。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;第一步: 生成自签署证书&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
安全加密的东西都得证书，我们需要用到 openssl，没有就先安装它，命令是：&lt;br/&gt;
&lt;blockquote&gt;openssl req -new -x509 -days 365 -nodes -out server.crt -keyout server.key&lt;/blockquote&gt;
上面命令可以指定生成 server.crt 和 server.key 文件的目录，默认产生在当前目录下，假设这两个文件生成在&amp;nbsp;&lt;code&gt;/etc/apache2&lt;/code&gt; 目录下。</description></item><item><title>PlayFramework 1 输出所有 WS.url() 访问的 URL</title><link>https://yanbin.blog/playframework-1-trace-all-web-call-urls/</link><pubDate>Sat, 29 Mar 2014 01:57:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/playframework-1-trace-all-web-call-urls/</guid><description>
记录下这个其实没有多大的意义，新入手 Play 框架的应该直接就是用版本 2 了，只因我们还要系统工作在 Play1 下，所以记下来，估计在 Play2 中已无借鉴作用了。&lt;/p&gt;
&lt;br/&gt;
我们在 Play1 中访问外部 WebService 资源都是用 &lt;code&gt;WS.url(url)&lt;/code&gt;，然后调用它的 get(), post(), delete() 等相对应的 HTTP 请求方法。我们可以系统中所有通过 WS.url() 访问的 URL, 需找到切面，Play 是在哪里为访问的 URL 创建请求的。&lt;br/&gt;&lt;br/&gt;
看 WS 类的实现，它是一个 PlayPlugin 插件，其中定义了&lt;br/&gt;
&lt;blockquote&gt;
private static WSImpl wsImpl = null;
&lt;/blockquote&gt;
来看 Play 是如何获得 WSImple 实现的，WS 插件的初始化方法中：</description></item><item><title>快速启用 Tomcat 的 HTTPS 协议访问</title><link>https://yanbin.blog/enable-tomcat-https-protocol/</link><pubDate>Sat, 29 Mar 2014 00:59:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/enable-tomcat-https-protocol/</guid><description>
有时候安全考虑会要开启 Tomcat 的 https 协议访问，最快速的配置，两步&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;1. 创建 keystore 文件&lt;/strong&gt;&lt;br/&gt;
执行 JDK 带的命令&lt;br/&gt;
&lt;blockquote&gt;
keytool -genkey -alias tomcat -keyalg RSA
&lt;/blockquote&gt;
按命令提示各个信息即可，最后在用户主目录下创建了一个 .keystore 文件&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;2. 配置 Tomcat 使用 keystore 文件&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
打开 server.xml 找到下面被注释的这段&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;Connector port=&amp;#34;8443&amp;#34; protocol=&amp;#34;org.apache.coyote.http11.Http11NioProtocol&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; maxThreads=&amp;#34;150&amp;#34; SSLEnabled=&amp;#34;true&amp;#34; scheme=&amp;#34;https&amp;#34; secure=&amp;#34;true&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; clientAuth=&amp;#34;false&amp;#34; sslProtocol=&amp;#34;TLS&amp;#34; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;--&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>JDK8 的 Lambda 表达式 -- 理解新式集合操作</title><link>https://yanbin.blog/jdk8-lambda-collection-operations/</link><pubDate>Tue, 25 Mar 2014 03:11:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-collection-operations/</guid><description>
有了前面的 SAM，Lambda 表达式，以及默认接口方法作铺垫后，我们可以去很好的去理解 Java8 用 Lambda  表达式操作集合的基本原理了。此篇我们想要化解的示例代码如下：&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cc.unmi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Arrays&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Collection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.stream.Collectors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * @author Unmi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestJava8Collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arrays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;cde&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;efg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Collectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果对其他支持闭包的语言，如 JavaScript, Groovy, Ruby, Scala 等有所了解的话，很容易看出前面的代码输出为&lt;br/&gt;
&lt;blockquote&gt;
abc&lt;br /&gt;
cde</description></item><item><title>JDK8 的 Lambda 表达式 -- 默认和静态接口方法</title><link>https://yanbin.blog/jdk8-lambda-default-static-interface-methods/</link><pubDate>Tue, 25 Mar 2014 01:41:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-default-static-interface-methods/</guid><description>
&lt;p&gt;进入 Java8 之后我们会发现接口可以有方法实现了，这与我们一直看待 Java 接口的观念产生了冲突，不过也别急，接口中的方法实现必须是一个默认方法，即像&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Shape&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;isShape&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
本文旨在探讨 Java8 的默认接口方法存在的合理性，Java8 在这点上如何保持与前面版本的兼容性。&lt;br/&gt;&lt;br/&gt;
Lambda 和方法引用使得 Java 语言更具表现力。说到 Lambda 和方法引用的关系，Lambda 表达式的目的就是让你更为便捷的去绕过对象直接引用方法。&lt;br/&gt;&lt;br/&gt;
接口应该是相对稳固的，我们应该有这样的经验，类中使用了接口中定义的常量，如果在接口中改变了该常量值，单纯的替换接口对应的 class 文件是不奏效的，因为编译类时其实是把接口中的常量直接固化在类中了。如果类中要体现出最新常量值，那么使用接口的类也要重新编译。即使在接口中添加或改变了方法定义，也不能强制使用到它的类重新编译，早先的类完全可以自由的运行，因为接口中定义的常量和方法的所有内容都在自身，类一旦编译后便可脱离所实现的接口而运转。</description></item><item><title>Java 控制台输出百分比进度指器</title><link>https://yanbin.blog/java-console-perent-progress/</link><pubDate>Thu, 20 Feb 2014 00:46:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-console-perent-progress/</guid><description>
用 Java 在控制台下输出用 &lt;code&gt;System.out.print()&lt;/code&gt; 这样的方法，那么要在同一行输出象 FTP 传文件那样的进度指示怎么做呢，它需要在同一行相同的位置上擦除并输出新百分数。比如前一行打印了 1%， 下一次就把 1% 擦除掉，同一位置上打印 2%，就形成了走进度的效果。如下：&lt;/p&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2014/java-console-perent-progress/console_percent.gif"
width="340px"
/&gt;
我们要用到的办法是&lt;code&gt; System.out.print("\b")&lt;/code&gt; 就会在控制台下往回删掉一个字符，如果你想回删多个字符就打印多个 "\b" 吧，只回删除到当前行首，也就是不回影响到上一行的输出。怎么删除当前行全部输出或指定的字符数呢，两个办法：&lt;br/&gt;&lt;br/&gt;
输出足够多的 "\b"，会删除到行首为止，这样做其实也不保险，谁知道上一行会多少字符呢&lt;br /&gt;
精确控制回删多少个字符，有时候也需要保留前面一些内容</description></item><item><title>Maven 工程生成可执行的 JAR 包</title><link>https://yanbin.blog/maven-generate-executable-jar/</link><pubDate>Sun, 16 Feb 2014 23:52:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maven-generate-executable-jar/</guid><description>
远离 Maven 工程有些时日了，也不知道当前还是否在流行 Maven 管理工程与依赖，当前类似工具有 &lt;a href="https://ant.apache.org/" target="_blank" rel="noopener"&gt;Ant&lt;/a&gt;+&lt;a href="https://ant.apache.org/ivy/" target="_blank" rel="noopener"&gt;Ivy&lt;/a&gt;, &lt;a href="http://gruntjs.com" target="_blank" rel="noopener"&gt;Grunt&lt;/a&gt;, &lt;a href="http://www.scala-sbt.org" target="_blank" rel="noopener"&gt;sbt&lt;/a&gt; 和 &lt;a href="http://www.gradle.org" target="_blank" rel="noopener"&gt;Gradle&lt;/a&gt;。Web 工程的输出发布包没什么好说的，因为 &amp;lt;packaging&amp;gt;&lt;code&gt;war&lt;/code&gt;&amp;lt;/packaging&amp;gt;，所以 &lt;code&gt;mvn package&lt;/code&gt; 出来的 WAR 包里就有站点运行的所有内容了，用到的依赖会在 WEB-INF/lib 目录下列着。&lt;/p&gt;
&lt;br/&gt;
而对于那些 &amp;lt;packaging&amp;gt;&lt;code&gt;jar&lt;/code&gt;&amp;lt;/packaging&amp;gt; 的工程，用 &lt;code&gt;mvn package&lt;/code&gt; 只会生成一个 JAR 包，它所依赖的各个类库仍然分散在本地仓库中，而我们的发布包应该包含这些第三方依赖的。&lt;br/&gt;&lt;br/&gt;
假定，有一个 GoSSH  Maven 工程，设定的版本是 0.0.1，它依赖了 jsch-0.1.50, commons-cli-1.3-SNAPSHOT，想要发布的包有如下布局&lt;br/&gt;
&lt;blockquote&gt;
GoSSH-0.0.1.jar&lt;br /&gt;
lib&lt;br /&gt;
        jsch-0.1.50.jar&lt;br /&gt;
        commons-cli-1.3-20140216.032825-101.jar     #这里的 revision 号是随 SNAPSHOT 动态而定的
&lt;/blockquote&gt;
并且在 GoSSH-0.0.1.jar 中的 META-INF/MANIFEST.MF 文件中指定了</description></item><item><title>Debian 6 上安装 Nginx+PHP+MySQL</title><link>https://yanbin.blog/debian-6-install-nginx-php-mysql/</link><pubDate>Fri, 14 Feb 2014 20:06:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/debian-6-install-nginx-php-mysql/</guid><description>
正在使用亚马逊的 VPS，一年的小心翼翼的免费期，选择的是 Debian 6 32 位的系统，因搭建 WordPress 试图在上面安装 Nginx+PHP 的环境。看着在 Nginx 环境下多是使用 php-fpm，而不是 php-cgi，前者与后者的区别是在孤立的 php-cgi 之上又多了一个调度层。&lt;br/&gt;
&lt;br/&gt;
一开始打算一步步纯手工打造，但在执行&amp;nbsp;&lt;code&gt;apt-get install php-fpm&lt;/code&gt; 时总有不可逾越的依赖症，所以转而寻求更傻瓜的一键包安装方式。试过 &lt;a href="http://lnmp.org/" target="_blank"&gt;LNMP&lt;/a&gt;，包太大，东西太过于齐全，把人太当傻瓜，虽然如此，我在用它安装时也有些慢，因为它也是采取的编译安装方式，针对本机应该有很好的优化。没装完我就中断了，下次有时间可以再试下。&lt;br/&gt;
&lt;br/&gt;
正好在这期间看到了 Dvid Pennington 制作的 &lt;a href="https://github.com/Xeoncross/lowendscript" target="_blank"&gt;lowendscript lnmp&lt;/a&gt; 一键安装脚本，很省事，连安装&amp;nbsp; WordPress 也考虑到了，这里有介绍，比较详细 &lt;a href="http://my.oschina.net/u/1448992/blog/198429" target="_blank"&gt;lowendscript:最省资源lnmp一键包&lt;/a&gt;。我只记录下我自己的安装过程，以备忘。</description></item><item><title>JMockit 一个 Expectations 中 Mock 多个方法</title><link>https://yanbin.blog/jmockit-expectations-mock-multi-methods/</link><pubDate>Mon, 03 Feb 2014 01:49:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-expectations-mock-multi-methods/</guid><description>
从 JMockit 系列的开篇 &lt;a title="JMockit 之 Expectations" href="https://yanbin.blog/jmockit-with-expectations" target="_blank" rel="noopener"&gt;JMockit 之 Expectations&lt;/a&gt; 中了解到了一个最基本的 Mock 的写法，这里记录下在一个 Expectations 中如何同时 Mock 多个方法。基本框架是这样的：&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Expectations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Welcome to website: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExternalService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://unmi.cc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Java 语法告诉我们 new Expectations(){{......}} 省略号处的代码会在 Expectations 匿名类实例初始化时被调用，那么其中对 result 的赋值便是新创建的 Expectations 匿名类实例的 result 的属性值，那两次的 result 赋值难道不是以最后一个为准吗，有点文章了。先来跑个例子，见识一下现象，由三个类组成，分别是：</description></item><item><title>JMockit Mock 私有方法和私有属性</title><link>https://yanbin.blog/jmockit-mock-private-methods-fields/</link><pubDate>Mon, 03 Feb 2014 01:02:39 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-mock-private-methods-fields/</guid><description>
前面说过 JMockit 因身处前线，所以简直无不可，本节例子演示 JMockit 怎么 Mock 私有方法和私有属性，示例虽然是静态方法和属性，但因采用的是反射手法，所以这种 Deencapsulation 的 Mock 手段同样适用于公有的方法或属性，无论是否静态。&lt;br/&gt;
&lt;br/&gt;
本文所用 JMockit 版本为 1.6, 可能网上所搜索的方法与此有所不同，请注意 JMockit 版本差异。仍需重复一下，运行 JMockit 的例子 classpath 上必须让 jmockit.jar 在 junit.jar 之前，或用 javaagent 参数来加载 jmockit.jar，并且 junit 要 4.8 及以上版本.&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;1. Mock 私有方法(非静态类似)&lt;/strong&gt;&lt;/span&gt;</description></item><item><title>JMockit 之 Expectations</title><link>https://yanbin.blog/jmockit-with-expectations/</link><pubDate>Thu, 30 Jan 2014 02:39:31 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jmockit-with-expectations/</guid><description>
TDD 推求测试先行，不光在自己代码未实现时可以先做好测试，即使平台依赖或第三方接口未准备好我们也能先行一步的，这就要对接口依赖进行 Mock。同时 Mock 也使得我们的测试代码在运行当中不至于随着第三方接口的沦陷而坠入深渊。&lt;/p&gt;
&lt;br/&gt;
Java 中 Mock 工具也不少，像通用 &lt;a href="http://easymock.org/" target="_blank" rel="noopener"&gt;EasyMock&lt;/a&gt;, &lt;a href="http://jmock.org/" target="_blank" rel="noopener"&gt;jMock&lt;/a&gt;, &lt;a href="http://code.google.com/p/mockito" target="_blank" rel="noopener"&gt;Mockito&lt;/a&gt;, &lt;a href="http://www.unitils.org/summary.html" target="_blank" rel="noopener"&gt;Unitils Mock&lt;/a&gt;, &lt;a href="http://code.google.com/p/powermock" target="_blank" rel="noopener"&gt;PowerMock&lt;/a&gt;, 再比如偏专业的 HttpMock, StrutsMock 等。但 &lt;a href="http://code.google.com/p/jmockit" target="_blank" rel="noopener"&gt;JMock&lt;/a&gt; 与前面各位相比简直是全能选手，对 final/static/native/private 方法都能 Mock，功能上还远不止这些了，可以看看一个对比图 &lt;a href="https://code.google.com/p/jmockit/wiki/MockingToolkitComparisonMatrix" target="_blank" rel="noopener"&gt;https://code.google.com/p/jmockit/wiki/MockingToolkitComparisonMatrix&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
JMockit 是基于 Java5 的 java.lang.instrument 包开发的，所以它才能夺得先机，也可陷得更深。自然它要求 JDK5 及以上，JUnit 4.8 及以上版本。命令行下原来用 -javaagent:/.../lib/jmockit.jar 加载 JMockit，现在发现把 jmockit.jar 放在 classpath 下就 OK 的，但是必须放在 junit.jar 包之前，否则你会看到这个 &lt;span style="color: #800000;"&gt;java.lang.IllegalStateException: JMockit wasn't properly initialized; check that jmockit.jar precedes junit.jar in the classpath&lt;/span&gt;。 JMockit 有两种 Mock 方式：&lt;br/&gt;&lt;br/&gt;
1. Behavior-oriented(Expectations &amp;amp; Verifications)  --- 基于代码执行行为的模仿，象黑盒测试&lt;br /&gt;
2. State-oriented(MockUp&amp;lt;GenericType&amp;gt;)   --- 侵入类内部，随意模仿，似白盒，可以说是能为所欲为&lt;br/&gt;&lt;br/&gt;
此篇体验下第一种 Mock 方式，在测试代码中最直观就是那个 &lt;code&gt;new Expectations(...){{result = some;}}&lt;/code&gt;，下面来看个实际的例子。应用场景是</description></item><item><title>Ant 提示输入并根据用户输入执行不同的任务</title><link>https://yanbin.blog/ant-prompt-input-call-different-task/</link><pubDate>Wed, 29 Jan 2014 02:18:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ant-prompt-input-call-different-task/</guid><description>
使用 Ant 进行自动化处理时，不想记住每一个 target  的名称，而是让默认的 target 列出需要的 target 出来，让用户输入名称或数字选择执行哪个 target。这样做自然是多了一步，有时候确也方便不少，但 Ant 还是有个缺点，它不能持久性的保持在 Ant 控制台下，持续的进行用户交互。&lt;/p&gt;
&lt;br/&gt;
对于实现 Ant 的简单用户交互，我们可以借助于两个 Task，&lt;a href="http://ant.apache.org/manual/Tasks/input.html" target="_blank" rel="noopener"&gt;input&lt;/a&gt; 和 &lt;a href="http://ant.apache.org/manual/Tasks/antcall.html" target="_blank" rel="noopener"&gt;antcall&lt;/a&gt;, input 用来提示用户输入值，再根据 input 设定的属性来确定 antcall 调用哪个 target。执行完退出到系统 Shell 下，想要再来，就再执行一下 ant 吧，我也只能做到这一步了。&lt;br/&gt;&lt;br/&gt;
看下面的例子 build.xml 内容</description></item><item><title>SQLite 使用主键，ROWID 及自增列</title><link>https://yanbin.blog/sqlite-primary-rowid-autoincrement/</link><pubDate>Tue, 28 Jan 2014 00:10:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sqlite-primary-rowid-autoincrement/</guid><description>
之前关注过一些嵌入式数据库，倒时 SQLite 风头更劲，在 Android 上被应用，在 HTML5 中一些浏览器的 Local Database 的实现也是 SQLite。因在 PhoneGap 中使用数据库存储的选择也期待着它的表现，首先要建个数据库，第一要义就是主键的选择，自增列是最有效更简单的。&lt;br/&gt;
&lt;br/&gt;
这里就看下 SQLite 怎么使用自动列，了解三个内容，ROWID, ROWID 的别名，自动列与序列表，归根结底它们都是 ROWID。&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;1. ROWID&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
每个表默认都有 rowid 列，除非创建表时指定了 WITHOUT ROWID, 它现在是 64 位长的。在查询时用 &lt;code&gt;select * from table1&lt;/code&gt;&amp;nbsp;里没有它，要显式的用 &lt;code&gt;select rowid, * from table1&lt;/code&gt;&amp;nbsp;就会列出它来。&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;2. ROWID 的别名&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;</description></item><item><title>PhoneGap, 第一个应用</title><link>https://yanbin.blog/phonegap-first-application/</link><pubDate>Sat, 25 Jan 2014 20:41:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/phonegap-first-application/</guid><description>
移动开发有三条路，Native, HTML5 和 Hybrid，Native 可以把各种平台的特性充分发挥，HTML5 简单，可限制太多，混合型是个折中的办法。对于那些不很注重特效又需要访问部分本地资源的应用可采用 Hybrid 的方式，而其中的佼佼者就数 PhoneGap，又说 Cordova。它们是两个主，却又基本相似的东西，见&amp;nbsp;&lt;a href="http://phonegap.com/2012/03/19/phonegap-cordova-and-what%E2%80%99s-in-a-name/" target="_blank"&gt;PhoneGap, Cordova, and what’s in a name?&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
我们既可以选择 PhoneGap，也可以选择 Cordova，安装使用起来基本类似，再往下的发展的就无定数了。鉴于 PhoneGap 依托于 Adobe 那么一个强大的后台，我还是选择试用下 PhoneGap，并且 Adobe 还为它提供了在线构建和源码托管 &lt;a href="http://build.phonegap.com" target="_blank"&gt;http://build.phonegap.com&lt;/a&gt;。当然这是一家像 Oracle 那样商业气息比较浓的公司，使用在线构建免费用户只能建立一个应用。&lt;br/&gt;
&lt;br/&gt;
现在看一个应用 PhoneGap 的例子，从安装开始，演示平台为 Mac, 其他平台都类似。PhoneGap 可由 NodeJS 来安装，所以首先要在 &lt;a href="http://nodejs.org" target="_blank"&gt;http://nodejs.org&lt;/a&gt; 安装 NodeJS。然后执行下面的命令安装 phonegap:</description></item><item><title>Emacs 24 使用第三方主题</title><link>https://yanbin.blog/emacs-24-use-color-theme/</link><pubDate>Sat, 25 Jan 2014 01:24:32 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/emacs-24-use-color-theme/</guid><description>
Emacs 默认的样子总觉不怎么可爱，所以想要找个主题来装饰一番。试过 &lt;a href="http://www.emacswiki.org/emacs/ColorTheme" target="_blank" rel="noopener"&gt;http://www.emacswiki.org/emacs/ColorTheme&lt;/a&gt; 介绍的办法，从这里下载 &lt;a href="http://www.nongnu.org/color-theme" target="_blank" rel="noopener"&gt;http://www.nongnu.org/color-theme&lt;/a&gt; 主题文件，并且在 ~/.emacs 中添加了以下几句话：&lt;/p&gt;
&lt;blockquote&gt;
(require 'color-theme)                                                                                                                                              &lt;br /&gt;
(color-theme-initialize)                                                                                                                                            &lt;br /&gt;
(color-theme-robin-hood)
&lt;/blockquote&gt;
重启 Emacs 后不能发现界面有变化，但是在 Emacs 中可以用 &lt;code&gt;M-x color-theme-select&lt;/code&gt; 看到装载进来的主题，&lt;code&gt;M-x color-theme-hober&lt;/code&gt; 执行后也能看到应用上了这个主题，就是无法持久到下次 Emacs 运行。再次试验了ColorTheme 提到的多种方法都不奏效。&lt;br/&gt;&lt;br/&gt;
好像是因为我安装的是 Emacs 24 的版本，它有自己的默认主题 &lt;code&gt;deftheme&lt;/code&gt;, 和其他备选主题，&lt;code&gt;M-x customize-themes&lt;/code&gt; 可以看到下面这样的界面</description></item><item><title>Backbone.js 使用 Collection</title><link>https://yanbin.blog/backbone-js-with-collection/</link><pubDate>Thu, 09 Jan 2014 03:39:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/backbone-js-with-collection/</guid><description>
在前面我们的 Backbone.js 用上了 Model, 但绝大数的情况下我们处理的都是一批的 Model 数据列表，所以需要有一个 Collection 来容纳 Model, 就像 Java 里最常用的 List。&lt;/p&gt;
&lt;br/&gt;
声明 Collection 时需要指定他处理的 Model 类型，也就是个泛型参数，如我们这样定义 Collection:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//define Collection
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;PeopleCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Backbone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="c1"&gt;//like generic
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
然后就是往 Collection 中如何填充 Model 实例，有好多种，这里只演示最直接的方式。还 add, fetch, 及对 Collection 排序, 遍历等各种操作。</description></item><item><title>Backbone.js 中使用 Model</title><link>https://yanbin.blog/backbone-js-with-model/</link><pubDate>Mon, 06 Jan 2014 17:13:59 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/backbone-js-with-model/</guid><description>
前面几篇 Backbone.js 的例子中有使用到 template, 及数据的填充，其实这已经很接近 Model 了。现在来学习怎么创建自己的 Model 类，并简单的使用。Backbone.js 中 Model 会涉及到很多的概念，如 Model 的初始化，默认值，属性的读写，属性值改变的监听，数据渲染，校验，以及与服务端的数据同步等。&lt;/p&gt;
&lt;br/&gt;
本文不打算讲前面大部分的内容，最初思考的一个脉络是怎么把 Model 引入进来，所以线索会是 &lt;code&gt;建立 Model 类 -&amp;gt; 初始化 Model 实例 -&amp;gt; 设置值 -&amp;gt; 渲染到页面&lt;/code&gt;，其余 Model 特性分别再深入。&lt;br/&gt;&lt;br/&gt;
直观地能想到的 JavaScript 的 Model 是一个 JSON 对象，差不多，不过 Backbone 赋予 Model 默认属性，只能通过 getter/setter 方法来访问 Model 中的属性值，不能直接用点操作符，相当于 Backbone 的属性是私有的。</description></item><item><title>Backbone.js 的 View 中定义事件</title><link>https://yanbin.blog/backbone-view-define-events/</link><pubDate>Mon, 06 Jan 2014 00:48:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/backbone-view-define-events/</guid><description>
使用 Backbone 的 View 时，可以象传统 jQuery 那样定义事件，&lt;code&gt;$("selector").click(function(){...})&lt;/code&gt;。幸运的是 Backbone 让我们在 View 中定义事件变得更为简单和集中，只要设置 View 的 &lt;code&gt;events&lt;/code&gt; 属性，配置 事件，元素以及相应的处理方法，基本模式如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;click button&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;event_handler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;focus #name&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;event_handler&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;event_handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
格式是 &lt;code&gt;"事件 选择器": "事件处理函数名"&lt;/code&gt;, 奇怪为什么这里不是使用事件处理函数名的引用，也就是&lt;strong&gt;不能&lt;/strong&gt;写成下面两种方式：</description></item><item><title>Backbone.js 使用模板</title><link>https://yanbin.blog/backbone-js-use-template/</link><pubDate>Sun, 05 Jan 2014 17:43:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/backbone-js-use-template/</guid><description>
前面一篇 &lt;a title="Backbone.js HelloWorld，应用起步" href="https://yanbin.blog/backbone-get-started/" target="_blank" rel="noopener"&gt;Backbone.js HelloWorld，应用起步&lt;/a&gt; 只是让 Backbone 跑起来，实际的应用中会使用到模板，Model 等，而模板又是进阶的基础。所以这里介绍在 View 中使用模板，以及如何向模板填充值，模板可以用字符串，或是用 &lt;span style="color: #800000;"&gt;&amp;lt;script type="text/template"&amp;gt;&lt;/span&gt; 声明的内容。使用 &lt;span style="color: #800000;"&gt;&amp;lt;%= %&amp;gt;&lt;/span&gt; 或 &lt;span style="color: #800000;"&gt;&amp;lt;%- %&amp;gt;&lt;/span&gt; 来声明变量输出的占位符。&lt;/p&gt;
&lt;br/&gt;
Backbone 的模板要用到的是 &lt;a title="userscorejs" href="http://underscorejs.org" target="_blank" rel="noopener"&gt;underscore.js&lt;/a&gt; 库, 要深入了解 Unserscore 就看官方的 &lt;a href="http://documentcloud.github.io/underscore/" target="_blank" rel="noopener"&gt;Underscore.js 的文档&lt;/a&gt;，Underscore 的 template 方法的原型如下：&lt;br/&gt;
&lt;blockquote&gt;
_.template(templateString, [data], [settings])
&lt;/blockquote&gt;</description></item><item><title>Debian Linux VPS 为 Wordpress 的系统准备</title><link>https://yanbin.blog/debian-vps-for-wordpress/</link><pubDate>Sun, 05 Jan 2014 03:32:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/debian-vps-for-wordpress/</guid><description>
本人博客几经周折，最初 BPS，到独立的域名、空间，每一次也是为向更自由的管理迈进一步。现在终于搞了个 VPS，不过是在美国的，慢是慢了点，不过自己想怎么弄就怎么弄，现也无需经常性的找客服了。&lt;br/&gt;&lt;br/&gt;
VPS 是 768M RAM, 1536 的交换空间，40G硬盘空间，2500G带宽，两固定 IP，其实我只要一个就行了。服务器是在我所在的城市，所以管理起来应该要快些，至少感觉会有点。&lt;br/&gt;&lt;br/&gt;
基于系统资源和稳定性的考虑，我选择了 Debian 系统，Ubuntu 就是基于它的。在一个干净的系统上要搭建 Wordpress 需要预先准备好 Apache2+PHP5+MySQL5 这样的环境，Apache2 是以 worker 模式运行，PHP 是 FastCGI 方式，Wordpress 连接 MySQL 可以本地 Socket 方式。下面是步骤：&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;&lt;span style="color: #000080;"&gt;&lt;strong&gt;一. 系统设置&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;
配置时区，方便查看日志能对应上你当地的时间&lt;br/&gt;
&lt;blockquote&gt;
#dpkg-reconfigure tzdata
&lt;/blockquote&gt;
选择相应的城市，这步会修改 /etc/timezone 和 /etc/localtime 文件</description></item><item><title>Wordpress 插件 All in One SEO Pack 生成中文描述的解决</title><link>https://yanbin.blog/wordpress-all-in-one-seo-pack-chinese-description-patch/</link><pubDate>Sat, 04 Jan 2014 01:16:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-all-in-one-seo-pack-chinese-description-patch/</guid><description>
之前也有记录过一篇是针对 All in One SEO Pack 1.6.X 的解决截取中文描述的办法--&lt;a title="让 All in One SEO Pack 插件适用于截取中文描述" href="http://unmi.cc/all-in-one-seo-pack-support-chinese/" target="_blank" rel="noopener"&gt;让All in One SEO Pack 插件适用于截取中文描述&lt;/a&gt;，随着该插件一直升级到当前的 2.1.2，我的插件仍然滞留在 1.6.x 的版本，原因是未曾了解下 2.x 下如何处理中文描述的问题。现在有幸找来办法整理了下，特记录在下：&lt;/p&gt;
&lt;br/&gt;
中间插一句，All in One SEO Pack 可真是个吃内存的大户，小心使用。好，和针对 1.6.x  版本的修改一样，对于 2.x 也是要改两个变量值和一个方法，那就是在 aioseop.class.php 文件中&lt;br/&gt;&lt;br/&gt;
改下面两个变量分别为&lt;br/&gt;
&lt;blockquote&gt;
var $maximum_description_length = 300;  //原本为 160;&lt;br /&gt;
var $minimum_description_length = 80;//原本为 1;
&lt;/blockquote&gt;
然后修改方法 trim_excerpt_without_filters($text) 的内容为</description></item><item><title>Backbone.js HelloWorld，应用起步</title><link>https://yanbin.blog/backbone-get-started/</link><pubDate>Wed, 01 Jan 2014 03:28:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/backbone-get-started/</guid><description>
Backbone 是一个 JavaScript 框架，可用于创建模型-视图-控制器 (model-view-controller, MVC) 类应用程序和单页界面。它试图让 JavaScript 应用程序更清晰结构化，所涉及到的概念有  View, Event, Model, Collection 和 Router，所以新手刚开始接触 Backbone 反而会显得零乱了。再加上网上找来的一些起步教程一上来就把 Event, Model, Collection 或是 Router 揽上了。其实新手最想要一个最简单的真相，因此第一个例子只需用到 View，其他的玩艺暂且搁置。&lt;/p&gt;
&lt;br/&gt;
Backbone 强依赖于 Underscore，jQuery 是可选的，不过为操作 DOM 方便一般都会引用到 jQuery。&lt;br/&gt;&lt;br/&gt;
最简单的例子，分两步走：&lt;br/&gt;&lt;br/&gt;
1. Backbone.View.extend(obj) 创建一个 View 类型&lt;br /&gt;
2. 创建上一步得到的 View 的实例，实例化时将会调用前一步 obj.initialize() 方法&lt;br/&gt;&lt;br/&gt;
上代码(为方便起见，所有 JS 脚本都从公网上加载)：</description></item><item><title>JavaScript 获得代码行号和脚本文件名</title><link>https://yanbin.blog/javascript-get-execution-line-file/</link><pubDate>Wed, 01 Jan 2014 01:50:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-get-execution-line-file/</guid><description>
比如要写一个 JavaScript 的日志输出工具，在方法 log.info() 中能得到调用它所在的文件和代码行号。和众多的日志工具一样，像 log4j，
都是在程序代码中主动抛出异常，然后从异常栈中去查找到调用者所在的代码行和文件名。
不过也就刚刚才了解到 JavaScript V8 引擎提供了自己的 StackTrace API，Chrome 和 Node.js 可用。
先来看可通用的 JavaScript &amp;nbsp;抛异常 throw new Error() 的方式，下面的代码：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Stack:&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Error\n/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\n/&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^\s+|\s+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Location: &amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
点击链接&amp;nbsp;&lt;a href="http://jsfiddle.net/Unmi/53xas/" target="_blank"&gt;http://jsfiddle.net/Unmi/53xas/&lt;/a&gt; 执行，其中是用 document.write() 输出的，完整异常栈是：</description></item><item><title>JSON.stringify(obj) 时 TypeError: cyclic object value 异常</title><link>https://yanbin.blog/json-stringifyobj-raise-typeerror/</link><pubDate>Tue, 31 Dec 2013 15:12:39 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/json-stringifyobj-raise-typeerror/</guid><description>
从 ECMAScript 5th Edition 开始，JavaScript 内建了 JSON 对象，用来处理 JSON 的序列化和反序列化，有以下几个方法原型&lt;br/&gt;
&lt;br/&gt;
JSON.stringify(obj [,filter] &amp;nbsp;[,indent])&lt;br/&gt;
JSON.parse(text [,reviver])&lt;br/&gt;
&lt;br/&gt;
jQuery 也提供了&amp;nbsp;jQuery.parseJSON 方法，但是没有相应的序列化方法。&lt;br/&gt;
&lt;br/&gt;
如果用 &amp;nbsp;JSON.stringify() 来对一个有循环引用的 JSON 对象进行序列化，会产生&amp;nbsp;&lt;span style="color: #800000;"&gt;TypeError: cyclic object value&lt;/span&gt; 异常，类似下面的代码&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
点击链接 &lt;a href="http://jsfiddle.net/Unmi/6eLFF/" target="_blank"&gt;http://jsfiddle.net/Unmi/6eLFF/&lt;/a&gt;&amp;nbsp;运行上面的代码&amp;nbsp;</description></item><item><title>JavaScript 模块应用与编程</title><link>https://yanbin.blog/javascript-module-programming/</link><pubDate>Thu, 26 Dec 2013 03:22:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-module-programming/</guid><description>
前边有一篇关于 &lt;a href="https://yanbin.blog/javascript-object-declaration" target="_blank" rel="noopener"&gt;JavaScript 对象声明&lt;/a&gt;，可以作为是 JavaScript 模块化的一个铺垫。这里会涉及到两个话题模块的定义和引入，对于模块化的编程语言，我们可以用 include, require 或 import 那样的谓词来引入模块。回想下我们对于非模块化的 JavaScript 是怎么引入的，比如在 html 文件里 js 文件，用 &amp;lt;script src="some.js"&amp;gt;&amp;lt;/script&amp;gt;。如果是更动态一点就会用 document.createElement("script"), 再指定它的 src 属性为一个 js 文件，添加加到 DOM 中去的方式来加载 js 文件。&lt;/p&gt;
&lt;br/&gt;
上面两种方式都不够优雅，我们现在想要实现为 require() 函数来引入 js 库的方式，所以 JavaScript 模块的规范就出现了 &lt;a href="http://wiki.commonjs.org/wiki/Modules/1.1" target="_blank" rel="noopener"&gt;CommonJS&lt;/a&gt; 和 &lt;a href="https://github.com/amdjs/amdjs-api/wiki/AMD" target="_blank" rel="noopener"&gt;AMD&lt;/a&gt;(Asynchronous Module Definition) 两种。&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;1. CommonJS&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
CommonJS 中有一个全局的  require() 方法，它就是执行 require() 代码后马上使用模块提供的属性或方法，它假定执行完 require() 行后，模块即已就绪。例如加载一个 'math' 模块&lt;br/&gt;
&lt;blockquote&gt;
&lt;span style="color: #800000;"&gt;var math = require('math');&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt; math.add(2, 3);&lt;/span&gt;
&lt;/blockquote&gt;</description></item><item><title>RESTful, 说说 http 的 patch method</title><link>https://yanbin.blog/restful-http-patch-method/</link><pubDate>Thu, 12 Dec 2013 10:40:24 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/restful-http-patch-method/</guid><description>
最早的时候，我们只需要 GET 和 POST 方法，POST 方法的引入也只是为了消除 URL 过长，参数隐藏，上传文件的问题，完全和语义无关。接触到 RESTful 之后，我们开始思考 GET 和 POST 的不同语义，并且十分必要的去发掘出所有的 HTTP method，HTTP/1.1 所实现的 method，见 &lt;a href="http://www.ietf.org/rfc/rfc2616" target="_blank" rel="noopener"&gt;RFC 2616&lt;/a&gt;, 有这些：&lt;/p&gt;
&lt;br/&gt;
OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT&lt;br/&gt;&lt;br/&gt;
规范是这么定义的，这还要看容器实现了多少，比如 Tomcat 7 中的 &lt;a href="https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/http/HttpServlet.html" target="_blank" rel="noopener"&gt;servlet api&lt;/a&gt; 实现了&lt;br/&gt;&lt;br/&gt;
doOptions, doGet, doHead, doPost, doPut, doDelete, doTrace 就差个 doConnect 了。&lt;br/&gt;&lt;br/&gt;
而我们这里要说的 PATCH method 是在 Servlet 3.0 和当前 Tomcat 7 中都提到的，也就是尚未实现它。&lt;br/&gt;&lt;br/&gt;
这也难怪，PATCH 在 2010 年三月份才成为正式的方法，见 &lt;a href="http://tools.ietf.org/html/rfc5789" target="_blank" rel="noopener"&gt;RFC 5789&lt;/a&gt;。没有 PATCH 的时候我们进行更新的操作采用的是 PUT 方法。那么 PATCH 和 PUT 有什么区别呢？&lt;br/&gt;&lt;br/&gt;
同样可以从语义上去理解，有两方面的对比：</description></item><item><title>Apache .htaccess 重定向在别名应用中的问题</title><link>https://yanbin.blog/apache2-htaccess-redirect-alias/</link><pubDate>Sun, 24 Nov 2013 07:26:55 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache2-htaccess-redirect-alias/</guid><description>
在 Apache 应用的目录中有 .htaccess 文件来进行重定向，目的是实现省略扩展名 .php 来访问相应的 php 文件，例如用 url&lt;br/&gt;
&lt;br/&gt;
http://localhost/unmi/forgotPassword&amp;nbsp; 来访问&amp;nbsp; http://localhost/unmi/forgotPassword.php&lt;br/&gt;
&lt;br/&gt;
.htaccess 文件的内容是：&lt;br/&gt;
&lt;blockquote&gt;RewriteEngine on&lt;br/&gt;
RewriteCond %{REQUEST_FILENAME} !-d&lt;br/&gt;
RewriteCond %{REQUEST_FILENAME}\.php -f&lt;br/&gt;
RewriteRule ^(.*)$ $1.php&lt;/blockquote&gt;
结果访问 http://localhost/unmi/forgotPassword 时出现错误：&lt;br/&gt;
&lt;blockquote&gt;&lt;br/&gt;
&lt;h1&gt;Not Found&lt;/h1&gt;&lt;br/&gt;
The requested URL /data/unmi/forgotPassword.php was not found on this server.&lt;/blockquote&gt;
实际上文件 /data/unmi/forgotPassword.php 是存在的。这就奇怪了，怀疑过是文件权限的问题(Mac 平台)，改成 755 也不行。也经为是 AllowOverride 的问题，可它的值我设置成了 All 啊，又不是 None，应该不是症结所在。&lt;br/&gt;
&lt;br/&gt;
费了一番功夫也明白了我是配置了别名来访问应用的，在 Apache httpd.conf 中是这样定义的别名：</description></item><item><title>JavaScript 对象声明</title><link>https://yanbin.blog/javascript-object-declaration/</link><pubDate>Sat, 23 Nov 2013 09:39:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-object-declaration/</guid><description>
对象，类，模块的概念可以让系统更清晰，亦能增强代码的重用性。目前 JavaScript 规范本身并不支持类或模块，正在制定中的
&lt;a href="https://people.mozilla.org/~jorendorff/es6-draft.html" target="_blank" rel="noopener"&gt;ECMAScript 6&lt;sup&gt;th&lt;/sup&gt; Edition&lt;/a&gt;
将会纳入类，模块的概念，不过不知会是何年马月的事，毕竟这不能像 JDK 升级那么干脆。&lt;/p&gt;
&lt;br/&gt;
还有种办法来写模块化的 JavaScript 就是使用微软的  &lt;a href="http://www.typescriptlang.org/" target="_blank" rel="noopener"&gt;TypeScript&lt;/a&gt;
或是 Google 的 &lt;a href="https://www.dartlang.org/" target="_blank" rel="noopener"&gt;Dart&lt;/a&gt;，它们是面向对象的编程语言，
能直接编译成兼容的 JavaScript 代码，有条件的可以去尝试。&lt;br/&gt;&lt;br/&gt;
回到现实来，就现有的 JavaScript 规范怎么去模块化。众所周知，JavaScript 中函数是第一等公民，所以我们早先是这样使用函数来声明对象的：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #000080;"&gt;&lt;strong&gt;1. 原始写法&lt;/strong&gt;&lt;/span&gt;(比直接调用函数显得高级些)：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>CountDownLatch 协调线程</title><link>https://yanbin.blog/countdownlatch-threads/</link><pubDate>Sat, 19 Oct 2013 08:41:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/countdownlatch-threads/</guid><description>
JDK8 都快要出来了，在 JDK 5 中仍有许多好宝贝值得去挖掘。提到 JDK5 我们或许只知道它给了我们泛型，其实还有那个并发包 java.util.concurrent 却不那么引人注目，其实就是 NIO。&lt;br/&gt;
&lt;br/&gt;
若是并发包是在某个 JDK 版本中单独奉上，反响就不同了，想想 JDK 6 似乎未带来多少改变--至少对于编程者来说没有明显感受。java.util.concurrent 包中的东西对于我们处理线程带来了很大的便利，例如线程池，线程同步，Future, Callable 等。&lt;br/&gt;
&lt;br/&gt;
这里我记录一下 CountDownLatch 的使用，在此之前在处理&lt;br/&gt;
&lt;br/&gt;
线程 A 等待线程 B，C，D 全部执行完后才继续执行 （比如要每个线程都访问一个 Web 服务，等所有的请求响应成功后进行结果处理）&lt;br/&gt;
&lt;br/&gt;
这样场景的时候，我一般能想到的办法是，初始一个计数器，线程 B，C，D 各自初始化的时候，计数器加一，然后 A 线程等待，每个线程执行完后计数器减一，当计数器为 0 时表明所有任务执行完毕，就通知 A 可以开始运作起来。但这样的方案还是得小心的处理好同步的问题。</description></item><item><title>JDK8 的 Lambda 表达式 -- 方法引用</title><link>https://yanbin.blog/jdk8-lambda-method-references/</link><pubDate>Thu, 17 Oct 2013 08:15:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-method-references/</guid><description>
Lambda 允许我们定义匿名方法(即那个 Lambda 表达式，或叫闭包)，作为一个功能性接口的实例。如果你不想把一个 Lambda 表达式写得过大，那么你可以把表达式的内容分离出来写在一个方法中，然后在放置 Lambda 表达式的位置上填上对那个方法的引用。&lt;/p&gt;
&lt;br/&gt;
方法引用也应看作是一个 Lambda 表达式，所以它也需要一个明确的目标类型充当功能性接口的实例。简单说就是被引用的方法要与功能接口的 SAM(Single Abstract Method) 参数、返回类型相匹配。方法引用的引入避免了 Lambda 写复杂了可读性的问题，也使得逻辑更清晰。&lt;br/&gt;&lt;br/&gt;
为了应对方法引用这一概念， JDK8 又重新借用了 C++ 的那个 “::” 域操作符，全称为作用域解析操作符。&lt;br/&gt;&lt;br/&gt;
上面的表述也许不好明白，我看官方的那份 State of the Lambda 也觉得不怎么容易理解，特别是它举了那个例子很难让人望文生意。我用个自己写的例子来说明一下吧。&lt;br/&gt;&lt;br/&gt;
目前的 Eclipse-JDK8 版还不能支持方法引用的特性，幸好就是在昨天正式版的 NetBeans IDE 7.4 对 JDK8 有了较好的支持，所以在 NetBeans 7.4 中写测试代码。</description></item><item><title>JDK8 的 Lambda 表达式 -- 词法范围和变量捕获</title><link>https://yanbin.blog/jdk8-lambda-lexical-scoping-variable-capture/</link><pubDate>Wed, 16 Oct 2013 08:35:23 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-lexical-scoping-variable-capture/</guid><description>
Lambda 表达式的词法范围，一言以蔽之就是没有引入新的词法范围。这里的词法范围要研究的课题是 this 的指向有没有在变。我们知道在匿名类内部 this 指向的匿名类的实例，这种关系是在编译期就确定的。而在 Lambda 表达式中的 this 与外部的 this 没有差别，也就是说你可以把 Lambda 表达式当成一般的语句来看待，多简单啊，不像 JavaScript 中的 this 被搞的那么魔幻。&lt;/p&gt;
&lt;br/&gt;
可以这么测试：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arrays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
输出的是同一个地址里的东西：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;cc.unmi.testjdk8.TestLambda@28a418fc&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;cc.unmi.testjdk8.TestLambda@28a418fc&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
接下来瞧瞧 Lambda 表达式对外层变量的捕获。Lambda 表达式的有个好处就是它是轻量级，可重用单元，并且可捕获外层变量。</description></item><item><title>JDK8 的 Lambda 表达式 -- 类型推断</title><link>https://yanbin.blog/jdk8-lambda-4-type-inference/</link><pubDate>Tue, 15 Oct 2013 09:00:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-4-type-inference/</guid><description>
现在我们来看看 JDK8 是怎么对 Lambda 表达式进行类型推断的，Lambda 的实际类型叫做它的目标类型(target type)，因为 JDK8 沿承了已有的类型系统，所以象这样的写法：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;button.addActionListener(&lt;span style="color: #0000ff;"&gt;(ActionEvent e) -&amp;gt; foo()&lt;/span&gt;);&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
addActionListener() 方法接收的是一个 ActionListener 类型的参数，所以这里的 Lambda 表达式&lt;span style="color: #800000;"&gt; (ActionEvent e) -&amp;gt; foo()&lt;/span&gt; 代表的就是一个 ActionListener 实例，编译器是怎么知道这一点的，而且相同的 Lambda 是可以表示不同的类型的，见：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Callable&amp;lt;String&amp;gt; c = &lt;span style="color: #0000ff;"&gt;() -&amp;gt; "done"&lt;/span&gt;;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #800000;"&gt;//这个 () -&amp;gt; "done" 是 Callable&amp;lt;String&amp;gt; 类型&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;PrivilegedAction&amp;lt;String&amp;gt; a = &lt;span style="color: #0000ff;"&gt;() -&amp;gt; "done"&lt;/span&gt;;&amp;nbsp; //同样的 () -&amp;gt; "done" 却是 PrivilegedAction&amp;lt;String&amp;gt; 类型&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
答曰，根据 Lambda 表达式所处的上下文去感知。上下文决定了 Lambda 所期盼的类型，比如说变量声明类型，方法要求的输入参数类型，所以它必须是明确，不能模棱两可。所以一个 Lambda 表达式能否放在某处需要满足以下几个条件：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;期盼的类型必须是一个功能性接口，这样就能唯一定位到那个抽象方法上去，确定方法签名&lt;/strong&gt;&lt;/span&gt;，接下来就是</description></item><item><title>JDK8 的 Lambda 表达式 -- 实现原理初探</title><link>https://yanbin.blog/jdk8-lambda-3-inside/</link><pubDate>Sun, 13 Oct 2013 08:28:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk8-lambda-3-inside/</guid><description>
JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情，那么它是怎么实现的呢？从所周知，匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件，如写在 TestLambda 中的匿名类产生成类文件是 TestLambda$1.class, TestLambda$2.class 等。&lt;/p&gt;
&lt;br/&gt;
我试验了一下，如果使用的是 Lambda 表达式并不会生成额外的类文件，那么字节码里是什么样子的？来看下用  javap -c 反编译出下面文件产生的 TestLambda.class，两个方法，一个是  byAnonymousClass() 使用匿名类，另一个是 byLambda 使用 Lambda 的方式：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cc.unmi.testjdk8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.awt.event.ActionEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.awt.event.ActionListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javax.swing.JButton&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestLambda&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JButton&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JButton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;byLambda&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ActionEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Lambda&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;byAnonymousClass&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ActionListener&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;actionPerformed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ActionEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Anonymous class&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>JDK 8 的 Lambda 表达式 -- 写法</title><link>https://yanbin.blog/jdk-8-lambda-2-expression/</link><pubDate>Sat, 12 Oct 2013 11:00:53 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-8-lambda-2-expression/</guid><description>
过去，我们使用匿名类的实现像  ActionListener 这样的接口，即使最简单的情景都需要写上五六行代码，这就显得有些累赘了。因为其实我们在 new ActionListener {} 时就是在创建一个 ActionListener 子类型，重载了方法，并初始化一个实例出来。现在我们手上有了 Lambda 表达式这一武器的话，就变得简单明了了，所以说  Lambda 是一种轻量级的实现机制。&lt;/p&gt;
&lt;br/&gt;
这里是几个 Lambda 表达式的样子：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//两整形参数，返回它们的和&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//无参数，直接返回 42&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//可推断出参数 x 的类型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;传入一个字符&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;只执行一个操作&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;无返回值&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Lambda 的格式就是三部分：参数列表，-&amp;gt; 分隔符，执行体。其他语方的 Lambda 格式都这样的，可能就是分隔符不一样，如 Ruby 的 |，Scala 的 =&amp;gt; 等。&lt;br/&gt;&lt;br/&gt;
执行体里的 return 只是标明返回到匿名方法的调用者，不是返回值的意思； 最上层是不能用 break 和 continue 关键字的，可用在循环中； 如果 Lambda 表达式有返回值，那么在每一条路径上都要有返回值或是抛出异常。路径上最后一条语句的返回值即为 Lambda 表达的返回值。</description></item><item><title>JDK 8 的 Lambda 表达式 -- 应用背景</title><link>https://yanbin.blog/jdk-8-lambda-1-background/</link><pubDate>Fri, 11 Oct 2013 11:00:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-8-lambda-1-background/</guid><description>
即将到来的 JDK8 最为引人入胜之处非 Lambda 表达式莫数了，这在其他动态语方，如 Ruby, Groovy, Scala 等语言中早已大行其道。一旦 JDK 搭上了 Lambda 这趟车，从此操作事件，线程，处理集合时又大为方便了。关于现阶段如何体验 JDK8 的特性可以参考
&lt;a title="抢鲜设置 JDK8 的编程环境，印象" href="https://yanbin.blog/setting-jdk8-programming-environment" target="_blank"&gt;抢鲜设置 JDK8 的编程环境，印象&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
本文主要参考于官方的 &lt;a href="http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html" target="_blank"&gt;State of the Lambda&lt;/a&gt;，并对源码或反编译出字节码，作一定的深入以助于各位理解，在 JVM 中是如何实现 Lambda 的。&lt;br/&gt;
&lt;br/&gt;
Lambda 表达式，又称闭包(Closure)，或称匿名方法(anonymous method)。这在其他语言中，如 Ruby, Groovy, Scala, JavaScript 等，甚至是在 C# 中运用得如火纯清的特性，JDK8 这才问候他，真有些晚了。没有&amp;nbsp;Lambda 时，Java 不得不求助于匿名类的回调方法来达到相似的目的，为了捕获外部变量，变量必须声明为 final。&lt;br/&gt;
&lt;br/&gt;
一见到&amp;nbsp;Lambda，第一个反应就是&amp;nbsp;Lambda 表达式(局部而已)，其实我们这里要说的 JDK8 的&amp;nbsp;Lambda 包含以下几块内容：&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;Lambda 表达式，俗称闭包或匿名方法&lt;/li&gt;
&lt;li&gt;方法和构造器引用&lt;/li&gt;
&lt;li&gt;扩充的目标类型和更强的类型推断&lt;/li&gt;
&lt;li&gt;接口中的默认静态方法&lt;/li&gt;
&lt;/ol&gt;
Lambda 这一特性使得 Java 也开始向函数式编程倾斜，关键是能更有效的应对并发环境。&lt;br/&gt;
&lt;br/&gt;
说到&amp;nbsp;Lambda，我们不得不了解一下当前背景，例如下面一个典型的事件处理场景：</description></item><item><title>JDK 7 代码中对捕获再抛出异常时的处理--即异常类型推断</title><link>https://yanbin.blog/jdk7-rethrow-exception/</link><pubDate>Fri, 04 Oct 2013 10:13:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk7-rethrow-exception/</guid><description>
JDK 发展过程中，第一次变化最大数 JDK1.5，加入了变长参数，泛型。泛型的最大的受益者是集合。JDK7 虽说引进了同时捕获多个异常(Multi-Catch)，更聪明一点类型推断，资源的释放等，但我觉得变化还不大。接下来众人期望的 JDK8 的 Lambda 表达式才是激动人心的，恐怕这一特性的大赢家仍是集合。&lt;/p&gt;
&lt;br/&gt;
这样 JDK 才不至于离 C# 太远，纯粹语言上讲，我更景仰 C#，比如它的匿名类型，更不论人家的 Lambda 了。&lt;br/&gt;&lt;br/&gt;
&lt;span style="font-family: Menlo;"&gt; var person = new { Name = "Unmi", Website = "http://unmi.cc" };&lt;br /&gt;
Console.WriteLine(person.Name);&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
这要到 Scala 中才能见到这种影子。&lt;br/&gt;
&lt;blockquote&gt;
注: JDK 从 1.5 起就加入了象 Chrome, Firefox 那样的版本党了，所以这个版本也叫做 JDK5，不管是后来的 JDK6, JDK7 等等，其实在命令行下 java -version 显示出来的也还是 1.5.0, 1.7.0_40 这种理智的版本号的。
&lt;/blockquote&gt;
回过神来，我们要说的是 JDK7 对异常的处理，不讲同时捕获多个异常和 try-with-resource 的处理，而要说的是捕获异常再次抛出时进步，这在某方面得益于 JDK7 类型推断。JDK7 类型推断对于泛型来说，它可以不用这么写</description></item><item><title>Play Framework 2.1(Scala) 调用 WebService 返回 Response</title><link>https://yanbin.blog/play-framework-2-1-scala-webservice-return-response/</link><pubDate>Wed, 18 Sep 2013 12:07:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play-framework-2-1-scala-webservice-return-response/</guid><description>
在 Play Framework 2.1 中用 Scala 编程进行 WebService 调用想返回 Response，或 xml, json 结果时，初入可能会费些功夫，因为无论是 WS.url.get，或 post, put 方法返回的都是个 Future[Response] 对象，也就是如何从 Future[Response] 如何分离出 Response 对象的问题。&lt;/p&gt;
&lt;br/&gt;
这里我们 map, 或 match/case 也可以取出其中 response 对象来，如果要作为结果返回还要用到 Await.result() 方法。&lt;br/&gt;&lt;br/&gt;
为了不把代码出现在摘在页面中，这里插点内容，像是拖延时间的战术。&lt;br/&gt;&lt;br/&gt;
如果你在使用 Play Framework 2 with Scala 时碰到这个错误：&lt;br/&gt;&lt;br/&gt;
Error: Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global&lt;br/&gt;&lt;br/&gt;
依照 &lt;a href="http://play.lighthouseapp.com/projects/82401/tickets/899-error-cannot-find-an-implicit-executioncontext-either-require-one-yourself-or-import-executioncontextimplicitsglobal" target="_blank" rel="noopener"&gt;899 号 Issue&lt;/a&gt;, 解决办法是你必须加上这个引入语句：&lt;br/&gt;&lt;br/&gt;
import play.api.libs.concurrent.execution.Implicits._&lt;br/&gt;&lt;br/&gt;
因为里面有我们需要用到的隐式的 global 吧。&lt;br/&gt;&lt;br/&gt;
下面是我用来请求 WebService 返回 Response 的代码：</description></item><item><title>抢鲜设置 JDK8 的编程环境，印象</title><link>https://yanbin.blog/setting-jdk8-programming-environment/</link><pubDate>Wed, 18 Sep 2013 11:14:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/setting-jdk8-programming-environment/</guid><description>
和 JDK7 还没有热和，却开始关注 JDK8 起来了，JDK8 已进行到了 M8 开发预览版了，其中包含了一个千呼万唤的功能，即 &lt;a href="http://openjdk.java.net/projects/lambda/" target="_blank" rel="noopener"&gt;Lambda 闭包&lt;/a&gt;的支持。在当前有对闭包特性的诉求，而 Java 本身不提供时，我们不得不转而求助于 JVM 上的其他的语言, 像 Scala, Groovy, JRuby 等。&lt;/p&gt;
&lt;br/&gt;
不得不说 Scala 给我们带来了不少的快感，以致于想以更多的精力投入到 Scala 编程中去，不过还正在换取项目实践的机会。JDK8 引入 Lambda 后同样也是能更好的解决多核计算的问题，从程序员的角度来看，只要觉得 Lambda 能给我们带来无与伦比的灵活与便利性就足够了，水很深。&lt;br/&gt;&lt;br/&gt;
JDK8 还有增加的功能就是 &lt;a href="http://openjdk.java.net/jeps/0" target="_blank" rel="noopener"&gt;JEP&lt;/a&gt;(JDK Enhancement-Proposal) 中的了。&lt;br/&gt;&lt;br/&gt;
来这里 &lt;a href="http://openjdk.java.net/projects/jdk8/" target="_blank" rel="noopener"&gt;http://openjdk.java.net/projects/jdk8/&lt;/a&gt; 看看 JDK8 的里程碑&lt;br/&gt;&lt;br/&gt;
&lt;del&gt;2012/04/26     M1     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2012/06/14     M2     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2012/08/02     M3     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2012/09/13     M4     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2012/11/29     M5     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2013/01/31     M6     &lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2013/06/13     M7     Feature Complete&lt;/del&gt;&lt;br /&gt;
&lt;del&gt;2013/09/05     M8     Developer Preview&lt;/del&gt;</description></item><item><title>35 个最好的 Ajax jQuery 自动完成插件，带例子</title><link>https://yanbin.blog/35-best-ajax-jquery-autocomplete-tutorial-plugin-with-examples/</link><pubDate>Fri, 06 Sep 2013 12:49:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/35-best-ajax-jquery-autocomplete-tutorial-plugin-with-examples/</guid><description>
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br/&gt;
In this article,We are providing the&lt;strong&gt; best tutorials of Autocomplete in jQuery , ajax autocomplete and autosuggest with examples&lt;/strong&gt;.
&lt;strong&gt; jQuery Autocomplete&lt;/strong&gt; an input field to enable users quickly finding and selecting some value, leveraging
searching and filtering.In these days every body want fast and instant search,for this reason many popular search engines like google
are using this feature&lt;strong&gt; ajax autocomplete&lt;/strong&gt;.Combination of
&lt;strong&gt;Php,ajax and jQuery autocomplete&lt;/strong&gt; gives some extra feature to &lt;strong&gt;autosuggest&lt;/strong&gt;.&lt;br/&gt;
&lt;h2&gt;1. Ajax AutoComplete for jQuery&lt;/h2&gt;&lt;br/&gt;
&lt;strong&gt;Ajax Autocomplete for jQuery&lt;/strong&gt; allows you to easily create &lt;strong&gt;autocomplete/autosuggest boxes for text input fields&lt;/strong&gt;. Built with focus on performance – results for every query are cached and pulled from …&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2013/35-best-ajax-jquery-autocomplete-tutorial-plugin-with-examples/jquery-ajax-autocomplete-1.jpg"
width="500px"
/&gt;
&lt;br/&gt;</description></item><item><title>Varnish 的安装使用，及简单配置</title><link>https://yanbin.blog/varnish-install-quick-start/</link><pubDate>Sun, 28 Jul 2013 22:19:35 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/varnish-install-quick-start/</guid><description>
Varnish 是一个开源的反向代理软件，所以可做缓存服务器进行 Web 加速，类似的有 Squid, Nginx, 和  HAProxy。但 Varnish 与其他三个相比性能更优，例如 Squid 的成名是它可作为上网代理服务器， Nginx 是一个优秀 Web 服务器，这两都非专业的 Web 加速器，而 HAProxy 与  Varnish 有的一比。&lt;/p&gt;
&lt;br/&gt;
实际上 Varnish 的表现如何了，网上找来找去都讲这么一个实证：挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid，性能比以前更好。并且要知道 Varnish 的作者Poul-Henning Kamp是FreeBSD的内核开发者之一，牛人一个。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;Varnish 有什么特点呢？&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
VCL 进行配置，正则表达式条件判定进行规则设置&lt;br /&gt;
支持负载均衡和健康检查，多种条件请求分分&lt;br /&gt;
支持 ESI( Edge Side Include)，即页面局部组件的缓存&lt;br /&gt;
URL 地址重写，响应头改写</description></item><item><title>由 TextMate 2 重回 TextMate 1.5.11, 附注册方法</title><link>https://yanbin.blog/back-to-textmate-1-5-11-key/</link><pubDate>Sun, 28 Jul 2013 02:46:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/back-to-textmate-1-5-11-key/</guid><description>
我最爱在 Mac 下用 TextMate 了，2.0 后 TextMate 可免费使用了。在 &lt;a href="http://macromates.com/download" target="_blank"&gt;http://macromates.com/download&lt;/a&gt; 上目前提供有 1.5.10, 1.5.11 和 2.o alpha。而且还开源了，见 &lt;a href="https://github.com/textmate/textmate" target="_blank"&gt;https://github.com/textmate/textmate&lt;/a&gt;。但 2.0 之前的版本要用的话仍是需要注册，TextMate 2.0 还是自动更新的，可在 &lt;a href="http://macromates.com/download" target="_blank"&gt;http://macromates.com/download&lt;/a&gt;，也可在 &lt;a href="https://github.com/textmate/textmate/downloads" target="_blank"&gt;https://github.com/textmate/textmate/downloads&lt;/a&gt; 下载。&lt;br/&gt;
&lt;br/&gt;
本来可以用&amp;nbsp; TextMate 2.0 挺好的，可是在 2.0 安装 Scala 的 Bundle 后，即使为它设置了 SCALA_HOME 仍是无法在 TextMate 中运行 Scala 程序，于是不得已为这个还得用 TextMate 1.5.11 了。&lt;br/&gt;
&lt;br/&gt;
在此附上 Scala 和 Play2 的 Bundle 的下载安装方法:&lt;br/&gt;
&lt;br/&gt;
$&amp;nbsp; git clone git://github.com/mads379/scala.tmbundle.git; open scala.tmbundle&lt;br/&gt;
$&amp;nbsp; git clone git://github.com/drewhjava/Play2.tmbundle.git; open Play2.tmbundle&lt;br/&gt;
&lt;br/&gt;
使用 TextMate 是必须注册的，否则只能试用 30 天，从网上找来注册方法：</description></item><item><title>XSLT 调用 Java 的类方法 -- Tomcat 环境</title><link>https://yanbin.blog/xslt-call-java-functions-tomcat-env/</link><pubDate>Fri, 26 Jul 2013 01:47:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-call-java-functions-tomcat-env/</guid><description>
很早很久以前写过一篇 &lt;a title="XSLT 调用 Java 的类方法" href="https://yanbin.blog/xslt-call-java-method" target="_blank" rel="noopener"&gt;
在 xslt 调用 java 方法的日志 XSLT 调用 Java 的类方法&lt;/a&gt;, 其中介绍是使用 org.apache.xalan.processor.TransformerFactoryImpl
实现的例子，JDK 本身就是用这个的。但是在 Tomcat 环境下，它有它自己的 XSLT 默认实现是 net.sf.saxon.TransformerFactoryImpl，
这时候 XSLT 中的内容略有不同，而且显示更简便一些。&lt;/p&gt;
&lt;br/&gt;
这么说吧, xalan 支持以下两种调用 java 方法的方式，而 saxon 只认得第二种&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. 声明到包，调用时带上类名&lt;/strong&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;xsl:stylesheet&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsl=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://www.w3.org/1999/XSL/Transform&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;xmlns:java=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;cc.unmi.commons&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;exclude-result-prefixes=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;xsl:output&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;xml&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;user/name&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;java:XsltFunctions.replace(.)&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Using Network Link Conditioner in iOS 6</title><link>https://yanbin.blog/ios6-network-link-conditioner/</link><pubDate>Sun, 23 Jun 2013 15:00:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ios6-network-link-conditioner/</guid><description>
来到芝加哥后好久没写博客了，有些文章本想翻译后介绍给大家，可如今越发没先前那翻译的意愿了，所以不妨直接贴出原文如下：&lt;br/&gt;
&lt;br/&gt;
I &lt;a title="Slow Your App's Roll" href="http://www.neglectedpotential.com/2012/05/slow-your-apps-roll/"&gt;previously covered&lt;/a&gt;
using Network Link Conditioner to test how your app does in less-than-ideal network scenarios.
One of the inconveniences of using it is in order to test your app on a device, you have to take the extra steps to
configure a proxy on your computer that your device can connect to over a wi-fi connection. Well, not anymore. With iOS 6,
Apple has given us Network Link Conditioner right on the device.&lt;br/&gt;
&lt;br/&gt;
To get started with Network Link Conditioner, first your device must be set up as a developer device.
If you haven’t done this yet, you’ll need to go to Xcode, open Organizer (Shift-Command-2), select your device,
and click the button that says Use for Development to add the device your developer account. Once you’ve done that,
you should be able to open Settings on your device and see a Developer menu near the bottom, just above the apps list.
Tapping on that reveals some helpful settings, Network Link Conditioner Status obviously being the one relevant to
our interests here.</description></item><item><title>扩展 JUnit 4，使用定制的 Runner</title><link>https://yanbin.blog/extend-junit-4-customized-runner/</link><pubDate>Wed, 17 Apr 2013 02:59:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/extend-junit-4-customized-runner/</guid><description>
JUnit 的测试用例总是由 Runner 去执行，JUnit 提供了 @RunWith 这个测试类的 Annotation, 可来指定自定义的 Runner。如果未指定特别的  Runner，那么会采用默认的 Runner，可能不同的环境，如 Eclipse，控制台下会有不同的默认 Runner。&lt;/p&gt;
&lt;br/&gt;
如果不清楚 Runner 是什么，那么可能见过 @RunWith(SpringJUnit4ClassRunner.class) 这个东西，它有助你加载 Spring 的配置文件，及与 Spring 相关的事物。&lt;br/&gt;&lt;br/&gt;
那么自定义的 Runner 有什么用呢？它可以截获到 @BeforeClass, @AfterClass, @Before, @After 这些事件，也就是能在测试类开始和结束执行前后，每个测试方法的执行前后处理点事情。&lt;br/&gt;&lt;br/&gt;
比如说从外部读取内容进行初始化测试数据，而且 JUnit 本身就提供了 @RunWith(Parameterized.class)  这个参数化 Runner，用了为带参数测试方法循环填充数据进行测试。JUnit 的参数化测试比 C# 还是要笨拙一些，C# 直接用方法注解一行行设置参数，我想 JUnit 稍加定制的话也行的。</description></item><item><title>使用 JDK 5 后的线程并发，Callable, Future, ExecutorServie ...</title><link>https://yanbin.blog/jdk-5-concurrent-callable-futuretask-etc/</link><pubDate>Wed, 10 Apr 2013 23:53:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-5-concurrent-callable-futuretask-etc/</guid><description>
被问及 Java 多线程，多会想到 Thread, Runnable，更通常是用 new Thread(){public void run(){...}}.start() 来启动一个线程。那都是 JDK 1.5 之前的年代了，现在还这么回答就 Out 了。用用  JDK 1.5 给我们带来的  java.util.concurrent 吧，更酷了。这里不涉及它的并发集合类，同步互斥机制，只说线程及线程池的应用举例。&lt;/p&gt;
&lt;br/&gt;
&lt;span style="font-size: x-large; color: #ff0000;"&gt;1. &lt;/span&gt;新的启动线程的方式：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;callable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;callable executed.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FutureTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FutureTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;do your things here&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>正则表达式之环视(Lookaround)[转]</title><link>https://yanbin.blog/regexlookaround/</link><pubDate>Wed, 13 Mar 2013 03:18:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/regexlookaround/</guid><description>
&lt;strong&gt;&lt;span style="font-size: large;"&gt;1       环视基础&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
环视只进行子表达式的匹配，不占有字符，匹配到的内容不保存到最终的匹配结果，是零宽度的。环视匹配的最终结果就是一个位置。&lt;br/&gt;&lt;br/&gt;
环视的作用相当于对所在位置加了一个附加条件，只有满足这个条件，环视子表达式才能匹配成功。&lt;br/&gt;&lt;br/&gt;
环视按照方向划分有顺序和逆序两种，按照是否匹配有肯定和否定两种，组合起来就有四种环视。顺序环视相当于在当前位置右侧附加一个条件，而逆序环视相当于在当前位置左侧附加一个条件。&lt;br/&gt;&lt;br/&gt;
&lt;table border="1" cellspacing="0" cellpadding="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="130"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;表达式&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="416"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;说明&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="130"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;(?&amp;lt;=Expression)&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="416"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;逆序肯定环视，表示所在位置左侧能够匹配Expression&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="130"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;(?&amp;lt;!Expression)&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="416"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;逆序否定环视，表示所在位置左侧不能匹配Expression&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="130"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;(?=Expression)&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="416"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;顺序肯定环视，表示所在位置右侧能够匹配Expression&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="130"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;&lt;strong&gt;(?!Expression)&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="416"&gt;
&lt;p align="left"&gt;&lt;span style="color: #99cc00;"&gt;顺序否定环视，表示所在位置右侧不能匹配Expression&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;</description></item><item><title>Scala 的学习笔记系列(持续更新中)</title><link>https://yanbin.blog/scala-study-collection/</link><pubDate>Wed, 27 Feb 2013 22:40:17 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-study-collection/</guid><description>
最近学习 Scala，因它是灵活的函数式编程，还有就是能为 PlayFramework 2.0 服务，看的是 《Programming in Scala》 那本书，并记下自己认为值得记录的东西，列举&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-multiple-method-results"&gt;Scala 用元组/列表类型实现多返回值的函数&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-parameterless-uniform-acces-principle"&gt;Scala 无参数方法和统一访问原则&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-apply-update-methods"&gt;Scala 的 apply 和 update 方法的应用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-trait-super-dynamic-binding"&gt;Scala 特质(trait) 的 super 方法调用是动态绑定的&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-yield-samples-for-loop"&gt;Scala 的 yield 例子 (for 循环和 yield 的例子)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-ensuring-usage"&gt;Scala 中 ensuring 方法的使用说明 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-2-10-0-new-features-implicit-class" target="_blank"&gt;Scala 2.10.0 新特性之使用隐式类进行类型隐式转换&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-2-10-0-new-feature-dynamic" target="_blank"&gt;Scala 2.10.0 新特性之动态属性、方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/scala-2-10-0-feature-string-interpolation" target="_blank"&gt;Scala 2.10.0 新特性之字符串插值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/sbt-np-create-folders-build-sbt" target="_blank"&gt;使用 sbt 的 np 插件自动创建目录结构和 build.sbt 文件&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
关于 PlayFramework 2.0 的应用请参考我的另一个系列：&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://my.oschina.net/u/2791/blog/85220"&gt;Play 2.0 中文资料--翻译附注解(持续更新中)&lt;/a&gt;</description></item><item><title>Play 2.0 中文资料--翻译附注解(持续更新中)</title><link>https://yanbin.blog/play2-0-tutorials-cn-collection/</link><pubDate>Wed, 27 Feb 2013 20:43:54 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-collection/</guid><description>
最近在研究 PlayFramework 2.0，主要关注在使用 Scala 来写 Play 2.0 的应用，下面是从官方文档进行的翻译，并加入了大量的自己的理解与备注( &lt;span style="color: #003399;"&gt;Unmi 注: 起头的蓝色文字&lt;/span&gt;)&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-getting-started"&gt;Play 2.0 中文资料 - 开篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-installing"&gt;Play 2.0 中文资料 - 安装&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-creating-application"&gt;Play 2.0 中文资料 - 创建一个新的应用&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-anatomy-application"&gt;Play 2.0 中文资料 - 剖析 Play 2.0 应用&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/httpunmi-ccplay2-0-tutorials-cn-creating-application-using-console"&gt;Play 2.0 中文资料 - 使用 Play 2.0 控制台&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-http-programming"&gt;Play 2.0 中文资料 - HTTP 编程&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-setting-up-ide"&gt;Play 2.0 中文资料 - 搭建你偏爱的 IDE&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-action-controller-result"&gt;Play 2.0 中文资料 - Action, Controller 和 Result&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-http-routes"&gt;Play 2.0 中文资料 - HTTP 路由&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-manipulating-results"&gt;Play 2.0 中文资料 - 操控 Result&lt;/a&gt;&amp;nbsp;</description></item><item><title>WordPress 升级到 3.5 之后 Widget 无法拖动, 设置的解决办法</title><link>https://yanbin.blog/wordpress-3-5-widget-non-editable/</link><pubDate>Tue, 05 Feb 2013 13:45:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-3-5-widget-non-editable/</guid><description>
WordPress 升级到 3.5 之后发现 Widget 无法拖动，也无法展开设置。在之前也发生过类似情况，一般来说是某个插件或主题的影响，原来一一试过，才知是因为 Use Google Libraries 这个插件在作祟，禁了就能恢复正常。这回也以为旧病复为，禁为 Use Google Libraries，仍然无济于事。&lt;/p&gt;
&lt;br/&gt;
后通过 FireFox 的 Firebug Console Errors 中看到浏览 Widget 编辑页面时出现脚本错误：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Error: Syntax error, unrecognized expression: #enh_rp-show-select-%i%&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;...eturn t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=...&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
点击进去，才发现坏在了  jQuery 1.8.3 上去了，jQuery 我想应该是久经考验的革命 LD 吧，自然黑锅就得让 WordPress 来背了，这时的 WordPress 只能算作是临时工，更时髦的名称是合同工。&lt;br/&gt;&lt;br/&gt;
查了下，这个问题在 WordPress 的坛子里有不少人上报，有人列出了些解决方案，拣亲身试过的在这里供出来。</description></item><item><title>简单实现 Java 的 Tuple 元组数据类型</title><link>https://yanbin.blog/simple-java-tuple-datatype/</link><pubDate>Mon, 04 Feb 2013 06:32:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/simple-java-tuple-datatype/</guid><description>
元组类型，即 Tuple 常在脚本语言中出现，例如 Scala 的 ("Unmi", "fantasia@sina.com", "blahbla")。元组可认为是象数组一样的容器，它的目的是让你方便构造和引用，例如 Pair 可认为是一个只能存两个元素的元组，像是个 Map； 真正的元组应该是可以任意多个元素的容器，绕来绕去，它还是数组，或列表，所以我们实现上还是要借助于数组或是列表。&lt;/p&gt;
&lt;br/&gt;
先看 Scala 中什么是元组：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;tuple&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;fantasia@sina.com&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;blahblah...&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;输出&lt;/span&gt; &lt;span class="nc"&gt;Unmi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Scala 中访问从 1 开始，用 ._1 方式来访问其中的元素。&lt;br/&gt;&lt;br/&gt;
参照于此，写出一个 Java 版本的 Tuple，为增长你的键盘的使用寿命，我们把方法名也缩短了，例如 make 缩写为 mk，引用元素的方法名为 _，仍然保持 Java 的习惯，索引从 0 开始：</description></item><item><title>Java 里把 InputStream 转换成 String 的几种方法</title><link>https://yanbin.blog/java-convert-inputstream-to-string/</link><pubDate>Fri, 01 Feb 2013 05:35:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-convert-inputstream-to-string/</guid><description>
我们在 Java 中经常会碰到如何把 InputStream 转换成 String 的情形，比如从文件或网络得到一个 InputStream，需要转换成字符串输出或赋给别的变量。&lt;/p&gt;
&lt;br/&gt;
未真正关注这个问题之前我常用的办法就是按字节一次次读到缓冲区，或是建立 BufferedReader 逐行读取。其实大可不必费此周折，我们可以用 Apache commons IOUtils，或者是 JDK 1.5 后的 Scanner，还可用 Google  Guava 库的 CharStreams。到了 JDK7，若要从文件中直接得到字符串还能用 java.nio.file.Files#readAllLines 和 java.nio.file.Files#readAllBytes 方法。&lt;br/&gt;&lt;br/&gt;
下面看各个例子，为能够实际用运，例子写在 main 方法里，并从文件获得一个 InputStream，代码中把可能要捕获的异常抛出来。再就是注意处理输入输出流时有涉及到字符集，字符集乱了就乱码了，默认字符集是 System.getProperty("file.encoding")，通常我们都用 UTF-8，异常 UnsupportedEncodingException 继承自 IOException。&lt;br/&gt;&lt;br/&gt;
下面的 6 个方法中应该有一个你能看得上的吧，用 Groovy，Scala 的除外，若未找到一个遂意的，告诉我，你有好办法更应该告诉我。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. 使用 JDK 5 的 Scanner&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>Objective-C 类方法 load 和 initialize 的区别</title><link>https://yanbin.blog/objective-c-different-between-load-initialize/</link><pubDate>Sun, 27 Jan 2013 10:08:54 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-different-between-load-initialize/</guid><description>
过去两个星期里，为了完成一个工作，接触到了NSObject中非常特别&lt;a href="http://developer.apple.com/library/ios/#documentation/General/Conceptual/DevPedia-CocoaCore/ClassMethod.html"&gt;的两个类方法(Class Method)&lt;/a&gt;。它们的特别之处，在于iOS会在运行期提前并且自动调用这两个方法，而且很多对于类方法的规则（比如继承，&lt;a href="http://developer.apple.com/library/ios/#documentation/General/Conceptual/DevPedia-CocoaCore/Category.html"&gt;类别(Category)&lt;/a&gt;）都有不同的处理。&lt;/p&gt;
&lt;br/&gt;
而因为这两个方法是在程序运行一开始就被调用的方法，我们可以利用他们在类被使用前，做一些预处理工作。比如我碰到的就是让类自动将自身类名保存到一个NSDictionary中。&lt;br/&gt;&lt;br/&gt;
先来看看NSObject Class Reference里对这两个方法说明：&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;&lt;a href="http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html"&gt;+(void)initialize&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
The runtime sends &lt;code&gt;initialize&lt;/code&gt; to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.
&lt;/blockquote&gt;
&lt;h3&gt;&lt;a href="http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html"&gt;+(void)load&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
The &lt;code&gt;load&lt;/code&gt; message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.&lt;br /&gt;
The order of initialization is as follows:</description></item><item><title>Xcode 4 中使用自定义字体，非 Outlet 方式</title><link>https://yanbin.blog/xcode-4-use-custom-fonts/</link><pubDate>Sun, 27 Jan 2013 03:01:05 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-4-use-custom-fonts/</guid><description>
在 Xcode 3 的时代，如果要使用自定义字体，只要把字体安装到系统中，就能够在 Interface Builder  中字体列表中选择到，当然需要把字体文件加到项目中，并且在 Xxx-Info.plist 文件中列举出来。&lt;/p&gt;
&lt;br/&gt;
而到了 Xcode 4 后，要用个自定义的字体反而更难了，字体安装到了系统中，它也不会出现在 Interface Builder 中的字体列表，必须用 setFont 为需要的控件应用自定义字体，这是一个浩大的工程，要么遍历控件，要么就得为控件与 IBOutletCollection 关联起来。&lt;br/&gt;&lt;br/&gt;
难道是 Apple 不希望看到应用中各色字体令人眼花缭乱的景象，所以制造点障碍。也不知道新的  Interface Builder 中的字体列表由哪得来的，是否能把自己的字体出现在该列表中呢？这个嘛，有点难。&lt;br/&gt;&lt;br/&gt;
既然不想使用 setFont 方法对 Xib 中的众多的控件一一设置字体，那就得另寻 Hack 方法了，幸好有了 &lt;a href="https://github.com/0xced/FontReplacer" target="_blank" rel="noopener"&gt;FontReplacer&lt;/a&gt; 的方法。FontReplacer 是为 UIFont 做的一个 Category，它的实现原理是利用 &lt;span style="color: #800000;"&gt;method_exchangeImplementations&lt;/span&gt; 机制把 UIFont 的 &lt;span style="color: #800000;"&gt;fontWithName:size:&lt;/span&gt; 和 &lt;span style="color: #800000;"&gt;fontWithName:size:traits:&lt;/span&gt; 换成自定义的方法，然后在自定义的方法中按照预设置的字体映射，当遇到某个系统字体 A 时，换成自定义的字体  Custom-A。&lt;br/&gt;&lt;br/&gt;
而你所要做的就是在 Interface Builder 中寻找字体，自己记住当选用了 系统字体 A，运行时会是 Custom-A 字体，系统字体 B 运行时会是 Custom-B，根据需要自己映射就行。一般方便起见，我们会选择一种字体(Family)，然后用它的 Style 来与我们的自定义字体对应。这样只需要 Interface Builder 里像往常一样设置字体就行了，从而达到一劳永逸。</description></item><item><title>使用 sbt 的 np 插件自动创建目录结构和 build.sbt 文件</title><link>https://yanbin.blog/sbt-np-create-folders-build-sbt/</link><pubDate>Sat, 26 Jan 2013 09:55:49 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sbt-np-create-folders-build-sbt/</guid><description>
通常我们要创建一个 sbt 项目需要经历以下几步：&lt;/p&gt;
&lt;blockquote&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;$&lt;/span&gt; mkdir testscala &amp;amp;&amp;amp; cd testscala      #创建项目目录 testscala，并进入该目录&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;testscala $&lt;/span&gt;  mkdir -p src/{main,test}/scala   #创建源文件目录结构，可能还需要 mkdir -p src/{main,test}/resources&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;$&lt;/span&gt; vi build.sbt # 填入基本的信息 (name, organization, version)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;$&lt;/span&gt; sbt&lt;/span&gt;
&lt;/blockquote&gt;
这一看怎么这么复杂啊，特别是习惯了 Maven 的同学们更会这么觉得。Maven 不仅可应用各种模板来创建不同类型的工程，完整的工程目录结构都有了。&lt;br/&gt;&lt;br/&gt;
那么 sbt 能不能做到自动创建目录和配置文件呢，答案是: 本身不能。你可以基于一个项目模板目录结构拷来拷去。前面说是 sbt 本身不能，要知道 sbt 是一个插件化更彻底的玩意，如今的 0.12.2 的 sbt.zip 才 1M 大小，其余全赖插件帮你插来插去了。&lt;br/&gt;&lt;br/&gt;
因此，我们找来的 np(New Project) 插件 &lt;a href="https://github.com/softprops/np" target="_blank" rel="noopener"&gt;https://github.com/softprops/np&lt;/a&gt; 帮我们创建工程的目录结构和 build.sbt 文件。它能让我们创建项目的过程简化为以下两步操作：&lt;br/&gt;
&lt;blockquote&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;$&lt;/span&gt; mkdir testscala &amp;amp;&amp;amp; cd testscala   #这一步还是不能少&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #0000ff;"&gt;$&lt;/span&gt; &lt;span style="color: #800000;"&gt;sbt&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;&lt;span style="color: #0000ff;"&gt;&amp;gt;&lt;/span&gt; np name:testscala org:cc.unmi   #这样 testscala 下 {main.test}/{scala,resources} 和  build.sbt 文件都有了&lt;br /&gt;
&lt;/span&gt;
&lt;/blockquote&gt;
np 的安装及使用可以参考：&lt;a href="https://github.com/softprops/np" target="_blank" rel="noopener"&gt;https://github.com/softprops/np&lt;/a&gt;，就是下面的步骤：</description></item><item><title>Objective-C 实现自己的 Subscripting Methods 下标方法</title><link>https://yanbin.blog/objective-c-implement-subscripting-methods/</link><pubDate>Sat, 26 Jan 2013 00:56:47 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-implement-subscripting-methods/</guid><description>
在前一篇 &lt;a title="Xcode 4.4/4.5 新特性 / LLVM 4.0 新语法" href="https://yanbin.blog/xcode-4-4-4-5-new-features" target="_blank" rel="noopener"&gt;Xcode 4.4/4.5 新特性 / LLVM 4.0 新语法&lt;/a&gt; 提到了 Xcode 4.4 之后对数组和字典的操作可以用下面的方式：&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;id value = array[i];&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;array[i] = newObj;&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;id value = dictionary[@"key"];&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;dictionary[@"key"] = newObj;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
上面几行语句实际调用的方法分别对应如下：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;NSArray : - (id)objectAtIndexedSubscript: (NSUInteger)index;&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;NSMutableArray : - (void)setObject: (id)obj atIndexedSubscript: (NSUInteger)index;&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;NSDictionary : - (id)objectForKeyedSubscript: (id &amp;lt;NSCopying&amp;gt;)key;&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;NSMutableDictionary : - (void)setObject: (id)anObject forKeyedSubscript: (id &amp;lt;NSCopying&amp;gt;)aKey;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
所以，如果你希望自己定义的类可以支持 obj[i] 和 obj[@"key"] 的方式来读取和设置值的话，就可以实现上面相应的方法，只是要处理好你自己参数和结果类型。现在就来看看下面自定义类 TestClass 的实现和执行效果：</description></item><item><title>Xcode 4.4/4.5 新特性 / LLVM 4.0 新语法</title><link>https://yanbin.blog/xcode-4-4-4-5-new-features/</link><pubDate>Mon, 21 Jan 2013 13:09:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-4-4-4-5-new-features/</guid><description>
跟着苹果干的好处就是，有什么新特性就尽快的就享用它们，这非常符常程序员们的学习心态。不像国内的某些技术领导，他们很领会某种舞台上的精髓 -- 稳定压倒一切 ，JDK 1.6 都出来了，还不少项目还牢牢固守在 JDK 1.3 上，程序员真是无言而又苦 B 啊。当然你在使用  Objective-C 时能时刻得到更新也得利于 Obj-C 完全由 Apple 主导，免了不少口水，瞧瞧 C++ 到 C++11，历时 13 年啊。&lt;/p&gt;
&lt;br/&gt;
OK，言归正传，Xcode 当前版本 4.5.2，来看下自 4.4 以来它给我们带来了什么语言上的新特性。每个版本有新特性时一定要看官方的  &lt;a href="developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/WhatsNewXcode" target="_blank" rel="noopener"&gt;What's New in Xcode&lt;/a&gt;。这么说来 Xcode 4.4 也算是个关键性的版本更新。&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;@ 不光可用于创建 NSString，还简单化了 NSNumber，NSArray 和  NSDictionary 对象的创建，一一来看。&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;@ 创建 NSNumber 实例&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
从 Xcode 4.4 起，无段  [NSNumber numberWithInt: 10] 这么写了，@10 就完事，下面是各种数值类型的 NSNumber 创建方式：</description></item><item><title>Scala 2.10.0 新特性之字符串插值</title><link>https://yanbin.blog/scala-2-10-0-feature-string-interpolation/</link><pubDate>Sun, 20 Jan 2013 16:11:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-2-10-0-feature-string-interpolation/</guid><description>
Scala 在处理字符串方面终于也与时俱进了，引入了字符串插件，这在许多脚本语言中就早已这么做了。有了这个特性，字面量字符串和变量连接可以不需要总是用加号进行丑陋的连接了。有些脚本语言会是只对双引号字符串解析其中的变量，单引号的不会，当然Scala 是要区分字符串和字符类型。&lt;/p&gt;
&lt;br/&gt;
直白的讲字符串插值就是，&lt;span style="color: #800000;"&gt;val name="Unmi"; val greeting=s"Hello $name"; greeting 的值就是 "Hello name"&lt;/span&gt;。产生效应的就是那个 s 方法。&lt;br/&gt;&lt;br/&gt;
Scala 2.10.0 为我们提供了三种字符串插值的方式，分别是 s, f 和  raw。它们都是定义在 &lt;a href="http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.StringContext" target="_blank" rel="noopener"&gt;StringContext&lt;/a&gt; 中的方法。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;s 字符串插值器&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
可以解析字符串中的变量，可以调用方法，还能进行计算。实际调用的是  StringContext 中的  s 方法。</description></item><item><title>Scala 2.10.0 新特性之动态属性、方法</title><link>https://yanbin.blog/scala-2-10-0-new-feature-dynamic/</link><pubDate>Sat, 19 Jan 2013 13:34:15 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-2-10-0-new-feature-dynamic/</guid><description>
最早的介绍 Scala 语言的书都是以 Scala 的静态类型系统为傲。Scala 也算是个脚本语言，却不像其他许多脚本语言那样类型是动态的，只有执行时才确定，而 Scala 在执行前就确定了类型，比如依赖于比 Java 更强大的类型推断行为。&lt;/p&gt;
&lt;br/&gt;
静态类型不光是变量类型是确定的，还有比如在使用  qual.sel 时，sel 这个属性或是方法(Scala 的访问一致性，属性和方法有时候并没有那么大的区别)必须在 qual 的类型中声明了的。&lt;br/&gt;&lt;br/&gt;
Scala 思考再三还是加入了 Dynamic Types，这个特性在 Scala 2.9 中是试验性的，必须用 &lt;span style="color: #800000;"&gt;-Xexperimental&lt;/span&gt; 进行开启，到了 Scala 2.10.0 中，只有代码中 &lt;span style="color: #800000;"&gt;import scala.language.dynamics&lt;/span&gt; 就可用了，或是编译时加 &lt;span style="color: #800000;"&gt;-language:dynamics&lt;/span&gt; 选项。&lt;br/&gt;&lt;br/&gt;
虽然 Scala 2.10.0 加进了 Dynamic Types 特性，但 Scala 仍然是静态类型的语言，因为在编译器同样会检查多出来的类型。&lt;br/&gt;&lt;br/&gt;
有了 Dynamic Types 之后，Scala 又可更 DSL 了，方法名的动态上可以让它随时包括深刻的业务含义。相比 Java 的 DSL 的能力就太逊了，我们几乎无法在 Java 面前提 DSL 这回事。</description></item><item><title>使用 Jackson 把 Java 对象转换成 JSON 字串</title><link>https://yanbin.blog/jackson-java-object-json-string/</link><pubDate>Sat, 19 Jan 2013 00:21:07 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jackson-java-object-json-string/</guid><description>
原来 Java 项目中用的 JSON 组件库主要是 &lt;a href="http://code.google.com/p/google-gson/" target="_blank" rel="noopener"&gt;Gson&lt;/a&gt; 和 &lt;a href="http://json-lib.sourceforge.net/" target="_blank" rel="noopener"&gt;json-lib&lt;/a&gt;，Gson 算是很错的库，json-lib 略显寒碜。好啦，最近 Play 2.x 中弃用了 Gson 而采纳了 &lt;a href="http://jackson.codehaus.org" target="_blank" rel="noopener"&gt;Jackson&lt;/a&gt;，所以现在就来打探一下 Jackson，踩个点吧。&lt;/p&gt;
&lt;br/&gt;
Jackson 号称非常高的性能，听说比另两位兄弟 Gson 和 json-lib 高出一大截，我没有亲测，可是有心人做了，看这个链接 &lt;a href="http://wangym.iteye.com/blog/738933" target="_blank" rel="noopener"&gt;两款JSON类库Jackson与JSON-lib的性能对比(新增第三款测试)&lt;/a&gt; 中的数据。2010 年 8 月份的测试结果，不知现在随着版本的变更是否仍然保持着这种悬殊。&lt;br/&gt;&lt;br/&gt;
通常我会在把文章开头塞丰满，做足前戏，并不是因为在天涯混习惯了的缘故，况且我在天涯总是讷于言的; 在这里，自己的地盘自己作主，不会有要求码足多少字才能发表的自虐性需求，仅仅是让本文在主页上显示时的的概要不空洞而已，可以简单粗暴的称之为废话。&lt;br/&gt;&lt;br/&gt;
了了，先了解 Jacson 最贴近实际应用场景的应用，即 Jackson 怎么把一个 Java 生成对应的 JSON 字符串，看看前面的文字有这么多了，直接上一段代码吧，而后再慢慢假设与分解：</description></item><item><title>Scala 2.10 新特性之使用隐式类进行类型隐式转换</title><link>https://yanbin.blog/scala-2-10-new-features-implicit-class/</link><pubDate>Fri, 18 Jan 2013 12:50:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-2-10-new-features-implicit-class/</guid><description>
&lt;p&gt;在 Scala 2.10.0 之前类型的隐式转换必须通过隐式方法来完成，现在的 Scala 可以用 implicit class 来声明类，
并且它的主构造器 (Primary Constructor) 只有一个参数时，就可以用来把参数隐式转换成该类型。
&lt;/p&gt;
&lt;p&gt;能理解上面什么意思，知道怎么用隐式类吗？ 就上面那句话，我自己都不知道在说什么。&lt;/p&gt;
&lt;p&gt;首先要知道 Scala 先前是怎么依据隐式方法进行类型的隐式转换，其次又何谓主构造器呢？ 关于 Scala 2.10.0 的 implicit class，
官方的解释在这里 &lt;a title="Scala 2.10.0 implicit class" href="http://docs.scala-lang.org/sips/pending/implicit-classes.html" target="_blank"&gt;
http://docs.scala-lang.org/sips/pending/implicit-classes.html
&lt;/a&gt;
再多说只能让大家更摸不到头，实例演示是王道：&lt;/p&gt;
&lt;p&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2013/scala-2-10-new-features-implicit-class/scala-2.10.0-implicit-class.png"
width="750px"
/&gt;</description></item><item><title>Play 2.0 中文资料 - 应用程序全局对象</title><link>https://yanbin.blog/play2-0-tutorials-cn-application-global-object/</link><pubDate>Thu, 17 Jan 2013 10:30:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-application-global-object/</guid><description>
&lt;strong&gt;&lt;span style="font-size: large;"&gt;应用全局设置&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;全局对象&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
在你的项目中定义一个 &lt;code&gt;Global&lt;/code&gt;&amp;nbsp;对象，它为你处理应用的全局设置.&amp;nbsp;这个对象必须定义在默认包中(即不带 package&amp;nbsp;声明的包).&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;play.api._&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Global&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;GlobalSettings&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;&lt;strong&gt;小贴士:&lt;/strong&gt;&amp;nbsp;你也可使用 &lt;code&gt;application.global&lt;/code&gt; key&amp;nbsp;值在在配置中指定自己的 &lt;code&gt;GlobalSettings&lt;/code&gt;&amp;nbsp;实现类名称.&lt;/blockquote&gt;
&lt;span style="color: #0000ff;"&gt;Unmi&amp;nbsp;注: GlobalSettings.scala&amp;nbsp;放在&amp;nbsp;app&amp;nbsp;目录比较合适，Scala&amp;nbsp;的 package&amp;nbsp;层次与目录并不存在严格的对应关系。&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;要说呢，不带 package&amp;nbsp;声明的写法确实不是什么好习惯，因为其他带 package&amp;nbsp;声明的类无法使用它。见：&lt;a title="为何Java的有包名的类不能引用默认包中的类" href="http://unmi.cc/java-cannot-import-default-package" target="_blank"&gt;&lt;span style="color: #0000ff;"&gt;为何Java的有包名的类不能引用默认包中的类&lt;/span&gt;&lt;/a&gt;。这就对了，本来这个全局对象就不是给别的类用的 － Don't call me, I'll call you。&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;勾住应用启动和停止事件&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
你可以覆盖方法 &lt;code&gt;onStart&lt;/code&gt; 和 &lt;code&gt;onStop&lt;/code&gt;，它们会在应用的生命周期启动，停止时得到通知:</description></item><item><title>程序员为何喜欢半夜工作？</title><link>https://yanbin.blog/why-programmer-liking-working-at-night/</link><pubDate>Wed, 16 Jan 2013 20:23:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/why-programmer-liking-working-at-night/</guid><description>
有种流传甚广的说法：如果你想将咖啡豆磨成代码，那就去买台“程序员”好了。确实，如果你在一家编程高手云集的互联网公司工作，问问你身边那些 程序员们，他们一般什么时候效率最高，点子最好？得到的答案，大部分是半夜——不管前半夜，还是后半夜。有些习惯在早上四点起床，然后工作到日出，还有些 人则是习惯在早上四点后才睡觉。&lt;br/&gt;
&lt;br/&gt;
如果只是为了避开无谓的干扰，何不关上门，带上耳机，为何一定要在深夜工作呢？&lt;br/&gt;
&lt;br/&gt;
我认为主要有三条原因：创作者的日程表、困倦的大脑，以及闪亮的屏幕。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;创作者的日程表&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
通常来说，这个世界有两种日程表，一种属于管理者，一种属于创作者。传统的管理者日程表将一天按小时划分，而通常十分钟的打扰，对于程序员来说，可能需要一个小时去重新整理思路。</description></item><item><title>Play 2.0 中文资料 - Play WS API</title><link>https://yanbin.blog/play2-0-tutorials-cn-ws-api/</link><pubDate>Wed, 16 Jan 2013 11:43:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-ws-api/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-ws-api"&gt;Play WS API &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.playframework.org/documentation/2.0.4/ScalaOpenID"&gt;Connecting to OpenID services &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.playframework.org/documentation/2.0.4/ScalaOAuth"&gt;Accessing resources protected by OAuth &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
有时候我们要在 Play 应用中调用其他的 HTTP 服务. Play 是通过它的 &lt;code&gt;play.api.libs.ws.WS&lt;/code&gt; 库对此支持的, 该库提供了进行异步的 HTTP 调用.&lt;br/&gt;&lt;br/&gt;
任何由 &lt;code&gt;play.api.libs.ws.WS&lt;/code&gt; 发出的调用都必须返回 &lt;code&gt;Promise[play.api.libs.ws.Response],&lt;/code&gt; 我们可以接着可用 Play 的异步机制来处理它.&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 这里有必须挪出 Play 1 和 Play 2 的 WS 稍加对比: &lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Play 1 中  play.libs.WS 类，Play 2 里是 play.api.libs.ws.WS，Play 2 中许多给外部调用的 API 都放在 play.api.libs 包中了。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt; Play 1 的 play.libs.WS.WSRequest  中有 get():HttpResponse, getAsync():Promise&amp;lt;HttpResponse&amp;gt;, put():HttpResponse, putAsync():Promise&amp;lt;HttpResponse&amp;gt;，它把同步和异步调用分得很清楚。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;而 Play 2 的 play.api.libs.ws.WS.WSRequestHolder 里只有 get(): Promise&amp;lt;Response&amp;gt;, post(): Promise&amp;lt;Response&amp;gt; 这样方法，在这个位置上，它不再分同步还是调用，这个任务交给 Async 方法去决定。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;对异步请求的数据获仍然是由 Action 处理，如 Play 1 中 Action 的各个 await() 方法; Play 2 里的 Action 用 Async 方法。</description></item><item><title>Play 2.0 中文资料 - Play 缓存 API</title><link>https://yanbin.blog/play2-0-tutorials-cn-cache-api/</link><pubDate>Wed, 16 Jan 2013 10:46:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-cache-api/</guid><description>
默认的 Cache API 实现是用的 &lt;a href=" http://ehcache.org/"&gt;EHCache&lt;/a&gt;. 你也可以通过插件来提供自己的缓存实现.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;访问 Cache API&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
缓存 API 由 &lt;code&gt;play.api.cache.Cache&lt;/code&gt; 对象提供. 它需要注册一个缓存插件.&lt;br/&gt;
&lt;blockquote&gt;
&lt;strong&gt;注:&lt;/strong&gt; Cache API 尽力保持自身最小化，而允许插入多种实现. 如果你需要一个更具体的 API, 那就通过你自己的缓存插件来使用它.
&lt;/blockquote&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: EhCachePlugin 的注册方式可以查看 play_x.x.x.jar(比如 $PLAY_HOME/repository/local/play/play_2.9.1/2.0.4/jars/play_2.9.1.jar) 包中的 play.plugins 中的内容：&lt;/span&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;100:play.api.i18n.MessagesPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;200:play.api.db.BoneCPPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;300:play.db.ebean.EbeanPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;400:play.db.jpa.JPAPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;500:play.api.db.evolutions.EvolutionsPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;600:play.api.cache.EhCachePlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;1000:play.api.libs.concurrent.AkkaPlugin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;10000:play.api.GlobalPlugin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;这是 Play2.0.4 默认注册的插件，其中就包含了 600:play.api.cache.EhCachePlugin 插件。</description></item><item><title>Play 2.0 中文资料 - 编写功能性测试</title><link>https://yanbin.blog/play2-0-tutorials-cn-writing-functional-tests/</link><pubDate>Wed, 16 Jan 2013 09:36:21 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-writing-functional-tests/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-writing-tests"&gt;编写测试 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-writing-functional-tests"&gt;编写功能性测试 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;测试模板&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
因为模板自身就是一个标准的 Scala 函数, 所以你可以在你的测试代码中直接执行它, 并检查结果:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;render index template&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Coco&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;contentAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello Coco&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;测试你的 controllers&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
你可以通过提供一个 &lt;code&gt;FakeRequest 来调用任何 Action 代码&lt;/code&gt;:</description></item><item><title>Play 2.0 中文资料 - 编写测试</title><link>https://yanbin.blog/play2-0-tutorials-cn-writing-tests/</link><pubDate>Tue, 15 Jan 2013 11:36:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-writing-tests/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-writing-tests" target="_blank"&gt;编写测试 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-writing-functional-tests" target="_blank"&gt;编写功能性测试 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi&amp;nbsp;注:&amp;nbsp;当你采用 TDD&amp;nbsp;进行开发过一段时间后，很快你就会对&amp;nbsp;TDD&amp;nbsp;产生依恋了。没直正去实践过&amp;nbsp;TDD，对于测试先行的提法只会觉得是一种理想状态。其实不然，从此开发模式变成&amp;nbsp;测试-&amp;gt;实现-&amp;gt;重构，同时也指导你的设计，基础架构并不需要那么稳定，它总在应需而变。所以&amp;nbsp;TDD&amp;nbsp;让你不该总在项目中后期抱怨架构设计不好之类的事，因为主动权一直在你。废话一堆后就是：测试真的很重要，尽快进入角色吧。&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: large;"&gt;测试你的应用程序&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
测试源代码文件必须放置在你的应用的 &lt;code&gt;test&lt;/code&gt;&amp;nbsp;目录下.&amp;nbsp;你可以在 Play&amp;nbsp;控制台下用&amp;nbsp; &lt;code&gt;test&lt;/code&gt; 和 &lt;code&gt;test-only&lt;/code&gt;&amp;nbsp;任务来运行它们.&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;使用 specs2&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
测试一个 Play 2&amp;nbsp;应用默认的方式是采用 &lt;a href=" http://etorreborre.github.com/specs2/"&gt;specs2&lt;/a&gt;.&lt;br/&gt;
&lt;br/&gt;
单元规格须继承自 &lt;code&gt;org.specs2.mutable.Specification&lt;/code&gt; 特质并应用 should/in&amp;nbsp;的格式:</description></item><item><title>Play 2.0 中文资料 - 使用 XML</title><link>https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-xml-requests/</link><pubDate>Tue, 15 Jan 2013 11:20:40 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-xml-requests/</guid><description>
&lt;span style="color: #0000ff;"&gt;Unmi 注: 有了前面对 Play 2.0 的 Json 支持的了解。见：1. &lt;a title="Play 2.0 中文资料 - Play JSON 库" href="https://yanbin.blog/play2-0-tutorials-cn-play-json-library" target="_blank" rel="noopener"&gt;&lt;span style="color: #0000ff;"&gt;Play 2.0 中文资料 - Play JSON 库&lt;/span&gt;&lt;/a&gt;，2. &lt;a title="Play 2.0 中文资料 - 处理和响应 JSON 请求" href="https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests" target="_blank" rel="noopener"&gt;&lt;span style="color: #0000ff;"&gt;Play 2.0 中文资料 - 处理和响应 JSON 请求&lt;/span&gt;&lt;/a&gt;， 3. &lt;a title="Play 2.0 中文资料 - Play JSON 库使用泛型" href="https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics" target="_blank" rel="noopener"&gt;&lt;span style="color: #0000ff;"&gt;Play 2.0 中文资料 - Play JSON 库使用泛型&lt;/span&gt;&lt;/a&gt;。现在来看 Play 2.0 对 XML 的支持就更简单了，原因是 Scala 使用 Json 还需依赖于第三方的库 Jackson，而 Scala 对 XML 的支持直接利益于它的内建语法。如在 Scala 控制台下：&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2013/play2-0-tutorials-cn-handling-and-serving-xml-requests/play-scala-xml.png"
/&gt;
&lt;span style="color: #0000ff;"&gt;上面演示了 Scala XML 遍历 XML，访问属性，文本节点。方法和遍历 JSON 类似，也有 \ 和 \\ 方法。例如 user\"@name" 访问 name 属性。&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;而且有了 Scala 这样的内建语法，想要实现 toXML, fromXML 方法也很简单。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;处理 XML 请求&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
XML 请求是指请求体为一个有效的 XML 数据的 HTTP 请求. 必须用 &lt;code&gt;Content-Type&lt;/code&gt; 头指定 MIME 类型为 &lt;code&gt;text/xml&lt;/code&gt;.</description></item><item><title>Play 2.0 中文资料 - Play JSON 库使用泛型</title><link>https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics/</link><pubDate>Mon, 14 Jan 2013 11:23:14 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;strong&gt;使用 Json &lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-play-json-library" target="_blank" rel="noopener"&gt;Play Json 库 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests" target="_blank" rel="noopener"&gt;处理 Json 请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics"&gt;Play Json 库使用泛型 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;概述&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
当使用基于 JSON 库的 typeclass&lt;span style="color: #0000ff;"&gt;(Unmi: typeclass 还没摸准翻译成什么词较合适，此前译作 类型类，觉得有点不妥，所以暂时保留原样)&lt;/span&gt; 时，可能会把泛型支持包含进这些 typeclass 中来. 针对基于终端控制查询参数，使用基本的结构作为查询结果的 REST API 来说可能是一个很好的应用方式.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;Scala 对泛型的支持&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
给定如下基本的结构作为搜索结果:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SearchResults&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;elements&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 上面  case class 涉及到了 Scala 的样本类的特性，Scala 会给这个类自动添加一些句法：1）添加与类名一致的工厂方法，2）参数列表中的所有参数前隐式获得了 val 前缀，即会由相应的的实例变量保持状态，3）自动添加了 toString, hashCode, 和  equals 方法。</description></item><item><title>Play 2.0 中文资料 - 处理和响应 JSON 请求</title><link>https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests/</link><pubDate>Sun, 13 Jan 2013 06:46:56 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;strong&gt;使用 Json &lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-play-json-library" target="_blank" rel="noopener"&gt;Play Json 库 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests"&gt;处理 Json 请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics" target="_blank" rel="noopener"&gt;Play Json 库使用泛型 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;处理 JSON 请求&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
JSON 请求是一个以有效的 JSON 数据作为请求体的 HTTP 请求. 它必须指定 &lt;code&gt;Content-Type&lt;/code&gt; 头为 &lt;code&gt;text/json&lt;/code&gt; 或是 &lt;code&gt;application/json&lt;/code&gt; 作为 mime 类型.&lt;br/&gt;&lt;br/&gt;
默认的， &lt;code&gt;Action&lt;/code&gt; 使用 &lt;strong&gt;any content&lt;/strong&gt; 作为 Body 解析器, 这让你接收 Body 并解析为 JSON (实际为 &lt;code&gt;JsValue&lt;/code&gt;):&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sayHello&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asJson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="n"&gt;\&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="n"&gt;asOpt&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}.&lt;/span&gt;&lt;span class="n"&gt;getOrElse&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Missing parameter [name]&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}.&lt;/span&gt;&lt;span class="n"&gt;getOrElse&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Expecting Json data&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
更好的(也是更简单的)办法是指定你自己的 &lt;code&gt;BodyParser&lt;/code&gt; 用以告诉 Play 把类容 Body 直接解析为 JSON:</description></item><item><title>Play 2.0 中文资料 - Play JSON 库</title><link>https://yanbin.blog/play2-0-tutorials-cn-play-json-library/</link><pubDate>Sun, 13 Jan 2013 01:09:05 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-play-json-library/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;strong&gt;使用 Json &lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-play-json-library"&gt;Play Json 库 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-handling-and-serving-json-requests" target="_blank" rel="noopener"&gt;处理 Json 请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics" target="_blank" rel="noopener"&gt;Play Json 库使用泛型 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;概述&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
推荐的处理  JSON 的方式是使用 Play 基于 JSON 库的类型类, 位置在 &lt;code&gt;play.api.libs.json&lt;/code&gt;.&lt;br/&gt;&lt;br/&gt;
这个库是构建于 &lt;a href="https://github.com/codahale/jerkson/"&gt;Jerkson&lt;/a&gt;, 之上的，它又是基于 Java 的超快的 JSON 库 &lt;a href="http://jackson.codehaus.org/"&gt;Jackson &lt;/a&gt;的 Scala 封闭。&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注：在 Play 1.x 所用的 JSON 库是 Gson，而 Play 2.0 后更换成了 Jackson。还得 Play 2.0 是基于 SBT 构建的，所以 Play 2.0 的所有的 jar 都是在 $PLAY_HOME/repository/local 目录中。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
这样做的好处是无论是 Java 还是 Scala 的 Play 应用依赖了相同的底层库 (Jackson), 同时 Scala 用户可以享受到  Play’s JSON 所带来的额外的类型安全性.&lt;br/&gt;&lt;br/&gt;
&lt;code&gt;play.api.libs.json&lt;/code&gt; 包含有七种 JSON 数据类型:&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JsObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsNull&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsUndefined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsBoolean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsNumber&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsArray&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JsString&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
上面的类型都继承自通用的 JSON 值类型, &lt;code&gt;JsValue&lt;/code&gt;.</description></item><item><title>JavaDoc 编程，书写自定义的 Doclet, 定制输出</title><link>https://yanbin.blog/javadoc-customize-doclet/</link><pubDate>Thu, 20 Dec 2012 06:12:07 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javadoc-customize-doclet/</guid><description>
前一篇介绍了 &lt;a title="JavaDoc 编程，书写自定义的 Taglet 支持 @unmi 等" href="https://yanbin.blog/javadoc-programming-customize-taglet" target="_blank" rel="noopener"&gt;JavaDoc 编程，书写自定义的 Taglet 支持 @unmi 等&lt;/a&gt;, 那时提到了 Doclet，但是差点无视了 Doclet，现在才知道 Doclet 真是太强大了，有了它你会觉得 javadoc 已经不是原本的那个 javadoc 了，别再把 javadoc 看作只会生成一大堆 HTML 文件的工具了。尤其是搭配上自定义的 Taglet，那可是，自已体验吧。&lt;/p&gt;
&lt;br/&gt;
Doclet 是 JavaDoc 的一个很隆重的扩展点，可以在执行 javadoc 时用 -doclet 来指定自己的 Doclet，那么 doclet 可以为我们做些什么呢？&lt;br/&gt;&lt;br/&gt;
可以为我们生成 HTML 的 JavaDoc API 文档，这就是默认的 com.sun.tools.doclets.standard.Standard 为我们做的事，还可以像以前那样从源文件中抽取信息生成各种 XML 文件，或是 PDF, Excel， UML 图等等任何可能的内容，或做任何有作为的事情。总之在 doclet 中可以感知道对任何包，类，方法，字段等的遍历。这里 &lt;a href="http://doclet.com/" target="_blank" rel="noopener"&gt;Doclet.com&lt;/a&gt; 有大量的第三方的 doclet 供你选择，如：&lt;br/&gt;&lt;br/&gt;
AntDoclet, API Guide Doclet, EJBGen, Java2Rose Doclet, JDiff, JUnitDoclet, LaTeXtaglet, PDFDoclet, PublishedApiDoclet, ServletDoclet, Spell Check Doclet, UMLGraph, VelocityDoclet, XDoclet, xml-doclet 等数十种 Doclet, 还可以找到别的，所以你要是不想自定义 Doclet 的话，有第三方可用就直接用人家的就行。&lt;br/&gt;&lt;br/&gt;
总有特殊需求的时候，总有要自定义 Doclet 的时候。说那么多总要来看看 Doclet 可以用来做什么，见如下 DocumentDoclet:</description></item><item><title>如何设置 Windows 默认命令行窗口大小和缓冲区大小</title><link>https://yanbin.blog/save-windows-command-size/</link><pubDate>Thu, 20 Dec 2012 04:03:03 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/save-windows-command-size/</guid><description>
Windows 命令行 cmd 窗口系统默认的大小(80*40)对于现在的屏幕配置已经跟不上时代了，我们总是要把它改大些，而且缓冲区大小也想改得大大的。单纯的为当前的 Windows 命令行窗口修改显示大小和缓冲区大小就简单了，右键命令行窗口标题，属性里改屏幕缓冲区和窗口大小就是，系统会为与当前标题相同的命令行窗口记住你的设置，比如 &lt;span style="color: #800000;"&gt;C:\Windows\system32\cmd.exe&lt;/span&gt;。但是经常你又会打开不同标题的命令行窗口，如 Tomat，这时候它又是默认的 80*40 的窗口大小，又得改，再碰不同标题又要改。&lt;br/&gt;
&lt;br/&gt;
于是能否直接修改系统默认的 cmd 窗口和它的缓冲区大小呢，以后碰到新的标题就参考于它。行的，方法是改注册表。&lt;br/&gt;
&lt;br/&gt;
先来看下你可以在命令行下直接指定命令行窗口的大小了，进到命令行执行 &lt;span style="color: #800000;"&gt;mode&lt;/span&gt;，可以看到关于控制台的信息如下：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Status for device CON:&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt; ----------------------&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Lines:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2000&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Columns:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 120</description></item><item><title>JavaDoc 编程，书写自定义的 Taglet 支持 @unmi 等</title><link>https://yanbin.blog/javadoc-programming-customize-taglet/</link><pubDate>Sun, 16 Dec 2012 00:47:02 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javadoc-programming-customize-taglet/</guid><description>
javadoc 可为我们的 Java 项目生成 API 文档，别人的应该是看得多了，自己的可能不好意思晾出来看。那 Java 源代码里的 @author, @see, @param 等应该是司空见惯了吧。除此之外我们还可以自定义自己的 tag，并让它们的内容按照我们需要的格式生成到 javadoc 文档中，或作他用。还记得没有 Maven 的时代我们是怎样用 XDoclet 生映射文件的吗？现在的 Taglet 定制想要做的事情大抵如此。&lt;/p&gt;
&lt;br/&gt;
执行一下 &lt;a href="http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html" target="_blank" rel="noopener"&gt;javadoc&lt;/a&gt; 命令看看，一堆的参数可以指定，又有学问在里头，且看：&lt;br/&gt;&lt;br/&gt;
-tag &amp;lt;name&amp;gt;:&amp;lt;locations&amp;gt;:&amp;lt;header&amp;gt;  Specify single argument custom tags&lt;br /&gt;
-taglet                           The fully qualified name of Taglet to register&lt;br /&gt;
-tagletpath                       The path to Taglets&lt;br/&gt;&lt;br/&gt;
和&lt;br/&gt;&lt;br/&gt;
-doclet &amp;lt;class&amp;gt;           Generate output via alternate doclet&lt;br /&gt;
-docletpath &amp;lt;path&amp;gt;        Specify where to find doclet class files&lt;br/&gt;&lt;br/&gt;
关于 doclet 部份这儿暂且不说，单讲 tag 部分的东西。&lt;br/&gt;&lt;br/&gt;
对于自定义 tag，简单的时候，用参数 -tag  都可以不写自己的 taglet 类，例如有这样一个代码：</description></item><item><title>Java 的多行字符串 Here Document 的实现</title><link>https://yanbin.blog/java-implement-here-document/</link><pubDate>Fri, 14 Dec 2012 23:21:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-implement-here-document/</guid><description>
寻求了很久关于如何在 Java 中实现多行字符串，即 &lt;a href="http://en.wikipedia.org/wiki/Here_document" target="_blank" rel="noopener"&gt;Here Document&lt;/a&gt;。因为在测试中准备大的字符串数据是不得不用加号去拼接，甚至是麻烦。稍好就是用 &lt;a href="http://www.htmlescape.net/javaescape_tool.html" target="_blank" rel="noopener"&gt;http://www.htmlescape.net/javaescape_tool.html&lt;/a&gt; 把你输入的大段文字生成 Java 的字符串。&lt;/p&gt;
&lt;br/&gt;
找过一些介绍 Java 实现 Here Document 的方法，首先大家无一不是把这个多行字符串塞在注释里，有些实现在运行还在依赖于 Java 源文件中的注释，这不太可取。聪明的做法应该要去打编译器的主意，让编译后体现在 Class 文件中，变量就被赋上了多行字符串值，这就是 JDK1.5 引入的 APT(Annotation Processing Tool)，到 JDK1.6 后可操作性更强了，可以 javac 的时候带上 -processor 参数。&lt;br/&gt;&lt;br/&gt;
单单从语法特性上来讲，我觉得 Java 与现今流行的语言还是有差距，不过它一直在成长，像 JDK 1.5 和 1.7 这两个版本就带来了不少好东西。想要见识一下其他些个语言，如 Perl, PHP, Ruby, C++11 怎么实现 Here Document 还是请看 &lt;a href="http://en.wikipedia.org/wiki/Here_document" target="_blank" rel="noopener"&gt;http://en.wikipedia.org/wiki/Here_document&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
就连 Java 最亲密的战友 C# 都早实现了 Here Document，用 @ 符号：</description></item><item><title>保持顺序的 Java Properties 类</title><link>https://yanbin.blog/ordered-java-properties-class/</link><pubDate>Thu, 06 Dec 2012 23:46:36 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ordered-java-properties-class/</guid><description>
Java 的 Properties 加载属性文件后是无法保证输出的顺序与文件中一致的，因为 Properties 是继承自 Hashtable 的， key/value 都是直接存在 Hashtable 中的，而 Hashtable 是不保证进出顺序的。&lt;/p&gt;
&lt;br/&gt;
总有时候会有关心顺序一致的需求，恰如有 org.apache.commons.collections.OrderdMap（其实用 LinkedHashMap 就是保证顺序） 一样，我们也想要有个 OrderdProperties。网上查了下还真有：&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://livedocs.adobe.com/jrun/4/javadocs/jrunx/util/OrderedProperties.html" target="_blank" rel="noopener"&gt;http://livedocs.adobe.com/jrun/4/javadocs/jrunx/util/OrderedProperties.html&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;a href="http://www.openrdf.org/doc/alibaba/2.0-rc4/apidocs/org/openrdf/repository/object/composition/helpers/OrderedProperties.html" target="_blank" rel="noopener"&gt;http://www.openrdf.org/doc/alibaba/2.0-rc4/apidocs/org/openrdf/repository/object/composition/helpers/OrderedProperties.html&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
不过没找到源码，其实自己写一个 OrderedProperties  也不难，并不需要重头写起，只要继承自 Properties，覆盖原来的 put/keys,keySet,stringPropertyNames 即可，其中用一个 LinkedHashSet 来保存它的所有 key。完整代码如下：</description></item><item><title>Play 2.0 中文资料 - 模板引擎(用例)</title><link>https://yanbin.blog/play2-0-tutorials-cn-template-engine-common-use-cases/</link><pubDate>Thu, 25 Oct 2012 11:49:40 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-template-engine-common-use-cases/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;strong&gt;模板引擎&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 模板引擎(语法)" href="https://yanbin.blog/play2-0-tutorials-cn-template-engine-syntax" target="_blank" rel="noopener"&gt;Scala 模板语法 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 模板引擎(用例)" href="https://yanbin.blog/play2-0-tutorials-cn-template-engine-common-use-cases" target="_blank" rel="noopener"&gt;通用案例 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
模板, 实际作为简单函数存在的, 它可以任何你希望的方式被组合应用. 下面是一些通用场景的使用案例.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;布局&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
我们来声明一个 &lt;code&gt;views/main.scala.html&lt;/code&gt; 模板来用作主布局模板:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;@(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="nc"&gt;DOCTYPE&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;@&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;@&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
正如你所看到的, 这个模板有两个参数: 一个标题和一个 HTML 内容块. 现在我们可在另一个模板 &lt;code&gt;views/Application/index.scala.html&lt;/code&gt; 中用它:</description></item><item><title>Play 2.0 中文资料 - 模板引擎(语法)</title><link>https://yanbin.blog/play2-0-tutorials-cn-template-engine-syntax/</link><pubDate>Sun, 21 Oct 2012 05:33:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-template-engine-syntax/</guid><description>
&lt;ul style="float: right; border-left: solid 1px green; border-bottom: solid 1px green;"&gt;
&lt;li&gt;&lt;strong&gt;模板引擎&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 模板引擎(语法)" href="https://yanbin.blog/play2-0-tutorials-cn-template-engine-syntax" target="_blank" rel="noopener"&gt;Scala 模板语法 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 模板引擎(用例)" href="https://yanbin.blog/play2-0-tutorials-cn-template-engine-common-use-cases" target="_blank" rel="noopener"&gt;通用案例 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注：因为是 Web 框架，所以展示层必不可少，也就是接下来两章，我们进入到 Play2.0 中的模板引擎的世界，不同与Play 1.x 让你眼花缭乱的 ${}, #{}, @{}, @@{}, &amp;amp;{}, %{}% 和 &lt;em&gt;{}&lt;/em&gt; 如此众多的标签样式，而在 Play 2.0 中只有一个魔幻的 @ 符号。这就叫化繁为简，Play 1.x 的模板是基于 Groovy 的，而 Play 2.0 是基于 Scala 的，它在效率上也改善不少。&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;基于 Scala 的类型安全的模板引擎&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
Play 2.0 带来了崭新而真正强大的基于 Scala 的模板引擎. 这一新引擎的设计灵感来自于 ASP.NET 的 Razor.  具体表现在:&lt;br/&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;简洁, 富有表现力, 且流畅&lt;/strong&gt;: 它最小化了一个文件所需的字符和按键的数量, 并且开启了一个快速而流畅的代码流. 不像多数的模板语法, 你不必中断你的代码去显式的告诉你的 HTML 说这儿有一个服务端代码块. 解析器足够的聪明，能依据你的代码进行推断(Unmi 注: 推断出哪块代码应该由服务端来执行). 这使得简洁而富有表现力的语法显得更干净,  并且输入变得快速而有乐趣.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易学&lt;/strong&gt;: 源于其最小化的概念，而让你快速而富有成效的掌握它. 只需用到你现有的 Scala 语言和 HTML 知识.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并非新的语言&lt;/strong&gt;: 我们尽量选择不去创建一门新语言. 相应的，我们希望能让开发者使用它们现有的 Scala 语言技能。也就是要在选择你自己语言的情况下，提供一套模板标记语法，从而让你书写出令人敬畏的 HTML 结构流.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可用任何文本编辑器&lt;/strong&gt;: 无须特别的工具，在任何纯文本编辑器中都能保持高效.</description></item><item><title>Play 2.0 中文资料 - 组合 Action</title><link>https://yanbin.blog/play2-0-tutorials-cn-actions-composition/</link><pubDate>Sat, 20 Oct 2012 04:48:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-actions-composition/</guid><description>
本章介绍几种定义通用 Action 功能的方法.&lt;br/&gt;&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;基本的 Action 组合&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
我们首先从一个简单的日志装饰器(logging decorator) 开始: 我们想要记录下当前 Action 每次被调用的日志.&lt;br/&gt;&lt;br/&gt;
我们不直接就定义自己的 Action, 而是首先提供一个用于创建标准 Action 的帮助方法:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;LoggingAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;AnyContent&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;AnyContent&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Calling action&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
接着你可以这么来使用:</description></item><item><title>VMware 9 安装 Mac OS X 10.8 Mountain Lion 图文全程</title><link>https://yanbin.blog/vmware9-install-mac-os-x-mountain-lion/</link><pubDate>Sat, 20 Oct 2012 02:35:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vmware9-install-mac-os-x-mountain-lion/</guid><description>
本教程是在 VMware 9 下安装当前最新版的 Mac OS X Mountain Lion 苹果系统。曾在 VirtualBox/VMware 下安装过 Mac OS Lion 系统，但安装后是无法升级到 Mountain Lion 的，所以直接上最新版。以前虚拟机安装苹果系统总有点麻烦事要处理，比如 About This Mac 黑屏，分辨率修改复杂些，借助别的 ISO 文件来引导系统。下面是我在 VMware 9 中安装 Mac OS X Mountain Lion 的全过程，比以往的安装方法要简单。&lt;br/&gt;
&lt;br/&gt;
首先，硬件上你要有一个支持虚拟化技术的 CPU，足够用的内存。我的软硬件环境是 i7 CPU + 8G RAM + Win7 64Bit 英文版。&lt;br/&gt;
&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;安装所需软件：&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
1. VMware 9.0&amp;nbsp; -- 可在网络上下载并可找到注册码，VMware 8 破解后也没有 Mac OS X 10.8 的选项&lt;br/&gt;
&lt;br/&gt;
2. VMware 安装 Mac OS 的补丁: &lt;a href="http://unmi.cc/wp-content/uploads/2012/10/unlock-all-v110.zip"&gt;unlock-all-v110&lt;/a&gt;&amp;nbsp; -- 它可让你在 VMware 中选择 Mac OS X 系统&lt;br/&gt;
&lt;br/&gt;
3. Mac OS X Mountain Lion 下载： &lt;a href="http://unmi.cc/wp-content/uploads/2012/10/Mac-OS-X-10.8.torrent.zip"&gt;Mac OS X 10.8.torrent&lt;/a&gt; -- 下载来的文件是 InstallESD.dmg&lt;br/&gt;
&lt;br/&gt;
4. 7-Zip 压缩解压缩工具，免费的，网络随处找得到，用 Winrar 都解不了 InstallESD.dmg</description></item><item><title>Play 2.0 中文资料 - Body 解析器</title><link>https://yanbin.blog/play2-0-tutorials-cn-body-parsers/</link><pubDate>Fri, 12 Oct 2012 09:02:05 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-body-parsers/</guid><description>
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;什么是 Body 解析器?&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
HTTP PUT 或 POST 请求含有一个请求体(Body). 请求体可以使用任何格式, 只要在请求头中指定相应的 &lt;code&gt;Content-Type&lt;/code&gt; 即可. 在 Play 中, 一个&lt;strong&gt; Body 解析器&lt;/strong&gt; 转换请求体为对应的 Scala 值.&lt;br/&gt;&lt;br/&gt;
然而，HTTP 请求体可能非常的大，这时候&lt;strong&gt; Body 解析器&lt;/strong&gt; 不可能在解析之前光等着把数据全部加载到内存. &lt;code&gt;BodyParser[A]&lt;/code&gt; 是个基本的 &lt;code&gt;Iteratee[Array[Byte],A]&lt;/code&gt;, 这就是说它一块一块的接收数据 (只要 Web 浏览器在上传数据) 并计算出类型为 A 的值作为结果.&lt;br/&gt;&lt;br/&gt;
让我们考虑一下几个例子.&lt;br/&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;一个&lt;strong&gt;文本&lt;/strong&gt;型 Body 解析器能够把逐块的字节数据连缀成一个字符串, 并把计算得到的字符串作为结果 (&lt;code&gt;Iteratee[Array[Byte],String]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;一个&lt;strong&gt;文件&lt;/strong&gt;型 Body 解析器能够把逐块的字节数据存为一个本地文件, 并以 &lt;code&gt;java.io.File&lt;/code&gt; 引用作为结果 (&lt;code&gt;Iteratee[Array[Byte],File]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;一个 &lt;strong&gt;s3&lt;/strong&gt; 型 Body 解析器能够把逐块的字节数据推送给 Amazon S3 并以 S3 对象 ID 作为结果 (&lt;code&gt;Iteratee[Array[Byte],S3ObjectId]&lt;/code&gt;).</description></item><item><title>Play 2.0 中文资料 - 会话和 Flash 域</title><link>https://yanbin.blog/play2-0-tutorials-cn-session-flash-scope/</link><pubDate>Thu, 11 Oct 2012 13:11:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-session-flash-scope/</guid><description>
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;会话和 Flash 域在 Play 中有何不同&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
如果你不得不要跨多个 HTTP 请求来保存数据, 你可以存在会话或是 Flash 域中. 存储在会话中的数据在整个会话期间可用, 而存储在 Flash 域中数据只对下一次请求有效.&lt;br/&gt;&lt;br/&gt;
理解到会话和 Flash 数据不是由服务器来存储，而是以 Cookie 机制添加到每一次后续的 HTTP 请求是很重要的. 这也就意味着数据的大小是很受限的 (不到 4 KB) ，而且你只能存储字符串.&lt;br/&gt;&lt;br/&gt;
当然了, Cookie 值是由密钥签名了的，这使得客户端不存修改 Cookie 数据(或者说这样做会让它失效).&lt;br/&gt;&lt;br/&gt;
Play 会话不能当成缓存来用.  假如你需要缓存与某一会话相关的数据, 你可以使用 Play 内置的缓存机制并存储一个唯一的 ID 在用户会话中与之关联.&lt;br/&gt;
&lt;blockquote&gt;
会话没有技术上的超时控制. 它在用户关闭 Web 浏览器后就会过期. 如果你的应用需要一种功能性的超时控制, 那就在用户会话中存储一个时间戳，并在应用需要的时候用它 (例如: 最大的会话持续时间, 最大的非活动时间, 等等.).
&lt;/blockquote&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;读取会话值&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
你可以从 HTTP 请求中获取传入的会话:</description></item><item><title>Play 2.0 中文资料 - 操控 Result</title><link>https://yanbin.blog/play2-0-tutorials-cn-manipulating-results/</link><pubDate>Wed, 10 Oct 2012 11:47:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-manipulating-results/</guid><description>
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;改变默认的 Content-Type&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
结果内容的类型能够由你所指定的响应体的 Scala 值自动推断出来.&lt;br/&gt;&lt;br/&gt;
例如:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;textResult&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
将会自动设置 &lt;code&gt;Content-Type&lt;/code&gt; 头为 &lt;code&gt;text/plain&lt;/code&gt;, 而:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;xmlResult&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="nc"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="o"&gt;;/&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
会设置 Content-Type header 为 &lt;code&gt;text/xml&lt;/code&gt;.&lt;br/&gt;
&lt;blockquote&gt;
&lt;strong&gt;小贴士:&lt;/strong&gt; 这是由 &lt;code&gt;play.api.http.ContentTypeOf&lt;/code&gt; 类型类完成的.
&lt;/blockquote&gt;
这相当有用, 但是有时候你想手工改变它. 只需要调用 Result 的 &lt;code&gt;as(newContentType)&lt;/code&gt; 方法来创建一个新的，类似的，具有不同 &lt;code&gt;Content-Type&lt;/code&gt;  头的 Result:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;htmlResult&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="nc"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;).&lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
或者更好点, 用:</description></item><item><title>Play 2.0 中文资料 - HTTP 路由</title><link>https://yanbin.blog/play2-0-tutorials-cn-http-routes/</link><pubDate>Mon, 08 Oct 2012 01:04:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-http-routes/</guid><description>
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;内建的 HTTP 路由器&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
路由器组件掌管把传入的 HTTP 请求转发到 Action.&lt;br/&gt;&lt;br/&gt;
一个HTTP 请求可被 MVC 框架看成是一个事件. 这个事主要包含两方面的信息:&lt;br/&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;请求路径 (如 &lt;code&gt;/clients/1542&lt;/code&gt;, &lt;code&gt;/photos/list&lt;/code&gt;), 包括查询字符串&lt;/li&gt;
&lt;li&gt;HTTP 方法 (像 GET, POST, …).&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
路由定义在 &lt;code&gt;conf/routes&lt;/code&gt; 文件中, 它会被编译. 这表明你可以直接在浏览器上看到路由错误信息:</description></item><item><title>Play 2.0 中文资料 - Action, Controller 和 Result</title><link>https://yanbin.blog/play2-0-tutorials-cn-action-controller-result/</link><pubDate>Sat, 06 Oct 2012 13:56:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-action-controller-result/</guid><description>
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;什么是 Action?&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
多数的请求被 Play 应用接收到，并由  &lt;code&gt;Action 进行处理&lt;/code&gt;.&lt;br/&gt;&lt;br/&gt;
一个 &lt;code&gt;play.api.mvc.Action&lt;/code&gt; 简单说来就是一个 &lt;code&gt;(play.api.mvc.Request =&amp;gt; play.api.mvc.Result)&lt;/code&gt; 函数，它处理请求并产生一个 result 发送给客户端.&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nc"&gt;Ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Got request [&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;]&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Action 返回一个 &lt;code&gt;play.api.mvc.Result&lt;/code&gt; 值, 代表着发送给 Web 客户端的 HTTP 响应. 上面的例子中 &lt;code&gt;Ok&lt;/code&gt; 构造了一个 &lt;strong&gt;200 OK&lt;/strong&gt; 的响应，它包含了 &lt;strong&gt;text/plain&lt;/strong&gt; 类型的响应体.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;建造一个 Action&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;code&gt;play.api.mvc.Action&lt;/code&gt; 伴生对象提供有多个帮助方法用于构建 Action 值.&lt;br/&gt;&lt;br/&gt;
最简单的一个方法是以返回值为 &lt;code&gt;Result&lt;/code&gt; 的表达式块作为参数:</description></item><item><title>Play 2.0 中文资料 - 搭建你偏爱的 IDE</title><link>https://yanbin.blog/play2-0-tutorials-cn-setting-up-ide/</link><pubDate>Fri, 05 Oct 2012 10:26:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-setting-up-ide/</guid><description>
用 Play 进行工作其实很简单的. 你甚至不需要一个复杂的 IDE, 因为 Play 会在你对源文件作了修改之后自动编译和刷新, 因此你可以很轻易的使用一个简单的文本编辑器来工作.&lt;br/&gt;
&lt;br/&gt;
然而, 使用一个现代的 Java 或 Scala IDE 或给予你很 Cool 生产效率, 比如即时的编译, 良好的重构和调试功能.&lt;br/&gt;
&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;Eclipse&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="font-size: small;"&gt;&lt;strong&gt;生成配置&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
Play 提供了一条命令简化了 Eclipse 配置. 为转换一个 Play 应用为有效的 Eclipse 项目, 使用&amp;nbsp;&lt;code&gt;eclipsify&lt;/code&gt; 命令:&lt;br/&gt;
&lt;br/&gt;
无需关联下载依赖的源文件 jars 包:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;My first application&lt;span class="o"&gt;]&lt;/span&gt; $ eclipsify&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 对于一个新建的项目，执行 eclipsify 之后会产生 Eclipse 必须的 .project, .classpath 和 .settings 文件，并且这个命令还会创建 test 和 target 目录。Play 1.x 的 play eclipsify 命令有些不同，它会生成一个 eclipse 目录，里面有三个分别用于运行, 调试和测试用的 launch 文件。</description></item><item><title>Play 2.0 中文资料 - HTTP 编程</title><link>https://yanbin.blog/play2-0-tutorials-cn-http-programming/</link><pubDate>Fri, 05 Oct 2012 05:19:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-http-programming/</guid><description>
&lt;strong&gt;这是一个 &lt;span style="font-size: medium;"&gt;HTTP 编程&lt;/span&gt; 的大纲, 点击以下链接进入各节内容:&lt;/strong&gt;&lt;br/&gt;
&lt;ul&gt;&lt;br/&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - Action, Controller 和 Result" href="https://yanbin.blog/play2-0-tutorials-cn-action-controller-result"&gt;Action, Controller 和 Result&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - HTTP 路由" href="https://yanbin.blog/play2-0-tutorials-cn-http-routes"&gt;HTTP 路由&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 操控 Result" href="https://yanbin.blog/play2-0-tutorials-cn-manipulating-results" target="_blank"&gt;操控 results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 会话和 Flash 域" href="https://yanbin.blog/htplay2-0-tutorials-cn-session-flash-scope" target="_blank"&gt;会话和 Flash&amp;nbsp;域 (Flash Scope)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - Body 解析器" href="https://yanbin.blog/play2-0-tutorials-cn-body-parsers" target="_blank"&gt;Body 解析器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 组合 Action" href="https://yanbin.blog/play2-0-tutorials-cn-actions-composition" target="_blank"&gt;组合 Action &lt;/a&gt;&lt;br/&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Play 2.0 中文资料 - 使用 Play 2.0 控制台</title><link>https://yanbin.blog/httpunmi-ccplay2-0-tutorials-cn-creating-application-using-console/</link><pubDate>Fri, 05 Oct 2012 03:51:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/httpunmi-ccplay2-0-tutorials-cn-creating-application-using-console/</guid><description>
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;启动控制台&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
Play 2.0 控制台是基于 sbt 的为开发而准备的控制吧, 它允许你管理整个 Play 应用的开发周期.&lt;br/&gt;
&lt;br/&gt;
要启动控制台, 进入到现有的 Play 应用的目录，然后运行&amp;nbsp;&lt;code&gt;play&lt;/code&gt;&amp;nbsp;脚本:&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 虽然 sbt 是为 Scala 准备的构建工具，但是即使你创建的应用选的是 Java 语言, 我们仍然是要依赖于 sbt.
在 Play 1.x 的时候不存在 Play 控制台，要么就是直接把应用运行起来了。在 Play 1.x 的项目目录下运行 play 命令只会显示简单的帮助信息。&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;play 命令进入到当前应用的控制台下，提示符是 &lt;strong&gt;[应用的名称] $&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/any/application
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;$ play&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p style="text-align: center;"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2012/httpunmi-ccplay2-0-tutorials-cn-creating-application-using-console/console.png"
width="750px"
/&gt;
&lt;/p&gt;</description></item><item><title>Play 2.0 中文资料 - 剖析 Play 2.0 应用</title><link>https://yanbin.blog/play2-0-tutorials-cn-anatomy-application/</link><pubDate>Thu, 04 Oct 2012 23:06:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-anatomy-application/</guid><description>
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;标准的应用目录布局&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
Play 应用的目录布局是标准化的，这样可保持尽量的简单. 一个标准的 Play 应用看起来如下:&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;app → 应用程序源文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; └ assets → 待编译的资源文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; └ stylesheets → 比如放置 LESS CSS 源文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; └ javascripts → 比如放置 CoffeeScript 源文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; └ controllers → 应用的控制器
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; └ models → 应用的业务层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; └ views → 视图模板
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;conf → 配置文件和其他无需编译的放在 classpath 下的文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; └ application.conf → 主配置文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; └ routes → 路由定义文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;public → 公共资源
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; └ stylesheets → CSS 样式文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; └ javascripts → Javascript 脚本文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; └ images → 图片
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;project → sbt 配置文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; └ build.properties → sbt 项目属性配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; └ Build.scala → 应用构建脚本
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; └ plugins.sbt → sbt 插件配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;lib → 不受管理的库依赖
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;logs → 标准的日志目录
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; └ application.log → 默认的日志文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;target → 构建生成的东西
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; └ scala-2.9.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; └ cache
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; └ classes → 编译生成的类文件
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; └ classes_managed → 受管理的类文件 (如模板等生成的)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; └ resource_managed → 受管理的资源文件 (如 Less 等生成的)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; └ src_managed → 产生的源文件 (如模板等)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;test → 单元和功能性测试的源文件目录&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Play 2.0 中文资料 - 创建一个新的应用</title><link>https://yanbin.blog/play2-0-tutorials-cn-creating-application/</link><pubDate>Thu, 04 Oct 2012 08:46:21 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-creating-application/</guid><description>
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;用 play 命令创建一个应用&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
创建一个新应用最初的方式是用 &lt;code&gt;play new&lt;/code&gt; 命令.&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ play new myFirstApp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这会询问你一些信息.&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;应用的名称 (仅用于显示, 这个名称后面将在几个消息中使用到).&lt;/li&gt;
&lt;li&gt;用于这个应用的模板. 你可以从 默认的 Scala 应用, 默认的 Java 应用, 和 空应用 三个选项目中选择.&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 新版 play new 命令允许你带上目录名，让你在指定的目录下创建应用，在 2.0-beta 版中，只是在当前目录下创建你的应用。运行 play help 看显示的 new 是否是: new &lt;strong&gt;[directory]&lt;/strong&gt;    Create a new Play application in the specified directory. 就知道能否指定应用的目录。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;并且此时的 play new 命令可以用&lt;/span&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ play new ~/Desktop/unmi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;这样就会在 ~/Desktop/unmi 目录中创建应用，后面的提示是一样的. 在 ~/Desktop/unmi 目录下有应用所需的 app, conf, public 等目录。&lt;/span&gt;&lt;br /&gt;</description></item><item><title>Play 2.0 中文资料 - 安装</title><link>https://yanbin.blog/play2-0-tutorials-cn-installing/</link><pubDate>Thu, 04 Oct 2012 01:38:33 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-installing/</guid><description>
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;系统要求&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
为了运行 Play 框架, 你需要 &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html"&gt;JDK 6 或更新的版本&lt;/a&gt;.&lt;br/&gt;
&lt;blockquote&gt;假如你正使用 MacOS, Java 已经内置了. 如果你正在使用 Linux, 请确认使用了 Sun&amp;nbsp; 的 JDK 或 OpenJDK (不是 gcj, 它是许多 Linux 发行版的默认&amp;nbsp; Java 命令). 要是用的是 Windows, 只下载安装最新版的 JDK 安装包即可.&lt;/blockquote&gt;
确可 &lt;code&gt;java&lt;/code&gt; 和 &lt;code&gt;javac&lt;/code&gt; 命令被设置到了当前路径下 (你可以在命令行提示符下通过输入 &lt;code&gt;java -version&lt;/code&gt; 和 &lt;code&gt;javac 来检查它&lt;/code&gt;).&lt;br/&gt;
&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;下载二进制包&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
下载 &lt;a href="http://download.playframework.org/releases/"&gt;Play 2.0 的二进制包 &lt;/a&gt; (选择最新的 2.0 RC 版) 并解压到有读&lt;strong&gt;和写&lt;/strong&gt;权限的目录下. (运行 &lt;code&gt;play&lt;/code&gt; 会向安装目录中写入文件, 所以不要安装在 &lt;code&gt;/opt&lt;/code&gt;, &lt;code&gt;/usr/local&lt;/code&gt; 或任何你无需设置写入权限的目录中.)&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注: 当前最新的版本是 2.0.4。</description></item><item><title>Play 2.0 中文资料 - 开篇</title><link>https://yanbin.blog/play2-0-tutorials-cn-getting-started/</link><pubDate>Wed, 03 Oct 2012 11:12:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/play2-0-tutorials-cn-getting-started/</guid><description>
Play 当前版本是 2.0.4，2.0 前的版本通常会称为 PlayFramework，或者 Play!，现在干脆直呼为 Play 了，名气大了些就这样的，似乎越来越显得不可一世了。本人有意更深点了解 Play 框架，所以打算翻译官网 &lt;a href="http://www.playframework.org/documentation/2.0.4/Home" target="_blank"&gt;Manual, tutorials &amp;amp; references&lt;/a&gt; 一系列资料，并且着重点会先放在基于 Scala 语言的 Play 2.0 的应用。&lt;br/&gt;
&lt;blockquote&gt;Play 2.0 是一个高生产率的 Java 和 Scala 的 Web 应用框架，它集成了现代 Web 应用开发所而的组件和 API。&lt;br/&gt;
&lt;br/&gt;
Play 是轻量级的，无状态的，Web 友好的架构，并且是特征可预期的(features predictable)。还借助于它的反应模型(reactive model) 和 Iteratee IO 的使用，让 Play 为高伸缩性的应用最小化了资源消耗(CPU, 内存和线程).&lt;/blockquote&gt;
&lt;span style="color: #0000ff;"&gt;Unmi 注：关于 Iteratee IO, 可参考：&lt;a href="http://www.haskell.org/haskellwiki/Iteratee_I/O" target="_blank"&gt;Iteratee I/O&lt;/a&gt; 和 &lt;/span&gt;&lt;a href="http://blog.higher-order.com/blog/2010/10/14/scalaz-tutorial-enumeration-based-io-with-iteratees/" target="_blank"&gt;Scalaz Tutorial: Enumeration-Based I/O with Iteratees&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
第一步要翻译的内容只想覆盖到以下：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;&lt;strong&gt;开始使用&lt;/strong&gt;&lt;/span&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 安装" href="https://yanbin.blog/play2-0-tutorials-cn-installing" target="_blank"&gt;安装 Play 2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 创建一个新的应用" href="https://yanbin.blog/play2-0-tutorials-cn-creating-application" target="_blank"&gt;创建一个新的应用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 剖析 Play 2.0 应用" href="https://yanbin.blog/play2-0-tutorials-cn-anatomy-application" target="_blank"&gt;剖析 Play 2.0 应用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a title="Play 2.0 中文资料 - 使用 Play 2.0 控制台" href="https://yanbin.blog/httpunmi-ccplay2-0-tutorials-cn-creating-application-using-console" target="_blank"&gt;使用 Play 2.0 控制台&lt;/a&gt;</description></item><item><title>Scala 用元组/列表类型实现多返回值的函数</title><link>https://yanbin.blog/scala-multiple-method-results/</link><pubDate>Wed, 03 Oct 2012 03:32:37 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-multiple-method-results/</guid><description>
Java 的方法不支持多返回值，或者我们想达到返回多个值的效果时，不得不借用数组、列表或对象等来容纳多个值返回给调用者。这样使用起来不怎么优美，特别是为返回多个值而创造一个类成本有点高，如果 Java 也存在像 C# 那样的匿名类倒好。C# 如何使用匿名类返回多个值可参看本文后面的例子。&lt;/p&gt;
&lt;br/&gt;
把 Scala 当作是一种脚本语言，它的灵活性就应该与 Perl 或 Ruby 看齐。Scala 的方法也可以有多个返回值，它实现些种行为，可借助于元组和列表类型，虽然你也可以发掘更多的实现方式，但到目前为止，我还是觉得用元组和列表最简单。&lt;br/&gt;&lt;br/&gt;
下面给出代码例子，让我们瞧瞧 Scala 如何实现方法多个返回值，并且作为对比我还会贴上 Perl, Ruby 和 C# 可以怎么实现多返回值。我是 Java 的惯用者，不过我一直都未否认，从语言层面 C# 比 Java 要显得优雅的多。&lt;br/&gt;&lt;br/&gt;
&lt;h3&gt;&lt;strong&gt;用元组实现 Scala 方法多返回值：&lt;/strong&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//定义方法返回元组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="c1"&gt;//用元组模式去接收方法 foo 的结果
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//输出为 100:Unmi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="c1"&gt;//调用方法的结果直接给元组变量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;输出同样是&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;Unmi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
方法返回一个元组，里面可以组合任何多个的不同类型的返回值，调用时可以用元组变量或元组模式去接收方法的返回值，用元组模式的方式更好看些，也才能算作真正的多返回值。</description></item><item><title>Scala 无参数方法和统一访问原则</title><link>https://yanbin.blog/scala-parameterless-uniform-acces-principle/</link><pubDate>Mon, 01 Oct 2012 12:03:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-parameterless-uniform-acces-principle/</guid><description>
在 Scala 中当方法不需要接受参数时可定义成两种类型的方法&lt;br/&gt;
&lt;br/&gt;
1.&amp;nbsp; def width(): Int&amp;nbsp;&amp;nbsp; 空括号方法(empty-paren method)&lt;br/&gt;
&lt;br/&gt;
2. def width: Int&amp;nbsp; 无参数方法(parameterless method)&lt;br/&gt;
&lt;br/&gt;
从语法上讲，Scala 在调用上面那种类型的方法时都可以统一用 obj.width 的方式，实际对于无参数方法是只能用 obj.width 的方式，而空括号方法，既可以用 obj.width 也可以用 obj.width() 的形式。&lt;br/&gt;
&lt;br/&gt;
那么 Scala 在方法无须参数时是定义空括号方法还是无参数方法时有什么约定呢？&lt;br/&gt;
&lt;blockquote&gt;当方法没有副作用(side effect)时，定义成无参数方法；当方法会产生副作用时，定义成空括号方法&lt;/blockquote&gt;
那么又该如何识别方法是否有副作用呢？一般来说有副作用的地方会在于其结果类型是否为 Unit。如果某个函数不返回任何有用的值，也就是说如果返回类型为 Unit，那么这个函数唯一能产生的作用就只能是产生某种副作用，例如改变对象内部的某种状态，或是向控制台的输出等。有副作用的方法就像是数据库的存储过程(一般用于更新数据)，无副作用的方法像是数据库的函数(一般用于查询得到数据)。</description></item><item><title>Scala 的 apply 和 update 方法的应用</title><link>https://yanbin.blog/scala-apply-update-methods/</link><pubDate>Sun, 30 Sep 2012 12:57:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-apply-update-methods/</guid><description>
Scala 是构建在 JVM 上的静态类型的脚本语言，而脚本语言总是会有些约定来增强灵活性。灵活性可以让掌握了它的人如鱼得水，也会让初学者不知所措。比如说 Scala 为配合 DSL 在方法调用时有这么一条约定：&lt;/p&gt;
&lt;blockquote&gt;
在明确了方法调用的接收者的情况下，若方法只有一个参数时，调用的时候就可以省略点及括号。如 “0 to 2”，实际完整调用是 “0.to(2)”。但 “println(2)” 不能写成 “println 10“”，因为未写出方法调用的接收者 Console，所以可以写成 “Console println 10”
&lt;/blockquote&gt;
到这里就要讲到 apply 和 update 方法相关的约定，描述的是直接在对象(对象)后直接加圆括号的代码的情况下，那就是:&lt;br/&gt;
&lt;blockquote&gt;
用括号传递给变量(对象)一个或多个参数时，Scala 会把它转换成对 apply 方法的调用；与此相似的，当对带有括号并包括一到若干参数的进行赋值时，编译器将使用对象的 update 方法对括号里的参数和等号右边的对象执行调用。
&lt;/blockquote&gt;
对上面那段话的解释可由下面几个例子得到很好的理解&lt;br/&gt;&lt;br/&gt;
1. theArray(0), 取数组的第一个元素的操作会转换成 theArray.apply(0) 操作，这也能解释为什么 Scala 数组取值不用中括号括下标的方式，因为它也是一次方法调用</description></item><item><title>Scala 特质(trait) 的 super 方法调用是动态绑定的</title><link>https://yanbin.blog/scala-trait-super-dynamic-binding/</link><pubDate>Sat, 29 Sep 2012 09:59:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-trait-super-dynamic-binding/</guid><description>
在 Java 或者 Scala 的类中，super.foo() 这样的方法调用是静态绑定的，也就是说当你在代码中写下 super.foo() 的时候就能明确是调用它的父类的 foo() 方法。然而，如果是在特质中写下了 super.foo() 时，它的调用是动态绑定的。调用的实现奖在每一次特质被混入到具体类的时候才被决定。&lt;/p&gt;
&lt;br/&gt;
确切的讲，特质的 super 调用与混入的次序很重要，参照下面的例子说话：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicIntQueue&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Incrementing&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Doubling&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
直截的讲就是超靠近后面的特质越优先起作用。当你调用带混入的类的方法是，最右侧特质的方法首先被调用。如果那个方法调用了 super，它调用其左侧特质的方法。可以这么认为，Doubling 的 super 指向了  Incrementing，Incrementing 的 super 指向了 BasicIntQueue。&lt;br/&gt;&lt;br/&gt;
来看个完整的实例实际体验一把，如果要帮助理解，最好应该实际运行一下这个实例</description></item><item><title>Scala 的 yield 例子 (for 循环和 yield 的例子)</title><link>https://yanbin.blog/scala-yield-samples-for-loop/</link><pubDate>Fri, 28 Sep 2012 10:07:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-yield-samples-for-loop/</guid><description>
我看了《Programming in Scala》一书，仍然对 Scala yield 关键字的理解不甚清楚。起初我以为 Scala yield 的与 Ruby 的 yield 是一样，Ruby 中 yield 是被传入代码块的占位符。Scala 中的 &lt;em&gt;yield&lt;/em&gt; 关键字好像总是在 &lt;em&gt;for&lt;/em&gt; 循环中用的. 下面一些例子可以帮助你更好的理解 yield 关键字。下面是摘自 《Programming in Scala》关于 yield 的解释:&lt;/p&gt;
&lt;blockquote&gt;
For each iteration of your &lt;em&gt;for&lt;/em&gt; loop, yield generates a value which will be remembered. It's like the for loop has a buffer you can't see, and for each iteration of your for loop, another item is added to that buffer. When your for loop finishes running, it will return this collection of all the yielded values. The type of the collection that is returned is the same type that you were iterating over, so a Map yields a Map, a List yields a List, and so on.
Also, note that the initial collection is not changed; the for/yield construct creates a new collection according to the algorithm you specify.
&lt;/blockquote&gt;
上面那段话的意义就是，for 循环中的 yield 会把当前的元素记下来，保存在集合中，循环结束后将返回该集合。Scala 中 for 循环是有返回值的。如果被循环的是 Map，返回的就是  Map，被循环的是 List，返回的就是 List，以此类推。</description></item><item><title>Scala 中 ensuring 方法的使用说明</title><link>https://yanbin.blog/scala-ensuring-usage/</link><pubDate>Mon, 24 Sep 2012 11:28:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/scala-ensuring-usage/</guid><description>
Scala 在方法里除了可用 assert() 方法像 Java 那样进行断言，还可以使用 ensuring() 方法在返回结果的分支的花括号同一行上进行断言。它们不同的是 assert 可以随意放在哪里对任何的 boolean 类型进行断言，而 ensuring 是用来对返回结果行断言的，所以它必须尾随返回结果处。 assert 和  ensuring 方法都是定方在 Predef 中的，所以可以直接写。&lt;/p&gt;
&lt;br/&gt;
在 《Programming in Scala》一书中对 ensuring 使用的示例代码，会让人感到很费解的，并且对 ensuring 的解释也不多，只是说了 ensuring 中用 "_" 作为当前返回结果对象的占位符。在 《Programming in Scala》中 ensuring 示例代码是：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;widen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;)/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;beside&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;beside&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="n"&gt;ensuring&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
第一个让人一下不好理解的是方法定义第一行等号后没有加花括号，一般来说方法只包一条(行)语句时花括号可以省略，多行时虽然程序能够推断出该方法在何处解释，但人来阅读时却不容易把握方法代码在哪里结束，所以最好多行时用花括号括住函数代码。再说，如果在 Scala 控制台敲上面的代码，中间加回车的时候就会报错。</description></item><item><title>切分 Tomcat 的 catalina.out 文件，解决日志文件过大的问题</title><link>https://yanbin.blog/split-tomcat-catalina-out-file/</link><pubDate>Mon, 17 Sep 2012 03:06:54 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/split-tomcat-catalina-out-file/</guid><description>
Tomcat 下日志文件 catalina.out 过大，几百兆或几个G，进而造成再也无法写入更多的日志内容，至使 Tomcat 无法处理请求。需然你可以在 $TOMCAT_HOME/logs 目录下看到有 catalina.2012-09-16.log 这样分日期的归档，但是主文件 catalina.out 却一直在膨胀。&lt;/p&gt;
&lt;br/&gt;
你可以每次手工或定时(crontab/计划任务)清理 catalina.out 文件，或是规划好日志输出(终究也会有满的时候)。再就是有两种较好的解决方案去真正的切分 catalina.out 文件，让 catalina.out 只存有最新的日志。&lt;br/&gt;&lt;br/&gt;
一. 改用 Log4J 来输出 Tomcat 日志，借助 Log4J 的各种日志切分的功能。详情可参考： &lt;a href="http://baalwolf.iteye.com/blog/1464093" target="_blank" rel="noopener"&gt;http://baalwolf.iteye.com/blog/1464093&lt;/a&gt;， &lt;a href="http://tomcat.apache.org/tomcat-6.0-doc/logging.html" target="_blank" rel="noopener"&gt;http://tomcat.apache.org/tomcat-6.0-doc/logging.html&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
二. Linux 下使用 cronolog 工具来切分 catalina.out&lt;br/&gt;&lt;br/&gt;
这里重点介绍这种方法，具体步骤如下：</description></item><item><title>再说 Java 中使用正则表达式进行后向引用($1,$2...)</title><link>https://yanbin.blog/re-java-regex-replace/</link><pubDate>Fri, 17 Aug 2012 10:31:18 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/re-java-regex-replace/</guid><description>
前面通过 &lt;a title="在 Java 中使用正则表达式进行后向引用($1,$2...)" href="https://yanbin.blog/java-regex-replace-string" target="_blank" rel="noopener"&gt;在 Java 中使用正则表达式进行后向引用($1,$2...)&lt;/a&gt; 讲了在 Java 中使用 JavaScript 和 JRegex 来实现正则表达式的后向分组引用 $1, $2 替换。&lt;br/&gt;&lt;br/&gt;
自 JDK 1.4 出现正则表达式以来我还真不知道 Java 的正则表达式是可以在替换时用 $1, $2 达成后向分组替换的，所以前一篇表述有些出入的。也就是要实现 JavaScript 中的&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;abc def&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;des&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(\w+)\s+(\w+)/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;$2 $1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;des&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//输出为： def abc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
的功能，完全可以不求助于第三方的正则表达式组件库或是通过 ScriptEngine + JavaScript 来实现，在 Java 字符串的:&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;public String replaceFirst(String regex, String replacement)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #800000;"&gt;public String replaceAll(String regex, String replacement)&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
两方法的第三个参数中是可以用 $1, $2 ... 来引用第一个参数的括号分组的，简单 Java 示例代码如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;abc def&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceFirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(\\w+)\\s+(\\w+)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;$2 $1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//结果为 def abc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;abc def aaa bbb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(\\w+)\\s+(\\w+)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;$2 $1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;结果是&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bbb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aaa&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
Java 的正则表达式原本还是很强大的，只怕不能被人发现。&lt;br/&gt;&lt;br/&gt;
2015-07-14： 补充一点，在正则表达式外是用  $1, $2 ... 来进行后向引用，如果是在正则表达式中就需要用  \1, \2 ... 的形式来进后向引用。下面例子，替换重复出现的两位数及之间的内容&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;&amp;#34;xx12abcd12345&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(\\d{2}).+?\\1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;结果为&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;xx345&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Java 下高效的反射工具包 ReflectASM 使用例解</title><link>https://yanbin.blog/java-reflectasm-bytecode-usage/</link><pubDate>Thu, 16 Aug 2012 13:57:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-reflectasm-bytecode-usage/</guid><description>
&lt;a title="ReflectASM" href="http://code.google.com/p/reflectasm/" target="_blank" rel="noopener"&gt;ReflectASM&lt;/a&gt; 使用字节码生成的方式实现了更为高效的反射机制。执行时会生成一个存取类来 set/get 字段，访问方法或创建实例。一看到 ASM 就能领悟到 ReflectASM 会用字节码生成的方式，而不是依赖于 Java 本身的反射机制来实现的，所以它更快，并且避免了访问原始类型因自动装箱而产生的问题。&lt;/p&gt;
&lt;br/&gt;
下面三个图是 ReflectASM 与 Java 自身反射机制的性能对比，表现很不错的。&lt;br/&gt;&lt;br/&gt;
&lt;p style="text-align: center;"&gt;&lt;img class="aligncenter" src="http://chart.apis.google.com/chart?chma=100&amp;amp;chtt=Field%20Set/Get&amp;amp;chs=700x62&amp;amp;chd=t:1402081,11339107&amp;amp;chds=0,11339107&amp;amp;chxl=0:%7CJava%20Reflection%7CFieldAccess&amp;amp;cht=bhg&amp;amp;chbh=10&amp;amp;chxt=y&amp;amp;chco=6600FF&amp;amp;.png" alt="" width="700" height="62" /&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;p style="text-align: center;"&gt;&lt;img class="aligncenter" src="http://chart.apis.google.com/chart?chma=100&amp;amp;chtt=Method%20Call&amp;amp;chs=700x62&amp;amp;chd=t:97390,208750&amp;amp;chds=0,208750&amp;amp;chxl=0:%7CJava%20Reflection%7CMethodAccess&amp;amp;cht=bhg&amp;amp;chbh=10&amp;amp;chxt=y&amp;amp;chco=6600AA&amp;amp;.png" alt="" width="700" height="62" /&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;p style="text-align: center;"&gt;&lt;img class="aligncenter" src="http://chart.apis.google.com/chart?chma=100&amp;amp;chtt=Constructor&amp;amp;chs=700x62&amp;amp;chd=t:2853063,5828993&amp;amp;chds=0,5828993&amp;amp;chxl=0:%7CJava%20Reflection%7CConstructorAccess&amp;amp;cht=bhg&amp;amp;chbh=10&amp;amp;chxt=y&amp;amp;chco=660066&amp;amp;.png" alt="" width="700" height="62" /&gt;&lt;/p&gt;
&lt;br/&gt;
测试代码包含在项目文件中. 上面图形是在 Oracle 的 Java 7u3, server VM 下测试出的结果。&lt;br/&gt;&lt;br/&gt;
下面我们自己来做个测试，测试环境是 Mac OS X 10.8, 2.4G Core 2 Duo, 4G RAM, 64 位 JDK 1.6.</description></item><item><title>AspectJ 基于自定义的方法注解来拦截方法</title><link>https://yanbin.blog/aspectj-baseon-annotation-method/</link><pubDate>Thu, 16 Aug 2012 08:57:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aspectj-baseon-annotation-method/</guid><description>
通常在使用 AspectJ 时都是基于识别方法的规则来进行方法拦截，例如切片里这样写&lt;/p&gt;
&lt;br/&gt;
&lt;code&gt;@Pointcut&lt;/code&gt;&lt;code&gt;(&lt;/code&gt;&lt;code&gt;"execution(* *..StockService.getBaseInfo(..))")&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;
&lt;code&gt;&lt;/code&gt;它拦截到的是以 StockService 结尾的，方法名为 getBaseInfo，参数任意，返回值任意的方法。而我这里要说的一种方式是基于自定义注解来拦截方法的，此处的注解不是指 @Aspect, @Pointcut, 或 @Before 那一堆东西，而是指你可以自定义一个注解，如 @cc.unmi.testaspectj.MonitorMethod，被它所注解的方法即被拦截，像：&lt;br/&gt;&lt;br/&gt;
@cc.unmi.testaspectj.MonitorMethod&lt;br /&gt;
public void foo();&lt;br/&gt;&lt;br/&gt;
这可以给我们很大的自由度来快捷控制哪些方法需要被拦截，加个上面的注解 @MonitorMethod 即可，而不像从前那般要想像用什么规则去匹配某个方法，用 || 连接起来，同时还要防止影响到别的不期望被拦截的方法。&lt;br/&gt;&lt;br/&gt;
需要的代码并不多，四步，创建自定义注解类 MonitorMethod, 需被拦截的方法加上 @MonitorMethod，方面类，测试类。</description></item><item><title>Spring+AspectJ+ 简单方式来拦截方法，监测性能</title><link>https://yanbin.blog/spring-aspectj-intercept-method/</link><pubDate>Wed, 15 Aug 2012 07:15:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-aspectj-intercept-method/</guid><description>
还是在很久以前，作过一篇 &lt;a href="https://yanbin.blog/aop-spring-aspectj-method-invoke-time" target="_blank" rel="noopener"&gt;用 AOP 来记录每个方法的执行时间(Spring 或直接 AspectJ)&lt;/a&gt;, 其中例示了三种方法来拦截方法，用以监测方法调用时间它们分别是：&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;1. Spring 2.0 用 AspectJ 实现 AOP&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;2. Spring 通用的方法拦截&lt;br /&gt;
3. 直接用 AspectJ 实现&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
在这里再次使用 &amp;lt;aop:aspect-autoproxy/&amp;gt; 再 @Aspect 注解的方式来写个新的例子。原理与前面基本一致，只是在类里用 @Aspect, @Pointcut, @Before, @After, @Around, @AfterReturning, @AfterThrowing 来写拦截类。&lt;br/&gt;&lt;br/&gt;
局限仍然是必须通过 Spring 的 BeanFactory 获得的实例才能被拦截到，除非是在 Eclipse 里安装 AJDT 或是使用 Maven-AspectJ Plugin 来编译工程。&lt;br/&gt;&lt;br/&gt;
好，我们来看完整的例子，下面列出所有的项目文件，这是一个 Maven 的项目，所以从 pom.xml 开始。</description></item><item><title>Mac 下 You don't have permission to access /~{username}/ on this server</title><link>https://yanbin.blog/mac-you-dont-have-permission-to-access-on-this-server/</link><pubDate>Tue, 31 Jul 2012 01:28:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-you-dont-have-permission-to-access-on-this-server/</guid><description>
当你在 Mac OS X 下启用了 Web Sharing，其实就是 Apache2 服务, 到浏览器里访问 http://localhost/~{username} 时提示&lt;/p&gt;
&lt;br/&gt;
Forbidden&lt;br/&gt;&lt;br/&gt;
You don't have permission to access /~{username}/ on this server.&lt;br/&gt;&lt;br/&gt;
{username} 代表当前系统用户名。&lt;br/&gt;&lt;br/&gt;
看到上面的问题，首先会想到的是目录下 /Users/{username}/Sites 下的文件权限问题，有可能，但我碰到的问题不是这样的。&lt;br/&gt;&lt;br/&gt;
而是需要在目录 /etc/apache2/users 创建一个文件 {username}.conf，如 Unmi.conf，甚至可以任意文件名，文件内容如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apache" data-lang="apache"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/Users/{username}/Sites/&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Options&lt;/span&gt; Indexes MultiViews
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AllowOverride&lt;/span&gt; AuthConfig Limit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Order&lt;/span&gt; allow,deny
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Allow&lt;/span&gt; from &lt;span class="k"&gt;all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Java 使用 dom4j 来获得 XML 文档的 innerXML</title><link>https://yanbin.blog/java-dom4j-get-innerxml/</link><pubDate>Tue, 17 Jul 2012 07:27:01 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-dom4j-get-innerxml/</guid><description>
在网页的 Document 中如果想要获得某个结点的 innerHTML 就行，而在 Java 处理 XML 文档时想要获得某个节点的的 innerXML 就没那么简单的，标准的 org.w3c.Element 和  org.w3c.Node 均未提供类似 innerXML 的方法。&lt;/p&gt;
&lt;br/&gt;
幸好，我们常用的 dom4j 里的 org.dom4j.Node 有一个方法是 String asXML()，不过它的意义相当于是 outerXML，也就是说它返回的内容还包括节点本身。&lt;br/&gt;&lt;br/&gt;
比如有这么在个 XML 文档：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;BookList&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; description of book 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;Book&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Unmi&lt;span class="nt"&gt;&amp;lt;/Book&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/BookList&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Mac OS X 下给系统自带的字典安装词库</title><link>https://yanbin.blog/mac-osx-dictionary-resources/</link><pubDate>Sat, 14 Jul 2012 13:29:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-osx-dictionary-resources/</guid><description>
Mac&amp;nbsp;自带了 Dictionary,&amp;nbsp;你可以给它安装丰富的字典，中文的或各种其他领域和语言的。安装方法如下：&lt;br/&gt;
&lt;br/&gt;
1. 下载 词典的转换程序 &lt;a href="http://code.google.com/p/mac-dictionary-kit/"&gt;DictUnifier&lt;/a&gt;，并执行它&lt;br/&gt;
&lt;br/&gt;
2. 下载&amp;nbsp;StarDict 词典. &amp;nbsp;目前可从&amp;nbsp;&lt;a href="http://abloz.com/huzheng/stardict-dic/"&gt;http://abloz.com/huzheng/stardict-dic/&lt;/a&gt; 处选择您需要的词典&lt;br/&gt;
&lt;br/&gt;
3. 下载完成之后,&amp;nbsp;把下载的压缩包直接拖到&amp;nbsp;DictUnifier&amp;nbsp;界面上转换.&lt;br/&gt;
&lt;br/&gt;
4.&amp;nbsp;打开&amp;nbsp;Dictionary，在它的&amp;nbsp;Cmd ＋ ,&amp;nbsp;进入的&amp;nbsp;Preferences&amp;nbsp;配置界面中就可以选入新安装的字典&lt;br/&gt;
&lt;br/&gt;
完工！</description></item><item><title>Mac OS X 下开发 Android 程序时使用 WiFi ADB 连真机调试</title><link>https://yanbin.blog/mac-osx-dev-android-wifi-adb/</link><pubDate>Sat, 14 Jul 2012 11:39:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-osx-dev-android-wifi-adb/</guid><description>
其实不必使用 WiFi ADB 连接的方式，Google USB Driver 说不兼容是因为 Mac 根本用不着这个就能直接连手机，可以用 USB 直接连接 Android 手机进行调试。大家可点击看这个&lt;br/&gt;
&lt;h2 style="font-weight: normal; color: #494949;"&gt;&lt;a style="color: #027ac6;" href="https://yanbin.blog/mac-osx-dev-android-wifi-adb"&gt;Mac OS X 下开发 Android 程序时使用 WiFi ADB 连真机调试&lt;/a&gt;&lt;/h2&gt;&lt;br/&gt;
可忽略掉下面的内容。&lt;br/&gt;
&lt;br/&gt;
&lt;hr /&gt;&lt;br/&gt;
&lt;br/&gt;
我在 Mac OS X 下用 Eclipse 开发 Android 程序，当自己的 Android 手机插上电脑上是检测试不到的，不能想在 Windows 下那样，开启了 &lt;strong&gt;连接 USB 调试模式&lt;/strong&gt; 就能在启动程序的的时候可选手机来调试或测试代码。&lt;br/&gt;
&lt;br/&gt;
在 Mac 下的 Android SDK Manager 中其实也明确显示了&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;Google USB Driver Not compatible with Mac OS X&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
也就是无法即插即用了，那么在 Mac 下该如何联接真机来调试或测试程序呢？&lt;br/&gt;
&lt;br/&gt;
有些地方的解决办法是，把手机的 USB 调试选项关掉，启用 USB 数据存储，把 Eclipse 生成的 APK 文件拷到手机的存储卡。手机上再自行安装，而且先得到 USB 数据存储项关掉后手机才能读取存储卡上的内容。&lt;br/&gt;
&lt;br/&gt;
上面那样做只是告诉了你怎么把 APK 程序安装到手机上，还不如通过网络安装方便，并且肯定是不能联机单步调试。&lt;br/&gt;
&lt;br/&gt;
相信还是不少人在 Mac 下开发 Android 程序的，定然会有更好的解决办法，那就是：</description></item><item><title>下载 Android 平台源代码(适合于各种平台)</title><link>https://yanbin.blog/download-android-platform-sources/</link><pubDate>Sat, 14 Jul 2012 00:56:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/download-android-platform-sources/</guid><description>
Android 既然是开源的，那么我们开发时自然会想要得到一份它的源代码，这大大有助于我们更好的深入理解这个平台，更快捷的定位程序出错的根源。当然对于国内那些想自主研发 Mobile OS 的厂商来说，下载 Android 源码是步骤是不可或缺的。&lt;/p&gt;
&lt;br/&gt;
我在标题中为什么要加上说是适合于各种平台呢，因为大概不少的地方都专门说在 Linux 下怎么去下载 Android 源代码，好说 Windows/Mac 下就不是那么回事似的，其实并无区别。&lt;br/&gt;&lt;br/&gt;
官方在说明怎么下载 Android 源代码(&lt;a href="http://source.android.com/source/downloading.html"&gt;http://source.android.com/source/downloading.html&lt;/a&gt;) 的解说中也挺啰嗦的，不光要 git，还要 repo sync 来同步。&lt;br/&gt;&lt;br/&gt;
其实不管是什么平台，只要有 git 客户端就行了。想要下载哪个版本的 Android 源码，先在浏览器里打开 &lt;a href="https://android.googlesource.com/platform/frameworks/base"&gt;https://android.googlesource.com/platform/frameworks/base&lt;/a&gt;，就能看到在 Tags 下有什么版本的源码可下载。如图：</description></item><item><title>JUnit 4 如何正确测试异常</title><link>https://yanbin.blog/junit-4-how-to-test-exceptions/</link><pubDate>Thu, 07 Jun 2012 13:07:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-4-how-to-test-exceptions/</guid><description>
&lt;p&gt;本篇讲述如何在 JUnit 4 下正确测试异常，我会从 try..catch 的方式谈起，然后说到 @Test(expected=Exception.class), 最后论及 @Rules public ExpectedException 的实现方式，最终基本可确定用 @Rules 是最方便的。&lt;/p&gt;&lt;p&gt;我们在用 JUnit 测试方法异常的时候，最容易想到的办法就是用 try...catch 去捕获异常，需要断言以下几个条件：&lt;/p&gt;
&lt;p&gt;1. 确实抛出的异常 &lt;br /&gt;2. 抛出异常的 Class 类型 &lt;br /&gt;3. 抛出异常的具体类型，一般检查异常的 message 属性中包含的字符串的断定&lt;/p&gt;&lt;p&gt;所以常用的代码你可能会这么写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;passwordLengthLessThan6LettersThrowsException&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;No exception thrown.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;instanceof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InvalidPasswordException&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;contains at least 6&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里被测试的方法是 Password.validate() 方法是否抛出了相应的异常，注意这里别漏 try 中的&lt;/p&gt;&lt;p&gt;&lt;span style="color: #800000;"&gt;fail("No Exception thrown.")&lt;/span&gt;</description></item><item><title>jQuery 使用 $.getJSON() 跨域获取 JSON 数据</title><link>https://yanbin.blog/jquery-getjson-cross-domain/</link><pubDate>Sun, 27 May 2012 12:31:35 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jquery-getjson-cross-domain/</guid><description>
&lt;p&gt;假设在服务器上有文件 http://test.unmi.cc/json.php 文件，它的内容为：&lt;/p&gt;
&lt;br/&gt;
[代码 1]&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-type: application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://unmi.cc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nx"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
要是在同一个域上要取该文件的内容，那好办，比如同为 test.unmi.cc 域上的 ajax.html 文件中写上：&lt;br/&gt;&lt;br/&gt;
[代码 2]</description></item><item><title>Mac 下包抓取(sniffer) 工具介绍</title><link>https://yanbin.blog/mac-sniffer-tools/</link><pubDate>Sun, 27 May 2012 08:55:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mac-sniffer-tools/</guid><description>
由于近期常工作于 Mac 环境下，对有些应用稍加深入点研究就免不了要用到网络包抓取工具来分析。早先在 Windows 环境用过老牌的 Sniffer Pro 和 Netxray，以及后来都用免费开源的 &lt;a href="http://sourceforge.net/projects/packetyzer/" target="_blank" rel="noopener"&gt;Packetyzer&lt;/a&gt; 和  &lt;a title="Wireshark" href="http://www.wireshark.org/" target="_blank" rel="noopener"&gt;Wireshark&lt;/a&gt;。偶尔还用过 Windows 服务版自带的网络监视器。&lt;/p&gt;
&lt;br/&gt;
那 Mac 下有什么好用的抓包工具呢，试用了一下下面几个工具：&lt;br/&gt;&lt;br/&gt;
一. tcpdump 命令&lt;br/&gt;&lt;br/&gt;
因为它是 Unix 族的，所以首先想到的是 tcpdump 工具，确实 Mac 下也有这个命令，但用起来也太难了，全在命令行下，看起来不直观。&lt;br/&gt;&lt;br/&gt;
tcpdump 命令的使用可参考以下两个链接：&lt;br/&gt;&lt;br/&gt;
1. &lt;a title="mac tcpdump" href="http://support.apple.com/kb/HT3994" target="_blank" rel="noopener"&gt;Mac OS X: How to capture a packet trace&lt;/a&gt;&lt;br /&gt;
2. &lt;a href="http://danielmiessler.com/study/tcpdump/" target="_blank" rel="noopener"&gt;A tcpdump Tutorial and Primer&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
二. &lt;a title="Ethereal" href="http://www.ethereal.com/" target="_blank" rel="noopener"&gt;Ethereal&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
图形界面的，跨多平台，功能也不错，不过基本你可以忽略它了，它自 2006 年以来就没再发展了，永远死守在 0.99 版了。它就是大我鼎鼎的 Wireshark  的前生。&lt;br/&gt;&lt;br/&gt;
三. &lt;a title="Wireshark" href="http://www.wireshark.org/" target="_blank" rel="noopener"&gt;Wireshark&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
轮到 Wireshark 的隆重出场了，作为 Ethereal 更名后的产品，它仍然是开源的且多平台通吃的。你只要在 Windows 上用过它的话，在 Mac 下的操作方式都一样的。</description></item><item><title>Wordpress Code Editor 插件让你在线编辑主题/插件即时语法高亮</title><link>https://yanbin.blog/wordpress-code-editor-realtime-syntaxhighlight/</link><pubDate>Mon, 30 Apr 2012 15:22:09 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-code-editor-realtime-syntaxhighlight/</guid><description>
晚上找了一些时候在线的即时语加亮组件，很多的，在线的代码编辑器就语法即时高亮上都可与客户端的编辑器相媲美了。有商业的，功能很丰富的，也有开源的，像 &lt;a href="http://codepress.sourceforge.net/" target="_blank" rel="noopener"&gt;CodePress&lt;/a&gt;,   就很简单它支持 PHP, JavaScript, Java, Perl, SQL, HTML 和 CSS 代码的即时着色，完全由 JavaScript 来进行渲染。&lt;/p&gt;
&lt;br/&gt;
顺着 CodePress 来搜索，恰好还找到一个能让我们在 WordPress 的后台在线编辑插件/主题的代码编辑框中实现语法着色，并且时编辑时实时语法着色哦，那就是插件：&lt;a href="http://www.naden.de/blog/wordpress-code-editor" target="_blank" rel="noopener"&gt;WordPress Plugin Code Editor&lt;/a&gt;，可以点击 &lt;a href="http://www.naden.de/blog/shorturl/11" target="_blank" rel="noopener"&gt;Code Editor&lt;/a&gt;  链接下载并安装为 WordPress 的插件。&lt;br/&gt;&lt;br/&gt;
安装完启用后，使能看到编辑插件/主题时的代码都高亮了，使代码的可读性大大增加，编辑时即时的效果，能让你修改代码时出错的几率明显降低。</description></item><item><title>HTML各种标签的默认样式和浏览器默认css样式 [转]</title><link>https://yanbin.blog/html-tags-default-style-in-browser/</link><pubDate>Thu, 26 Apr 2012 20:40:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/html-tags-default-style-in-browser/</guid><description>
下面列出了所有HTML标签的默认样式，供大家参考，写出这些东西的初衷是因为今天在修改一个样式的时候【&lt;a href="http://www.qiandabao.com/cxrs/37.html" target="_blank"&gt;怎么样修正zblog文章内容中ol列表样式为原来的样式&lt;/a&gt;]】，不清楚OL的默认样式距离左边距有多少像素，所以这里就列出这些，以供参考，我脑子不太好用，记不住，&lt;img src="http://www.qiandabao.com/image/face/Haha.gif" alt="" /&gt;</description></item><item><title>在 Java 中使用正则表达式进行后向引用($1,$2...)</title><link>https://yanbin.blog/java-regex-replace-string/</link><pubDate>Wed, 25 Apr 2012 06:02:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-regex-replace-string/</guid><description>
自 JDK1.4 引入正则表达式的支持可称得上是次大改变，&lt;del&gt;可 Java 的正则表达式的能力还是很弱，别说和 Perl 比了，就是和 C# 和 JavaScript 的正则表达式比较来也逊色不少&lt;/del&gt;，不过现在 JVM 上有其他语言对正则表达式有所增强，像 Groovy, jRuby 和 Scala。&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;[修正一下] 第一句话的说法是有误的，其实 JDK 本身的正则表达式就支持替换时用 $1, $2 那样的后向分组引用，例如：&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; String s = "abc def".replaceAll("(\\w+)\\s+(\\w+)", "$2 $1");  //s 就是  "def abc"，replaceFirst 也可以用 $1, $2 的替换。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;del&gt;比如在 Java 中要实现正则表达式的分组，后向引用的方式进行替换挺复杂的&lt;/del&gt;，像 JavaScript 完成两个分组的调换：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;abc def&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;des&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(\w+)\s+(\w+)/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;$2 $1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;des&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//输出为： def abc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在 Java 中可得用上 Pattern, Matcher，还要 find()/group() 等操作，于是我就会考虑在 Java 中完成类似的功能有没有曲线的方法，有的。你可以调用 JVM 上其他语言的功能，我这里用列了两种实现方式&lt;br/&gt;&lt;br/&gt;
JDK1.6 起可借助 ScriptEngineManager 使用 JavaScript 相应的函数，除此之外还可用第三方的库 &lt;a href="http://jregex.sourceforge.net/" target="_blank" rel="noopener"&gt;JRegex&lt;/a&gt;，具体实现，请参见如下代码，还带了测试用例：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cc.unmi.utils&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javax.script.Invocable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javax.script.ScriptEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javax.script.ScriptEngineManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jregex.Pattern&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jregex.Replacer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StringUtils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;replaceStringByJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jsRegex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScriptEngineManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScriptEngineManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScriptEngine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEngineByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;JavaScript&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;function rep(target, regex, replacement){\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; var patt = new RegExp(regex,&amp;#39;g&amp;#39;);\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; return target.replace(patt, replacement);\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invocable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invocable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;rep&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jsRegex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;replaceStringByJRegex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Replacer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replacer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replacer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;link url=\&amp;#34;http://unmi.cc\&amp;#34; &amp;gt;\&amp;#34;Home\&amp;#34;&amp;lt;/link&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;link url=\&amp;#34;http://unmi.cc/tag/java\&amp;#34; &amp;gt;\&amp;#34;Java\&amp;#34;&amp;lt;/link&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jsString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StringUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceStringByJS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;link\\s+url=\\\&amp;#34;(.+?)\\\&amp;#34;.[^/]*?&amp;gt;\\\&amp;#34;(.+?)\\\&amp;#34;&amp;lt;/link&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;a href=\&amp;#34;$1\&amp;#34;&amp;gt;$2&amp;lt;/a&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jregexString&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StringUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceStringByJRegex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;link\\s+url=\\\&amp;#34;(.+?)\\\&amp;#34;.[^/]*?&amp;gt;\\\&amp;#34;(.+?)\\\&amp;#34;&amp;lt;/link&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;a href=\&amp;#34;$1\&amp;#34;&amp;gt;$2&amp;lt;/a&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jregexString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 程序输出
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * &amp;lt;a href=&amp;#34;http://unmi.cc&amp;#34;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;a href=&amp;#34;http://unmi.cc/tag/java&amp;#34;&amp;gt;Java&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * &amp;lt;a href=&amp;#34;http://unmi.cc&amp;#34;&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;a href=&amp;#34;http://unmi.cc/tag/java&amp;#34;&amp;gt;Java&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
起初我是用的第一种方法，后来找到了第二种方式，感觉用 JRegex 更优雅些。</description></item><item><title>SPRING-MVC 访问静态文件, 如 jpg/js/css</title><link>https://yanbin.blog/spring-mvc-access-static-resources-jpgjscss/</link><pubDate>Thu, 15 Mar 2012 09:59:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-mvc-access-static-resources-jpgjscss/</guid><description>
如何你的DispatcherServlet拦截 &lt;em&gt;.do这样的URL，就不存在访问不到静态资源的问题。如果你的DispatcherServlet拦截“/”，拦截了所有的请求，同时对&lt;/em&gt;.js,*.jpg的访问也就被拦截了。&lt;/p&gt;
&lt;br/&gt;
 目的：可以正常访问静态文件，不要找不到静态文件报404。&lt;br /&gt;
 &lt;br /&gt;
&lt;strong&gt;方案一：激活Tomcat的defaultServlet来处理静态文件&lt;/strong&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;*.jpg&lt;span class="nt"&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;*.js&lt;span class="nt"&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;*.css&lt;span class="nt"&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;要配置多个，每种文件配置一个</description></item><item><title>强制退出Mac程序的六种方法[转]</title><link>https://yanbin.blog/exit-mac-process-force/</link><pubDate>Sun, 04 Mar 2012 21:32:30 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/exit-mac-process-force/</guid><description>
用电脑时间长了，难免会遇到程序卡住，风火轮狂转不停，没有任何相应等情况。可能是由于程序冲突、缓存不足或者一些bug等情况导致，这个时候我们就需要强制退出这个程序了，下面有六种在Mac系统中强制退出程序的方法，大家至少应该记住一两个。&lt;br/&gt;
&lt;div id="ArticleCnt"&gt;
&lt;br/&gt;
&lt;strong&gt;1、使用键盘快捷键强制退出处于活跃状态的Mac程序&lt;/strong&gt;快捷键：Command+Option+Shift+Esc这样按住一两秒钟，就可以强制退出当前程序了，算是最方便的一种方法。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2、打开强制退出程序窗口&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
使用快捷键：Command+Option+Esc&lt;br/&gt;
&lt;br/&gt;
来打开“强制退出应用程序”的窗口，然后选中你需要退出的程序，再点右下方的“强制退出”即可。</description></item><item><title>谈Objective-C中的property[摘]</title><link>https://yanbin.blog/several-tips-about-objc-property/</link><pubDate>Sat, 25 Feb 2012 23:05:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/several-tips-about-objc-property/</guid><description>
property大概是objc中唯一的元编程机制，用来快速地生成类的数据成员的get和set方法。本文谈谈我对property的一些使用心得。&lt;/p&gt;
&lt;br/&gt;
1）retain，copy和assign：&lt;br/&gt;&lt;br/&gt;
retain的作用在于生成set方法时该数据成员（一定是objc对象）的引用计数+1，通常用于除NSString类型以外的objc对象。如&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;setMember:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Member&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;member&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;member_&lt;/span&gt; &lt;span class="k"&gt;release&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;member_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;member&lt;/span&gt; &lt;span class="k"&gt;retain&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
一个常见的例外是delegate的set方法通常设为assign，这是为了避免循环引用造成的引用计数无法归0，然后内存泄漏——考虑一个view的delegate为该view所在controller的情况。&lt;br/&gt;&lt;br/&gt;
NSString类的对象的set方法设为copy，字符串对象需要深拷贝。</description></item><item><title>iOS 开发中使用正则表达式－暨 RegexKitLite 库的用法</title><link>https://yanbin.blog/ios-regexkitlite-library/</link><pubDate>Sat, 25 Feb 2012 06:17:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ios-regexkitlite-library/</guid><description>
在 iOS 开发中要用正则表达式的话，至少有三种选择：&lt;/p&gt;
&lt;br/&gt;
1. 系统自带的  NSString 和 NSMutableString 相关的方法&lt;br /&gt;
2. RegexKit Framework -&lt;a href="http://regexkit.sourceforge.net" target="_blank" rel="noopener"&gt; http://regexkit.sourceforge.net&lt;/a&gt;&lt;br /&gt;
3. RegexKitLite - &lt;a href="http://regexkit.sourceforge.net/RegexKitLite/" target="_blank" rel="noopener"&gt;http://regexkit.sourceforge.net/RegexKitLite/&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
NSString 和 NSMutableString 里像&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSRange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;rangeOfString:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;aString&lt;/span&gt; &lt;span class="nf"&gt;options:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSStringCompareOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSUInteger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;replaceOccurrencesOfString:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt; &lt;span class="nf"&gt;withString:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;replacement&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;options:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSStringCompareOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;options&lt;/span&gt; &lt;span class="nf"&gt;range:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSRange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;searchRange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
带有 NSStringCompareOptions 参数的方法就可以支持正则表达式，只要对于 NSStringCompareOptions 参数取值中含有 NSRegularExpressionSearch 这个 Mask 值即可。&lt;br/&gt;&lt;br/&gt;
RegexKit Framework 用的是 PCRE(&lt;a href="http://www.pcre.org" target="_blank" rel="noopener"&gt;Perl Compatible Regular Expressions&lt;/a&gt;) 库，可能是我们熟悉正则表达式语法，但需要引入 PCRE 静态库。&lt;br/&gt;&lt;br/&gt;
RegexKitLite，看链接它与 RegexKit Framework 出自一家，但它用的是 Mac 自带的 ICU(&lt;a href="http://site.icu-project.org" target="_blank" rel="noopener"&gt;International Components for Unicode&lt;/a&gt;) 库，所以语法上与 RegexKit Framework 是不一样的，且引入它也较简单。</description></item><item><title>在FCKEditor中使用插件UGeSHi-GeSHi实现代码高亮显示</title><link>https://yanbin.blog/fckeditor-ugeshi-code-highlight/</link><pubDate>Tue, 21 Feb 2012 23:19:34 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fckeditor-ugeshi-code-highlight/</guid><description>
&lt;div&gt;
&lt;br/&gt;
本人安装了一个个人知识库管理系统 Knowledgeroot，选定了其中所见即所得编辑器比较好用的是 FckEditor，又再需要给它配上一款代码高亮的插件，于是找来了 UGeSHi-GeSHi。下面是从网络上摘来的在 FckEditor 中如何安装 UGeSHi-GeSHi 插件。&lt;br/&gt;
&lt;br/&gt;
什 么是UGeSHi？UGeSHi是一个连接器，它把FCKEditor与GeSHi（采用PHP编程语言写的代码高亮插件，目前它已能高亮100多种程序 语言）紧密的结合在一起，您可以称为它为FCK插件，也可以称它为连接器（因为代码高亮的核心文件不是本人编写，本人只编写了其与FCKEditor整合 的过程）。&lt;br/&gt;
&lt;br/&gt;
UGeSHi的功能：高亮程序代码，使程序代码美观且易读。&lt;br/&gt;
&lt;br/&gt;
为什么采用GeSHi作为核心文件？原因很简单，因为GeSHi高亮足够完美，而且支持高亮的程序语言也很完善。&lt;br/&gt;
&lt;br/&gt;
UGeSHi能高亮多少种程序语言？粗略的说可以高亮100多种，具体细节看下表：</description></item><item><title>UIView 中的控件事件穿透 Passthrough 的实现</title><link>https://yanbin.blog/uiview-event-passthrough/</link><pubDate>Sun, 19 Feb 2012 09:08:08 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/uiview-event-passthrough/</guid><description>
我们在有多个 UIView 层叠时，比如一个按钮被一个 UIView 遮盖时，想要在点击最上层的 UIView 时能触发按钮的相应事件，我们该如何实现呢，初步可以想到几种办法：&lt;/p&gt;
&lt;br/&gt;
1. 把按钮上层的所有 UIView 的 userInteractionEnabled 属性设置为 NO，要是 UIView 有自己的交互事件该如何办呢？而且这个 userInteractionEnabled 不能动态设置，等到点击后决定设置它的 NO 是没用的&lt;br/&gt;&lt;br/&gt;
2. UIView 接受到点击事件后主动去触发下面按钮的点击，这时的关题有三，按钮没有点击过程中的交换效果、多层 UIView 时不切实际，逐层下传吗、还有就是其他双击、三击或别的手势如何处理&lt;br/&gt;&lt;br/&gt;
我也一直被前面两种方式纠缠着，同时也让 UIPopoverController 的 NSArray *passthroughViews 属性提醒着，因为对于 UIPopoverController，设置到它的 passthoughViews 属性中的控件事件可以完全从 UIDimmingView 下透出来。但苦于不可能看到 UIPopoverController 的源码，还是后面一而再的 Google 终于发现了 UIView 的方法：&lt;br/&gt;&lt;br/&gt;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system</description></item><item><title>Windows 平台下的 TextMate：E-TextEditor (附下载)</title><link>https://yanbin.blog/windows-textmat-e-texteditor/</link><pubDate>Tue, 14 Feb 2012 23:40:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-textmat-e-texteditor/</guid><description>
在&amp;nbsp;Mac&amp;nbsp;平台下慢慢习惯了&amp;nbsp;TextMate&amp;nbsp;文本编辑器，功能很强，丰富的&amp;nbsp;Bundle&amp;nbsp;支持，于是想在&amp;nbsp;Windows&amp;nbsp;下找到一款类似的软件，那就是 &lt;a title="e-texteditor" href="http://www.e-texteditor.com/" target="_blank"&gt;E-TextEditor&lt;/a&gt;。E-TextEditor&amp;nbsp;可以支持许许多多的编程语言，关键它可以直接使用&amp;nbsp;TextMate&amp;nbsp;的 Bundle，这就能与&amp;nbsp;TextMate&amp;nbsp;达成一致了。&lt;br/&gt;
&lt;br/&gt;
官方当前版本是 2.0.2，可在 http://www.e-texteditor.com/&amp;nbsp;下载，可试用 30&amp;nbsp;天，可自己在网上找找，这里列出两方法：&lt;br/&gt;
&lt;br/&gt;
1.&amp;nbsp;破解文件覆盖&lt;br/&gt;
&lt;br/&gt;
只找到 2.0a&amp;nbsp;的 e.exe&amp;nbsp;文件，在 &lt;a title="E-TextEditor 2.0a" href="http://dl.vmall.com/c0dbndat0j" target="_blank"&gt;E-TextEditor.v2.0a.Cracked-EAT&lt;/a&gt;&amp;nbsp;同时有安装文件和 e.exe&amp;nbsp;文件下载&lt;br/&gt;
&lt;br/&gt;
2.&amp;nbsp;修改注册表法&lt;br/&gt;
&lt;br/&gt;
试用期 30&amp;nbsp;天记在 HKEY_CURRENT_USER\Software\e\CLSID2,&amp;nbsp;所以只要试用期快到了，把这个键删了就行，E-TextEditor&amp;nbsp;在试用期间好像也没有功能上的限制。</description></item><item><title>走近 Ruby 的世界(六)</title><link>https://yanbin.blog/close-to-ruby-world-6/</link><pubDate>Sun, 05 Feb 2012 09:36:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-6/</guid><description>
对于 Block，可以用 {} 或 do...end 来调用，如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Ruby!&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="err"&gt;＃可以写成&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Ruby!&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#也可以写成 1.upto(10) {|x| print x}  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
一般是多行时用 do...end，单行时用 {}&lt;br/&gt;&lt;br/&gt;
但对于 module、class、method 的声明不能像 C/Java 那样用 {}，要用 ...end 来定义。</description></item><item><title>走近 Ruby 的世界(五)</title><link>https://yanbin.blog/close-to-ruby-world-5/</link><pubDate>Sun, 05 Feb 2012 07:47:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-5/</guid><description>
Ruby 单行注释用 #，没有 /* */ 这样的多行注释方式，但是可以用文档注释方式来进行多行注释：&lt;/p&gt;
&lt;br/&gt;
=begin 注释描述&lt;br /&gt;
       中间的都是注释内容&lt;br /&gt;
=end&lt;br/&gt;&lt;br/&gt;
文档注释，像 Java 的 Javadoc, .net 的 ndoc，Ruby 也有 rdoc，Ruby 的 文档注释是也是在 method，class 和 module 定义之前进行注释的，注释的内容给 rdoc 生成 HTML 帮助文档，也给 ri 用的。也有一套语法，用 # = Headings 这样的语法，还支持些 HTML 代码。&lt;br/&gt;&lt;br/&gt;
Ruby 看到全大写的标识符，如 PI，还有像 LikeThis 或 LIKE_THIS 这样的标识符就认为是常量，对它们重新赋值就会告警：&lt;br/&gt;&lt;br/&gt;
warning: already initialized constant LikeThis&lt;br/&gt;&lt;br/&gt;
命名成 like_this 就是变量，这也是 Ruby 的约定优于配置，常量就是大写命名。</description></item><item><title>iPhone中Png图片格式的研究</title><link>https://yanbin.blog/iphone-png-format/</link><pubDate>Mon, 09 Jan 2012 20:11:18 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/iphone-png-format/</guid><description>
有时候我们看到一个App，想看看他的一些界面是如何实现的，这个时候需要查看一下它的图片资源，不过iOS的png图片编译后一般的图片阅读器都是没法查看的，本文将告诉的原因和转换出原图的方法（得安装XCode）。&lt;br/&gt;
&lt;blockquote&gt;ipa 解压，将png相关文件夹拷贝出来，在命令行下使用/Developer/Platforms/iPhoneOS.platform/Developer /usr/bin/pngcrush -revert-iphone-optimizations xxx.png yyy.png&lt;/blockquote&gt;
我们都知道一个编译好的iPhone app 其中的png图片一般普通的图片阅读器是无法直接读取的，这是因为XCode在编译的过程中，将图片进行了优化，实际上它已经不是一个png图片了。&lt;br/&gt;
这边有一些apple iPhone png自己格式的一些说明&lt;br/&gt;
&lt;a href="http://iphonedevwiki.net/index.php/CgBI_file_format"&gt;http://iphonedevwiki.net/index.php/CgBI_file_format&lt;/a&gt;</description></item><item><title>Objective-C 对 URL 进行 URLEncode 编码</title><link>https://yanbin.blog/objective-c-urlencode/</link><pubDate>Mon, 09 Jan 2012 08:33:50 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-urlencode/</guid><description>
在 iOS 程序访问 HTTP 资源时需要对 URL 进行 Encode，比如像拼出来的&lt;span style="color: #800000;"&gt; http://unmi.cc?p1=%+&amp;amp;sd f&amp;amp;p2=中文&lt;/span&gt;，其中的中文、特殊符号&amp;amp;％和空格都必须进行转译才能正确访问。&lt;/p&gt;
&lt;br/&gt;
在 Java、.net 和 JS 中都有相应的 encodeURL 方法可用，在 Objective-C 语言中，你可以试下&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;- (NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)enc;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
来对完整的 URL(带请求参数的)进行编码，比如执行下面的代码：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;@&amp;#34;http://unmi.cc?p1=%+&amp;amp;amp;sd &amp;amp;amp;p2=中文&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;encodedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nl"&gt;stringByAddingPercentEscapesUsingEncoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;NSUTF8StringEncoding&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>走近 Ruby 的世界(四)</title><link>https://yanbin.blog/close-to-ruby-world-4/</link><pubDate>Sun, 08 Jan 2012 11:02:39 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-4/</guid><description>
Ruby&amp;nbsp;解释器给我们提供了四个常用的命令行工具，ruby、irb、ri&amp;nbsp;和&amp;nbsp; gem。动态脚本语言的解释器类似工具的使用方法基本一致的，逐个来看下它们的用法：&lt;br/&gt;
&lt;br/&gt;
1.&amp;nbsp;ruby：脚本解释器，直接执行脚本或脚本文件&lt;br/&gt;
&lt;br/&gt;
％ ruby -e 'puts "hello world!"'&lt;br/&gt;
% ruby hello.rb&lt;br/&gt;
&lt;br/&gt;
&lt;em&gt;ruby&amp;nbsp;显示输出可以用三种方法：print、puts&amp;nbsp;和&amp;nbsp;p，print&amp;nbsp;和&amp;nbsp;puts&amp;nbsp;都会把操作对象的 to_s&amp;nbsp;返回值输出，只是 puts&amp;nbsp;会在输出内容没有换行时加上一个换行，有则和&amp;nbsp;print&amp;nbsp;是一样的。例如 puts "Hello World!"&amp;nbsp;就是 print "Hello World!\n"，puts "Hello World!\n"&amp;nbsp;也是&amp;nbsp; print "Hello World!\n"。p&amp;nbsp;会调用对象的&amp;nbsp;inspect&amp;nbsp;方法进行输出，最后加不加换行与&amp;nbsp;puts&amp;nbsp;的规则是一样的。&lt;/em&gt;&lt;br/&gt;
&lt;br/&gt;
2.&amp;nbsp;&amp;nbsp;&amp;nbsp; irb：(Interactive RuBy)&amp;nbsp;是个&amp;nbsp;Ruby&amp;nbsp;的交互命令行，可用来测试一些 Ruby&amp;nbsp;的特性，不过有 TextMate&amp;nbsp;那样的工具基本不会去用它。你还能在&amp;nbsp;irb&amp;nbsp;的&amp;nbsp;shell&amp;nbsp;下再次输入&amp;nbsp;irb&amp;nbsp;启动一个子&amp;nbsp;shell。</description></item><item><title>NSNotificationCenter 的使用详解</title><link>https://yanbin.blog/nsnotificationcenter-usage/</link><pubDate>Fri, 06 Jan 2012 12:22:24 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nsnotificationcenter-usage/</guid><description>
通常我们在 iOS 中发生什么事件时该做什么是由 Delegate 实现的，例如 View 加载完后会触发 viewDidLoad。  Apple 还为我们提供了另一种通知响应方式，那就是 NSNotification，系统中(UIKeyboardDidShowNotification 等) 以及某些第三方组件(例如 ASIHTTPRequest 的 kReachabilityChangedNotification 等)。&lt;/p&gt;
&lt;br/&gt;
&lt;a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html" target="_blank" rel="noopener"&gt;NSNotificationCenter&lt;/a&gt; 较之于 Delegate 可以实现更大的跨度的通信机制，可以为两个无引用关系的两个对象进行通信。NSNotificationCenter 的通信原理使用了观察者模式：&lt;br/&gt;&lt;br/&gt;
1. NSNotificationCenter 注册观察者对某个事件(以字符串命名)感兴趣，及该事件触发时该执行的 Selector 或 Block&lt;br /&gt;
2. NSNotificationCenter 在某个时机激发事件(以字符串命名)&lt;br /&gt;
3. 观察者在收到感兴趣的事件时，执行相应的 Selector 或 Block</description></item><item><title>给网页开发者的20个有用的Firefox插件</title><link>https://yanbin.blog/20-firefox-plugins-for-web-developers/</link><pubDate>Thu, 05 Jan 2012 23:09:13 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/20-firefox-plugins-for-web-developers/</guid><description>
&lt;h3&gt;Firebug&lt;/h3&gt;&lt;br/&gt;
&lt;p style="text-align: center;"&gt;
&lt;a href="https://addons.mozilla.org/en-us/firefox/addon/firebug/" target="_blank"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2012/20-firefox-plugins-for-web-developers/01202804_YZWG.jpg"
width="600px"
/&gt;
&lt;/a&gt;</description></item><item><title>走近 Ruby 的世界(三)</title><link>https://yanbin.blog/close-to-ruby-world-3/</link><pubDate>Wed, 04 Jan 2012 10:05:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-3/</guid><description>
Ruby 中有许多以 ? 和 !号结尾的方法，? 号方法返回布尔值用于判断的，如Array.empty?，可以想到三元操作符 &lt;span style="color: #800000;"&gt;a = b==1?1:2&lt;/span&gt; 。出现在方法名尾部的感叹号表明使用该方法是需要多加小心。许多Ruby的核心类都定义了成对的方法，它们具有同样的名称，只是结尾相差一个“！”,通常情况下，不带感叹号的方法返调用该方法的一个拷贝，带感叹号的方法则是一个可变方法，该方法会修改原来的对象，如 Array 类中的 sort 和 sort!。&lt;/p&gt;
&lt;br/&gt;
Ruby 里对变量名有这么一个约定：全局变量加前缀 $，实例变量用前缀 @，类变量前缀用 @@。&lt;br/&gt;&lt;br/&gt;
Ruby 的类和模块，来实现一个 Ruby 的示例类，从中了解类的构造方法，块的实现、块内循环和调用以及符号的重载，看如下代码：</description></item><item><title>走近 Ruby 的世界(二)</title><link>https://yanbin.blog/close-to-ruby-world-2/</link><pubDate>Mon, 02 Jan 2012 08:35:48 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-2/</guid><description>
还是零碎的记录，也不知道该为每一篇赋上什么合适的标题，姑且用一、二、三、四 ......&amp;nbsp;往下标吧。&lt;br/&gt;
&lt;br/&gt;
一种编程语 言，Coding&amp;nbsp;时一定要讲求效率的，我向来不认为用记事本来写程序就可被认为是多么的高明。就像测试&amp;nbsp;Java&amp;nbsp;程序我仍然是选用 Eclipse，当然命令行技术是要求掌握的。写&amp;nbsp;Ruby&amp;nbsp;程序可以用&amp;nbsp;NetBeans&amp;nbsp;那样带智能提示的工具，但是 Mac OS&amp;nbsp;下，TextMate&amp;nbsp;的口碑很不多的，它有很好用的&amp;nbsp;Bundle，Cmd＋R&amp;nbsp;就能看到运行结果，查帮助也很方便。&lt;br/&gt;
&lt;br/&gt;
从代码的智能提示来看，TextMate&amp;nbsp;或许只能称之为半智能，但它的效率还是非常的高，且基于其余时候打开 TextMate&amp;nbsp;的频度，我选择 TextMate&amp;nbsp;作为 Ruby&amp;nbsp;的 IDE。&lt;br/&gt;
&lt;br/&gt;
Ruby&amp;nbsp;的表达式和操作符，Ruby&amp;nbsp;的语法是面向表达式的，如在其他语言中的控制结构在&amp;nbsp;Ruby&amp;nbsp;中统统叫做表达式，只是它们的返回值为&amp;nbsp;nil。来看几个较有特色的&amp;nbsp;Ruby&amp;nbsp;运算符：</description></item><item><title>走近 Ruby 的世界(一)</title><link>https://yanbin.blog/close-to-ruby-world-1/</link><pubDate>Mon, 02 Jan 2012 03:30:34 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/close-to-ruby-world-1/</guid><description>
因工作需要，需用到 Ruby 来写 BDD(&lt;a title="BDD" href="http://behaviour-driven.org/" target="_blank" rel="noopener"&gt;Behaviour Driven Development&lt;/a&gt;) 的脚本，这样的话还是希望自己对 Ruby 有一定程度的了解，不能总是依葫芦画瓢吧。&lt;/p&gt;
&lt;br/&gt;
Ruby 是由松本行弘(Yukihiro Matsumoto, 简称：Matz) 发明的，确实应该佩服一下，不存在什么民族情节的，比起所谓的汉语编程要强上千百遍，再说，通常民族情节都为政治所利用的。蒙牛、百度，腾讯等等 ，民族品牌，狗屁，都是某些个岛国注册的。&lt;br/&gt;&lt;br/&gt;
Ruby is designed to make programmers happy. Ruby 吸收了 List、Smalltalk 和 Perl 的灵感，采用了类似于 C 和 Java 的语法。&lt;br/&gt;&lt;br/&gt;
Ruby 是完全面向对象的编程语言(completely object-oriented language)，不像 Java 和 C 存在基本(原始)类型，在 Ruby 的世界里，什么都是对象，像数字，true、false 或  nil 都是对象，它们有自己的&lt;del&gt;属性和&lt;/del&gt;方法，你可以对它们点一下点出东西来。如：</description></item><item><title>WordPress 站点带宽几天就耗尽，都是 WP Gravatar Mini Cache 惹的祸</title><link>https://yanbin.blog/plugin-wp-gravatar-mini-cache-exhaust-bandwidth/</link><pubDate>Tue, 27 Dec 2011 09:34:20 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/plugin-wp-gravatar-mini-cache-exhaust-bandwidth/</guid><description>
有两个月里，本站点只能月头坚持七八天，基本处于瘫患状态。访问时就出现几行文字信息，现在不记得啦，大概就是域名被临时挂起了。后来才知道是因为有限的带宽已消耗怠尽，才不允许访问的。似乎这种情况还能给我带来点惊喜，我的网站有这么受欢迎吗！从 Google 的网站分析数据来看，并不是这样，那么就是有异常了。&lt;/p&gt;
&lt;br/&gt;
站点停了几十天后，第一次找了下原因，似乎我站点上提供了几个文件下载量特别大，于是把那几个文件搬到文件分享网站上去分流，还是不奏效，没几天还是收到带宽将要耗尽的邮件通知。是不是别的几个服务器目录中的文件被人拼命下了呢，于是把它们改了名，还用插件对文件的下载进行了监控，未见异常。就这样，仍是没过几天，网站再一次被关闭了，距离月底还有几十天。</description></item><item><title>有时候可用 UIWebView 代替 UITextView，解决行间距问题</title><link>https://yanbin.blog/uiwebview-replace-uitextview-line-height/</link><pubDate>Sat, 10 Dec 2011 09:13:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/uiwebview-replace-uitextview-line-height/</guid><description>
我们在使用 UILabel 或是 UITextView 时，如果文字出现了换行，目前我还不知道如何去控制行间距，固定的 Label 文字 可以折成两个 Label 上下显示，但是对应大段显示的段落要多行显示时该如何呢，多行单独控制就变得不现实了。&lt;/p&gt;
&lt;br/&gt;
由于 UILabel 或 UITextView 并不提供对行间距 line-height 的设置，要达成所愿指定行间距的方法，我可以想到三种方式：&lt;br/&gt;&lt;br/&gt;
1. 自定义相应的控件，在控件内部方法中去做文章，未实践，可能是个好方法&lt;br /&gt;
2. 修改字体，由于默认行间距是由字体本身特性决定的，所以可以修改字体来满足有限的需求，不是很现实&lt;br /&gt;
3. 用    UIWebView 来代替 UITextView，这是本文的思想&lt;br/&gt;&lt;br/&gt;
先看个图片，看看在使用某些字体时失控的行间距是什么样的效果：</description></item><item><title>16个WordPress中使用的Google服务和应用</title><link>https://yanbin.blog/16-wordpress-plugi-google-service/</link><pubDate>Thu, 08 Dec 2011 22:54:19 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/16-wordpress-plugi-google-service/</guid><description>
Google提供了很多不错的用户端服务和应用，虽然最初是一个搜索引擎，今天Google几乎为每一个用户提供解决方案。当为了满足需求的时候，Google并不忘记满足Web开发人员和设计人员的需求。在这篇文章中，我们将了解一下Google提供了那些主要的服务，并且如何应用到wordpress中的。&lt;/p&gt;
&lt;br/&gt;
&lt;p style="text-align: center;"&gt;&lt;img class="aligncenter" src="http://articles.csdn.net/uploads/allimg/111208/09532G1O-0.jpg" alt="16个wordpress博客中使用的Google服务和应用" width="520" height="273" /&gt;&lt;/p&gt;
&lt;br/&gt;</description></item><item><title>iOS Programming – 触摸事件处理</title><link>https://yanbin.blog/ios-programming-touch-events/</link><pubDate>Fri, 04 Nov 2011 03:04:36 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ios-programming-touch-events/</guid><description>
iphone/ipad无键盘的设计是为屏幕争取更多的显示空间，大屏幕在观看图片、文字、视频等方面为用户带来了更好的用户体验。而触摸屏幕是iOS设备接受用户输入的主要方式，包括单击、双击、拨动以及多点触摸等，这些操作都会产生触摸事件。&lt;/p&gt;
&lt;br/&gt;
在Cocoa中，代表触摸对象的类是&lt;span style="color: #ff0000;"&gt;UITouch&lt;/span&gt;。当用户触摸屏幕后，就会产生相应的事件，所有相关的UITouch对象都被包装在事件中，被程序交由特定的对象来处理。UITouch对象直接包括触摸的详细信息。&lt;br/&gt;&lt;br/&gt;
UITouch类中包含5个属性：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #99cc00;"&gt;window&lt;/span&gt;：触摸产生时所处的窗口。由于窗口可能发生变化，当前所在的窗口不一定是最开始的窗口。&lt;br /&gt;
&lt;span style="color: #99cc00;"&gt;view&lt;/span&gt;:触摸产生时所处的视图。由于视图可能发生变化，当前视图也不一定时最初的视图。&lt;br /&gt;
&lt;span style="color: #99cc00;"&gt;tapCount&lt;/span&gt;：轻击（Tap）操作和鼠标的单击操作类似，tapCount表示短时间内轻击屏幕的次数。因此可以根据tapCount判断单击、双击或更多的轻击。</description></item><item><title>设置 UILabel 和 UITextField 的 Padding 或 Insets</title><link>https://yanbin.blog/uilable-uitextfield-padding-insets/</link><pubDate>Tue, 01 Nov 2011 13:01:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/uilable-uitextfield-padding-insets/</guid><description>
iOS 的控件，只看到 &lt;a href="http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIButton_Class/UIButton/UIButton.html"&gt;UIButton&lt;/a&gt; 可以设置 Padding/Insets，即按钮上文字或图片与按钮边界的间隙，对与 CSS 来说叫做 Padding，在 iOS 中叫做 Insets，UIButton 设置 Insets 相应的属性如下：&lt;/p&gt;
&lt;br/&gt;
Configuring Edge Insets&lt;br/&gt;&lt;br/&gt;
      contentEdgeInsets  property&lt;br /&gt;
      titleEdgeInsets  property&lt;br /&gt;
      imageEdgeInsets  property &lt;br/&gt;&lt;br/&gt;
它们接受的属性类型是：UIEdgeInsets，由函数 UIEdgeInsetsMake ( CGFloat top, CGFloat left, CGFloat bottom, CGFloat right );     构造出，分别表示其中的内容/标题/图片离各边的距离。&lt;br/&gt;&lt;br/&gt;
在 xib 中也有界面来对按钮的这三个 EdgeInsets 属性的设置，分别是按钮的 Edge 和 Inset 属性。&lt;br/&gt;&lt;br/&gt;
印像中，Swing 的许多组件都可设置 Insets 属性，可对于 iOS 的控件就没那么幸运了，比如我想设置 UILable 或 UITextField 中的文本离边界的间隙，无伦是在 xib 里还是直接代码的方式都无能为力，因为它们根据未开放相应的属性让你去控制。</description></item><item><title>本站意外休眠了十天后重新恢复正常</title><link>https://yanbin.blog/site-down-10-days/</link><pubDate>Mon, 24 Oct 2011 11:53:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/site-down-10-days/</guid><description>
本站前段时间有几天时好时坏，经常一访问：http://unmi.cc&amp;nbsp;就出现：&lt;br/&gt;
&lt;br/&gt;
This account has been suspended.&lt;br/&gt;
Either the domain has been overused, or the reseller ran out of resources.&lt;br/&gt;
&lt;br/&gt;
这样的错误提示，我真的还担心是不是触动了大墙而被&amp;nbsp;Block&amp;nbsp;了呢，又不怎么像。从大约十天前起就彻底不挺了，今晚问了一下主机提供商的客服，才知道，原来是被爆了－－超过月流量了。没理由呀，这个站点还没有这么知名到访问量骤然上升的，彻查啊，于是上图上真相：</description></item><item><title>设置 NSZombieEnabled 定位 EXC_BAD_ACCESS 错误</title><link>https://yanbin.blog/nszombieenabled-locate-exc_bad_access-error/</link><pubDate>Tue, 20 Sep 2011 12:29:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nszombieenabled-locate-exc_bad_access-error/</guid><description>
我们做 iOS 程序开发时经常用遇到 EXC_BAD_ACCESS 错误导致 Crash，出现这种错误时一般 Xcode 不会给我们太多的信息来定位错误来源，只是在应用 Delegate 上留下像&lt;span style="color: #800000;"&gt; Thread 1: Program received signal: "EXC_BAD_ACCESS"&lt;/span&gt;，让问题无从找起。&lt;/p&gt;
&lt;br/&gt;
比如你对已释放的对象发送消息时就会出现，EXC_BAD_ACCESS，再如 release 的对象再 release，release 那些 autorelease 的对象等也会报这样的错。默认设置下 Xcode 不会给你定位具体是哪一行代码，不该去使用已释放的对象，或者 release 用错了。&lt;br/&gt;&lt;br/&gt;
比如 UIViewController 子类中这样的代码：</description></item><item><title>让 Xcode 4 自动补全 []、 ""、 ''、 () 的插件</title><link>https://yanbin.blog/xcode-4-autocomplete-plugin/</link><pubDate>Sun, 18 Sep 2011 12:09:47 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-4-autocomplete-plugin/</guid><description>
我现在所使用 Xcode 4 Beta 5 尚不支持对&amp;nbsp;&amp;nbsp;[]、 ""、 ''、 () 的自动补全，本插件是由 CocoaChina 版主 “yoyokko” 制作的
Xcode Plugin Plus for Xcode 4 可以帮您实现 Xcode 4 对&amp;nbsp;[]、 ""、 ''、 () 符号的自动补全。&lt;br/&gt;
&lt;br/&gt;
例如输入左中括号，自动补全右中括号，并且光标停在括号中间，在删除左中括号时，右中括号也自动删除。对双引号，单引号，圆括号也是一样的。
大括号在 Xcode 中已能自动处理，输入一个左大括号，回车后就会补全右大括号。&lt;br/&gt;
&lt;br/&gt;
本插件的安装方法如下：&lt;br/&gt;
&lt;br/&gt;
1. 安装 SIMBL(&lt;a href="http://code.google.com/p/simbl/" target="_blank"&gt;SIMBL is the SIMple Bundle Loader&lt;/a&gt;)，
在&amp;nbsp;&amp;nbsp;&lt;a href=" http://www.culater.net/software/SIMBL/SIMBL.php " target="_blank"&gt;http://www.culater.net/software/SIMBL/SIMBL.php&lt;/a&gt;
下载&amp;nbsp;SIMBL-0.9.9.zip (Snow Leopard and Leopard)，Lion 系统也可以用这个版本</description></item><item><title>UIView.clipsToBounds 让子 View 只显示落在父 View 的 Frame 部分</title><link>https://yanbin.blog/uiview-clipstobounds-show-visible-part/</link><pubDate>Sun, 18 Sep 2011 06:29:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/uiview-clipstobounds-show-visible-part/</guid><description>
默认情况下，我们向一个&amp;nbsp;View&amp;nbsp;中添加一个子&amp;nbsp;View&amp;nbsp;时，子&amp;nbsp;View&amp;nbsp;的越界部分也会显示出来，当然超过屏幕部分是显示不出来，因为无法在你手上显示内容了。看下面的图：&lt;br/&gt;
&lt;table border="0" align="center"&gt;
&lt;tbody&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/uiview-clipstobounds-show-visible-part/clipsToBounds_1.png"
width="343px"
/&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/uiview-clipstobounds-show-visible-part/clipsToBounds_2.png"
width="336px"
/&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
上图中，橙色的&amp;nbsp;View&amp;nbsp;是蓝色&amp;nbsp;View&amp;nbsp;的子&amp;nbsp;View。看左图，由它们的&amp;nbsp;Frame&amp;nbsp;设置，橙色的&amp;nbsp;View&amp;nbsp;部分超出的蓝色&amp;nbsp;View&amp;nbsp;的区域，默认情况下，蓝色&amp;nbsp;View&amp;nbsp;超出部分也会显示出来，除非在屏幕之外的部分。</description></item><item><title>UIView 动画的两种编程方式</title><link>https://yanbin.blog/uiview-animation-programming/</link><pubDate>Sun, 18 Sep 2011 03:37:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/uiview-animation-programming/</guid><description>
iOS  中的 UIView 动画编程其实还是很简单的，像 CSS3 一样，在给定的时间内完成状态连续性的变化呈现。比如背景色，Frame 大小，位移、翻转，特明度等。&lt;/p&gt;
&lt;br/&gt;
以前我使用的编程方式都是用下面那样的三段式处理：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UIView&lt;/span&gt; &lt;span class="nl"&gt;beginAnimations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;nil&lt;/span&gt; &lt;span class="nl"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UIView&lt;/span&gt; &lt;span class="nl"&gt;setAnimationDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//要动画改变的属性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGRectMake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UIView&lt;/span&gt; &lt;span class="n"&gt;commitAnimations&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
那么被 beginAnimations 和  commitAnimations 框起来的代码就会让你产生动画效果，这种方式像是数据库中的事物编程一样。</description></item><item><title>UIView你知道多少[转]</title><link>https://yanbin.blog/knowledges-on-uiview/</link><pubDate>Sun, 18 Sep 2011 02:25:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/knowledges-on-uiview/</guid><description>
&lt;div&gt;曾经有人这么说过，在iphone里你看到的，摸到的，都是UIView，所以UIView在iphone开发里具有非常重要的作用。那么UIView我们到底知道多少呢。请看看下面的问题，&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;如果这些你都知道，那么本文章的内容就请绕道，如果你还不太清楚，我想看了下面的内容，你就明白了。&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;1。bounds和frame分别表示什么？&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;2。ContentMode里UIViewContentModeScaleToFill代表什么？&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;3。contentStretch 里的指定UIView里缩放区域是如何计算的？&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;4。UIVIew里的哪些属性变化可以用动画来呈现？&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;5。UIKit的坐标系和Core Graphics的坐标系的差别是什么？&lt;/div&gt;
&lt;br/&gt;
视图和窗口展示了应用的用户界面，同时负责界面的交互。UIKit和其他系统框架提供了很多视图，你可以就地使用而几乎不需要修改。当你需要展示的内容与标准视图允许的有很大的差别时，你也可以定义自己的视图。</description></item><item><title>几个 CGRect 函数例子</title><link>https://yanbin.blog/examples-for-cgrect/</link><pubDate>Sun, 18 Sep 2011 01:56:53 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/examples-for-cgrect/</guid><description>
&amp;nbsp;CocoaChina 会员“”分享的几个 CGRect 函数例子，希望对您的苹果开发有所帮助&lt;br/&gt;
&lt;br/&gt;
1.CGRectOffset使用从源CGRect偏移的原点来创建矩形&lt;br/&gt;
&lt;br/&gt;
&amp;nbsp; &amp;nbsp; float offset = 25.0;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CGRect r1 = CGRectMake(100, 100, 100, 100);&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CGRect r2 = CGRectOffset(r1, offset, offset);&lt;br/&gt;
&lt;br/&gt;
2.CGRectIntersectsRect允许我们确定两个矩形是否相交&lt;br/&gt;
&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;float offset = 25.0;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CGRect r1 = CGRectMake(100, 100, 100, 100);&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CGRect r2 = CGRectMake(150, 150, 100, 100);&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (CGRectIntersectsRect(r1, r2))</description></item><item><title>WordPress 3.2.1 不能保存 Widget 设置</title><link>https://yanbin.blog/wordpress-3-2-1-cannot-save-widget-setting/</link><pubDate>Fri, 16 Sep 2011 11:05:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-3-2-1-cannot-save-widget-setting/</guid><description>
WordPress&amp;nbsp;升级到 3.2.1&amp;nbsp;之后，反正有好长一段时间是不能在&amp;nbsp;Widgets&amp;nbsp;中，如左边栏或右边栏中增加&amp;nbsp;Widget&amp;nbsp;了。因为现在的工作与&amp;nbsp;WordPress&amp;nbsp;本身相去较远些，所以未能修改&amp;nbsp;Widget&amp;nbsp;几个月了也没去钻研是怎么回事。现象呢就是：既不能增加新的&amp;nbsp;Widget，也无法移除原有的&amp;nbsp;Widget，移动位置也是不行的。界面上是允许操作的，在你操作完后，保存完，页面一刷新又变成原样，就是说服务器无法保存你对&amp;nbsp;Widget&amp;nbsp;所做的修改，好像只一点是例如，仅仅可以更改已有&amp;nbsp;Widget&amp;nbsp;的标题。&lt;br/&gt;
&lt;br/&gt;
想来&amp;nbsp;WordPress 3.2.1&amp;nbsp;肯定不会出现这样的低有错误的，必定是哪理冲突了。Google&amp;nbsp;后找到一篇类似问题的贴子 &lt;a href="http://wordpress.org/support/topic/cant-edit-and-save-saidebar-widgets-with-wordpress-321" target="_blank"&gt;Can't edit and save saidebar widgets with WordPress 3.2.1&lt;/a&gt;，其中有介绍可能是哪个插件或主题引起的问题，有建议说把所有插件关了又启用，看问题是否依然，或使用默认主题看是否无问题。耐心的去试，总能查到罪魁祸首的。</description></item><item><title>UIScrollView 的基本用法</title><link>https://yanbin.blog/use-uiscrollview/</link><pubDate>Wed, 31 Aug 2011 11:59:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-uiscrollview/</guid><description>
iPhone/iPad 中 &lt;a href="http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIScrollView_Class/Reference/UIScrollView.html" target="_blank" rel="noopener"&gt;UIScrollView&lt;/a&gt; 还是经常要用到的，这里作了一个使用它最简单的例子，一个 ScrollView 中放了三个 UIView。这三个 UIView 分别设置了不同的背景色，它们在 ScrollView 中可以一页一页的滚动。&lt;/p&gt;
&lt;br/&gt;
这段代码放在 UIViewController 的 viewDidLoad 方法中执行的，请注意每个 Frame 和 ScrollView 的关键属性，如 contentSize。ScrollView 中按页滚动时每次走动一个 ScrollView 的宽度(横向时) 或高度(纵向时)。看下面两张图：&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/use-uiscrollview/uiscrollview1.png"
width="227px"
/&gt;
第一张图是</description></item><item><title>iOS开发 frame 与 bounds 的区别与关系</title><link>https://yanbin.blog/ios-bounds-frame/</link><pubDate>Mon, 29 Aug 2011 01:19:12 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ios-bounds-frame/</guid><description>
&lt;div id="cnblogs_post_body"&gt;
&lt;br/&gt;
frame和bounds是UIView中的两个属性(property)。&lt;br/&gt;
&lt;br/&gt;
frame指的是：该view在父view坐标系统中的位置和大小。（参照点是父亲的坐标系统）&lt;br/&gt;
&lt;br/&gt;
bounds指的是：该view在本身坐标系统中 的位置和大小。（参照点是本身坐标系统）&lt;br/&gt;
&lt;div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-(&lt;/span&gt;&lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;　　&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CGRectMake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-(&lt;/span&gt;&lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;　　&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CGRectMake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/ios-bounds-frame/201108160046268283.png"
width="557px"
/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/ios-bounds-frame/frame_bounds_rects.jpg"
width="540px"
/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/ios-bounds-frame/6_491_5b7986a91b1038b.png"
width="873px"
/&gt;
&lt;br/&gt;
&lt;/div&gt;</description></item><item><title>Objective-C 设计模式之单例</title><link>https://yanbin.blog/objective-c-dp-singleton/</link><pubDate>Thu, 25 Aug 2011 06:22:20 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-dp-singleton/</guid><description>
作为设计模式中最简单的一个，单例模式还是在很多系统中都会用到，总有那么一些实例，从始至终只需要它的一个实例，而且任何时候都要总方便的得到，像 Application 对象啦，环境对像啊。&lt;/p&gt;
&lt;br/&gt;
要保持你设计的类别人只会初始出一个实例来，像 Java/C++/C# 中的做法一般希望能保证以下那些：&lt;br/&gt;&lt;br/&gt;
1. 构造方法藏起来，private 修饰，这样只能通过统一的工厂方法获得实例，因为 new 出来的总是新对象&lt;br /&gt;
2. 克隆方法出来的对象仍然是原来的对象&lt;br /&gt;
3. 反序列化出的对象也仍然是原来那个唯一的对象(这好像有点难)&lt;br /&gt;
4. 一般程序运行期间，该单实例不被释放&lt;br /&gt;
5. 一般要求工厂方法是原子性的，也是避免返回了不同的实例&lt;br /&gt;
6. 等等................. 是否还有些补充？&lt;br/&gt;&lt;br/&gt;
不一定每一点都真正考虑到，因为不是每个使用都都那么变态或不存在某些使用场景。&lt;br/&gt;&lt;br/&gt;
而我们这里的 Objective-C 有其语言的较大特殊性，是通过 alloc 来创建实例的，无法把构造方法 alloc 隐藏起来，但我们总是有办法的，可以做到：&lt;br/&gt;&lt;br/&gt;
1. 工厂方法返回唯一的实例&lt;br /&gt;
2. 多次调用 alloc 构造出的实例与 工厂方法得到的是同一个&lt;br /&gt;
3. 让你释放不掉这个实例，引用计数不让归零</description></item><item><title>Objective-C 的方法原型及重载和覆盖</title><link>https://yanbin.blog/objectivec-prototype-overload-override/</link><pubDate>Mon, 22 Aug 2011 22:05:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objectivec-prototype-overload-override/</guid><description>
前一篇是：&lt;a title="Objective-C 的方法原型及重载" href="https://yanbin.blog/objectivec-method-prototype-overload" target="_blank" rel="noopener"&gt;Objective-C 的方法原型及重载&lt;/a&gt;，本篇呢在上面的话题稍进一步延伸，方法原型中方法的覆盖关系就较为密切的，所以再说下方法覆盖的话题。&lt;/p&gt;
&lt;br/&gt;
由前面，我们知道，在 Objective-C 中方法原型的得来是这样的，假如方法声明是：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;-/+ (返回类型) 方法名: (参数1类型) 形参1  参数2描述: (参数2类型) 形参2 参数3描述: (参数3类型) 形参3 .......&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
那么它的方法原型则是：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;方法名: 参数2描述:参数3描述: .......&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
关键的是它与参数类型无关。&lt;br/&gt;&lt;br/&gt;
重载的时候我们应避免方法原型相同，而子父类间进行方法覆盖的时候我们又应该要保持一样的方法原型。&lt;br/&gt;&lt;br/&gt;
我们可以再一次感性认识一下 Objective-C 的方法原型，从 Xcode 中，当 Command + 鼠标悬停 在调用方法上时：</description></item><item><title>Objective-C 的方法原型及重载</title><link>https://yanbin.blog/objectivec-method-prototype-overload/</link><pubDate>Sun, 21 Aug 2011 03:44:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objectivec-method-prototype-overload/</guid><description>
毫无疑问，Objective-C&amp;nbsp;也是一种面向对象语言，那么面向对象有三个基本特征(封装、继承、多态)。重载似乎与这三大特征没多大关系，方法覆盖才预示着多态。但重载还是与覆盖有一定的关联，它们都要求你能识别出方法的原型，这就引出方法原型与重载的关系。&lt;br/&gt;
&lt;br/&gt;
Objective-C&amp;nbsp;与最常见的语言如，C++、C#、Java&amp;nbsp;在这方面是不同的，我们知道，Java、C++&amp;nbsp;和 C#&amp;nbsp;识别方法原型是依据于方法名、参数个数、与参数类型这三要素决定，比如下面出自于一个类中的四个计算面积的方法，对于&amp;nbsp;Java、C++&amp;nbsp;或&amp;nbsp;C#，它们是合法的：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;float calculateArea()&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;float calculateArea(int width)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;float calculateArea(float width)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;float calculateArea(int width, int height)&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
因为它们的方法原型是不同的，分别是：</description></item><item><title>如何使用 Core Plot 的 API 帮助文档</title><link>https://yanbin.blog/how-to-use-coreplot-apidoc/</link><pubDate>Wed, 17 Aug 2011 13:47:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/how-to-use-coreplot-apidoc/</guid><description>
&lt;a href="http://code.google.com/p/core-plot/" target="_blank"&gt;Core Plot&lt;/a&gt;&amp;nbsp;可是&amp;nbsp;iOS&amp;nbsp;下绝好的图表组件，虽说它的相关资料不甚丰富，特别是中文的，英文的还是有几篇不错的文章，不过&amp;nbsp;Core&amp;nbsp;Plot&amp;nbsp;自身提供的 API&amp;nbsp;帮助文档，以及代码示例其实很有用的，不妨且在英文的(注意是英文的)&amp;nbsp;Google&amp;nbsp;中输入 core plot,&amp;nbsp;即时搜索出的第一页里的链接很有价值的。&lt;br/&gt;
&lt;br/&gt;
对于 Core Plot 中文资料的缺乏相信很快就会有改观的，譬如说不还有不少像敝人那样的工作者正努力着吗！&lt;br/&gt;
&lt;br/&gt;
既然&amp;nbsp;Core&amp;nbsp;Plot&amp;nbsp;为我们提供了文档，那我们就该好好利用好它内在的东西。都是在下载的 CorePlot_0.4.zip&amp;nbsp;解压后的 CorePlot_0.4&amp;nbsp;目录中的，Source/examples&amp;nbsp;下有周到的示例代码，Source/framework&amp;nbsp; 中源代码;&amp;nbsp;Documentation&amp;nbsp; 中便是我们可以查阅的&amp;nbsp;Core&amp;nbsp;Plot&amp;nbsp;的&amp;nbsp;API&amp;nbsp;帮助文档，有两种使用方式，导入到&amp;nbsp;Xcode&amp;nbsp;和浏览器阅读。</description></item><item><title>iOS 使用 Core Plot 绘制统计图表入门</title><link>https://yanbin.blog/ios-coreplot-chart-started/</link><pubDate>Wed, 17 Aug 2011 12:24:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ios-coreplot-chart-started/</guid><description>
iOS(iPhone/iPad) 下图形组件有两个有名的，&lt;a title="s7graphview" href="http://code.google.com/p/s7graphview/" target="_blank" rel="noopener"&gt;s7graphview&lt;/a&gt; 和
&lt;a title="Core Plot" href="http://code.google.com/p/core-plot/" target="_blank" rel="noopener"&gt;Core Plot&lt;/a&gt;，
它们都是在 Google 上托管的代码，听说 Core Plot 比较强，因为前者仅支持曲线图，后者呢曲线图、饼图、柱状图等通吃，且较活跃。那就专注下 Core Plot 的使用。
它提供了 Mac OS X 和 iOS 下的组件库，我只用到它的 iOS 图表库。&lt;/p&gt;
&lt;br/&gt;
Core Plot 能画出来图表的效果应该多看看：http://code.google.com/p/core-plot/wiki/PlotExamples，相信看过之后绝大多数的 iOS 下的图表可以用它来满足你了。&lt;br/&gt;&lt;br/&gt;
配置其实很简单的，先从 http://code.google.com/p/core-plot/downloads/list 下载最新版的 Core Plot，比如当前是：CorePlot_0.4.zip，解压开，然后就两步：&lt;br/&gt;&lt;br/&gt;
1. 把目录 CorePlot_0.4/Binaries/iOS 中的 libCorePlotCocoaTouch.a 和整个子目录 CorePlotHeaders 从 Finder 中一并拖入到当前项目中，
选择 Copy item into destination group's folder (if needed)，Add to targets 里选上相应的 target。
此时你可以在项目的 target 中 Build Phases 页里 Link Binary With Libraries 中看到有了 libCorePlot-CocoaTouch.a.&lt;br/&gt;&lt;br/&gt;
2. 再到相应 Target 的 Build Settings 页里，Other Linker Flags 项中加上 -ObjC -all_load</description></item><item><title>TextMate 通用快捷键</title><link>https://yanbin.blog/textmate-common-shortcuts/</link><pubDate>Sun, 14 Aug 2011 12:07:56 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/textmate-common-shortcuts/</guid><description>
&lt;p&gt;对于 Unix 族下的文本编辑器，VI 功能稍弱，Emacs 的能力是没得说，但学习曲线太陡峭了，于是簇拥到 TextMate 旗下。快捷键能让你大大的提 升使用的效率，它可以让你的手尽量少在键盘与鼠标之间飘来飘去。相比于 Windows 平台，Mac 更推崇使用快捷键来操作，搭配着大大的触摸板后，鼠标真的就有点多余了。&lt;/p&gt;
&lt;p&gt;这里列出的所谓通用快捷键是不怎么涉及那些 Bundle 中的特定的快捷键而言的，还有些是系统通用的快捷方式也会提到&lt;/p&gt;
&lt;p&gt;1. mate 命令：&lt;/p&gt;
&lt;p&gt;mate .      以当前目录启动一个项目&lt;br /&gt;&lt;br/&gt;
mate a.txt b.txt *.csv  afolder afolder/bfolder  指定文件、通配符的方式，或目录来启动一下项目&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;command + option + L 显示行号
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;ctrl + R 执行光标前或选择的 shell 命令，并把执行结果输出到当前位置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;ctrl + L 把当前行试图移动屏幕中央
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;ESC 自动补全&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>TextMate 编辑 HTML 使用的快捷键记录</title><link>https://yanbin.blog/textmate-html-shortcuts/</link><pubDate>Sat, 13 Aug 2011 05:32:23 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/textmate-html-shortcuts/</guid><description>
本篇是介绍来自于 HTML Bundle 的快捷键&lt;/p&gt;
&lt;br/&gt;
1. 产生 HTML 注释 &amp;lt;!-- --&amp;gt;，用 command+/，这与多数 IDE    产生注释的方式是一样的。而要产生多行的注释就用 option+command+/，如：&lt;br/&gt;&lt;br/&gt;
&amp;lt;!--&lt;br /&gt;
    &lt;br /&gt;
--&amp;gt;&lt;br/&gt;&lt;br/&gt;
如果选择文本后按 command+/ 或 option+command+/ 的效果分别是：&lt;br/&gt;&lt;br/&gt;
&amp;lt;!-- should get&lt;br /&gt;
multi-line comments --&amp;gt;&lt;br/&gt;&lt;br/&gt;
和&lt;br/&gt;&lt;br/&gt;
&amp;lt;!--&lt;br /&gt;
should get&lt;br /&gt;
multi-line comments&lt;br /&gt;
--&amp;gt;&lt;br/&gt;&lt;br/&gt;
⇥ 表示 tab 键，例如应用代码片断的时候，如输入 head，后 tab 键，有多个同名片断时，数字选择就得到完整的代码版断。&lt;br/&gt;&lt;br/&gt;
2. 插入 html 标签， ctrl+shift+&amp;lt;, 即 Insert Open/Close Tag&lt;br/&gt;&lt;br/&gt;
输入 html，然后按 ctrl+shift+&amp;lt;,  变为 &amp;lt;html&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
输入 a id="abc"，选择上 a id="abc"，就变为 &amp;lt;a id="abc"&amp;gt;&amp;lt;/a&amp;gt;&lt;br/&gt;&lt;br/&gt;
3. 包裹上标签，ctol+shift+w，即 Wrap Selection in Open/Close Tag，效果是：&lt;br/&gt;&lt;br/&gt;
直接按 ctrl+shift+&amp;lt; 和 ctrl+shift+w，的效果是一样的，得到 &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;，改前面 p 为 head，就变成 &amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;br /&gt;
输入 hello 并选择上 hello，然后 ctrl+shift+w 就得到 &amp;lt;p&amp;gt;hello&amp;lt;/p&amp;gt;，改前面的  p 为 div，就变成 &amp;lt;div&amp;gt;hello&amp;lt;/div&amp;gt;</description></item><item><title>Objective-C 中一些代码记录</title><link>https://yanbin.blog/objective-c-snippets/</link><pubDate>Thu, 28 Jul 2011 22:03:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-snippets/</guid><description>
&lt;strong&gt;1. 初始化一个空的数组&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
    NSMutableArray *array = [NSMutableArray arrayWithObjects:nil];&lt;br /&gt;
    &lt;br /&gt;
    //或者，这里的 Capacity 像 java 的 ArrayList 中的 Capacity&lt;br /&gt;
    //NSMutableArray ×array = [NSMutableArray arrayWithCapacity:5];&lt;br /&gt;
   &lt;br /&gt;
    MSLog(@"%i", [array count]);&lt;br/&gt;&lt;br/&gt;
如果用到了 alloc 的话，就必须自己处理好相应的 release 操作了，像：&lt;br/&gt;&lt;br/&gt;
    NSMutableArray *array = [[NSMutableArray alloc] init];&lt;br /&gt;
    NSMutableArray *array1 = [[NSMutableArray alloc] initWithCapacity:5];&lt;br /&gt;
    NSMutableArray *array2 = [[NSMutableArray alloc]initWithObjects:nil];&lt;br/&gt;&lt;br/&gt;</description></item><item><title>Objective-C 的属性与合成方法使用详解</title><link>https://yanbin.blog/objective-c-proerty-synthsiz/</link><pubDate>Thu, 30 Jun 2011 15:34:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-proerty-synthsiz/</guid><description>
Objective-C 中的属性可以结合 Java 和 C# 的属性来理解，Java 的属性需要自己实现 getter/setter 方法，在 C# 中现在可方便些了，写上｛set;get;｝自动生成相应的存取器。&lt;/p&gt;
&lt;br/&gt;
Objective-C 中声明属性及使用时会涉及到 @property, @synthesize 和点号(.) 访问，@property 用来指定属性及某些特性，@synthesize 能为你用 @property 指定的属性自动生成 getter/setter 方法。下面最常规的例子：&lt;br/&gt;&lt;br/&gt;
main.m 代码：本例在 Xcode 4.0.2 中编译运行的, 可能涉及到一些是 Objective-C 的新特性，Apple 总是推动大家用新版本的东西。</description></item><item><title>Objective-C 的 self 和 super 详解[转]</title><link>https://yanbin.blog/objective-c-self-and-super/</link><pubDate>Mon, 20 Jun 2011 12:01:26 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-self-and-super/</guid><description>
在 Objective-C 中的类实现中经常看到这两个关键字 ”self” 和 ”super”，以以前 oop 语言的经验，拿 c++ 为例，self 相当于 this，super 相当于调用父类的方法，这么看起来是很容易理解的。以下面的代码为例：&lt;br/&gt;
&lt;br/&gt;
@interface Person:NSObject {&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; NSString*&amp;nbsp; name;&lt;br/&gt;
}&lt;br/&gt;
- (void) setName:(NSString*) yourName;&lt;br/&gt;
@end&lt;br/&gt;
&lt;br/&gt;
@interface PersonMe:Person {&lt;br/&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; NSUInteger age;&lt;br/&gt;
}&lt;br/&gt;
- (void) setAge:(NSUInteger) age;&lt;br/&gt;
- (void) setName:(NSString*) yourName andAge:(NSUInteger) age;&lt;br/&gt;
@end</description></item><item><title>does not implement methodSignatureForSelector: -- trouble ahead</title><link>https://yanbin.blog/does-not-implement-methodsignatureforselector-trouble-ahead/</link><pubDate>Sat, 18 Jun 2011 12:27:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/does-not-implement-methodsignatureforselector-trouble-ahead/</guid><description>
刚开始写 Objetive-C 程序，有时候写了一个类时，一调用实例或是类的方法就报错，错误内容大约如下：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;2011-06-19 01:14:10.175 Hello World[26080:40b] *** NSInvocation: warning: object 0x4654 of class 'Car' does not implement methodSignatureForSelector: -- trouble ahead&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;2011-06-19 01:14:10.178 Hello World[26080:40b] *** NSInvocation: warning: object 0x4654 of class 'Car' does not implement doesNotRecognizeSelector: -- abort&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
调用代码，非常简单，就是：&lt;br/&gt;
&lt;br/&gt;
Car* car = [[Car alloc]init];&lt;br/&gt;
[car foo];&lt;br/&gt;
&lt;br/&gt;
看错误仿佛是找不到 foo 方法，可是仔细检查再检查，方法也是有的，也没有打错字。&lt;br/&gt;
&lt;br/&gt;
后来找到原因了，是源自于 Java 代码写多了的缘故，&lt;strong&gt;忘记了让自己定义的 Objective-C 类继承自 NSObject&lt;/strong&gt;，写成了：</description></item><item><title>Xcode4 相较于 Xcode3 使用中一些体验</title><link>https://yanbin.blog/xcode4-some-experiences/</link><pubDate>Sat, 18 Jun 2011 12:10:51 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode4-some-experiences/</guid><description>
从&amp;nbsp;Xcode3&amp;nbsp;升级到&amp;nbsp;Xcode4&amp;nbsp;的一些使用体验：&lt;br/&gt;
&lt;br/&gt;
1.&amp;nbsp;注释的时候更智能，按&amp;nbsp;Command ＋ /&amp;nbsp;时不会像&amp;nbsp;Xcode3&amp;nbsp;里那样在光标处插入 //，而是会在行首加 //，并且多行注释时 //&amp;nbsp;很整齐了&lt;br/&gt;
&lt;br/&gt;
2.&amp;nbsp;大括号有自动补齐的功能了，方法后输入左花括号 {&amp;nbsp;后，右边的花括号 }&amp;nbsp;由&amp;nbsp;Xcode&amp;nbsp;自动加上。&lt;br/&gt;
&lt;br/&gt;
3.&amp;nbsp;智能提示中的内容丰富的多，方法的参数返回类型看得很清楚，尚不明最前面标识的&amp;nbsp;K、f、P、V、x、T、C、M、＃等是什么意义，不过也差不多有些概念了。&lt;br/&gt;
&lt;br/&gt;
4. @interface&amp;nbsp;的模板也不一样了，出现默认 @private&amp;nbsp;修饰关键字，这是对的，默认的实例变量应该是隐藏的，@interface&amp;nbsp;的模板是：&lt;br/&gt;
&lt;br/&gt;
@interface &amp;lt;# class name #&amp;gt; : &amp;lt;# superclass #&amp;gt; {</description></item><item><title>Obejctive-C 中定义可变参函数</title><link>https://yanbin.blog/obejctive-c-var-arguments/</link><pubDate>Thu, 16 Jun 2011 13:38:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/obejctive-c-var-arguments/</guid><description>
Objective-C 中有许多不定参数函数，像 NSLog(format, arg1, arg2)，还有字符串或数组在构造时所用的 [NSString stringWithFormat: format, arg1, arg2, arg3]，它们的方法原型分别是：&lt;br/&gt;
&lt;br/&gt;
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);&lt;br/&gt;
+ (id)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);&lt;br/&gt;
&lt;br/&gt;
应该去掉后面的 NS_FORMAT_FUNCTION(1,2) 来看，否来会干扰到思维。我们现在的例子还不需要这么写，至于 NS_FORMAT_FUNCTION(1,2) 的功用为何，我有时间还得好生看下。OK, 看多了 Apple 的那些可变参函数，可能反而令你感到费解，还不如来试个实际的例子：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;foo:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;va_list&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;va_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nl"&gt;initWithFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="nl"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;va_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="n"&gt;UTF8String&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="k"&gt;release&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;IBAction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;doo:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UIButton&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//须留意不定参函数的调用格式，逗号分隔的序列，应该它们整体是作为函数的一个参数传入
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="nl"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;My name %@, %@&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Objective-C 里指针变量星号 * 你会放在哪个位置</title><link>https://yanbin.blog/objective-c-pointer-styles/</link><pubDate>Thu, 16 Jun 2011 11:53:12 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-pointer-styles/</guid><description>
话说在 Objective－C 里声明指针变量时，星号(*) 该放到哪个位置，是紧贴变量类型，紧贴变量名还是放它们之间两边用空格，或者全挤在一起？到底还是在思考 C/C++ 中指针变量的声明风格，因为 Objective－C 是 C 的超集。&lt;br/&gt;
&lt;br/&gt;
纯粹讲 Objective－C 的代码风格，我觉得 Google 的 &lt;a href="http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml" target="_blank"&gt;Google Objective-C Style Guide&lt;/a&gt; 非常有指导意义。转回来看 Objective－C 声明指针变量时什么风格好些，下面四种都符合语法：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-objc" data-lang="objc"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;name1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;//1. * 与变量类型与变量名之间都有空格&amp;lt;br/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;//2. * 紧挨变量类型，我推荐这种&amp;lt;br/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;//3. * 紧挨变量名&amp;lt;br/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSString&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;//4. * 变量类型、*、变量名紧挨在一起&amp;lt;br/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;NSLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@&amp;#34;%@, %@, %@, %@&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
。看显然，第四种风格是最不好看的，就像有人写 SQL 喜欢 “select*from table1" 一样</description></item><item><title>Xcode 4 常用快捷键（和 Xcode 3 有些不一样了）</title><link>https://yanbin.blog/xcode-4-shortcuts/</link><pubDate>Wed, 15 Jun 2011 13:10:22 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-4-shortcuts/</guid><description>
把&amp;nbsp;Xcode&amp;nbsp;由 3.2.5&amp;nbsp;升级到&amp;nbsp;Xcode 4.0.2&amp;nbsp;之后，满以为快捷键总该不会变化太大吧，没想又要经历一个快捷键的适应过程，发现有些在&amp;nbsp;Xcode 3.2.5&amp;nbsp;中的快捷键到了 4&amp;nbsp;之后不灵光了。
所以把之前的 &lt;a href="https://yanbin.blog/xcode-3-shortcuts" target="_blank"&gt;xcode-3-shortcuts&lt;/a&gt; 标题也由原来的标题“XCode 中我的常用快捷键”&amp;nbsp;改为“&lt;a href="https://yanbin.blog/xcode-3-shortcuts"&gt;XCode 3 中我的常用快捷键&lt;/a&gt;”，还在使用&amp;nbsp;Xcode 3&amp;nbsp;的可以回去看下，我想也是该丢弃&amp;nbsp;Xcode 3&amp;nbsp;的时候了。&lt;br/&gt;
&lt;br/&gt;
主菜单调整的大，Build, Run&amp;nbsp;都没啦，快捷键的变动自然就大。再由于 Interface Builder&amp;nbsp;已经内建到&amp;nbsp;Xcode&amp;nbsp;中了，所以先前的&amp;nbsp;command ＋&amp;nbsp;数字&amp;nbsp;显示出相应面板的快捷键被替代为&amp;nbsp;command ＋ option +&amp;nbsp;数字&amp;nbsp;了，但也不是直接对应的。连 option + command + up&amp;nbsp;在 .h&amp;nbsp;和 .m&amp;nbsp;间切换的快感的体验不到了，有触摸板的朋友请用三个手指上下划动来切换，对于&amp;nbsp;iMac&amp;nbsp;或黑苹果就不爽了。&lt;br/&gt;
&lt;br/&gt;
对于&amp;nbsp;Xcode 4&amp;nbsp;的快捷键直接上两张图吧，其实这两种图是一样的，只是背景色不一样，白底的可以打印出来贴在照壁上，每天可以审视。&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Xcode 4 的新特性介绍</title><link>https://yanbin.blog/xcode-4-new-features/</link><pubDate>Wed, 15 Jun 2011 11:46:01 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-4-new-features/</guid><description>
今天由 Xcode 3.2.5 升级到了 Xcode 4.0.2，Xcode 4.0.2 居然也要付费了，4.99 美刀，系统要求 Mac OS X 10.6.6 以上。安装之后发现有很大的不同，最让人意外的是居然连一些快捷键也变了，像 command + option + up 都没法在 .h 和 .m 文件间切换了，Interface Builder 就面目全非了。不过还好，反正使用 Xcode 3.2.5 的时间也不长，才三个星期左右，放心的忘了旧版本吧，努力去适应。苹果的风格是不想让人回头的，基本上一旦升级到新的版本上来，以后都不会有机会需要使用旧的版本的，何不让自己也 keep updated 呢？&lt;br/&gt;
&lt;br/&gt;
关于 Xcode 4.0. 2 的新性可以直接看官方的介绍：&lt;a href="http://developer.apple.com/technologies/tools/whats-new.html" target="_blank"&gt;What's New in Xcode 4&lt;/a&gt; 和 &lt;a href="http://developer.apple.com/technologies/tools/features.html" target="_blank"&gt;Developer Tools Features&lt;/a&gt;。别看标题是 What's New in Xcode 4，好像讲的是 Xcode 4 的新特性，但这个链接并未受限于 4 这个版本，保管 Xcode 5 出来后同样是前面的链接内容却是 Xcode 5 的新特性了。&lt;br/&gt;
&lt;br/&gt;
这里把别人翻译的 What's New in Xcode 4 转载到这里来，稍事整理。请对我们的开发工具更多些了解吧，用处呢，会很明显的。</description></item><item><title>读《Objective-C培训资料》的摘要</title><link>https://yanbin.blog/objective-c-training-notes/</link><pubDate>Wed, 25 May 2011 18:16:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/objective-c-training-notes/</guid><description>
本文为阅读文档：&lt;a href="
/posts/2011/objective-c-training-notes/Objective-C培训资料.pdf
" target="_blank"&gt;Objective-C 培训资料&lt;/a&gt; 的一些摘要，在此记录，以备回顾。&lt;br/&gt;
&lt;br/&gt;
#define nil NULL&lt;br/&gt;
bool型与c/c++是一样的，非0为 TRUE/YES, 0 为 FALSE/NO, 用 if(flag==YES) 就要注意了，此时 YES 就是 1，不过你直接用 if(flag) ..... 来判断就只要不是 0 就成立。&lt;br/&gt;
&lt;br/&gt;
#import 和 c/C++ 的 include 一样也有 &amp;lt;&amp;gt; 和 "" 两种查找方式。但是它不怕重复引入。&lt;br/&gt;
&lt;br/&gt;
NSLog() 会自动在输出后加 \n，相当于 System.out.println().&lt;br/&gt;
&lt;br/&gt;
Objective-C 的对象需要直接或间接的继承自 NSObject。Objective-C 的 @interface 相当 java 的 class，而 @protocol 才是 Java 的 interface。类声明的基本方式如下:&lt;br/&gt;
&lt;br/&gt;
@interface &amp;lt;#class#&amp;gt; : &amp;lt;#superclass#&amp;gt;&lt;br/&gt;
{&lt;br/&gt;
&amp;nbsp; &amp;nbsp;&amp;lt;#ivars#&amp;gt;&lt;br/&gt;
}&lt;br/&gt;
&lt;br/&gt;
&amp;lt;#methods#&amp;gt;&lt;br/&gt;
&lt;br/&gt;
@end</description></item><item><title>XCode 3 中我的常用快捷键</title><link>https://yanbin.blog/xcode-3-shortcuts/</link><pubDate>Tue, 24 May 2011 12:49:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xcode-3-shortcuts/</guid><description>
适时的使用快捷键会提高工作的效率，可以让你的手尽量少的在键盘与鼠标间漂移，就像玩帝国与星际时不用快捷键一般会死得快一样，当然不乏一些特牛的人了。培养使用快捷键最好的办法是，当你需要用哪个菜单项，点击鼠标的同时请留言所提示的快捷键，下次尽量用快捷键来做同样的事情，培养习惯，甚至是肌肉记忆。记得 Eclipse 有那么一个插件，当你用鼠标操作了，如果有相应的快捷键，它会在显著的地方告诉你对应的快捷操作键。&lt;br/&gt;
&lt;br/&gt;
下面是我日常中使用 概率比较高的 XCode 快捷键键。假如你用的是 PC 键盘的话，就要分清哪个是 Command 键，哪个是 Option。比如同一个 USB PC 键盘，我在 VirtualBox 中装的 Mac 系统，Alt 是 Command 键，Win 是 Option 键，VMWare 中的 Mac 系统则相反。&lt;br/&gt;
&lt;br/&gt;
1.  esc 　　　　           自动完成, 作用和VS中的ctrl+j类似，像 Eclipse 的 alt+/&lt;br/&gt;
      control + , 与上面相同的功能&lt;br/&gt;
2.  command + /　　               注释代码和反注释，用 //，相当于 Eclipse 的 ctrl+/&lt;br/&gt;
3.  command + option + 上箭头　 快速在头文件（.h）和实现文件（.m）之间切换&lt;br/&gt;
4.  command + b　　           编译程序，不运行&lt;br/&gt;
5.  command + return　　      编译并运行程序&lt;br/&gt;
6.  command + shift + b　　     打开编译结果窗口，显示编译详细信息&lt;br/&gt;
&lt;br/&gt;</description></item><item><title>Spring MVC 碰到 java.lang.NoSuchFieldError: APPLICATION_CONTEXT_ID_PREFIX</title><link>https://yanbin.blog/spring-mvc-no-such-field-error-application_context_id_prefix/</link><pubDate>Mon, 23 May 2011 10:02:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-mvc-no-such-field-error-application_context_id_prefix/</guid><description>
Spring MVC 应用在 Tomcat 启动的时候出现了下面的错误：&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;java.lang.NoSuchFieldError: APPLICATION_CONTEXT_ID_PREFIX&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:430)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:458)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:339)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:306)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at javax.servlet.GenericServlet.init(GenericServlet.java:212)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4350)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.core.StandardContext.start(StandardContext.java:4659)&lt;/span&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)</description></item><item><title>推荐19个很有用的 JavaScript 库</title><link>https://yanbin.blog/19-useful-javascript-libs/</link><pubDate>Tue, 17 May 2011 11:11:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/19-useful-javascript-libs/</guid><description>
&lt;p&gt;流行的 JavaScript 库有jQuery，MooTools，Prototype，Dojo和YUI等，这些 JavaScript 库功能丰富，加上它们众多的插件，几乎能实现任何你需要的功能，然而需要实现一些特定的功能，则可以选择功能更专一的轻量库，今天这篇文章与大家分享16个很有用的 JavaScript 库。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.gscottolson.com/blackbirdjs/"&gt;Blackbird: Open Source JavaScript Logging Utility&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br/&gt;
Blackbird 是一款非常酷的 JavaScript 调试工具，带有一个漂亮的界面显示和过滤调试信息。&lt;br /&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/19-useful-javascript-libs/2145130548970463.jpg"
width="500px"
/&gt;</description></item><item><title>15款很棒的 JavaScript 开发工具</title><link>https://yanbin.blog/15-javascript-dev-tools/</link><pubDate>Tue, 17 May 2011 10:39:40 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/15-javascript-dev-tools/</guid><description>
在开发中，借助得力的工具可以事半功倍。今天，这篇文章向大家分享最新收集的15款非常有用的 JavaScript 开发工具。&lt;br/&gt;
&lt;strong&gt;&lt;a href="http://www.webresourcesdepot.com/testswarm-continious-distributed-js-testing/"&gt;TestSwarm: Continious &amp;amp; Distributed JS Testing&lt;/a&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;p style="text-align: center;"&gt;TestSwarm是Mozilla实验室推出的一个开源项目，它旨在为开发者提供在多个浏览器版本上快速轻松测试自己JavaScript代码的方法。&lt;br/&gt;
&lt;a href="http://www.webresourcesdepot.com/testswarm-continious-distributed-js-testing/"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/15-javascript-dev-tools/1418360726876878.jpg"
width="600px"
/&gt;
&lt;/a&gt;</description></item><item><title>有一种方法给 XSLT 中变量进行赋值，是保持状态的那种</title><link>https://yanbin.blog/xslt-assign-variables/</link><pubDate>Tue, 03 May 2011 13:55:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-assign-variables/</guid><description>
在 XSLT 中声明变量可以用 &amp;lt;xsl:variable&amp;gt; 和 &amp;lt;xsl:param&amp;gt;，它们的区别是可以通过 &amp;lt;xsl:param&amp;gt; 从外部向 XSLT 文件传参数，除此之外，在 xslt 内部使用时这两者的用法基本是一样的。下面只以 &amp;lt;xsl:variable&amp;gt; 为例子，例子中的 xsl:variable 替换成 xsl:param 也是能 run 的。&lt;/p&gt;
&lt;br/&gt;
&amp;lt;xsl:variable&amp;gt; 的基本用法是：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;xsl:variable&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#39;Initial&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- 不写 select 则默认为 &amp;#39;&amp;#39; --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;xsl:variable&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#39;New Value&amp;#39;&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- 赋值 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;$username&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- 显示变量值，变量名前加上 $ 符号 --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Ubuntu 11.04 Unity最常用的31个快捷键[转]</title><link>https://yanbin.blog/ubuntu-11-04-unity-31-shortcut/</link><pubDate>Mon, 02 May 2011 01:26:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ubuntu-11-04-unity-31-shortcut/</guid><description>
&lt;div id="blog_text"&gt;首先奉上的是键盘快捷键：&lt;br/&gt;
&lt;br/&gt;
1、超级键（Win键） – 打开dash。&lt;br/&gt;
&lt;br/&gt;
2、长按超级键 – 启动Launcher。&lt;br/&gt;
&lt;br/&gt;
3、按住超级键，再按1,2,3等数字键 – 从Launcher打开一个应用程序，当你按住超级键时，每个应用程序图标上都会显示一个数字，按下对应的数字就会打开盖应用程序。&lt;br/&gt;
&lt;br/&gt;
4、Alt+F1 – 将键盘焦点移到Launcher上，使用箭头键进行移动，按回车即可启动一个应用程序，按下右箭头键显示Quicklist。&lt;br/&gt;
&lt;br/&gt;
5、Alt+F2 – 以特殊模式打开dash，以便运行任何命令。&lt;br/&gt;
&lt;br/&gt;
6、超级键+A – 从Launcher打开应用程序窗口。</description></item><item><title>Ubuntu 下为 Firefox 设置中文字体[摘]</title><link>https://yanbin.blog/ubuntu-firefox-set-fonts/</link><pubDate>Mon, 02 May 2011 01:12:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ubuntu-firefox-set-fonts/</guid><description>
默认风格下 Ubuntu 的 Firefox 浏览中文网页十分难看，字黑黑的不清楚，所以需要设定一下 Firefox 中的字体，幸好选择了中文语言包中系统中有文泉字体可用。网上找到了一篇 Firefox 中文字体的参考，如下：&lt;br/&gt;
1.编辑CSS文件，参考：&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://wiki.ubuntu.org.cn/Firefox_%E5%AD%97%E4%BD%93" target="_blank"&gt;http://wiki.ubuntu.org.cn/Firefox_%E5%AD%97%E4%BD%93&lt;br/&gt;
&lt;/a&gt;(&lt;strong&gt;声明：本链接内容更适用于，在Firefox首选项中无法设定中文字体的情况！&lt;/strong&gt;)&lt;br/&gt;
&lt;br/&gt;
2.运行Firefox3.6.13&lt;br/&gt;
&lt;br/&gt;
3.依次选择 Edit \ Preferences \ Content \ Advanced&lt;br/&gt;
&lt;br/&gt;
我的选择:&lt;br/&gt;
&lt;br/&gt;
Fonts for : User Defined&lt;br/&gt;
&lt;br/&gt;
Proportional: Sans Serif      Size:18&lt;br/&gt;
&lt;br/&gt;
Serif: WenQuanYi Bitmap Song</description></item><item><title>Ubuntu Linux 中 Firefox 升级到 4.0 版</title><link>https://yanbin.blog/update-firefox-for-ubuntu/</link><pubDate>Mon, 02 May 2011 00:52:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/update-firefox-for-ubuntu/</guid><description>
Ubuntu 11.04 系统已经是自带 Firefox 4.0 了，在之前的版本，像 10.10 及更早的 Ubuntu 中如果要升级到 4.0 的 Firefox 该如何做呢？利用 Firefox 的自动升级功能只会帮你下载到  firefox-4.0.1.tar.bz2 文件，剩下的事情还是要自己解压并找到 Firefox 的安装目录覆盖过去，这样看起来不怎么完美。&lt;br/&gt;
&lt;br/&gt;
最好还是用下面的 apt-get 的方法，步骤如下：&lt;br/&gt;
&lt;br/&gt;
sudo add-apt-repository ppa:ubuntu-mozilla-daily/ppa&lt;br/&gt;
&lt;br/&gt;
sudo apt-get update&lt;br/&gt;
&lt;br/&gt;
sudo apt-get install firefox-4.0&lt;br/&gt;
&lt;br/&gt;
三步搞定，我试过，行的。再次打开你的 Firefox 看看现在的版本是不是最新的了。</description></item><item><title>学习HTML5不可错过的12家国外网站(组图)</title><link>https://yanbin.blog/study-html5-12-websites/</link><pubDate>Sat, 23 Apr 2011 21:54:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/study-html5-12-websites/</guid><description>
HTML5是近十年来Web开发标准最巨大的飞跃。和以前的版本不同，HTML5并非仅仅用来表示Web内容，它的新使命是将Web带入一个成熟的应用平台，在HTML5平台上，视频、音频、图象、动画以及同电脑的交互都被标准化。对于广大开发人员和设计者，究竟该如何入手HTML5及获取相关的国外最新资源。以下是广大HTML5技术爱好者学习和参考相关技术不可错过的12家外网，它们或是采用HTML5技术开发出的案例，或是HTML5相关的学习资源，供国内的开发人员参阅。</description></item><item><title>应用 Castor 把 XML 转换成相应的 JavaBean(二)</title><link>https://yanbin.blog/castor-xml-to-javabean-2/</link><pubDate>Sat, 23 Apr 2011 04:38:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/castor-xml-to-javabean-2/</guid><description>
紧接前一篇 &lt;a href="https://yanbin.blog/castor-xml-to-javabean-1" target="_blank" rel="noopener"&gt;应用 Castor 把 XML 转换成相应的 JavaBean(一)，&lt;/a&gt;本例稍显复杂一些，也就是要使用到映射文件，更复杂的 JavaBean、XML 数据类型和结构，引入自定的 FieldHandler，还有存在 Namespace 的情形。&lt;/p&gt;
&lt;br/&gt;
关于如何使用 Castor 来把 XML 映射成 JavaBean 请着重阅读这个链接 &lt;a href="http://www.castor.org/reference/html/XML%20data%20binding.html"&gt;http://www.castor.org/reference/html/XML%20data%20binding.html&lt;/a&gt; 中的内容，里面有说明支持的类型，如何定义自己的类型 Handler 及配置 Handler 的属性，mapping.xml 文件怎么写及各部份的意义；还有 castor.properties 的配置，比其中的 suppressNamespaces 为 true 时可以忽略掉 Namespace，默认为 false.&lt;br/&gt;&lt;br/&gt;
直接看例子吧：&lt;br/&gt;&lt;br/&gt;
1. persons.xml，这里放了一个 Namespace，并定义了一个 Address 类型，还使得节点名与 Bean 的属性名不一致：</description></item><item><title>应用 Castor 把 XML 转换成相应的 JavaBean(一)</title><link>https://yanbin.blog/castor-xml-to-javabean-1/</link><pubDate>Sat, 23 Apr 2011 03:06:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/castor-xml-to-javabean-1/</guid><description>
之前有一篇用例子演示了 &lt;a href="http://unmi.cc/jaxb-xml-javabean-start" target="_blank" rel="noopener"&gt;应用 JAXB 把 XML 转换成相应的 JavaBean&lt;/a&gt;，现在来看另一款 XML Data Binding 工具 &lt;a href="http://www.castor.org/" target="_blank" rel="noopener"&gt;Castor&lt;/a&gt; 怎么把 XML 映射成 JavaBean  的，相对于 JAXB 规范性的东西，Castor 的官方网站上关于 Castor 的使用文档我觉得要多些。作为一个 XML Data Binding  工具，Castor 同样提供了 Marshaller 和 Unmarshaller 的功能，它不依赖于注解，还是采用映射文件的方式，像 Hibernate 那样的映射文件。&lt;/p&gt;
&lt;br/&gt;
从 Castor 的官方网站 &lt;a href="http://www.castor.org/"&gt;http://www.castor.org/&lt;/a&gt; 看到它的外围生态系统还是不错的，有 JDO、Maven、Spring、Eclipse Plugin 的支持，也有众多组件用到了它。和其他类似工具一样，Castor 也提供了 XML、Schema 生成 Class 类的功能，但照例这里还是看看先有类的情况，怎么从 XML 中获取相应值。&lt;br/&gt;&lt;br/&gt;
通常使用 Castor 从 XML 得到 JavaBean 需要一个映射文件，要是 JavaBean 的属性和 XML 中的节点名足够齐整规范就可以省去映射文件，先看不用映射文件的例子：</description></item><item><title>Burst 的 VPS (Ubuntu) 上安装 xfce 和 vnc</title><link>https://yanbin.blog/vnc-burst-vps-ubuntu-xfce/</link><pubDate>Thu, 21 Apr 2011 12:39:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vnc-burst-vps-ubuntu-xfce/</guid><description>
相信你也是个 Linux 的能手才会去购买一个 Linux VPS 系统，多数时间用 ssh 就足够了，但也有人免不了希望在图形界面下看些东西，那么为你的 VPS 安装个 VNC 就很有必要性了。本人已在 Burst 弄来的 512 M 内存的 Ubuntu 10.04 LTS VPS 实际操作怎么安装 VNC 远程使用它的图形界面，安装的方法可能也有好多种，不过这里是我成功例子的操作步骤，如下：&lt;br/&gt;
&lt;br/&gt;
1. 安装显示系统 x11，命令：&lt;br/&gt;
&lt;br/&gt;
    root@217352:~#&lt;span style="color: #800000;"&gt;apt-get install xorg&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
这一步要安装包的大小大约是 20 来 M，如果是美国的服务器，可以眼争争的看着它完成，很快的。完了我们就有了 X11 了。</description></item><item><title>应用 JAXB 把 XML 转换成相应的 JavaBean</title><link>https://yanbin.blog/jaxb-xml-javabean-start/</link><pubDate>Thu, 21 Apr 2011 07:23:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jaxb-xml-javabean-start/</guid><description>
跨系统的调用目前无疑是 WebService 的天下，指的是通过 HTTP 请求方式获得 XML 或 JSON 数据的方式，RESTFul 也得到了很好的应用。规范意义上的 Soap 调用不知道还不多不多，反正当年用过的 Corba 鲜有耳闻了，就像很多人对 EJB2 不会有概念一样。&lt;/p&gt;
&lt;br/&gt;
Java 在调用 WebService 获得了 XML 之后，接下来一种常见的处理方式就是把它转换成相应的 JavaBean，再丢给其他组件像 Jsp 标签，FreeMarker 等去就很流畅了。关于 XML 与 JavaBean 互相转换有两个操作叫做：Marshaller 和 Unmarshaller，还没见一个权威的翻译，大概就是编组与反编组，意义如同序列化与反序列化。也就是由 JavaBean 到 XML 叫做 Marshal，由 XML 到 Java 叫做 Unmarshal。&lt;br/&gt;&lt;br/&gt;
通常从 XML 到 JavaBean 的转换机会大的多，所以我主要也是研究了下如何把 XML 映射成 JavaBean，虽然现在的工具一样也都支持这两个方向的转换。有许多组件可以做到，像 &lt;a href="http://www.oracle.com/technetwork/articles/javase/index-140168.html" target="_blank" rel="noopener"&gt;JAXB&lt;/a&gt;(Java Architecture for XML Binding)、</description></item><item><title>让 MySQL 在 Linux 下表名不区分大小写(实为表名全小写)</title><link>https://yanbin.blog/linux-mysql-lower-case-table-names/</link><pubDate>Sat, 16 Apr 2011 23:35:03 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-mysql-lower-case-table-names/</guid><description>
把 Windows 下的应用部署到 Linux 下，使用到了 Quartz 集群的特性，所以建了 MySql 的中间表，一启动看到报错：&lt;br/&gt;
&lt;br/&gt;
Invocation of init method failed; nested exception is org.quartz.JobPersistenceException: Couldn't retrieve trigger: Table 'unmijob.QRTZ_TRIGGERS' doesn't exist [See nested exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'unmijob.QRTZ_TRIGGERS' doesn't exist]&lt;br/&gt;
&lt;br/&gt;
用 MySQL 客户端连接到那个数据库，看到 qrtz_triggers 表确确实实是存在的，MySQL 也是安装在 Linux 下的。在大小写敏感的操作系统中，估摸着会不会是表名大小的因素呢? 于是把 qrtz_triggers 改为大写的 QRTZ_TRIGGERS，再次启动应用服务器，这时候出现的是：</description></item><item><title>Struts2 使用 xslt 结果类型如何把字符串直接作为 Document 内容(终结)</title><link>https://yanbin.blog/struts2-xslt-string-document-final/</link><pubDate>Tue, 12 Apr 2011 06:47:14 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-xslt-string-document-final/</guid><description>
前面两篇：&lt;a href="https://yanbin.blog/struts2-xsltresult-details" target="_blank" rel="noopener"&gt;Struts2 使用 XSLTResult 输出页面内容详解&lt;/a&gt; 和
&lt;a href="https://yanbin.blog/struts2-xsltresult-string-to-document" target="_blank" rel="noopener"&gt;Struts2 使用 xslt 结果类型如何把字符串直接作为 Document 内容&lt;/a&gt; 对 Struts2 如何使用 xslt 结果和怎么把字符串直接作为 Document 内容进行的讲解、研究和探讨。关于 Action 中的字符怎么直接作为 xslt 的内容已有一种方法，就是把 String 转换成 org.w3c.dom.Document 类型再送给 xslt 文件。&lt;/p&gt;
&lt;br/&gt;
同时也发现 Struts2 使用了 &lt;a href="http://oss.org.cn/ossdocs/framework/struts/2.0.0/core-apidocs/org/apache/struts2/views/xslt/StringAdapter.html#StringAdapter%28org.apache.struts2.views.xslt.AdapterFactory,%20org.apache.struts2.views.xslt.AdapterNode,%20java.lang.String,%20java.lang.String%29" target="_blank" rel="noopener"&gt;org.apache.struts2.views.xslt.StringAdapter&lt;/a&gt; 了对字符串的处理，并提供了 parseStringAsXML 的开关选项(虽然是无从设置它的值)，但即使把 parseStringAsXML 设置成了 true，送给 xslt 文件的内容也是被包裹上了 &amp;lt;result&amp;gt; 根节点，xslt 中不得不用 &amp;lt;xsl:template match="/result/你的根节点"&amp;gt; 来应用模板。&lt;br/&gt;&lt;br/&gt;
通过在上一篇 &lt;a href="https://yanbin.blog/struts2-xsltresult-string-to-document" target="_blank" rel="noopener"&gt;Struts2 使用 xslt 结果类型如何把字符串直接作为 Document 内容&lt;/a&gt; 里琢磨来琢磨去的探寻，着实又走了不少弯路，甚至是误入了歧途，但终究是找到了一个较好的办法来让 Action 直接送 String 表示的 Document 给 xslt 文件。那就是自定义 StringXSLTResult，</description></item><item><title>几个免费的智能双线解析系统</title><link>https://yanbin.blog/several-smart-dns/</link><pubDate>Mon, 11 Apr 2011 07:17:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/several-smart-dns/</guid><description>
在互联网上，世界上最远的距离不是从中国到美国，而是从电信到网通。所以有时候为了速度，不得不为主机申请多条线路，一般是电信一个 IP、网通一个 IP，这就要求 DNS 解析同一域名时能智能的根据用户的网络类型解析成相应的 IP 上使用该线路。&lt;br/&gt;
&lt;br/&gt;
例如，对于同一个主机有两个 IP，分别为 192.168.0.1 和  172.16.0.1，域名为 &lt;a href="http://www.unmi.cc" target="_blank"&gt;www.unmi.cc&lt;/a&gt;, 当然用不同的 IP 访问会走不同的路由线路的。希望电信用户进来访问 &lt;a href="http://www.unmi.cc" target="_blank"&gt;www.unmi.cc&lt;/a&gt; 是走 192.168.0.1，而网通用户访问 &lt;a href="http://www.unmi.cc" target="_blank"&gt;www.unmi.cc&lt;/a&gt; 时走 172.16.0.1，默认为 192.168.0.1。&lt;br/&gt;
&lt;br/&gt;
所幸网络上有免费的几个双线智能域名解析系统可使用，下面列出：&lt;br/&gt;
&lt;br/&gt;
1. DNSPod，还记得它曾遭受过暴风影音的殃吧，这也证明它用户多吗。面向国际的是 &lt;a href="http://www.dnspod.com" target="_blank"&gt;http://www.dnspod.com&lt;/a&gt;，它不支持双线解析。中国用户请用 http://www.dnspod.cn，支持双线解析，并且响应还很快的。它们间的用户是不共享的，可以说是完全独立的，分免费和 VIP 付费用户，看起来似乎很强大。</description></item><item><title>Struts2 使用 xslt 结果类型如何把字符串直接作为 Document 内容</title><link>https://yanbin.blog/struts2-xsltresult-string-to-document/</link><pubDate>Sun, 10 Apr 2011 05:58:39 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-xsltresult-string-to-document/</guid><description>
在上一篇: &lt;a href="https://yanbin.blog/struts2-xsltresult-details" target="_blank" rel="noopener"&gt;Struts2 使用 XSLTResult 输出页面内容详解&lt;/a&gt; 中说到了，如果在 Action 中送给 xstl 的是一个字符串，例如 String user = "&amp;lt;user&amp;gt;&amp;lt;name&amp;gt;Unmi&amp;lt;/name&amp;gt;&amp;lt;/user&amp;gt;"，那么 xslt result 输出的将是:&lt;/p&gt;
&lt;br/&gt;
&amp;lt;result&amp;gt;&lt;br /&gt;
&amp;amp;lt;user&amp;amp;gt;&amp;amp;lt;name&amp;amp;gt;Unmi&amp;amp;lt;/name&amp;amp;gt;&amp;amp;lt;/user&amp;amp;gt;&lt;br /&gt;
&amp;lt;/result&amp;gt;&lt;br/&gt;&lt;br/&gt;
而不我们期望的&lt;br/&gt;&lt;br/&gt;
&amp;lt;user&amp;gt;&lt;br /&gt;
&amp;lt;name&amp;gt;Unmi&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;/user&amp;gt;&lt;br/&gt;&lt;br/&gt;
那么怎么才能做到这一点呢？在 XSLTResult 有 adapterFactory 以及相应的 setter/getter 方法，但它们是 protected，所以也无法定制自己的 AdapterFactory 来判断是字符串就作为 Document 的内容。</description></item><item><title>Struts2 使用 XSLTResult 输出页面内容详解</title><link>https://yanbin.blog/struts2-xsltresult-details/</link><pubDate>Sat, 09 Apr 2011 04:59:10 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-xsltresult-details/</guid><description>
Struts2 内置提供了 xslt 结果类型，实现类为 org.apache.struts2.views.xslt.XSLTResult，它让你方便的把获得的 XML 数据内容，或者是用 OGNL 能访问到的某个属性(像 ContenxtMap、Request 等中的属性)，通过一个 xslt 文件转换成你想要的格式。前面这句听来不怎么明白，后面慢慢道来。&lt;/p&gt;
&lt;br/&gt;
在 Struts2 的 struts-default.xml 中定义了 chain、dispatcher、freemarker、httpheader、redirect、redirectAction、stream、velocity、xslt 和 plainText 10 种类型的 Result；而在 Struts2 初期版本中的 jasper、chart、jsf 和 tiles 结果类型已移到相应的插件去实现了。&lt;br/&gt;&lt;br/&gt;
freemarker、velocity 和 xslt 可以很自由的使用各自的模板语言，velocity 渐渐淡出了我们的视野，那还剩下 freemarker 和 xslt。freemarker 要求实合并的变量是实体类型，满足了多数时候的需求，不过现在要说的 xslt 结果类型，向 xslt 文件送去的数据可以是实体类型，也可以是原生的 org.w3c.dom.Document 类型，当然到了 xslt 文件这一层处理的都是</description></item><item><title>命令行下创建 Android 工程，用 Ant 进行编译部署</title><link>https://yanbin.blog/cmd-android-ant-project/</link><pubDate>Wed, 06 Apr 2011 05:07:32 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cmd-android-ant-project/</guid><description>
安装完 Android SDK 后可以在命令行下 Android.bat 命令创建一个示例项目，有 Ant 的话还可直接用 Ant 来编译部署到模拟器上运行。&lt;br/&gt;
&lt;br/&gt;
环境准备：&lt;br/&gt;
&lt;br/&gt;
告诉 path 要指向到 Android SDK 目录的 tools 子目录中，如 d:\android-sdk-windows\tools&lt;br/&gt;
要用 ant 编译部署的话，再把 ant 的 bin 目录加入到 path 上，如 D:\apache-ant-1.8.2\bin&lt;br/&gt;
&lt;br/&gt;
CMD 进到命令行下，执行：&lt;br/&gt;
android create project -k cc.unmi.android.test -n Hello -a HelloAndroid -t 5 -p c:\TestAndroid&lt;br/&gt;
&lt;br/&gt;
注：以上各参数的意义，可参考：http://developer.android.com/guide/developing/projects/projects-cmdline.html&lt;br/&gt;
-k 工程包名: cc.unmi.android.test&lt;br/&gt;
-n 工程名  : Hello</description></item><item><title>推荐一种快速提高英语口语的方法 [转]</title><link>https://yanbin.blog/improve-english-quickly/</link><pubDate>Mon, 04 Apr 2011 23:20:38 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/improve-english-quickly/</guid><description>
&lt;p&gt;现在越来越多的外国企业在中国开设分公司，很多人有很强的技术和能力，但是英语不行进不了外企。这是因为中国糟糕的英语教育导致的，我们大家都是学英语学了几十年到头来都是英语聋哑人。 &lt;br /&gt;&lt;br /&gt;
本方法是笔者多年国内外学习英语的经历并结合语言专家们的教学经验总结出来的，只是提高口语和听力，适合那些有一定英语基础，想提高口语，但不想花钱留学又不想上昂贵的英语培训班的人。
另外其中有些方法会比较特别，颠覆传统的学习方法，可能不适合所有人，一般来说每天坚持本方法练习3到6个月就会有效果，如果仍然没有，那很可能本方法不适合你。 &lt;br /&gt;&lt;br /&gt;
首先在介绍方法之前，我先给英语口语水平分3个级别。level0，level1和level2。无论刚开始学ABC还是大学过了四，六级，大部分人的口语level都是0，是的别不服气。
什么是level1，0和1的区别就是当你说英语时候可以脱口而出，不用先在脑海里把要表达的中文翻译成英文。</description></item><item><title>如何在WordPress里使用Featured Image功能</title><link>https://yanbin.blog/wordpress-enable-featured-image/</link><pubDate>Thu, 31 Mar 2011 20:45:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-enable-featured-image/</guid><description>
我想大部分人都很清楚WordPress 2.9版本开始，就增加了Featured Image这个功能。而且这样的功能多用于杂志式的主题，从里一个角度来说，用缩略图的形式比摘要输出形式更能体现网站的可读性！这里就介绍下如何为你的 WordPress主题添加Featured Image功能。&lt;br/&gt;
&lt;br/&gt;
首先，看一看你的主题包，在functions.php文件里必须有下面的语句才能开启单篇日志以及页面里的Featured Image功能。&lt;br/&gt;
&lt;br/&gt;
&lt;code&gt;add_theme_support( 'post-thumbnails' );&lt;/code&gt;&lt;br/&gt;
&lt;br/&gt;
当然，如果你只希望在单篇日志里开启这样的功能，只要加入&lt;br/&gt;
&lt;br/&gt;
&lt;code&gt;add_theme_support( 'post-thumbnails', array( 'post' ) );&lt;/code&gt;&lt;br/&gt;</description></item><item><title>Android Dalvik虚拟机初识 分享</title><link>https://yanbin.blog/android-dalvik-virtual-machine/</link><pubDate>Wed, 30 Mar 2011 02:59:40 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/android-dalvik-virtual-machine/</guid><description>
首先，让我们来思考下面几个问题：&lt;br/&gt;
&lt;br/&gt;
什么是Dalvik虚拟机?&lt;br/&gt;
&lt;br/&gt;
Dalvik VM与JVM有什么区别？&lt;br/&gt;
&lt;br/&gt;
Dalvik VM有什么新的特点？&lt;br/&gt;
&lt;br/&gt;
Dalvik VM的架构是怎么样的？&lt;br/&gt;
&lt;br/&gt;
首先，我得承认第一个问题问得很傻：什么是Dalvik虚拟机？没有人给出过一个明确的定义，但是，我们似乎可以从人们对Java虚拟机的描述中得到些信息。&lt;br/&gt;
&lt;br/&gt;
Java 虚拟机（JVM）是一个虚构出来的计算机，是通过在实际的计算机上仿真模拟各种计算机功能来实现的。它有自己完善的硬件架构（如处理器、堆栈、寄存器 等），还具有相应的指令系统。使用“Java虚拟机”程序就是为了支持与操作系统无关、在任何系统中都可以运行的程序。</description></item><item><title>Android 开发者官网镜像站点收集</title><link>https://yanbin.blog/android-mirror-sites-collections/</link><pubDate>Sun, 20 Mar 2011 08:18:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/android-mirror-sites-collections/</guid><description>
怎么都搞不明白，一个纯技术性的网站 Android 开发者 http://developer.android.com/ 会被墙，找不到合适的理由。仅仅是出自于 Google，因 Google 不听 ZF 的话受影响？Android 牵动了什么人的神经，影响了什么群体的利益？&lt;br/&gt;
&lt;br/&gt;
作技术的不能因为正常方式访问不了就不去另想他途，翻墙方式的种种这里不累述了。我们不会像多数人一样，以为搜索引擎只有一个百度(多数人其实根本不知道那个叫做搜索引擎)。在常规方式下，幸好还有不少 Android 开发者网站的镜像可以访问，虽然有些并不能非常及时的跟进同步，但也足够用的。&lt;br/&gt;
&lt;br/&gt;
这里就收集一些可访问的 Android 镜像站点，千万不能因为我把它们集中在一块而给某些别有用心的人得了方便，而后害了那些热心的网站，那我可真的是很无聊。随着 &lt;a href="http://androidappdocs.appspot.com/index.html" target="_blank"&gt;appspot.com&lt;/a&gt; 的不幸，也让 http://androidappdocs.appspot.com 失效了。如果还有别的镜像站点，希望大家多多提供，谢谢啦。</description></item><item><title>解决 Eclipse 中 Android.jar 无法关联源代码的办法</title><link>https://yanbin.blog/eclipse-android-jar-attach-source-code/</link><pubDate>Sun, 20 Mar 2011 04:09:28 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/eclipse-android-jar-attach-source-code/</guid><description>
在 Eclipse 中一般的 jar 包在(F3 或 Ctrl + Click) 进到它的 Class 时，如果没有关联上源代码会有一个按钮让你关联下就可以查看源代码。可是在 Eclipse 中的 android.jar 中的类，却只会提示&lt;br/&gt;
&lt;br/&gt;
Source not found&lt;br/&gt;
The JAR of this class file belongs to container 'Android 1.6' which does not allow modifications to source attachments on its entries.&lt;br/&gt;
&lt;br/&gt;
也就是正常渠道关联不上源代码，那使用手动来引入 android.jar，用传统的方式来关联源代码如何，源码是能看到了，可是出现个错误。还是 Google 下吧，找到了 &lt;a href="http://stuffthathappens.com/blog/2008/11/01/browsing-android-source-in-eclipse/" target="_blank"&gt;Browsing Android Source in Eclipse&lt;/a&gt;，他给我们提供了两种解决办法：&lt;br/&gt;
&lt;br/&gt;
#Solution 1</description></item><item><title>Tomcat 7 启动时出现 java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory</title><link>https://yanbin.blog/tomcat-7-juli-lgging-logfactory/</link><pubDate>Thu, 17 Mar 2011 20:57:19 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tomcat-7-juli-lgging-logfactory/</guid><description>
在 Eclipse WTP 3.1.1 中换成了使用 Tomcat 7，当然选择的仍然是 Tomcat 6，只是指向的目录是 Tomcat 7 的，一启动便立马出现错误：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at org.apache.catalina.startup.Bootstrap.&amp;lt;clinit&amp;gt;(Bootstrap.java:60)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Caused by: java.lang.ClassNotFoundException: org.apache.juli.logging.LogFactory&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.net.URLClassLoader$1.run(URLClassLoader.java:200)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.security.AccessController.doPrivileged(Native Method)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.net.URLClassLoader.findClass(URLClassLoader.java:188)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.lang.ClassLoader.loadClass(ClassLoader.java:307)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.lang.ClassLoader.loadClass(ClassLoader.java:252)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt; ... 1 more&lt;/span&gt;&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Exception in thread "main"</description></item><item><title>Lucene 索引、查询日期类型字段</title><link>https://yanbin.blog/lucene-index-date-field/</link><pubDate>Tue, 15 Mar 2011 05:59:38 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lucene-index-date-field/</guid><description>
Lucene 的 Field 在构造时内容参数可接受一个字符串或者字节数组，流等，不支持 Date 等类型。如果把 Date.toString() 转换成字符串来索引的话那么存储在索引中的样子就不确定(new Date().toString() 为 “Tue Mar 15 13:25:41 GMT+08:00 2011”)，也给下次还原来为 Date 类型带来不便。在以前的版本 Lucene 专门提供了 &lt;a href="http://lucene.apache.org/java/3_0_3/api/all/org/apache/lucene/document/DateField.html" target="_blank"&gt;DateField&lt;/a&gt; 来索引 Date 类型，但&lt;br/&gt;
&lt;br/&gt;
现在不推荐使用了，而且也无法用，因为 Lucene 的 Document 在 add(Fieldable field) 时接受的是一个 Fieldable 类，然而 DateField 并未实现 Fieldable 接口，保留它是用来读取老的索引文件。&lt;br/&gt;
&lt;br/&gt;
那 Lucene 要怎么索引日期类型呢？说到底，还是把它转换为字符串，搜索的时候从存储的字符串还原出来，辅助工具类是 &lt;a href="http://lucene.apache.org/java/3_0_3/api/all/org/apache/lucene/document/DateTools.html" target="_blank"&gt;DateTools&lt;/a&gt;, 它所提供的方法：</description></item><item><title>JDK 7 中的语法增强 -- (4) 创建泛型实例时的类型推断</title><link>https://yanbin.blog/jdk-7-enhance-type-inference/</link><pubDate>Mon, 14 Mar 2011 11:20:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-7-enhance-type-inference/</guid><description>
Java 的泛型实例在声明时有点傻，比如像这样的语句 List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;(); 前明的 List&amp;lt;String&amp;gt; 已经提供了足够的信息让编译器知道 ArrayList 的参数类型，当然这是个简单的例子，如果复杂些，来点嵌套的话：&lt;/p&gt;
&lt;br/&gt;
Map&amp;lt;String, List&amp;lt;Map&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt; myMap = new HashMap&amp;lt;String, List&amp;lt;Map&amp;lt;String,String&amp;gt;&amp;gt;&amp;gt;();&lt;br/&gt;&lt;br/&gt;
那就够呛的，所以 Java 的这一语法要求也让 Scala 这样的语言所诟病，当然人家 Scala 是个趋近于动态性的语言，它认为上面的语句应该这么写：&lt;br/&gt;&lt;br/&gt;
val x = new HashMap[String, List[String, List[Map[String, String]]]() 或者是&lt;br /&gt;
val x: Map[String, List[String, List[Map[String, String]]] = new HashMap()&lt;br/&gt;&lt;br/&gt;
总之，只要一端的表态就行，其他事情交给编译器来推断。现在 JDK 也反省到了这一点，因为引入一个叫做菱形操作符(Diamond operator) 的东西，即两个尖括号 &amp;lt;&amp;gt;。让使得你像 Scala 一样只要在左边声明参数类型就行啦。这个操作符不免让我想起了在 perl 里有个 &amp;lt;=&amp;gt;  这样的操作符，好像叫做飞机，不是斗地主的飞机，它相当于 Java 里的 compareTo() 的功能。&lt;br/&gt;&lt;br/&gt;
于是泛型实例的声明就简单化成了 Map&amp;lt;String, List&amp;lt;Map&amp;lt;String, String&amp;gt;&amp;gt;&amp;gt; myMap = new HashMap&amp;lt;&amp;gt;(); 继续往下看：</description></item><item><title>JDK 7 中的语法增强 -- (3) swith 里用 String 类型</title><link>https://yanbin.blog/jdk-7-enhance-strings-witch/</link><pubDate>Sun, 13 Mar 2011 11:05:07 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-7-enhance-strings-witch/</guid><description>
java 在 switch 语句这一项上更多的是承袭了 c++，其实整个语法就是 c 风格的。java 的 switch 里只能用 byte、char、short、int 和 enum 类型，连 long 型都不能用(因为 switch 里的要被转换为 int  型，而 long 太长了)。要说不支持 float 和 double 那样的浮点那好理解，因为它们本身是不精确的，1 可能是 0.9999999999。boolean 就两值，放 switch 里无意义，因为 c++ 的 switch 里可用bool 型，但在 java 中遭摈弃。&lt;br/&gt;
&lt;br/&gt;
现在在 jdk7 里 switch 可以用字符串了，学了 c#，字符串的好处就是它是表意的，像 switch(action) case "create" : ...; case "delete" : .....，多写意啊，再也不用先定义一堆常量，然后再 switch(action) case Constants.ACTION_CREATE : ......; case Constants.ACTION_DELETE : ......，当然写过不少程序的尽量会避免写成 case 1... case 2 这样的写法了，会让人不知所以的。常量放在 case 中就要求我们经常要去查它定义的值是什么。</description></item><item><title>OpenVPN Windows 平台安装手记</title><link>https://yanbin.blog/openvpn-windows-installation/</link><pubDate>Fri, 11 Mar 2011 13:34:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/openvpn-windows-installation/</guid><description>
VPN 用途还是很广泛的，比如家里连接公司的网络解决紧急问题，安全因素考虑时两个分部间的互访。而且在兲朝 VPN 可能还承载着更大的功用，比如你在外面有个主机或是 VPS 就可以试下。OpenVPN 是免费的(Community 版免费)，它可以让你异地两个电脑组成一个局域网，可以是 client-to-client, 现 proxy 一下；或是 push 成 gateway，透明的进行 proxy -- 当然还需要更多的设置，像 NAT、Route 等。&lt;br/&gt;
&lt;br/&gt;
OpenVPN 是跨平台的，这里讲下 Windows 平台的 OpenVPN 如何安装设置。下载页面：&lt;a href="http://openvpn.net/index.php/open-source/downloads.html"&gt;http://openvpn.net/index.php/open-source/downloads.html&lt;/a&gt;，当前 Windows 平台下的版本是 &lt;a href="http://swupdate.openvpn.net/community/releases/openvpn-2.1.4-install.exe" target="_blank"&gt;openvpn-2.1.4-install.exe&lt;/a&gt;。</description></item><item><title>HttpClient 模拟登陆，保持会话并进行后续操作</title><link>https://yanbin.blog/httpclient-login-session/</link><pubDate>Wed, 09 Mar 2011 20:02:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/httpclient-login-session/</guid><description>
&lt;a href="http://hc.apache.org/" target="_blank"&gt;Apache HttpClient&lt;/a&gt; 是很方便的 Java 开源的访问 HTTP 资源的组件。网站上的资源不总是能匿名访问的，很多都需要登陆后才能操作，且不说论坛里登陆后才能发言，就是某些稍显敏感的 XML 等信息也是登陆后才能获取到的。&lt;br/&gt;
&lt;br/&gt;
没问题，HttpClient 能让你做到，它提供了 Basic 和 Form-Based 两种验证方式。登陆后获得服务器端发来的 Cookie 作为下一次访问的凭证, 让服务端认为你还是个合法用户。服务端不是用 Session 来维护会话的吗？是的，Session 也要有个载体，Cookie 了。或有时 Java Web 会用 jsessionid 参数在服务端与客户端来回关联 Session 信息，也没问题，HttpClient 同样能胜任。&lt;br/&gt;
&lt;br/&gt;
下面主要说明 Form-Based 的验证方式，Basic 的验证简单列了几行代码，还未实践，具体可参考文后的链接。</description></item><item><title>JDK 7 中的语法增强 -- (2) 数值常量中的下划线</title><link>https://yanbin.blog/jdk-7-enhance-underscores-literals/</link><pubDate>Tue, 08 Mar 2011 10:47:55 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-7-enhance-underscores-literals/</guid><description>
再次进入到 JDK7 语法增强的第二道道上来，也就是数值常量书写时可以中间插入下划线作为分隔线，也就是 int i = 22_33 相当于 int i = 2233，仅仅是一定程度上的有助于阅读代码，除此之外，别无它处。编译后或执行时这些下划线便透明掉了，编译后的字节码里也没有了附加的下划线，当然执行时也不可能向你输出下整数 i 为 22_33 的。&lt;br/&gt;
&lt;br/&gt;
也不知道是谁只是众目期盼中的版本净找些这类来增强，感觉意义不是很大，大约类同于二进制方式来表示整数。这里的数值包括整数和小数的，加上 16 进制那么可用来表示数字的字符就有 0-9 和 a-f(A-F) 了，注意&lt;strong&gt;下划线只能加在前面所列的某两个字符之间，而且可连续多个下划线&lt;/strong&gt;。看例子吧：</description></item><item><title>JDK 7 中的语法增强 -- (1) 二进制字面常量</title><link>https://yanbin.blog/jdk-7-enhance-binary-literals/</link><pubDate>Mon, 07 Mar 2011 09:47:43 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdk-7-enhance-binary-literals/</guid><description>
JDK 7 预览版已出，当然到实际的使用还有段时间，包括 IDE 的和各大主要的应用服务器的升级，及 JDK 7 本身的稳定尚须时日，但我们还是有必须瞧瞧新版 JDK 带来了哪些语法增加。&lt;/p&gt;
&lt;br/&gt;
我们没有盼到闭包，相对于 JDK 5 的语法增强，JDK 7 的动作还较小，大约 8 个，这里介绍第一个 二进制字面常量。&lt;br/&gt;&lt;br/&gt;
1）二进制字面常量 -- 任何整数类型(byte, short, int 和 long) 声明时可用二进制的形式，即 01 串，只要数字前加上 0b 或 0B 即行。</description></item><item><title>Struts2 中如何覆盖已内置拦截器的属性(参数)值</title><link>https://yanbin.blog/struts2-how-to-override-interceptor-parameters/</link><pubDate>Sun, 06 Mar 2011 02:19:01 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-how-to-override-interceptor-parameters/</guid><description>
在前面一篇 &lt;a href="https://yanbin.blog/struts2-modeldriven-setmodel-nothing" target="_blank" rel="noopener"&gt;Struts2 中 ModelDriven Action 的 setModel() 为何不起作用？&lt;/a&gt; 中发现最漂亮的办法就是修改 ModelDrivern 的 refreshModelBeforeResult 属性为 true，同时留下一个话题。这里接着，即如何覆盖内置拦截器的属性(参数) 值。&lt;/p&gt;
&lt;br/&gt;
如果是自定的拦截器，则可以在 struts.xml 中的 &amp;lt;interceptor&amp;gt; 或 &amp;lt;interceptor-ref&amp;gt; 中用 &amp;lt;param name="paramName"&amp;gt;paramValue&amp;lt;/param&amp;gt; 来指定参数。但是对于 struts2 核心包内置的或是第三方的拦截器的参数需要进行重新设置该如何呢？&lt;br/&gt;&lt;br/&gt;
比如在 struts2 包中的 struts-default.xml 中定义很多拦截器，通常我们使用的是其中组织好的 defaultStack 拦截器栈，看 struts-default.xml 中关键几行配置：</description></item><item><title>iText5.0.6 用 HTMLWorker 转换 HTML 为 PDF 已支持单元格宽度设置</title><link>https://yanbin.blog/itext5-0-6-htmlworker-html-pdf-support-cell-width/</link><pubDate>Fri, 04 Mar 2011 03:05:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/itext5-0-6-htmlworker-html-pdf-support-cell-width/</guid><description>
前有一文 iText 用 HTMLWorker 转换 HTML 为 PDF 时可设置表格列宽度 介绍了在 iText5.0.4 的基础上转换 html 为 pdf 时，如何让 table 中的单元格能支持宽度设定，并且还修改了三份源代码: HTMLWorker.java、IncCell.java 和 IncTable。也只是实现了以百分比形式来设定每列的相对宽度，现在好了，iText 顺理的发现了用户的这一需求，在进化后的 iText5.0.6 中可以让你设定 &amp;lt;td&amp;gt; 的宽度，可直接用百分比数值(20%)相对宽度，或用参照数值来设置，之后呢，HTMLWorker 会帮你转换为相对的宽度--比如有三列，分别设置为 20，10，50，被转换为相对宽度就是 25%，12.5% 和 62.5%。&lt;br/&gt;
&lt;br/&gt;
看iText5.0.6 简短的 release 说明，iText5.0.6 has been released(&lt;a href="http://lowagie.com/node/73"&gt;http://lowagie.com/node/73&lt;/a&gt;):</description></item><item><title>Struts2 文件下载及找不到文件的处理办法</title><link>https://yanbin.blog/struts2-download-file-not-found/</link><pubDate>Tue, 01 Mar 2011 19:12:08 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-download-file-not-found/</guid><description>
Struts2 对文件的上传和下载提供了很好的支持，上传时直接用 java.io.File 来接收，下载时也无需自己去设置相应的 Stream 响应头，它提供了 org.apache.struts2.dispatcher.StreamResult 给 Action 使用。它反映到 struts.xml 配置里就是type="stream" 的 result，由 result 去负责把文件内容写入响应中去。Action 的执行方法在 result 之前执行，所以你可以借此控制下载权限，本文也演示了如何判断文件是否存在，不存在则导向到 FileNotFound 页面。StreamResult 中有哪些相关的配置参数可以参考它的源代码：</description></item><item><title>Web开发人员应当知道的15个开源项目[转]</title><link>https://yanbin.blog/web-dev-15-open-source/</link><pubDate>Thu, 24 Feb 2011 10:33:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/web-dev-15-open-source/</guid><description>
如今，构建网站和开发Web应用程序已经不仅要求开发人员是一名优秀的程序员，更需要聪明的程序员。这也就是说，在可能的情况下，重复使用已有的代 码和应用程序，而不是自己重头开始。开源由来已久，并且很多网站的构建也使用了开源。开发人员都知道Linux、Apache、MySQL及 PHP （LAMP）。 但是除那些之外的其他开源项目呢？作为一名Web开发人员，你可以借助诸多可用的项目，以节省开发时间，或提高程序性能。&lt;br/&gt;
&lt;br/&gt;
下面让我们看一下哪些开源项目可供大家免费使用。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;可供使用的新型数据库&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
传统上讲，你可能习惯使用MySQL。尽管MySQL是一个非常棒的数据库，但诸多试图解决MySQL存在的问题的新型数据库已经问世。</description></item><item><title>Hibernate 的 formula 简单用法</title><link>https://yanbin.blog/hibernate-formula-simple/</link><pubDate>Wed, 23 Feb 2011 07:26:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-formula-simple/</guid><description>
Hibernate 3 之后，formula(公式，Excel 里常见的) 得到了更广泛的应用，不过这里还是简单记录一下它在 &amp;lt;property&amp;gt; 中的应用。&lt;/p&gt;
&lt;br/&gt;
假如我们要映射这么一个类：&lt;br/&gt;&lt;br/&gt;
package cc.unmi.model;&lt;br/&gt;&lt;br/&gt;
public class User {&lt;br /&gt;
    private int id;&lt;br /&gt;
    private String name;&lt;br /&gt;
    Private Set&amp;lt;Order&amp;gt; orders = new HashSet&amp;lt;Order&amp;gt;();&lt;br/&gt;&lt;br/&gt;
    //setter &amp;amp; getter ......&lt;br /&gt;
}</description></item><item><title>PHP 使用 GeoLiteCity 库解析 IP 为地理位置</title><link>https://yanbin.blog/php-geolitecity-ip-location/</link><pubDate>Mon, 21 Feb 2011 07:47:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-geolitecity-ip-location/</guid><description>
关于把 IP 地址转换为地理位置可以使用网络上很多的 API，好处就是不用在本地存储一个 IP 数据库，而且一般网络上的 IP 库会自动更新，不利的地方就是太依赖于网络，性能表现也可能会弱些。比如像下面的 API:&lt;/p&gt;
&lt;br/&gt;
&lt;a href="http://api.hostip.info/get_html.php?ip=58.63.236.31"&gt;http://api.hostip.info/get_html.php?ip=58.63.236.31&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://api.hostip.info/flag.php?ip=58.63.236.31"&gt;http://api.hostip.info/flag.php?ip=58.63.236.31&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
这里介绍 PHP 如何使用 GeoLiteCity.dat 库把 IP 转换为地理位置，GeoLiteCity.dat 可以在 &lt;a href="http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz"&gt;http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz&lt;/a&gt; 下，解压出 GeoLiteCity.dat，即可，我们可以手动去更新新的 IP 库。&lt;br/&gt;&lt;br/&gt;
下面 PHP 解析 IP 的过程参考自 WordPress 插件 &lt;a href="http://wordpress.org/extend/plugins/visitor-maps/" target="_blank" rel="noopener"&gt;Visitor Maps and Who's Online&lt;/a&gt; 的实现。可以找到该插件的两个文件 &lt;a href="http://unmi.cc/wp-content/uploads/2011/02/include-whos-online-geoip.php_.txt" target="_blank" rel="noopener"&gt;include-whos-online-geoip.php&lt;/a&gt; 和 &lt;a href="http://unmi.cc/wp-content/uploads/2011/02/visitor-maps.php_.txt" target="_blank" rel="noopener"&gt;visitor-maps.php&lt;/a&gt; 告诉了我们怎么做。你可以点击这里的链接下载到这两个文件</description></item><item><title>Spring 2.0可扩展XML配置初探[转]</title><link>https://yanbin.blog/spring-xml-custom-schema/</link><pubDate>Mon, 21 Feb 2011 06:30:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-xml-custom-schema/</guid><description>
本文通过一个简单的例子，说明如何去扩展XML配置，它大致需要的几个步骤。具体的需求是使用自定义标签定义一个简单的bean，这个bean有一个或多个属性，标签定义完成后，可以在其他项目中用自定义标签来定义该bean。&lt;/p&gt;
&lt;br/&gt;
Spring 2.0版本支持扩展XML配置，着实兴奋了一下，在我看来，Spring作为目前最流行的框架，不能扩展用户自定义的配置，实在是Spring的一个很不爽的地方，的方式用起来比较通用，起码到目前为止符合大部分人的使用习惯，并且能完成Spring所有的配置操作，但是对于第三方的提供商或则会经常扩展Spring功能的开发者来说，使用这样的配置方式或许不是他们最想要的，他们需要使组件的配置更加直观、易阅读、易扩展……试想使用下面的配置方式。</description></item><item><title>Slfj + Logback 时，基本的 logback.xml 配置</title><link>https://yanbin.blog/slfj-logback-xml-base-config/</link><pubDate>Fri, 18 Feb 2011 07:33:12 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/slfj-logback-xml-base-config/</guid><description>
现在越来越多的项目用 Slfj，而与它结合最好的 Log4j 替代品就是 Logback，下面是 Logback 的一个可供参考的基本的配置文件 logback.xml，可以控制台和文件同时输出，文件可进行两种类型的归档，分天存储日志，或按文件大小压缩日志归档。&lt;/p&gt;
&lt;br/&gt;
Logback 和 Log4J 同出一人之手，而作者的专注点也在 Logback，Logback 有更好的性能，特别是在现代 Java 程序中。&lt;br/&gt;&lt;br/&gt;
一般是 rollingPolicy 和 triggeringPolicy 搭配使用，rollingPolicy 负责做什么，triggeringPolicy 管理什么时候做，而 FixedWindowRollingPolicy 本身就实现了 TriggeringPolicy 接口，所以它能够自我管理，不需要别人来推动就会做的。</description></item><item><title>Struts2 中 ModelDriven Action 的 setModel() 为何不起作用？</title><link>https://yanbin.blog/struts2-modeldriven-setmodel-nothing/</link><pubDate>Fri, 18 Feb 2011 07:04:53 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-modeldriven-setmodel-nothing/</guid><description>
我们在使用 Struts2 的时候一定有用过它的 ModelDriven 类型的 Action，它适于把零散的属性自动组装到一个 JavaBean 中，它的 Model 就相当于 Struts1 的 FormBean。你的 Action 必须实现 com.opensymphony.xwork2.ModelDriven 接口及它的 getModel() 方法，当然要声明一个存储 Model 对象的属性，这个属性将会被压入到 ValueStack 中。&lt;/p&gt;
&lt;br/&gt;
你也可以自然或不自觉在 Action 中加入一个 setModel() 方法，试图在执行期修改当前的 Model 对象，比如像下面这样的 IndexAction 代码：</description></item><item><title>Web开发人员必备的20款超赞的jQuery插件</title><link>https://yanbin.blog/20-super-jquery-plugins/</link><pubDate>Thu, 17 Feb 2011 22:58:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/20-super-jquery-plugins/</guid><description>
&lt;div&gt;
&lt;br/&gt;
&lt;strong&gt;导语：&lt;/strong&gt;jQuery的易扩展性吸引了来自全球的开发者来共同编写jQuery插件。jQuery插件不仅能够增强网站的可用性，有效地改善用户体验，还可以大大减少开发时间。现在的jQuery插件很多，可以根据您的项目需要来选择。这里为您介绍20款非常不错的插件。&lt;br/&gt;
&lt;h2&gt;Creative Radical Web Typography&lt;/h2&gt;&lt;br/&gt;
Lettering.js是一个轻量经的、易于使用的jQuery插件，可创造出极具个性的网页排版，是2010年最佳jQuery插件之一。</description></item><item><title>Struts2+jreloader 出现 NoClassDefFoundError: VelocityEngine</title><link>https://yanbin.blog/struts2-jreloader-no-velocityengine/</link><pubDate>Tue, 15 Feb 2011 02:05:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-jreloader-no-velocityengine/</guid><description>
用 &lt;a href="https://yanbin.blog/jreloader-reload-class" target="_blank" rel="noopener"&gt;用 jreloader 动态重新加载改变的类而不用重启 JVM &lt;/a&gt;来避免因 classes 目录中的文件改动而不停重启 Tomcat。但是却会出现如下的问题：&lt;/p&gt;
&lt;br/&gt;
Feb 15, 2011 3:51:06 PM org.apache.catalina.core.StandardContext filterStart&lt;br /&gt;
SEVERE: Exception starting filter struts2&lt;br /&gt;
java.lang.NoClassDefFoundError: Lorg/apache/velocity/app/VelocityEngine;&lt;br /&gt;
 at java.lang.Class.getDeclaredFields0(Native Method)&lt;br /&gt;
 at java.lang.Class.privateGetDeclaredFields(Class.java:2291)&lt;br /&gt;
 at java.lang.Class.getDeclaredFields(Class.java:1743)&lt;br /&gt;
 at com.opensymphony.xwork2.inject.ContainerImpl.addInjectors(ContainerImpl.java:89)&lt;br /&gt;
 at com.opensymphony.xwork2.inject.ContainerImpl$1.create(ContainerImpl.java:71)&lt;br /&gt;
 at com.opensymphony.xwork2.inject.ContainerImpl$1.create(ContainerImpl.java:67)&lt;br /&gt;
 at com.opensymphony.xwork2.inject.util.ReferenceCache$CallableCreate.call(ReferenceCache.java:150)&lt;br /&gt;
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)</description></item><item><title>Struts2-Json-Plugin 的使用(翻译自官方文档)</title><link>https://yanbin.blog/struts2-json-plugin-guide/</link><pubDate>Sun, 13 Feb 2011 04:40:43 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-json-plugin-guide/</guid><description>
在 Struts2 中要使用 Ajax 获得 Json 数据我认为目前还是 struts2-json-plugin 了。当然你你可以用手工用像 &lt;a href="http://xstream.codehaus.org/" target="_blank" rel="noopener"&gt;XStream&lt;/a&gt;、&lt;a href="http://code.google.com/p/google-gson/" target="_blank" rel="noopener"&gt;Google Gson&lt;/a&gt;、&lt;a href="http://jackson.codehaus.org/" target="_blank" rel="noopener"&gt;Jackson&lt;/a&gt; 这样的工具手工把 Java 对象转换成 Json 字符串再写往 Response 去，要写的代码自然多不了，还得留心字符集与 content type。而 struts2-json-plugin 毫无疑问是与 Struts2 最亲近了，只需你配置一些属性就能得到你想的结果。&lt;br/&gt;&lt;br/&gt;
本想分几篇逐步介绍如何使用 struts2-json-plugin 的，然而就在现在发现官方的 struts2-json-plugin 指南已经很详细了，所以干脆翻译一下 &lt;a href="http://struts.apache.org/2.2.1.1/docs/json-plugin.html"&gt;http://struts.apache.org/2.2.1.1/docs/json-plugin.html&lt;/a&gt;，同时自己加深对它的理解。</description></item><item><title>Struts2 的 &lt;s:component&gt; 中如何加条件参数 &lt;s:param&gt;...</title><link>https://yanbin.blog/struts2-s-param-conditions/</link><pubDate>Thu, 10 Feb 2011 05:11:56 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-s-param-conditions/</guid><description>
Struts2 的 &amp;lt;s:component&amp;gt; 标签可以让你消磨掉自定义标签的意愿，它可以指定一个模板文件，我比校亲赖于 ftl 文件，然后用 &amp;lt;s:param&amp;gt; 给上参数，其他的事情交给 freemarker 去处理了，而且在 ftl 文件能够直接以 &amp;lt;@s.textfield ..../&amp;gt; 的形式使用 Struts2 的标签。&lt;/p&gt;
&lt;br/&gt;
比如，jsp 文件里这么使用 &amp;lt;s:component&amp;gt; 标签：&lt;br/&gt;&lt;br/&gt;
&amp;lt;s:component template="/components/status.ftl" theme="simple"&amp;gt;&lt;br /&gt;
    &amp;lt;s:param name="status" value="#status"/&amp;gt;&lt;br /&gt;
&amp;lt;/s:component&amp;gt;&lt;br/&gt;&lt;br/&gt;
把 status.ftl 要放在 WEB-INF/classes/template/simple/components/status.ftl。当然你可以应用 &amp;lt;s:component&amp;gt; 的 templateDir 属性使 status.ftl 放在别的位置上，但可能会带来别的麻烦。</description></item><item><title>用 jreloader 动态重新加载改变的类而不用重启 JVM</title><link>https://yanbin.blog/jreloader-reload-class/</link><pubDate>Fri, 28 Jan 2011 06:02:27 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jreloader-reload-class/</guid><description>
在 Tomcat 中可以配置 reloadable="true" 做到类改变后，Tomcat 重新加载。其实这个过程大约也是当 Tomcat 发现有改变的类会重新启动一个新的应用程序重新加载所有的类来服务于新的请求，只是不需要你手动的去执行 shutdown.sh(.bat)，再 startup.sh(.bat)。这种方式对于古老的 jsp 程序完全能从容以对，因为 web.xml 里几乎没什么随应用一起启动且耗时长代码；但当下是框架横行，web.xml 中随应用一起启动的程度可谓是争先恐后的，所以仅仅依赖 reloadable="true" 是满足不了需求的。每改一个类(无论是改动了方法体中的代码还是变动了类的结构，准确的说是动了 WEB-INF/classes 目录中的任何文件) 你都可能就会在&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800000;"&gt;Jan 28, 2011 7:19:42 PM org.apache.catalina.core.ApplicationContext log&lt;br/&gt;
INFO: Initializing Spring root WebApplicationContext</description></item><item><title>简化 Struts2 OGNL 访问静态方法和静态变量</title><link>https://yanbin.blog/simplify-struts2-ognl-static-member/</link><pubDate>Wed, 26 Jan 2011 08:35:51 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/simplify-struts2-ognl-static-member/</guid><description>
前些日子折腾过这一话题，而且在 &lt;a href="https://yanbin.blog/struts2-ognl-access-static" target="_blank" rel="noopener"&gt;Struts2 中 OGNL 如何更简单的访问静态变量和静态方法&lt;/a&gt; 记述了其中的原理，也对实现作了初步的猜想，但没给出实际的答案。这里将会给出，探寻的过程中有过不少尝试，下面也将把解决的过程描述出来，仍有不少迷域之处。先交待一下答案，必须在一个自定义的 PreResultListener 里压入静态方法或静态变量所在类的对象实例，在返回结果之前方能保证它们是在栈顶的。&lt;/p&gt;
&lt;br/&gt;
解决此问题的一个明确目标就是要把静态方法或静态变量所在类的对象实例压到 ValueStatic 已知的位置上，当然最应该就是在栈顶，其他的栈数据，如当前 Action 实例或 Model 对象应该在它们之下，这样的话，你就能用 @vs@, 或 @vs1@... 这样的方式明确访问哪个类的静态成员。&lt;br/&gt;&lt;br/&gt;
最简单方法，前篇也讲过，在 Action 的 execute() 方法中往 ValueStatic 中压入的值会在栈顶--在 Action 实例和 Model 实例之上。</description></item><item><title>Spring 中如何向 Bean 注入系统属性或环境变量</title><link>https://yanbin.blog/spring-injection-system-properties-env/</link><pubDate>Wed, 26 Jan 2011 00:29:03 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-injection-system-properties-env/</guid><description>
在 Spring 中为 javabean 注入属性文件中的属性值一般人都知道的，可以通过 &lt;a href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html" target="_blank" rel="noopener"&gt;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&lt;/a&gt; 引入一个属性文件，然后给 bean 指定属性的时候就可以用 ${jdbc.url} 方式赋值了。比如在 Spring 中是这样的配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;bean&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;propertyConfigurer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;locations&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&lt;/span&gt;classpath:jdbc.properties&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;bean&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;dataSource&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;org.apache.commons.dbcp.BasicDataSource&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;destroy-method=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;close&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${jdbc.url}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;username&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${jdbc.username}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
只是有时候我们需要给 bean 赋上系统属性(System.getProperties() ) 中的值或环境变量(System.getenv() ) 中的值，根据程序所处的环境产生不同的行为，这样我们无法事先在某个 properties 文件预先设定好值的。</description></item><item><title>安装 MySQL 5 后无法启动(不能 Start service) 的解决办法</title><link>https://yanbin.blog/install-mysql-cannot-start-service/</link><pubDate>Tue, 25 Jan 2011 06:14:03 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/install-mysql-cannot-start-service/</guid><description>
安装的 MySQL 5.1.48 或是 MySQL 5.5.8，配置好最后点击 Execute 按钮了，但是进行不到 Start service 这一步。检查了下 MySQL 系统服务已添加，但是无法启动，手工也不行。这时候用事件查看器可以看到程序事件里有几个来自于 MySQL 的错误：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Plugin 'InnoDB' registration as a STORAGE ENGINE failed.&lt;br/&gt;
Unknown/unsupported table type: INNODB&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
原来是因为这两版本的 MySQL 默认使用了支持事物的 INNODB 引擎，打开 my.ini 文件，在 MySQL 的安装文件夹, 如 c:\program files\MySQL\ 中，看到：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;default-storage-engine=INNODB&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
解决办法是把该设置改为&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;default-storage-engine=MYISAM</description></item><item><title>Linux VPS一键安装包集合再次更新</title><link>https://yanbin.blog/linux-vps-installation-packages-2/</link><pubDate>Mon, 24 Jan 2011 09:58:41 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-vps-installation-packages-2/</guid><description>
无名博客再次收集更新了一些Linux VPS下的一键安装包，一键安装包可以大大简化人工成本，节约时间，不仅方便了使用Linux VPS的小白，对于Linux老手来讲编译安装需要输入大量的命令，如果是配置生产环境需要耗费大量的时间。这些一键安装包使用非常简单，傻瓜化的安装方 式，采用Shell编写，容易修改。这次添加了cnBeta网友留言里的一键安装包。 Web套装一键安装包&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;LNMP一键安装包&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
LNMP一键安装包是一个用Linux Shell编写的可以为CentOS/RadHat、Debian/Ubuntu VPS(VDS)或独立主机安装LNMP(Nginx、MySQL、PHP、phpMyAdmin)生产环境的Shell程序。&lt;br/&gt;
适用系统：CentOS/Debian/Ubuntu&lt;br/&gt;
安装方法：&lt;a href="http://yea.im/lnmp"&gt;猛击这里&lt;/a&gt;</description></item><item><title>Spring JdbcTemplate 调用存储过程</title><link>https://yanbin.blog/spring-jdbctemplate-call-procedure/</link><pubDate>Mon, 24 Jan 2011 06:49:11 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-jdbctemplate-call-procedure/</guid><description>
以前一篇中写到了 &lt;a href="https://yanbin.blog/hibernate3-call-procedure" target="_blank" rel="noopener"&gt;hibernate 调用存储过程&lt;/a&gt;，这里介绍 Spring 借道 JdbcTemplate 如何调用数据库存储过程。还是以前面的那个 DB2 存储过程为例，该过程的代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;procedure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selectAllUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;IN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;piAge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;DYNAMIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RESULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SETS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp_cursor1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CURSOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLIENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;piAge&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OPEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp_cursor1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这个过程中最后一行直接打开了一个游标，也就是返回了一个结果集。调用存储过程的方法应该看看 org.springframework.jdbc.core.JdbcTemplate 的各个 execute() 方法，具体点就是带了 CallableStatementCallback&amp;lt;T&amp;gt; 参数的那两个 execute()，究底的话又归结为其中之一。</description></item><item><title>Struts2 中 OGNL 如何更简单的访问静态变量和静态方法</title><link>https://yanbin.blog/struts2-ognl-access-static/</link><pubDate>Thu, 20 Jan 2011 07:35:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-ognl-access-static/</guid><description>
&lt;p&gt;在 struts2 标签中是使用 OGNL 来访问对象属性或方法的，也就是访问的 ValueStack 中的值，即 OgnlValueStack。OGNL 也能让你直接访问类的静态变量和静态方法，标准写法是：&lt;/p&gt;
&lt;p&gt;@类全限定名@静态属性或方法名，比如访问某个常量：&lt;/p&gt;
&lt;p&gt;&amp;lt;s:property value="@cc.unmi.common.Constants@MY_NAME"/&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;s:property value="@cc.unmi.common.Utils.now()"/&amp;gt;&lt;/p&gt;
&lt;p&gt;麻烦就是一定要写上类的全限定名称，实际应用时肯定比这个还要长，所以必须寻求一种更精练的写法。幸好像这篇文章：&lt;a href="http://blog.csdn.net/icemenlover/archive/2009/10/20/4702040.aspx" target="_blank"&gt;Struts2 OGNL 的增强&lt;/a&gt; 提到了可以用：</description></item><item><title>引入了 struts2-spring-plugins 包后 Spring 就会管理你的 Action</title><link>https://yanbin.blog/struts2-spring-plugins-action/</link><pubDate>Thu, 20 Jan 2011 05:51:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-spring-plugins-action/</guid><description>
Struts2 与 Spring 结合，使用 Spring 来管理 Action 实例，在项目中引入了struts2-spring-plugin-2.2.1.jar 包。然后想到的是既然是要用 Spring 来管理 Action 实例，就得在 struts.xml 里加上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;constant&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;struts.objectFactory&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;spring&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
或者是在 struts.properties 里加上一条属性：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;struts.objectFactory = spring&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
OK，这也没问题，然而有次在某个测试项目中想暂时不用 Spring 来管理 Bean，于是把上面的配置去了，也把 web.xml 中的相关 Spring 的 ContextLoaderListener 也格啦。容器启动的时候却发现：</description></item><item><title>Tomcat 7的七大特性：新特性与增强功能</title><link>https://yanbin.blog/top7-features-in-tomcat7/</link><pubDate>Mon, 17 Jan 2011 07:57:28 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/top7-features-in-tomcat7/</guid><description>
Tomcat 7 在加强原有功能的同时也增加了一些新特性。有些文章列举了Tomcat 7的新特性，但大多数都没有对它们进行详细地解释、评价并给出可执行的示例。除了列举出新特性，这篇文章还对Tomcat 7的七个最值得注意的特性与增强部分进行分类、评定以及演示可执行代码以使你能够对每个特性/加强有个更好的理解。&lt;/p&gt;
&lt;br/&gt;
我将下面列举的这些分类为“Tomcat 7 新特色：创新性改变”和“Tomcat 7 加强：进化性改变”：&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;Tomcat 7 新特性：创新性改变&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;使用&lt;em&gt;随机数令牌(nonce)&lt;/em&gt;防止跨站请求伪造（cross-site request forgery—CSRF）攻击&lt;/li&gt;
&lt;li&gt;验证时改变 &lt;em&gt;jsessionid&lt;/em&gt; 以防止固定会话攻击&lt;/li&gt;
&lt;li&gt;内存泄露检测及预防&lt;/li&gt;
&lt;li&gt;使用别名在&lt;em&gt;war&lt;/em&gt;文件外存储静态内容</description></item><item><title>jQuery ajax 在 IE 中碰到 parsererror 异常的分析与应对</title><link>https://yanbin.blog/jquery-ajax-ie-parsererror/</link><pubDate>Wed, 12 Jan 2011 22:50:02 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jquery-ajax-ie-parsererror/</guid><description>
在用 jquery 1.4.2 的 $.ajax() 时：
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;updateRoles.action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;dataType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在其他浏览器中表现的很好，但是在 IE（7/8） 下总报 parsererror 错误。在 jquery 中找到 parsererror，定位在 5989 行：</description></item><item><title>Java 何日能随意实现自定义的对象装箱</title><link>https://yanbin.blog/java-how-to-implements-custom-autobox/</link><pubDate>Tue, 11 Jan 2011 06:31:47 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-how-to-implements-custom-autobox/</guid><description>
Java 步入到 Tiger 后增加了自动拆装箱特性，构造 Integer 不用 Integer i = new Integer(1); 或者 Integer i = Integer.valueOf(1); 只要写成 Integer i=1 就行了。Java 的自动拆装箱只是针对基本类型与其封装类型之间的转换，无法自定义类似的行为，比如想实现某个自定义类 Item，能够通过 Item i = 1; 就完成从整形 1 到自定义类的自动装箱功能。&lt;/p&gt;
&lt;br/&gt;
Java 在目前还是没办法做到的，当然也是我自己的一个猜想。Java 的自动拆装箱与 C# 相比也是要弱些，C# 中 int 仅仅是 Int32 这样的类型的别名，所以它们也是等效的，声明 int i=0; 你就可以呼叫 i.ToString() 方法了。可以说 C# 比 Java 更对象化了。&lt;br/&gt;&lt;br/&gt;
现在来看下 C++ 是怎么实现自定义类的 Item i = 10; 这样的声明的，见代码：</description></item><item><title>Java 的方法签名与字段类型表示-[Ljava.lang.String;</title><link>https://yanbin.blog/java-signatures-data-types/</link><pubDate>Mon, 10 Jan 2011 11:19:39 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-signatures-data-types/</guid><description>
我们什么时候会接触到 Java 的方法签名呢？在进行 JNI 调用时，还有在看方法重载时。重载的方法是有不同的方法签名的，而是不区分返回值，而实际方法签名还揉入了返回值类型的，还有就是 javap -s 查看方法签名时，如 javap -s java.util.Date。&lt;br/&gt;
&lt;br/&gt;
看来方法签名与我们实际工作的关系还真的不大。倒是有次遇着了，事出于 Struts2 应用中提交表单时报出了下面的错误：&lt;br/&gt;
&lt;br/&gt;
00:43:59.716 [http-8080-4] WARN  com.opensymphony.xwork2.ognl.OgnlValueStack - Error setting expression 'version' with value &lt;span style="color: #993300;"&gt;'[Ljava.lang.String;@e18a9a'&lt;br/&gt;
&lt;/span&gt;ognl.MethodFailedException: Method "&lt;span style="color: #993300;"&gt;setVersion&lt;/span&gt;" failed for object &lt;a href="mailto:cc.unmi.model.Post@ed0cd7"&gt;cc.unmi.model.Post@ed0cd7&lt;/a&gt;&lt;br/&gt;
 at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1285) ~[ognl-3.0.jar:na]&lt;br/&gt;
 at ognl.OgnlRuntime.setMethodValue(OgnlRuntime.java:1474) ~[ognl-3.0.jar:na]&lt;br/&gt;
 at ognl.ObjectPropertyAccessor.setPossibleProperty(ObjectPropertyAccessor.java:85</description></item><item><title>XSLT 调用 Java 的类方法</title><link>https://yanbin.blog/xslt-call-java-method/</link><pubDate>Mon, 10 Jan 2011 05:30:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-call-java-method/</guid><description>
曾经有两篇介绍了在 XSLT 里如何调用 C# 或 Js 写的函数，其中用到了与微软相关的，像：xmlns:msxsl="urn:schemas-microsoft-com:xslt",  &amp;lt;msxsl:script implements-prefix="unmifn" language="C#"&amp;gt;。回到了 Java 环境同样得考虑在 XSLT 中如何调用 Java 的方法，毕竟在 XSLT 外处理内容要方便许多。&lt;/p&gt;
&lt;br/&gt;
参考了一些网上的文章，大多讲的不怎么好理解与应用，未尝试之前不免让人想缩手。其实做起来可以更简单些，两步而已：&lt;br/&gt;&lt;br/&gt;
1） 声明时指定包名和函数前缀&lt;br /&gt;
2） 调用时加上前缀和类名及静态方法，传入参数&lt;br/&gt;&lt;br/&gt;
要想再深入些，可注意有时候 Java 函数应该传入的参数类型是什么？是否能调用非静态方法，public 是最基本的要求，以及通过什么组件来调用的等等。</description></item><item><title>10个能帮你提高工作效率的CSS工具</title><link>https://yanbin.blog/10-productivity-enhancing-css-and-css3-tools-and-generators/</link><pubDate>Sat, 08 Jan 2011 20:49:26 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/10-productivity-enhancing-css-and-css3-tools-and-generators/</guid><description>
CSS工具可以有效地节省你设计网站的时间，因为它们可以自动生成一些代码，从而提高你的工作效率。这篇文章中，我们给你推荐了10个能够帮助您提高工作效率的CSS工具：&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://css3please.com/" target="_blank"&gt;CSS3 Please&lt;/a&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2011/10-productivity-enhancing-css-and-css3-tools-and-generators/150307bb-85d8-3e64-862e-42726215f707.jpg"
width="500px"
/&gt;</description></item><item><title>灵活控制 Hibernate 的日志或 SQL 输出，以便于诊断</title><link>https://yanbin.blog/hibernate-log-sql-log4j-slf4j/</link><pubDate>Thu, 06 Jan 2011 06:45:45 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-log-sql-log4j-slf4j/</guid><description>
我们在使用 Hibernate 时一般只会关注是否显示生成的 SQL 语句，不过有些时候还不够。默认时 Hibernate 执行的 SQL 语句是打印在控制台上的，它也可以配置为输出给 Log4J 或是 Log4Back，还能显示出更详细的参数和取值信息。这里简单讲来。&lt;/p&gt;
&lt;br/&gt;
Hibernate 的配置文件 hibernate.cfg.xml 里提供有三个有关显示 SQL 的配置项，如果是与 Spring 联合，也可以配置到 Spring 的配置中。它们的取值是 boolean 值。&lt;br/&gt;&lt;br/&gt;
1) hibernate.show_sql - 是否显示所生成 SQL 语句，我们最常和它打交道&lt;br /&gt;
2) hibernate.format_sql - 是否格式化生成的 SQL 语句，增加可读性，不然全挤在一行&lt;br /&gt;
3) hibernate.use_sql_comments - 是否显示注释，用以指出什么操作产生的 SQL 语句，相比上面两条而言，这个配置会稍稍陌生些&lt;br/&gt;&lt;br/&gt;
 来看看加了上面三条配置后产生的效果，执行了 Hibernate 查询后，在控制台上产生如下输出：</description></item><item><title>显示出 Hibernate 查询语句的绑定参数值</title><link>https://yanbin.blog/show-parameters-for-hql/</link><pubDate>Tue, 28 Dec 2010 11:03:15 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/show-parameters-for-hql/</guid><description>
在使用 Hibernate 进行数据库操作的时候可以显示出所执行的 SQL 语句，相信用 Hibernate 的兄弟姐妹们都知道怎么设置。就设置 hibernate.show_sql  或者 show_sql 属性为 true 就行，在 hibernate.cfg.xml 或者与 Spring 集成时配置在 Spring 的配置文件中。再就是要让显示的 SQL 语句是格式化的，就配置 hibernate.format_sql 或是 format_sql 为 true，否则总在一行中执行。Hibernate 显示的 SQL 语句好像是通过 System.out.println() 直接输出的。&lt;/p&gt;
&lt;br/&gt;
Hibernate 大部份时候是通过 PreparedStatement 来执行 SQL 语句的，它的好处是可预处理、被缓存，可防止注入。只是这样让 Hibernate 执行的 SQL 语句输出到控制台是带问号的，如：</description></item><item><title>两个非常实用的获取 Spring Bean 的 Struts Action 基类方法</title><link>https://yanbin.blog/two-useful-getbean/</link><pubDate>Tue, 28 Dec 2010 09:30:16 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/two-useful-getbean/</guid><description>
我们在实际的 Spring+Struts 的项目中一般都会定义一个 Struts Action 基类。基类中当然是提供一些公共的方法了，用上了 Spring 的话，里面放几个 getBean() 方法就会方便许多。Web 项目多数时候我想还是在 Action 中有经常性的获取 Bean 实例，如果有用在别处的话，也可以借鉴。&lt;/p&gt;
&lt;br/&gt;
在没有泛型支持下，我们从 Spring Context 中获取 Bean 要强型转换类型，如：&lt;br/&gt;&lt;br/&gt;
SomeClass some = (SomeClass)context.getBean("someClass");&lt;br/&gt;&lt;br/&gt;
上面对应的方法原型是 Object getBean(String beanName);&lt;br/&gt;&lt;br/&gt;
从 jdk 1.5 开始有泛型了，可以加一个泛型,省却了转型步骤，看起来要舒服一些，如：</description></item><item><title>Maven使用第三方jar文件的两种方法</title><link>https://yanbin.blog/maven-3-methods-to-use-3rd-jar/</link><pubDate>Thu, 23 Dec 2010 23:26:42 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maven-3-methods-to-use-3rd-jar/</guid><description>
在Maven中，使用第三方库一般是通过pom.xml文件中定义的dependency从远程repository中下载该库。但是如果库文件是公司内部的库，或者在本地而不能通过远程repository下载，这种情况下，有两种方法可以满足需求。&lt;/p&gt;
&lt;br/&gt;
方法一：&lt;br/&gt;&lt;br/&gt;
将本地的jar文件安装到本地repository中，具体步骤如下：&lt;br /&gt;
1、准备好本地的jar文件&lt;br /&gt;
2、使用如下命令安装jar文件&lt;br /&gt;
      mvn install:install-file -Dfile=abc.jar&lt;br /&gt;
     -DgroupId=com.mycompany.myproduct -DartifactId=abc&lt;br /&gt;
     -Dversion=1.0 -Dpackaging=jar -DgeneratePom=true&lt;br /&gt;
    其中，－Dfile制定jar文件的位置。执行完该命令后，会在本地repository（通常是$HOME\.m2目录）中出现一个目录（com），在其下会有一个abc-1.0.jar的文件和一个自动产生的pom文件。</description></item><item><title>语法加亮插件 SyntaxHighlighter 的好伴侣，轻便的 TinyMCE 工具按钮</title><link>https://yanbin.blog/unmi-syntax-button/</link><pubDate>Sun, 12 Dec 2010 11:26:22 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-syntax-button/</guid><description>
用 SyntaxHighlighter 有一段时间了，版本从 1.x、2.x，到现在的最新版本是 3.0.83，1.x 和 2.x 相差较大，2.x 到 3.x 未感觉多大的变化。SyntaxHighlighter 原来是在 google 中的项目，&lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;http://code.google.com/p/syntaxhighlighter/&lt;/a&gt;，现在由 &lt;a href="http://www.dbnetsuite.com/shared/track.aspx?code=SH" target="_blank"&gt;DbNetLink&lt;/a&gt; 赞助，项目移至：&lt;a href="http://alexgorbatchev.com/SyntaxHighlighter/"&gt;http://alexgorbatchev.com/SyntaxHighlighter/&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
在 WordPress 中关于应用 SyntaxHighlighter 进行语法加亮有众多的插件支持，搜索 &lt;a href="http://wordpress.org/extend/plugins/search.php?q=syntaxhighlighter" target="_blank"&gt;SyntaxHighlighter&lt;/a&gt; 就知道了。但是为 TinyMCE 辅助的工具按钮却未找到一个较好用的，之前也有介绍过：&lt;a href="https://yanbin.blog/tinymce-syntax-highlighter" target="_blank"&gt;给 TinyMCE 添加一个语法加亮插件的工具按钮&lt;/a&gt;，也是根据某个插件进行修改的。不如之处就是点击按钮后还需与服务端作一次交互，这对于使用国外较慢的空间有些受不了。</description></item><item><title>Web开发者必知的12款jQuery插件[转]</title><link>https://yanbin.blog/useful-jquery-plugins-for-every-project/</link><pubDate>Tue, 07 Dec 2010 20:17:10 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/useful-jquery-plugins-for-every-project/</guid><description>
jQuery插件能够增强网站的可用性，有效地改善用户体验，还可以大大减少创建一个新站点的开发时间。现在的jQuery插件很多，尽可以根据您的项目 要求来选择，不过也有一些插件很好用，几乎各种项目都能够用得上。这里就为您介绍12款开发中最常用的jQuery插件。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1.jQuery BBCode Editor&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
下载地址：&lt;a href="http://www.w3theme.com/jquery-bbedit/" target="_blank"&gt;http://www.w3theme.com/jquery-bbedit/&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
Bbcode简单易用，它是BBS、论坛、Blog等网络应用上的一种常用输入语法。jQuery BbCode Editor提供自定义设置，可以打造出很个性化的bbcode编辑器，它的源代码都可以根据需求灵活修改。</description></item><item><title>如何让 Map 中的值按放入顺序输出</title><link>https://yanbin.blog/map-sorted-output-by-input/</link><pubDate>Tue, 07 Dec 2010 07:20:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/map-sorted-output-by-input/</guid><description>
&lt;span style="color: #ff0000;"&gt;笔者致: 当你打开这个页面的时候, 基本就无需再看后面的内容了, 因为直接用 &lt;code&gt;LinkedHashMap&lt;/code&gt; 就能满足您的需求.  文中对这个问题的探索只是不知道别人早已过河了, 仍然在河里摸着石头. &lt;code&gt;LinkedHashMap&lt;/code&gt; 是 JDK 1.4 加进来的, 之所以有 &lt;code&gt;apache-commons ListOrderedMap&lt;/code&gt; 大约是因为它最早所处的是 JDK 1.4 之前的年代.&lt;/span&gt;&lt;/p&gt;
&lt;hr /&gt;&lt;br/&gt;
在使用 Java 的 Hashmap 时，输出 Map 中的值如何使之按放入的顺序输出来，像 List 那样保持顺序。这里的顺序不是说对集合中的数据进行的排序，要是的话用 TreeMap，像 TreeSet 一样就行。 我们看个例子：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keySet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Slf4j + Logback 怎么过滤掉第三方包中的日志显示</title><link>https://yanbin.blog/slf4j-logback-filter-logger/</link><pubDate>Mon, 06 Dec 2010 06:17:52 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/slf4j-logback-filter-logger/</guid><description>
其实这跟 Slf4J 通用日志框架没什么关系，单单是 Logback 的事，把 Slf4J 扯进来只因它们是对黄金组合。&lt;/p&gt;
&lt;br/&gt;
我们在 log4j 中想要不显示第三方包的非常冗余的日志输出时，只要在 log4j.properties 中写上：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java-properties" data-lang="java-properties"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;log4j.logger.org.hibernate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
这样在 hibernate 里的日志级别在 ERROR 以下的 TRACE、DEBUG、WARN 和 INFO 将不被输出，控制台会比较的干净。&lt;br/&gt;&lt;br/&gt;
Logback 的配置文件是 logback.xml，曾经在里面给 &amp;lt;appender&amp;gt; 加的 &amp;lt;pattern&amp;gt; 是：</description></item><item><title>iText 用 HTMLWorker 转换 HTML 为 PDF 时可设置表格列宽度</title><link>https://yanbin.blog/itext-htmlworker-html2pdf-table-cellwidth/</link><pubDate>Sun, 05 Dec 2010 06:58:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/itext-htmlworker-html2pdf-table-cellwidth/</guid><description>
生成 pdf 的方式有多种，直接用 iText 编码控制产生，fop 转换，不过我觉得很方便的一种方法就是用 iText 的 HTMLWorker 类直接把 HTML 转换成相应的 PDF 文档，HTMLWorker 可支持图片和表格，一定数量的样式控制。这样可以让我们关注到 HTML 页面设计上，至于转换出来的效果就要看母版 HTML 的设计功底了。要知道这个 HTML 中的样式要让 HTMLWorker 可理解的，并非说能支持 CSS2 还是 CSS3。HTMLWorker 能支持的样式还是非常有限，并且还未文档化的，有些多试试，能看看源代码是自然可以让胸中更有数了。&lt;/p&gt;
&lt;br/&gt;
项目中有一个比较迫切的问题是把 HTML 中的表格转换成 PDF 的 PdfPTable 时，无论是在 &amp;lt;td&amp;gt; 上用 width="xxx" 或是 style="width:xxx" 时都看不到宽度的设置效果，iText 总是让生成的 PdfPTable 平均分配单元格宽度的。这对于单元格宽度有过于悬殊的需求是无法妥协的，所以要一种办法来支持自定义单元格宽度，让 width="xxx"  或是 style="width:xxx" 有一种控制能力就行。又得改源代码了，网上找到了是支持 iText 2.1.7 的改法，本篇是针对 iText 5.0.4 的做法，使支持 width="10%" 百分比来控制单元格宽度。</description></item><item><title>Windows 下推荐使用的端口转发(映射/重定向)工具：PassPort</title><link>https://yanbin.blog/windows-port-forwarding-passport/</link><pubDate>Sun, 05 Dec 2010 00:48:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-port-forwarding-passport/</guid><description>
前一篇里 &lt;a href="https://yanbin.blog/windows-linux-port-forwarding" target="_blank" rel="noopener"&gt;Windows 和 Linux 平台下的端口转发工具&lt;/a&gt; 介绍 Windows 下的端口重定向工具有 rinited 和 FPipe，rinited 可以同时应用多个规则，FPipe 一个命令一个规则，它们都是命令行的。&lt;/p&gt;
&lt;br/&gt;
本文中介绍另一款更好用的端口重定向工具 &lt;a href="http://sourceforge.net/projects/pjs-passport/" target="_blank" rel="noopener"&gt;PassPort&lt;/a&gt;, 它是免费开源的，图形界面的，支持 TCP/UDP、配置简单，可配置多组映射规则，可作为 Windows 系统服务运行。&lt;br/&gt;&lt;br/&gt;
下载地址是：&lt;a href="http://sourceforge.net/projects/pjs-passport/"&gt;http://sourceforge.net/projects/pjs-passport/&lt;/a&gt;。安装后运行界面如下：</description></item><item><title>Windows 和 Linux 平台下的端口转发工具</title><link>https://yanbin.blog/windows-linux-port-forwarding/</link><pubDate>Sat, 04 Dec 2010 23:57:35 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-linux-port-forwarding/</guid><description>
这里记录一下我曾经使用过的几个端口转发工具，即端口映射、端口重定向，和 NAT 也是差不多的概念。&lt;/p&gt;
&lt;br/&gt;
Linux 下用过 iptables，&lt;a href="http://www.boutell.com/rinetd/" target="_blank" rel="noopener"&gt;rinetd&lt;/a&gt;；Windows 下用过某个防火墙的 NAT 功能、RemoteAnywhere 的端口重定向、&lt;a href="http://www.foundstone.com/us/resources/proddesc/fpipe.htm" target="_blank" rel="noopener"&gt;FPipe&lt;/a&gt;，还有最近刚找到的 &lt;a href="http://sourceforge.net/projects/pjs-passport/" target="_blank" rel="noopener"&gt;PassPort&lt;/a&gt;。试着去设置一下 Windows 2000 的 NAT  功能，但未成功，还是特定的软件简单易用，下面介绍逐一介绍下。&lt;br/&gt;&lt;br/&gt;
iptables 的功能当然强大，但理解与设置却有点抽象，曾用它进行过透明代理上网，见：&lt;a href="http://unmi.cc/linux-iptables-transparent-proxy" target="_blank" rel="noopener"&gt;Linux 下仅用 iptables 实现为透明代理服务器&lt;/a&gt;。真要用它进行端口重定向太麻烦，会用它当然很方便的，在 Linux  平台下是开箱即用的，很多机器都安装了它。</description></item><item><title>理解 Java 正则表达式怪异的 \\ 和 \\\\，让您见怪不怪</title><link>https://yanbin.blog/understand-java-regex-backslash/</link><pubDate>Mon, 29 Nov 2010 06:40:57 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-regex-backslash/</guid><description>
&lt;p&gt;Java 语言里的几大变革，一为 jdk1.4 引入的正则表达式，jdk1.5 引入的泛型。没有泛型之前有不少人曾想方设法从编译器入手让 Java 支持泛型。说到泛型  Perl 无疑是该方面的佼佼者，虽然我们不要求 Java 的正则表式能像 Perl 那样可以用来写诗，但至少能有 JavaScript 好用些，可是还不如。JavaScript 里 // 两斜线一框就是一个模式，分组和后向引用更方便，当然前面那两家伙是动态的，不太好比。&lt;/p&gt;
&lt;p&gt;复杂的用法不说，且说 Java 的正则表达式在匹配点(.)  和斜杠(\)，表达式要分别写作 \\. 和 &lt;a&gt;\\\\&lt;/a&gt;，难看些，不好理解。幸好还有些人记住了，匹配点(.) 或  {、[、(、?、$、^ 和 * 这些特殊符号要要前加双斜框，匹配 \ 时要用四斜杠，这确实能让你包走天涯的。那么为什么是这样呢，不是一个斜杠、三个或更多呢，所以知其然还要知其所以然，这样才能每次心中有数，方能以一变应万变。</description></item><item><title>本博客前几日又完成一次搬迁--历数博客搬迁之路</title><link>https://yanbin.blog/migrate-again-again/</link><pubDate>Fri, 26 Nov 2010 12:49:47 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/migrate-again-again/</guid><description>
&lt;p&gt;本博客再次完成了一次搬迁，从 &lt;a href="http://bloghost.cn"&gt;BlogHost主机&lt;/a&gt; SoftLayer达拉斯机房 到 加州硅谷T2机房 的搬迁，这一次搬迁相比前两次容易得多，因为仅仅是 WordPress 程序和数据的转移。国为达拉斯的服务器访问起来较慢，所以需搬动一下提提速。&lt;/p&gt;
&lt;p&gt;主要是国内的主机贵且对备案什么的很反感，所以一直没考虑用国内的主机，不然国内主机供国人访问是要快得多。虽然可以曲线方式进行备案，但基于现在的白名单政策，仍然会让人焦虑，每加一个二级域名都得通知一声机房。那等于是即使域名解析支持泛域名解析，但接入到机房这一关也是白搭。&lt;/p&gt;
&lt;p&gt;所以只好在国外主机里挪来挪去，看以后考虑香港、马来西亚也是个不错的选择。</description></item><item><title>用 CSS 的伪类 hover 轻易实现 js 的 mouseover/mouseout 功能</title><link>https://yanbin.blog/css-a-hover-js-over-out/</link><pubDate>Fri, 26 Nov 2010 10:29:47 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/css-a-hover-js-over-out/</guid><description>
原来做过一个在 html 表格中，当鼠标悬停在某行时，该行出行操作按钮，其他行的操作按钮隐藏。那时候的实现思想是当 onmouseover 在某行时，循环所有行，全隐藏掉按钮，再显示该行的。偶尔还得用下 onmouseourt 事件，现在想来这种实现会很影响页面效率的。&lt;/p&gt;
&lt;br/&gt;
前几日看 WordPress 的后台列表，对相应记录的操作也使用了该功能，鼠标停在哪一行就只出现该行的可操作按钮，曾以为是用 js 实现的，看到要隐现的元素是 &amp;lt;div class="row-actions"&amp;gt;edit|trash...&amp;lt;/div&amp;gt;，还在 js 文件里搜索过 row-actions 字符串。忙了一会儿才把目光转移到了 css 中找实现，在 wp-admin/css/wp-admin.css 中找到像下面的样式:</description></item><item><title>让 All in One SEO Pack 插件适用于截取中文描述</title><link>https://yanbin.blog/all-in-one-seo-pack-support-chinese/</link><pubDate>Wed, 24 Nov 2010 05:47:48 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/all-in-one-seo-pack-support-chinese/</guid><description>
建议看过这里来 &lt;a title="WordPress 插件 All in One SEO Pack 生成中文描述的解决" href="http://unmi.cc/wordpress-all-in-one-seo-pack-chinese-description-patch/" target="_blank" rel="noopener"&gt;WordPress 插件 All in One SEO Pack 生成中文描述的解决&lt;/a&gt;&lt;/p&gt;
&lt;br/&gt;
对于 SEO 来说，Title、Description、Keywords 是很重要的，尤其是前两者，它们直接反应在搜索引擎的条目中。对于英文博客来说 All in One SEO Pack 是做得很不错的，自动截取的描述完全能胜任。然而中文字符集的问题，依然按照英文那样来计算长度截取描述的话，你会发现在搜索引擎出来的条目中描述基本过短，因为 UTF8 的字符基本是一个顶 3。&lt;br/&gt;&lt;br/&gt;
所以要用这个插件来支持中文博客，那就得改，改改更健康。很多人说 All in One SEO Pack 影响了 Baidu 的收录，我不觉得，因为我一直用它，挺好的。&lt;br/&gt;&lt;br/&gt;
下面说说怎么来改它使之更好的支持中文，需要改动的文件只有一个，就是 aioseop.class.php。这里所针对的版本是今天的最新版 1.6.13，有三处要改动。</description></item><item><title>如何在 js 代码中使用 jsp 标签或 Java 代码</title><link>https://yanbin.blog/use-jsp-tag-in-js/</link><pubDate>Wed, 17 Nov 2010 06:01:04 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-jsp-tag-in-js/</guid><description>
JSP 标签还是很方便的，比如 Struts、Spring 等提供给我们的 JSP 标签，可以用它们来获取变量或进行一些计算。比如 struts2 的 &amp;lt;s:url value="/admin/unmi.action"/&amp;gt; 会为我们自动在前面附加上应用上下文，如实际会生成 /testsite/admin/unmi.action。还有更多更方便的标签使用，比如用标签获取到 session 或请求中的数据作为 js 的变量等。 &lt;/p&gt;
&lt;br/&gt;
引申此话题的，其实不光是在 JS 中使用 JSP 标签，可用 JSP 标签的地方当然可以直接写 Java 代码，即 ScriptLet 代码。</description></item><item><title>xampp 错误：Deprecated: Assigning the return value of new by reference is deprecated</title><link>https://yanbin.blog/xampp-php5-3-deprecated/</link><pubDate>Thu, 11 Nov 2010 03:03:40 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xampp-php5-3-deprecated/</guid><description>
Wordpress 3.0.1 之前是在 xampp 1.7.1 中运行的，没什么问题，php 版本为 5.2.9。后来升级 xampp 到 1.7.3 版，打开网站最上方出现提示：&lt;br/&gt;
&lt;br/&gt;
Deprecated: Assigning the return value of new by reference is deprecated in C:\xampp\htdocs\footnoted\wp-content\plugins\simple-tags\inc\client.php on line 1700&lt;br/&gt;
&lt;br/&gt;
这是插件 simple-tags 里的代码，定位到该行是：&lt;br/&gt;
&lt;br/&gt;
$error = &amp;amp; new WP_Error('invalid_taxonomy', __('Invalid Taxonomy'));&lt;br/&gt;
&lt;br/&gt;
而 WP_Error 是用 function 进行定义的：&lt;br/&gt;
&lt;br/&gt;
function WP_Error($code = '', $message = '', $data = '')</description></item><item><title>Spring 3.0.5.RELEASE 版悄无声息的发布了</title><link>https://yanbin.blog/spring-3-0-5-release/</link><pubDate>Mon, 25 Oct 2010 12:13:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-3-0-5-release/</guid><description>
Spring 3.0.5 Release 版在 2010-10-20 悄然发布，真正支持了 Hibernate 3.6 Final。在常去的几个网站都没看到相关信息，而且在 Spring 的官方网站上也是小声细作，好不容易才翻出一个 changelog 出来：&lt;a href="http://static.springsource.org/spring/docs/3.0.x/changelog.txt"&gt;http://static.springsource.org/spring/docs/3.0.x/changelog.txt&lt;/a&gt;。&lt;/p&gt;
&lt;br/&gt;
可在 &lt;a href="http://ebr.springsource.com/repository/app/bundle/version/detail?name=org.springframework.core&amp;amp;version=3.0.5.RELEASE"&gt;http://ebr.springsource.com/repository/app/bundle/version/detail?name=org.springframework.core&amp;amp;version=3.0.5.RELEASE&lt;/a&gt; 下载，&lt;br/&gt;&lt;br/&gt;
假如你是用 Ivy 管理依赖，请添加：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;&amp;lt;dependency org="org.springframework" name="org.springframework.core" rev="3.0.5.RELEASE" /&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
用 Maven 管理依赖的话，pom.xml 加：</description></item><item><title>用 DbUnit 执行 export 导出数据时报 MySQLSyntaxErrorException</title><link>https://yanbin.blog/dbunit-export-from-exception/</link><pubDate>Mon, 25 Oct 2010 11:21:46 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dbunit-export-from-exception/</guid><description>
我是借助于 dbunit-maven-plugin 来使用 DbUnit 的，数据库是 MySql，在使用 &lt;span style="color: #800000;"&gt;mvn dbunit:export&lt;/span&gt; 指令时，总报错：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Embedded error: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'desc from unmi_test_table order by Id' at line 1&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
若是加上 -e 或 -X 参数，如 &lt;span style="color: #800000;"&gt;mvn dbunit:export -e&lt;/span&gt; 时可以看到更详细的异常信息：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Caused by: org.apache.maven.plugin.MojoExecutionException: Error executing export</description></item><item><title>C++ 多重继承和虚继承的内存布局[转]</title><link>https://yanbin.blog/cpp-multi-extend-memory-layout/</link><pubDate>Sun, 24 Oct 2010 11:35:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cpp-multi-extend-memory-layout/</guid><description>
这篇文章主要讲解虚继承的C++对象内存分布问题，从中也引出了dynamic_cast和static_cast本质区别、虚函数表的格式等一些大部分C++程序员都似是而非的概念。原文见&lt;a href="http://www.phpcompiler.org/articles/virtualinheritance.html"&gt;这里&lt;/a&gt;(By Edsko de Vries, January 2006)&lt;/p&gt;
&lt;br/&gt;
敬告: 本文是介绍C++的技术文章，假定读者对于C++有比较深入的认识，同时也需要一些汇编知识。&lt;br/&gt;&lt;br/&gt;
本文我们将阐释GCC编译器针对多重继承和虚拟继承下的对象内存布局。尽管在理想的使用环境中，一个C++程序员并不需要了解这些编译器内部实现细节，实际上，编译器针对多重继承(特别是虚拟继承)的各种实现细节对于我们编写C++代码都或多或少产生一些影响(比如downcasting pointer、pointers to pointers 以及虚基类构造函数的调用顺序)。如果你能明白多重继承是如何实现的，那么你自己就能够预见到这些影响，进而能够在你的代码中很好地应对它们。再者，如果你十分在意的代码的运行效率，正确地理解虚继承也是很有帮助的。最后嘛，这个hack的过程是很有趣的哦:)</description></item><item><title>用 dbunit-maven-plugin 来管理你的测试数据</title><link>https://yanbin.blog/dbunit-maven-test-data/</link><pubDate>Sat, 23 Oct 2010 02:14:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dbunit-maven-test-data/</guid><description>
单元测试有人写过，也有人没做过，数据库的 dbunit 的用的人应该更少了，它可以用来给你做测试准备数据。一般我们做测试会在一个测试数据库中不停的测，自然会累积许多垃圾数据，给单元测试会造成不便，功能测试倒无太紧要。如果我们想在单元测试的时候有一份干净的数据，有个做法是搞个备用的数据库，测试前导到测试库的，或用某些数据库的导入导出功能。&lt;/p&gt;
&lt;br/&gt;
这里我们来看 dbunit 怎么实现准备测试数据的，它可以用来导出数据库数据到数据文件中，从数据文件中导入干净的数据到数据库中，比较数据库与数据文件、或增量的插入记录等等。</description></item><item><title>把 Lucene 索引数据存到数据库表中</title><link>https://yanbin.blog/store-lucene-index-in-database/</link><pubDate>Tue, 19 Oct 2010 08:27:49 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/store-lucene-index-in-database/</guid><description>
一般我们都是把 Lucene 索引存放在文件系统中，大数据量时会考虑用分布式文件系统，如 Hadoop 及 MapReduce、GFS 的应用。也许你会想我们有数据库作为集中的数据存储地，是否可以把 Lucene 索引文件存储到关系型数据库中。可以这么做，不过好像性能上有些问题，本文就此也作这样一个尝试。&lt;br/&gt;
&lt;br/&gt;
看 &lt;a href="http://wiki.apache.org/lucene-java/LuceneFAQ"&gt;http://wiki.apache.org/lucene-java/LuceneFAQ&lt;/a&gt;&lt;br/&gt;
Can I store the Lucene index in a relational database?&lt;br/&gt;
Lucene does not support that functionality out of the box, but several people have implemented &lt;a href="http://www.google.com/search?q=jdbcdirectory%20lucene'" target="_blank"&gt;JdbcDirectory's&lt;/a&gt;. The reports we have seen so far indicate that performance with such implementations is not great, but it is doable.</description></item><item><title>在 Java Web 项目中选择使用 Slf4J 通用日志框架</title><link>https://yanbin.blog/java-web-slf4j-logback/</link><pubDate>Sat, 16 Oct 2010 12:00:13 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-web-slf4j-logback/</guid><description>
在前一篇日志：&lt;a href="https://yanbin.blog/ssh-ssi-slf4j-logback" target="_blank" rel="noopener"&gt;Java 流行框架(Spring/Struts2/Hibernate/iBatis)都在使用什么日志组件&lt;/a&gt;，介绍了当前流行框架 Spring、 Struts2、 Hibernate、iBatis(Mybatis) 分别使用的是什么通用日志组件：
&lt;br/&gt; &lt;br/&gt;
Spring 3.0.4：Commons-Logging&lt;br /&gt;
Struts 2.2.1：间接的优先支持 Commons-Logging&lt;br /&gt;
Hibernate 3.6.0：直接的使用 Slf4J&lt;br /&gt;
iBatis/MyBatis 3.0.2：间接的优先使用 Slf4J&lt;br/&gt;&lt;br/&gt;
再考虑 Slf4J 以及 Logback 的优越性，性能得到的改善，多 JVM 写同一日志文件都无妨，方便运用于 OSGI 环境，自动压缩日志，参数化。所以个人认为在 SSH 或 SSI 项目中推荐用&lt;br/&gt;&lt;br/&gt;
&lt;u&gt;Slf4J + Logback&lt;/u&gt; 对于 Hibernate 和 iBatis/MyBatis 直接走这条路&lt;br /&gt;
&lt;u&gt;jcl-over-slf4j + Logback&lt;/u&gt; 对于 Spring 和 Struts2，需用用 jcl-over-slf4j 把 Commons-Logging 桥接到 Slf4J，然后 Logback。</description></item><item><title>Java 流行框架(Spring/Struts2/Hibernate/iBatis)都在使用什么日志组件</title><link>https://yanbin.blog/ssh-ssi-slf4j-logback/</link><pubDate>Sat, 16 Oct 2010 06:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ssh-ssi-slf4j-logback/</guid><description>
做 Java Web 应用我想多会采用这两种框架组合，SSH 和 SSI，其实我是不太喜欢用这两种缩写方式：&lt;br/&gt;
&lt;br/&gt;
SSH = Spring + Struts + Hibernate，SSH 由久已久所代表的是 Secure Shell，像 https&lt;br/&gt;
SSI = Spring + Struts + iBatis，SSI 在很久以前就是 Server Side Include，像 shtml，而 iBatis 已更名为 MyBatis 了。&lt;br/&gt;
&lt;br/&gt;
要是在以往做这样的 Java 项目都会用 Commons-Logging + Log4J 来作为日志框架，因为多数组件用的是 Common-Logging 通用组件，而那时 Log4J 控制实际的日志输出也是最强的。&lt;br/&gt;</description></item><item><title>使用 SiteMesh2 时为每个页面指定不同的 body id 值</title><link>https://yanbin.blog/sitemesh2-different-body-id/</link><pubDate>Wed, 13 Oct 2010 12:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/sitemesh2-different-body-id/</guid><description>
SiteMesh 是一个很好的 Java Web 模板框架，它的思维方式与通常的模板实现(比如 Tiles 或 include)有些区别。SiteMesh 模板一旦配置好后，在编辑内容页时可以忽视它的存在。可以它是被动，内容页中的输出被&lt;strong&gt;抽取&lt;/strong&gt;到模板中，一般的模板为主动的把自己某部分内容去&lt;strong&gt;填充&lt;/strong&gt;模板。&lt;/p&gt;
&lt;br/&gt;
在站点中应用样式或 JS 代码时很可能会依赖于每个页面的 &amp;lt;body&amp;gt; 的 id，比如：&lt;br/&gt;&lt;br/&gt;
body#archive primary{....} /** 样式 **/&lt;br /&gt;
$("body#archive").bgColor='red'; //JS
所以就要求每个页面有不同的 body id 值。在很多 SiteMesh 的例子中页面渲染后 body 都没有 id 值的，或是相同的 id 值。</description></item><item><title>修正 FeedBurner Stats by DevMD.com 插件统计图中的月份显示错误</title><link>https://yanbin.blog/wordpress-feedbuner-stats-month-issue/</link><pubDate>Tue, 12 Oct 2010 05:01:40 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-feedbuner-stats-month-issue/</guid><description>
Feed 的烧制(代理) 程序还是 FeedBurner 有名，结果也是因为它让消息传得太快了，所以被墙了，才有了国内的 FeedSky。如同 Google 当年的遭遇一样才成就了百毒。&lt;br/&gt;
&lt;br/&gt;
FeedBurner 在自由的国家里用得还是很广泛，所以你的客户群是那些人的话，还是值得去使用 FeedBurner。假如是用 WordPress 建站的话，你可以安装 &lt;a href="http://wordpress.org/extend/plugins/feedburner-stats-by-devmdcom/" target="_blank"&gt;FeedBurner Stats by DevMD.com&lt;/a&gt; 插件来查看 Feed 的订阅情况，以统计图形直观的显示。&lt;br/&gt;
&lt;br/&gt;
这里是它的界面截图：&lt;a href="http://wordpress.org/extend/plugins/feedburner-stats-by-devmdcom/screenshots/"&gt;http://wordpress.org/extend/plugins/feedburner-stats-by-devmdcom/screenshots/&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
但是对于该插件的当前版本 0.1.6 的统计图在月份显示时还有个 Bug，就是月份相差 1，比如当前是 10 月，图中却显示为 November，11 月。&lt;br/&gt;</description></item><item><title>WordPress 3.0.1 中被迫用 fetch_feed() 替换 fetch_rss() 来获取 rss 数据</title><link>https://yanbin.blog/wordpress-fetch_rss_to_fetch_feed/</link><pubDate>Wed, 06 Oct 2010 04:17:15 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-fetch_rss_to_fetch_feed/</guid><description>
前一个多月中都是用 fetch_rss() 函数 (定义在 wp-includes/rss.php 中) 来获取外部 RSS 数据的，代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch_rss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://feed.unmi.cc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$articles&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;=&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;link&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//更多元素可用 var_dump($article) 查看
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
当然，你可以用 wp_rss() 或 get_rss() 直接在页面上显示获取的 rss 记录，我是需要处理 $articles 数组变量的。</description></item><item><title>软件编程21法则[译稿][转]</title><link>https://yanbin.blog/21-laws-of-computer-programming/</link><pubDate>Tue, 05 Oct 2010 21:34:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/21-laws-of-computer-programming/</guid><description>
任何一个有经验的程序员都知道，软件开发遵循着一些不成文的法则。然而，如果你不遵循这些法则也并不意味着会受到惩罚；相反，有时你还会获得意外的好处。下面的就是软件编程中的21条法则：&lt;br/&gt;
&lt;br/&gt;
任何程序一旦部署即显陈旧。&lt;br/&gt;
修改需求规范来适应程序比反过来做更容易。&lt;br/&gt;
一个程序如果很有用，那它注定要被改掉。&lt;br/&gt;
一个程序如果没用，那它一定会有很好的文档。&lt;br/&gt;
任何程序里都仅仅只有10%的代码会被执行到。&lt;br/&gt;
软件会一直膨胀到耗尽所有资源为止。&lt;br/&gt;
任何一个有点价值的程序里都会有至少一个bug。&lt;br/&gt;
原型完美的程度跟审视的人数成反比，反比值会随着涉及的资金数增大。</description></item><item><title>几款 WordPress 客户端的功能介绍(一)</title><link>https://yanbin.blog/introduce-to-wordpress-client-1/</link><pubDate>Sat, 25 Sep 2010 01:32:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/introduce-to-wordpress-client-1/</guid><description>
&lt;p&gt;其实一直以来，我写博客都比较喜欢在线来写，省得装那些客户端，主要是曾经试用 Windows Live Writer 写出的东西垃圾代码太多，且文件上传时目录不好控制。
不能因为一次不愉快的经历就害怕十年吧。为什么又再次提起博客的离线客户端呢？因为现在风声吃紧，特别是对于使用国外空间的，你非常的谨慎的写着，
该避讳的地方也避了，还是不知哪个字成敏感词，一提交，网络被重置，完蛋了，抓逛也无济于事，一切重来，如果你没有事先 Ctrl + C 的话。
所以我现在网上养成了一个良好习惯，写一段话在提交之前必按 Ctrl + C。
&lt;/p&gt;
&lt;p&gt;找个博客的客户端程序是十分必要的，由于我的是用的 WordPress，所以在此介绍几款支持 WordPress 的客户端。
有了 WordPress 客户端不光可以不受胡萝卜的侵扰，还能让你无论何时何地自由的书写，连上线后即发布，上传图片文件等事也省了许多麻烦，还有更多的在线编辑不能提供的功能。
&lt;/p&gt;</description></item><item><title>用 iTextSharp 修改 PDF 文件的元信息(MetaData)</title><link>https://yanbin.blog/itextsharp-edit-pdf-metadata/</link><pubDate>Tue, 21 Sep 2010 18:52:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/itextsharp-edit-pdf-metadata/</guid><description>
要向另一个服务程序传送 PDF 文件，同时要用另一个文件来给 PDF 文件加入一些描述信息，描述都很短，这时候那个描述文件其实是多余的，完全可以把这些描述信息写到 PDF 文件自身中去。比如在查看 PDF 文件属性 PDF 标签页中可以看到的 Title/Author/Subject/Keywords/Created/Modified/Application 这些信息都可以通过 iTextSharp 来编辑。其实不止这些，你可以加入任何命名的属性，只是它们不在属性窗口中显示而已。而且这些属性存的数据好像也不受限的，当然太大了直接就是增加 PDF 文件的体积，如果是可视属性的话，在查看文件属性时显示也要变慢，所以尽量还是在 PDF 的 MetaData 中存简短的信息。</description></item><item><title>定制 WordPress 的 Rss 输出到 &amp;lt;!--more--&amp;gt;</title><link>https://yanbin.blog/wordpress-rss-show-to-more/</link><pubDate>Sat, 18 Sep 2010 13:11:17 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-rss-show-to-more/</guid><description>
&lt;a href="https://yanbin.blog/wordpress-rss-show-to-more"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/wordpress-rss-show-to-more/wordpress-rss.png"
width="227px"
/&gt;
&lt;/a&gt;
看来我真是浸淫在了 WordPress 里，俗称工欲善其事，必先利其器，也不知道想把这一器磨成多利。大约和当初结识 Joomla 时同样的境遇，难道要变大杀器不成？接连都几篇与 WordPress 相关的内容，反而把主业给抛在了一边。&lt;/p&gt;
&lt;br/&gt;
好了，都是实际的问题，我的文章里因为有一些代码什么的，所以选择了用 &amp;lt;!--more--&amp;gt; 的形式显示片断，而不是用的摘要，虽然摘要也有可视编辑器，但还是用
&amp;lt;!--more--&amp;gt; 一点鼠标方便些。但是问题又有了，文章列表页里是显示了 more 前面的内容，但 RSS 不好控制，因为文章没有摘要，
所以在后台 Settings/Reading 里无论选择 Rss 输出是 Full Text 还是 Summary 都会以全文的方式输出。选择 Summary 时，WordPress
看到没有填写文章摘要就会代之以全文。</description></item><item><title>《Blog 启示录- WordPress 博客建设与经营》观感 -- 续</title><link>https://yanbin.blog/blog-wordpress-book-reading/</link><pubDate>Fri, 17 Sep 2010 22:11:58 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/blog-wordpress-book-reading/</guid><description>
客观的说，阅读本书的收获并不大，学了些东西，点点滴滴，零零碎碎的。上回话说到何处，今天接着记录。&lt;br/&gt;
&lt;br/&gt;
通过我们在 General Settings 里都是把 WordPress address (URL) 和 Site address (URL) 设置成一样的，但它们可以不一样的。比如说 wordpress 整个目录传到了虚拟主机中，默认的你要用 http://unmi.cc/wordpress 的方式来访问，这当然不太好看，你希望用 http://unmi.cc 的 URL 来访问，可以用的做法是把 wordpress 下的文件放在虚拟主机根目录中。&lt;br/&gt;
&lt;br/&gt;
另一种做法是仍然让保留 wordpress 目录，把其中的 index.php 以及 .htaccess 两个文移到根目录下，再编辑这个 index.php 文件，把其中的</description></item><item><title>WordPress 里要某功能时，你会去找插件还是修改代码</title><link>https://yanbin.blog/plugin-or-modify-code/</link><pubDate>Fri, 17 Sep 2010 10:40:27 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/plugin-or-modify-code/</guid><description>
用 WordPress 建站当初，每每想到要某一功能时首先想到的是有没有相关的插件，而不去思考可通过改哪里的代码来实现。因而造成插件的数量日渐膨胀，直接 40 多个插件同时绽放，还曾引以为豪。&lt;br/&gt;
&lt;br/&gt;
最初的想法是尽量不去修改 WordPress 的代码，用第三方插件，待到以后升级 WordPress 本身时会很方便，不用担心被覆盖的危险。还有就是老觉得配置插件比改代码更易于操作。但是忽略了主题文件也是 WordPress 外围的元件，改改主题文件中的代码又何妨，而且更多时候也只需要改主题中的某个文件。&lt;br/&gt;
&lt;br/&gt;
找第三方插件的结果是，数量多的话肯定要拖累 WordPress，因为不是用 Action 就是用 Filter 来拦截。到现在感觉慢了要整理插件的时候，</description></item><item><title>《Blog 启示录- WordPress 博客建设与经营》观感</title><link>https://yanbin.blog/cn-wordpress-book/</link><pubDate>Wed, 15 Sep 2010 09:19:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cn-wordpress-book/</guid><description>
偶然的一天，在书城里逛，并且在网络分类里极不显眼的地方发现了这本书 《Blog 启示录 WordPres 博客建设与经营》，拈起来翻了翻，可以说里面有 3/4 的内容是我知晓的。但是冲着对 WordPress 的那份感情，以及 WordPress 内在的美感，终是把它带回了家。一为收藏第一本 WordPress 书籍，二为如有他人有这方兴致，亦可借阅，第三就是我想总还是能从中获得我想要的东西，也算是梳理一下。&lt;br/&gt;
&lt;br/&gt;
拿回来几天时光一字不落的就把它给看完了，顺便记下些对我来说有意义的东西。被略过的内容并非就是它的不足，毕竟它的读者是广大的不同层次的博主们。</description></item><item><title>用 DMSGuestbook 给你的 WordPress 打造一个留言本</title><link>https://yanbin.blog/use-dmsguestbook/</link><pubDate>Tue, 14 Sep 2010 13:01:24 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-dmsguestbook/</guid><description>
不借助于任何插件也可以做个留言本，那就是建个 Page, 直接使用它的评论功能即可，而且给评论加上 Ajax 功能、WYSIWYG、引用、回复、留言分页等功能也可以做的很漂亮。但对于后台单独查看这一类留就没那么方便，因为与其他的文章评论在一起的。为了体现出一定程序的 Professional，最好找个第三方的留言本插件，经过对比再三，最终向大家推介 &lt;a href="http://wordpress.org/extend/plugins/dmsguestbook/" target="_blank"&gt;DMSGuestbook&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
在 WordPress 的插件页面，搜索 guestbook 结果其实很少
&lt;a href="http://wordpress.org/extend/plugins/guestbook-generator/" target="_blank"&gt;Guestbook Generator&lt;/a&gt;，
&lt;a href="http://wordpress.org/extend/plugins/gwolle-gb/" target="_blank"&gt;Gwolle-GB&lt;/a&gt;，
&lt;a href="http://wordpress.org/extend/plugins/wordpress-guestbook/" target="_blank"&gt;WordPress Guestbook&lt;/a&gt;。
&lt;a href="http://wordpress.org/extend/plugins/guestbook-generator/" target="_blank"&gt;Guestbook Generator&lt;/a&gt;
 没什么好说的，与 Page 评论差不多，中间那款还能选些风格，最后那个有些简陋。</description></item><item><title>用 Add Post URL 给文章加上版声明，并附加排除 ID 的功能</title><link>https://yanbin.blog/add-post-url-exclude-id/</link><pubDate>Mon, 13 Sep 2010 13:57:55 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/add-post-url-exclude-id/</guid><description>
就像是给邮件增加签名一样，我们有时候(可能多数时候) 也需要给日志的前或后附上固定的内容。比如文后加个本文链接、版权声明什么的，你肯定不会想在每篇文章后手工加这么一段，那会死人的。
这就是这里要介绍的 &lt;a href="http://wordpress.org/extend/plugins/wp-posturl/" target="_blank" rel="noopener"&gt;
Add Post URL&lt;/a&gt; 插件，它可为你在&lt;strong&gt;文前或文后&lt;/strong&gt;加上一段 HTML 代码，显然你不光可用它来加版权声明，甚至是外挂文章前后的广告。
实现上了解过 WordPress 插件的应该能反应到它是用 the_content 过滤器来实现的。</description></item><item><title>会有多少人去设置 WordPress 的 secret-key 呢？</title><link>https://yanbin.blog/set-wordpress-secret-key/</link><pubDate>Sat, 11 Sep 2010 10:49:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/set-wordpress-secret-key/</guid><description>
一般我们解压 WordPress 后，再设置完数据库连接的相关信息就开始用了，不知道会有多少人留意过 wp-config.php 中的&lt;/p&gt;
&lt;br/&gt;
define('AUTH_KEY',         'put your unique phrase here');&lt;br /&gt;
define('SECURE_AUTH_KEY',  'put your unique phrase here');&lt;br /&gt;
define('LOGGED_IN_KEY',    'put your unique phrase here');&lt;br /&gt;
define('NONCE_KEY',        'put your unique phrase here');&lt;br/&gt;&lt;br/&gt;
或者到 WordPress 3.0 之后增加到了八个，增加了与上面相应的四个 Salt</description></item><item><title>为 IE6 和非 IE6 选用不同的 CSS 文件</title><link>https://yanbin.blog/ie6-non-ie6-css/</link><pubDate>Sat, 11 Sep 2010 06:49:43 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ie6-non-ie6-css/</guid><description>
为什么拟这样的一个标题，而不是为不同浏览器、或不同的 IE 版本选用不同的样式文件呢，其时那些问题都不难找到。这里我主要是考虑到 IE6 与其他版本 IE(如 IE7, IE8 等)、或其他类型浏览器(像 FireFox, Opera, Chrome 和 Safari) 存在较大的差异性。IE7 与 IE8 已较接近，其他类型浏览器一般都会自动升级到较新版本，由于盗版与 XP 的因素，在国内使用 IE6 还奇怪的多。&lt;/p&gt;
&lt;br/&gt;
所以在样式布局方面就有必要区分出 IE6 与非 IE6，让他们选用不同的样式文件来进行界面排版，这就是产生本文的切实目的。另外在其他非 IE6 浏览器中可以通过 CSS Hack 技术来在同一个 CSS 文件里进行统一表现。&lt;br/&gt;&lt;br/&gt;
当然、仍旧是使用条件注释的方式，就像许多编程语言的条件编译，也可说是 HTML 中的条件预</description></item><item><title>为已有的 WebForm 项目引入 Asp.Net MVC 框架</title><link>https://yanbin.blog/webform-asp-net-mvc-support/</link><pubDate>Fri, 10 Sep 2010 19:31:52 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/webform-asp-net-mvc-support/</guid><description>
Asp.Net 的项目多数还是应用的 WebForm 进行开发，MVC 框架随着在其他语言中的兴起，也进入了 Asp.Net 领域，那就是 Asp.Net MVC，现在的正式版本是 2.0，已经出了 3.0 Preview 版的。&lt;/p&gt;
&lt;br/&gt;
WebForm 对于小项目的快速开发还是很方便的，对应对业务逻辑复杂而庞大的项目时，分层就很有必要的，MVC 恰到好处的层次结构，让人艳羡的。除此之外，应用了 MVC 框架的项目可测试性是 WebForm 无法比拟的，开始可以很好的进行单元测试了。</description></item><item><title>XSLT 中用 JavaScript 自定义函数处理参数</title><link>https://yanbin.blog/xslt-javascript-function-parameter/</link><pubDate>Wed, 08 Sep 2010 07:23:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-javascript-function-parameter/</guid><description>
曾经写过一篇 &lt;a href="https://yanbin.blog/xslt-csharp-jscript-vb-function" target="_blank" rel="noopener"&gt;XSLT 文件中使用C#/JScript/VB 自定义函数&lt;/a&gt; 怎么用 C#/JScript/VB 在 XSL/XSLT 中自定义函数。那时候原本想直接用 JavaScript 来自定义函数，因为 JavaScript 哪台机器上都能跑，但出了些问题，所以实际中是用的 C# 自定义的 XSLT 函数。&lt;/p&gt;
&lt;br/&gt;
这样的问题无论从哪方面讲都一直回避不了，情况是如果在 JavaScript 定义函数中直接把传入的参数返回是没问题的，但要作任何的处理，或者调用 JS 函数都会失败。XSLT 中的自定义函数，不对参数进行加工处理是没有意义的。比如在 XSLT 中写如下函数：</description></item><item><title>C# 如何连接操作 MySQL 数据库(使用 SourceForge 上的驱动)</title><link>https://yanbin.blog/csharp-operate-mysql-2/</link><pubDate>Tue, 07 Sep 2010 00:26:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-operate-mysql-2/</guid><description>
前面一篇 &lt;a href="https://yanbin.blog/csharp-operate-mysql-1"&gt;http://unmi.cc/csharp-operate-mysql-1&lt;/a&gt; 讲了 C# 里用 MySQL 官方驱动怎么去连接操作
MySQL 驱动，就是 MySQL 的 JDBC 驱动有两个一样，针对 .NET 的 MySQL 驱动也有两个。这里介绍的就是 .NET 连接 MySQL 的第二个驱动，来自于 Sourceforge 的开源驱动。&lt;br/&gt;
&lt;br/&gt;
首先，到 &lt;a href="http://sourceforge.net/projects/mysqldrivercs/"&gt;http://sourceforge.net/projects/mysqldrivercs/&lt;/a&gt; 下载，
写此篇时下载的版本是 MySQLDriverCS-n-EasyQueryTools-4.0.1-DotNet2.0.exe。安装，其中带有源代码和例子，只有三个动态库 libmySQL-4.0.dll、
libmySQL.dll、MySQLDriverCS.dll，光看动态库好像不及官版的驱动强大。把安装目录中的 MySQLDriverCS.dll 引入到你的 C# 项目中就可以开始用了。
具体用法还需时刻关注安装目录中的 help\SDK\Documentation.chm 帮助文档。</description></item><item><title>C# 如何连接操作 MySQL 数据库(使用官方驱动)</title><link>https://yanbin.blog/csharp-operate-mysql-1/</link><pubDate>Mon, 06 Sep 2010 00:26:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-operate-mysql-1/</guid><description>
MySQL 以其免费和足够的性能受到很大的青睐，当然对于国内小公司，甚至是大公司如果对版权看得薄，敢冒险的话，随便装个 SqlServer、DB2、Oracle 都行。
对于 SqlServer 数据库，因其与 MS 是一家，自然在 .net 类库中有内建支持，假如 MySQL 就得找第三方的驱动了 -- .net 中多讲 Provider。
在这里我也是作为一个预研专题，记录下 C# 连接 MySQL 的两种方法，分别使用 MySQL 官方的和 SourceForge 上一个开源的 MySQL 驱动。至于 ODBC
的办法，就不提了，觉得意义不大，同样要安装个 MySQL ODBC 驱动，而且 ODBC 又如此之笨拙。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;使用 MySQL 官方区动连接操作 MySQL 数据库&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>FeedBurner 不能用了，还有 FeedSky，附个相关插件</title><link>https://yanbin.blog/use-feedsky-insteadof-feedburner/</link><pubDate>Sun, 05 Sep 2010 06:16:50 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-feedsky-insteadof-feedburner/</guid><description>
FeedBurner 是一个很好的 Rss Feed 托管服务，它能让你详细获悉 Feed 的订阅情况，可是大概是因为 FeedBurner 成了消息快捷传播的通道，让某些人谎了神，而被伟大的墙禁了。针对我们的中文网站多数读者是中文语言的，所以 FeedBurner  这条路是走不太通了，只得找个替代品。&lt;br/&gt;
&lt;br/&gt;
Google 上搜索了下，国内有个 &lt;a href="http://www.feedsky.com" target="_blank"&gt;FeedSky&lt;/a&gt; 做了类似的事情，比起 FeedBurner 可能还没那么专业，或者能很好的适应中国国情。于是我到 FeedSky 上申请了自己的 Feed: &lt;a href="http://feed.feedsky.com/unmi"&gt;http://feed.feedsky.com/unmi&lt;/a&gt;。如果用户是通过这个Feed 的代理来的，我们可以在 FeedSky 上了解到我们网站的 Rss 的阅读订阅情况 。</description></item><item><title>form.action 怎么就不能设置表单的处理页了呢？</title><link>https://yanbin.blog/form-action-undefined-not-support/</link><pubDate>Tue, 31 Aug 2010 12:26:06 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/form-action-undefined-not-support/</guid><description>
一个网页里时常会写多个提交按钮，可以点击不同按钮时分别设置某个隐藏域的值，都是提交到同一个页面，然后在处理页面可以知道应如何区别对待。也可能各自的 onclick 事件中改变 form 的 action 属性指向不同的处理页面后提交表章。在处理表单页面逻辑复杂且无多少相关性的时候，我会选择后一种处理方式。&lt;/p&gt;
&lt;br/&gt;
可以今天碰到一个事情，在点击某个提交按钮，欲用(假设 form 的 id 为 'post')&lt;br/&gt;&lt;br/&gt;
document.getElementById('post').action='http://unmi.cc/some'</description></item><item><title>在 WordPress 的 Header 或 Footer 中加入代码的插件</title><link>https://yanbin.blog/wordpress-add-code-header-footer/</link><pubDate>Sat, 28 Aug 2010 14:13:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-add-code-header-footer/</guid><description>
做了个站点，一下想随时了解站点被访问的情况，或者进行 SEO。比如用 Google-Analytics, 也可能用了其他的站点统计服务，那就要求在页面中植入一段 JS 代码，如果每次有新需求都打开主题的 footer.php 文件来改就有点麻烦些。也存在往 header.php 中加入些信息的情况，比如 meta, css, js 等, 如果有个插件统一来管理有容易得多，省得直接改文件。&lt;br/&gt;
&lt;br/&gt;
去 WordPress 中搜索 Footer 就能看到一些解决此问题的插件，有好几个。类似的东西多了也好让人眼花的，就像商场里买东西，太多的选择反而使人犹豫不决。我找了下，有以下几个插件：</description></item><item><title>给 TinyMCE 添加一个语法加亮插件的工具按钮</title><link>https://yanbin.blog/tinymce-syntax-highlighter/</link><pubDate>Fri, 27 Aug 2010 05:13:35 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tinymce-syntax-highlighter/</guid><description>
&lt;p&gt;前面已有几篇讲了有关 WordPress 的语法加亮的插件，谁让我的日志中代码特别多呢。在此之前我都是要把编辑器切到 HTML 模式，然后写上 &amp;lt;pre class='brush:java'&amp;gt;CodeHere&amp;lt;/pre&amp;gt;，然后回到 Visual 模式，最后贴上代码，麻烦就在于模式切换。如果能给 TinyMCE 编辑器加个按钮，点击后弹出窗口中选择语言类型，贴入代码，不用切换就能加亮你的程序代码就好，这里就是说这个的，当然你要是用 [ code lang='java']CodeHere[/code] 这样的 BBCode 来张贴代码倒简单些。</description></item><item><title>关于 URL 的一些不可不知的知识[转]</title><link>https://yanbin.blog/knowledges-about-url/</link><pubDate>Thu, 26 Aug 2010 06:35:42 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/knowledges-about-url/</guid><description>
&lt;div id="OSChina_News_11105"&gt;
&lt;br/&gt;
URL 设计是 Web 设计中常被忽视的东西，事实上 URL 非常重要，这不仅是一个网页唯一的路径，还涉及到你的站点是否干净，友好。本文讲述 URL 这个司空见惯的 Web 元素中包含的大量不应为忽视的知识，准则与最佳实践。需要注意的是 &lt;a href="http://www.w3.org/TR/uri-clarification/"&gt;W3C 建议使用 URI 取代 URL 一说&lt;/a&gt;。&lt;br/&gt;
&lt;h2&gt;关于 URL 的一些准则&lt;/h2&gt;&lt;br/&gt;
首先是与 URL 有关的一些准则。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;一个 URL 必须唯一地，永久地代表一个在线对象&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
URL 的最基本的使命是唯一地代表 Internet 上的一个对象，URL 必须和 Internet 上的对象一对一匹配。然而</description></item><item><title>为语法高亮插件 SyntaxHighlighter Evolved 进行优化</title><link>https://yanbin.blog/optimize-syntaxhighlighter-evolved/</link><pubDate>Wed, 25 Aug 2010 13:41:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/optimize-syntaxhighlighter-evolved/</guid><description>
前面有一篇 &lt;a href="https://yanbin.blog/wordpress-use-syntaxhighlighter-evolved" target="_blank" rel="noopener"&gt;用 SyntaxHighlighter Evolved 作为自已的代码加亮插件&lt;/a&gt; 介绍了 &lt;a href="http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/" target="_blank" rel="noopener"&gt;SyntaxHighlighter Evolved&lt;/a&gt; 插件。当前所用的版本是 2.3.8,  我现在是用 &amp;lt;pre class='brush:java'&amp;gt;code here&amp;lt;/pre&amp;gt; 的形式来加亮代码，所以必须选上后台的 Load All Brushes 选项，用 [ code lang="js]code here[/code] 会动态的加载所需的 shBrushXxx.js 文件。后一种方式会有些问题，因而这里也是针对前一种方式来进行优化的，因为 Load All Brushes 选项一旦选上，页面将会依次:&lt;/p&gt;
&lt;br/&gt;
&amp;lt;script type='text/javascript' src='...shBrushAS3.js?ver=2.1.364b'&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;script type='text/javascript' src='...shBrushBash.js?ver=2.1.364b'&amp;gt;&amp;lt;/script&amp;gt;</description></item><item><title>介绍一款 jQuery 的 LoadMask(请稍候)的插件</title><link>https://yanbin.blog/jquery-mask-blockui/</link><pubDate>Tue, 24 Aug 2010 08:17:34 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jquery-mask-blockui/</guid><description>
jQuery 框架来说肯定没有哪个有 jQuery 用得那么广泛的，尤其是 jQuery 的插件思想吸引了众多贡献者。常常见到这样的应用，点击按钮时显示一个遮罩层，“请稍候，正在处理中......”。应该有不少类似功能的插件，我现在所用的是 &lt;a href="http://jquery.malsup.com/block/" target="_blank" rel="noopener"&gt;BlockUI&lt;/a&gt; 插件，还有一个叫做 &lt;a href="http://code.google.com/p/jquery-loadmask/" target="_blank" rel="noopener"&gt;LoadMask&lt;/a&gt; 的插件也能实现。&lt;br/&gt;&lt;br/&gt;
据我相比较而言，觉得 BlockUI 插件要比 LoadMask 强大些，它们都可以选择性的遮住整个 Body 或是某个指定的元素。BlockUI 无论是功能还是界面定制性都比 LoadMask 要丰富些，例如 BlockUI 提供了 Overlay 遮罩层的渐入渐出式动画显示，JS 中的样式定制，消息提示的支持，提示层可显示在右上角，还可做出模态窗口，以及结束后进行函数回调。</description></item><item><title>让你的 WordPress 显示日志的点击/阅读数-WP-PostViews</title><link>https://yanbin.blog/wordpress-post-views/</link><pubDate>Sat, 21 Aug 2010 00:34:29 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-post-views/</guid><description>
以前弄 Joomla，现在是纠结在 WordPress 上了。写的日志，想清楚的知道每篇日志受关注的程序，即点击/阅读数，不知道有些主题里是否能支持该功能，
反正我所用的主题是没有的，所以又需要安装一个插件在每篇日志下方显示该日志被阅读的次数。&lt;/p&gt;
&lt;br/&gt;
我找到了 &lt;a href="http://wordpress.org/extend/plugins/wp-postviews/" target="_blank" rel="noopener"&gt;WP-PostViews&lt;/a&gt;，
相关的还有 &lt;a href="http://wordpress.org/extend/plugins/wp-postviews-plus/" target="_blank" rel="noopener"&gt;WP-PostViews Plus&lt;/a&gt;,
&lt;a href="http://wordpress.org/extend/plugins/wp-postviews-plus-widget/" target="_blank" rel="noopener"&gt;WP-PostViews Plus widget&lt;/a&gt;。
许多东西，总是别人会搞些 Plus, Enhanced, Extended 或是 Yet Another 系列。一般来说都会比原始版本要好些，但这里我还是不细究 WP-PostViews Plus 了，
而 WP-PostViews Plus widget 是基于 WP-PostViews Plus 的 Widget。这里介绍下 WP-PostView 的安装与使用。&lt;br /&gt;
下载、安装、选项设置都还简单，关键是它被启用之后，不能马上见到效果，还需要改改你所用主题目录中的几个文件。看看我的站点上应用了它之后的效果：&lt;br/&gt;</description></item><item><title>WordPress 分页插件，WP-PageNavi 或 WP-PageNavi-LightWord</title><link>https://yanbin.blog/wordpress-pagenav-plugin/</link><pubDate>Fri, 20 Aug 2010 11:57:16 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-pagenav-plugin/</guid><description>
在 WordPress 中可以设置每天显示的日志数，并以上一篇、下一篇的链接进行导航。但是日志多了，一个是不能快速跳到哪几篇来浏览，还有就是别人对你有多少篇也没底。所以需要一个分页导航的插件，网上一找就看到了 &lt;a href="http://wordpress.org/extend/plugins/wp-pagenavi/" target="_blank"&gt;WP-PageNavi&lt;/a&gt; 比较受推崇，而 &lt;a href="http://wordpress.org/extend/plugins/wp-pagenavi-lightword/" target="_blank"&gt;WP-PageNavi-LightWord&lt;/a&gt; 只是 WP-PageNavi 针对 LightWord 主题的一个改造版，仅仅是自定义了下样式，其他如选项、代码几乎一样，由于它们使用了同样的函数，所以不能同时被激活。可以先看看它们界面上的一些区别：&lt;br/&gt;
&lt;br/&gt;
WP-PageNavi 的界面截图：
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/wordpress-pagenav-plugin/WP-PageNavi.png"
width="485px"
/&gt;
WP-PageNavi-LightWord 的界面截图：</description></item><item><title>PHP GD 库在 Linux 下使用字体</title><link>https://yanbin.blog/php-gd-linux-font/</link><pubDate>Fri, 20 Aug 2010 07:37:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-gd-linux-font/</guid><description>
现在一般的 PHP 主机都支持 GD 库和 FreeType/TrueType，普通的画字符串的函数 imagestring 只能指定字体大小，不能指定字体名称。像 imagettftext 这样的函数可以指定字体名称和字符串显示的角度，它的原型是：&lt;/p&gt;
&lt;br/&gt;
array imagettftext ( resource $image , float $size , float $angle , int $x , int $y , int $color , string $fontfile , string $text )&lt;br/&gt;&lt;br/&gt;
注意这里的 $fontfile 是个字体文件，不是字体名，我刚开始就搞错了，给它传递的是 "Arial" 或是 "Arial.ttf" 而不能显示出任何东西来。在 Windows 下字体文件一般都在 c:/windows/fonts/ 目录中，所以在 Windows 下用 imagettftext 使用 TrueType 字体时的代码例子如下：</description></item><item><title>PHP 中回调函数是全局的或是类实例时不同写法</title><link>https://yanbin.blog/php-callback-function-deference/</link><pubDate>Thu, 19 Aug 2010 07:00:31 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-callback-function-deference/</guid><description>
PHP 像 JavaScript 一样，很方便使用回调函数，函数名就像一个普通的变量名一样可作为其他函数的参数，即成为一个让宿主函数调用的回调函数。但回调函数的用法依据回调函数是否是全局函数还是一个类中的函数是有区别的。比如 PHP 的preg_replace_callback — 用回调函数执行正则表达式的搜索和替换函数可以接受一个回调函数，我们来看这种区别。&lt;/p&gt;
&lt;br/&gt;
如果是全局的回调函数，那容易，只要写上函数名作为字符串的形式传入就行的，看：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;/**@author Unmi*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;preg_replace_callback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/(\d{7}):(.+)/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;inverse&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1125535:fantasia@sina.com&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Output result: fantasia@sina.com:1125535
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
而如果 preg_replace_callback 函数是在类成员函数中被调用，同时 inverse 回调函数也是该类的成员函数，写法就不一样了。要是仍然写成下面这种方式的话：</description></item><item><title>XSLT 生成 XML 中带 CDATA 的节点</title><link>https://yanbin.blog/xslt-generate-cdata-node/</link><pubDate>Mon, 09 Aug 2010 11:32:57 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-generate-cdata-node/</guid><description>
原来曾经都是用代码或是像 Velocity 样的模板来转换生成 XML的，自从接触了 XSLT 后，已把它当成了 XML 到其他许多数据格式的不二之选了。有时候因为特殊字符等因素，需要转换后的数据在新节点中用 &lt;span style="color: #800000;"&gt;&amp;lt;![CDATA[...]]&amp;gt;&lt;/span&gt; 包裹起来更好看且不易出错，或者还需对生成的 XML 进一步处理时，用 &lt;span style="color: #800000;"&gt;&amp;lt;![[CDATA[...]]&amp;gt;&lt;/span&gt; 会更保险些 。&lt;/p&gt;
&lt;br/&gt;
通常我们在 xslt 文件中会这么写：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;&amp;lt;description&amp;gt;&amp;lt;xsl:value-of select="Description"/&amp;gt;&amp;lt;/description&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
如果原来的 Description 中有特殊字符，如 &amp;lt; 等，转换后，&amp;lt; 会自动变为 &amp;amp;lt; 的，即得到&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800000;"&gt;&amp;lt;description&amp;gt;Hello&amp;amp;lt;Unmi&amp;lt;/description&amp;gt;</description></item><item><title>真是瞎折腾了一回，设置 RSS 中输出的记录数原本如此简单</title><link>https://yanbin.blog/wordpress-setting-rss-number-easy/</link><pubDate>Mon, 09 Aug 2010 02:30:11 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-setting-rss-number-easy/</guid><description>
之前写有一篇 &lt;a href="https://yanbin.blog/wordpress-show-all-rss-data" target="_blank"&gt;让 WordPress 的 Rss 显示更多或所有的日志&lt;/a&gt;, 为了让 WordPress 的默认 Post 和 Comment 的 RSS 输出记录数多于10 条，竟然把这一系列的代码都穿越了一番。确实解决了问题，定位到了 RSS 在查询时 limit 值在哪行代码得到的，当时通过修改 query.php 手工指定输出的记录数。因为 WordPress 代码无法单步调试，所以为了找到这段代码着实费了好一番功夫的。&lt;br/&gt;
&lt;br/&gt;
其实何必来得如此周折呢，在后台里可以直接修改该参数的，wp-admin 中点击 Settings-&amp;gt;Reading，打开 Reading Settings 页面，选项 Syndication feeds show the most recent 就是用来设置输出各种 RSS 记录里显示最近多少条记录，反应到 SQL 就是 limit 0, X  中的 X 值，比如 20 条记录，或者你的博客有 2000 条记录，你设置的此值为 2000 大就输出所有的记录了。</description></item><item><title>用 SyntaxHighlighter Evolved 作为自已的代码加亮插件</title><link>https://yanbin.blog/wordpress-use-syntaxhighlighter-evolved/</link><pubDate>Sat, 07 Aug 2010 08:32:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-use-syntaxhighlighter-evolved/</guid><description>
敝人的博客常常要贴程序代码，因而装个合适的语法加亮的插件是必不可少的。可以预见得到的是基本上各种语法加亮的插件都是源自于 Google 上的一个项目 &lt;a href="http://code.google.com/p/syntaxhighlighter/" target="_blank"&gt;SyntaxHighlighter&lt;/a&gt;. 但具体选用哪一个却要好自体验斟酌的。有几篇很好的文章可参考下：&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://dddspace.com/2010/03/guide-to-find-a-wordpress-syntax-highlighter-that-works-translation.html"&gt;1. WordPress代码高亮插件指南[译文]&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://www.forece.net/post/2454.htm" target="_blank"&gt;2. Wordpress 高亮代码显示插件 – SyntaxHighlighter Evolved&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://hzhawk.cjlu.edu.cn/coding/wordpress-ckeditor-codecolorer/" target="_blank"&gt;3. 让 WordPress 下 CKEditor 支持 CodeColorer&lt;/a&gt;,&lt;br/&gt;
&lt;a href="http://www.seoxj.com/wordpress-wp-codebox-141/" target="_blank"&gt;4. wordpress代码高亮插件wp-codebox使用方法(语法详解)&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
怎么着那都是他人之言了，自己试了才知究意，我就说说 SyntaxHighlighter Evolved 吧，我正用着的。之前在 http://unmi.blogjava.net 的博客我往日志中嵌入代码的方式是 &amp;lt;textarea name=code class=php&amp;gt;&amp;lt;/textarea&amp;gt; 来写的，这样在 textarea 中代码能保持原有的格式，该有的空格都在，那是因为那边的编辑器支持的好，</description></item><item><title>WordPress 站点架设手记，你也可能会用到的插件</title><link>https://yanbin.blog/install-notes-plugin/</link><pubDate>Fri, 06 Aug 2010 16:11:30 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/install-notes-plugin/</guid><description>
说来还得益于工作中的内容就有要维护着一个 WordPress 的网站，必要时开发些插件。之前用 Joomla 架过一个站点(http://www.scalawork.com)，懂得了 WordPress 的精练之后才晓得 Joomla 是何等的复杂。当然功能上 WordPress 与 Joomla 恐怕还是有些差距，但适合自己就是最好的。其间对 Dropal 也略有了解，仍不妨碍我最终选择了用 WordPress，当然对它的熟悉程度也是最重要的一点。&lt;br/&gt;
&lt;br/&gt;
辛苦忙活了一阵总有不少收获，顺便记下了架设这个 WordPress 站点的过程。首先，要买个空间和域名自是不必多说，要想省事或是对某些行规反感的话就买国外的吧，代价就是有点慢。恰如此处，浏览时多加点耐心哦，看来应该是主机的缘故，得换到西部某个机房去试试。</description></item><item><title>让 WordPress 的 Rss 显示更多或所有的日志</title><link>https://yanbin.blog/wordpress-show-all-rss-data/</link><pubDate>Tue, 03 Aug 2010 07:27:45 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-show-all-rss-data/</guid><description>
默认 WordPress 提供的 RSS 链接只能获得最近的十篇日志，如果想得到更多，或是用于某种目的而想通过 RSS 获得所有的日志，那恐怕就得做些手脚了，或许还有更简单的办法，只是我这里改了下源代码中的 limit 查询参数。&lt;/p&gt;
&lt;br/&gt;
关于 WordPress 的 Feed 请参考：&lt;a href="http://codex.wordpress.org/WordPress_Feeds"&gt;http://codex.wordpress.org/WordPress_Feeds&lt;/a&gt;，用以下代码可以输出你实际的 RSS 的 URL:&lt;br/&gt;&lt;br/&gt;
URL for RDF/RSS 1.0 feed      &amp;lt;?php bloginfo('rdf_url'); ?&amp;gt;&lt;br /&gt;
URL for RSS 0.92 feed         &amp;lt;?php bloginfo('rss_url'); ?&amp;gt;&lt;br /&gt;
URL for RSS 2.0 feed          &amp;lt;?php bloginfo('rss2_url'); ?&amp;gt;&lt;br /&gt;
URL for Atom feed             &amp;lt;?php bloginfo('atom_url'); ?&amp;gt;&lt;br /&gt;
URL for comments RSS 2.0 feed &amp;lt;?php bloginfo('comments_rss2_url'); ?&amp;gt;&lt;br/&gt;&lt;br/&gt;
在我的机器上永久链接后是用 &lt;a href="http://unmi/feed/"&gt;http://unmi/feed/&lt;/a&gt; 来访问 rss2，模板将会用 wp-includes/feed-rss2.php 文件。要说定位到这段代码还真不简单。</description></item><item><title>WordPress 应用中如何实现 URL 内部重定向</title><link>https://yanbin.blog/wordpress-custom-url-redirection/</link><pubDate>Sat, 31 Jul 2010 02:00:59 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-custom-url-redirection/</guid><description>
这里要说的 URL 重定向不是指地址栏输入一个 url a，然后地址栏的 url a 被变为 url b 那样的重定向，而是指 url 不变的重定向，也就是重定向过程在服务端默默的帮你完成了。&lt;/p&gt;
&lt;br/&gt;
这个需求是源于开发 WordPress 插件时，然望在前端仅用 &lt;a href="http://unmi/dosth"&gt;http://unmi/dosth&lt;/a&gt; 这种形式来访问自己的插件程序，而不是用 http:/unmi/wp-content/my-wordpress-plugin/dosth.php 这么一长串不友好的 URL。&lt;br/&gt;&lt;br/&gt;
有三种办法：&lt;br/&gt;&lt;br/&gt;
1. WordPress 是极力推荐与 Apache 搭配使用，所以可以在 Apache 的配置文件中用 Alias 指令。其实一开始我是想在应用的 .htaccess 文件中加指令，使用 mod_rewrite 模块来实现 URL 内部重定向功能，但碰到一些问题，所以暂时先在 httpd.conf 中加过 Alias 指定别名来实现过：</description></item><item><title>Asp.Net 让文本框在按回车键时表单不提交</title><link>https://yanbin.blog/textbox-no-enter-submit/</link><pubDate>Wed, 28 Jul 2010 22:16:04 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/textbox-no-enter-submit/</guid><description>
&lt;span style="line-height: normal;"&gt;Asp.Net 的 WebForm 中光标在文本框时按回键默认就会提交表单，多数时候确实带来了很好的用户体验，输入数据后，手不用离开键去乱摸鼠标就能完成表单的提交。但总有例外不想要这种默认行为，一定有的，你遇到了就知道的，比如想在文本框中回车做别的事情，那就要点技巧的。&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;p class="contents_main" style="line-height: normal;"&gt;很多人可能会告诉你对文本框加个事件，回车的时候把 keyCode 由 13 变为 9(其实 keyCode 变为 9 是使回车相当于 Tab 键的功能)，不过还未完事，你并没有阻止事态进一步蔓延，Form 还是提交了。</description></item><item><title>Lucene 应用 WordNet 的同义词典实现同义词检索(C#版)</title><link>https://yanbin.blog/lucene-wordnet-synonym-search-csharp/</link><pubDate>Wed, 14 Jul 2010 22:35:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lucene-wordnet-synonym-search-csharp/</guid><description>
同义词检索应该很多时候会用得上的，举个简单的例子，我们搜索关键字 good 的时候，与 well 和 fine 等的词条也可能是你想要的结果。这里我们不自己建立同义词库，直接使用 WordNet 的同义词库，本篇介绍 C# 版的实现步骤，还会有续篇--Java 版。&lt;br/&gt;&lt;br/&gt;
由于 Lucene 是发源于 Java，所以 C# 的应用者就没有 Java 的那么幸福了，Java 版已经有 3.0.2 可下载，C# 的版本还必须从 SVN 库里：&lt;a href="https://svn.apache.org/repos/asf/lucene/lucene.net/tags/Lucene.Net_2_9_2/"&gt;https://svn.apache.org/repos/asf/lucene/lucene.net/tags/Lucene.Net_2_9_2/&lt;/a&gt; 才能取到最新的 2.9.2 的源码，二制包还只有 2.0 的。</description></item><item><title>在 WordPress 插件管理页面中引入样式或脚本文件</title><link>https://yanbin.blog/wordpress-admin-import-css-js/</link><pubDate>Mon, 12 Jul 2010 02:48:08 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-admin-import-css-js/</guid><description>
在开发 WordPress 插件时，你当然可以在每一个你需要用到样式或脚本文件的页面像传统方式那样把它们引入，比如：&lt;/p&gt;
&lt;br/&gt;
有个插件 hello-unmi, 要在管理页面中引入插件目录的 include 文件夹里的 admin-style.css 和 admin-core.js 文件，可以在每个要用到它们的页面都写上：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/wp-content/plugins/hello-unmi/include/admin-style.css&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/wp-content/plugins/hello-unmi/include/admin-core.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
页面多了会是个麻烦，要说问题也不大，不过我们既然在 WordPress 的插件体系中，应该考虑好利用上下文给我们带来的便利。可以在 'admin_enqueue_scripts' 的 action 扩展中去引入，这样我们就只要写一次就能为所有(或特定)的管理页面引入想要的样式和脚本文件.</description></item><item><title>用 hMailServer 和 AfterLogic Webmail 架设邮件服务器</title><link>https://yanbin.blog/hmailserver-afterlogic-webmail/</link><pubDate>Sat, 10 Jul 2010 12:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hmailserver-afterlogic-webmail/</guid><description>
&lt;p&gt;hMailServer &lt;a href="http://www.hmailserver.com/"&gt;http://www.hmailserver.com/&lt;/a&gt;，Windows 下开源的邮件服务器，带管理端程序，需用第三方邮件客户端链接收发邮件&lt;br /&gt;
MailSuite Lite for Windows &lt;a href="http://www.afterlogic.com/community-editions/mailsuite-lite"&gt;http://www.afterlogic.com/community-editions/mailsuite-lite&lt;/a&gt;，
一个 WebMail 程序，有 asp.net 和 php(可以支持 Linux) 版，支持 Web 管理邮件和 Web 收发邮件，地址簿管理。&lt;br /&gt;&lt;br /&gt;
其实在 &lt;a href="http://www.afterlogic.com"&gt;http://www.afterlogic.com&lt;/a&gt; 网站上还有一些支持多平台的 Pro 邮件软件下载，可以使用 5 用户无限期版来做测试。&lt;br /&gt;&lt;br /&gt;
&lt;br /&gt;&lt;a href="http://hi.baidu.com/kk126/blog/item/4927962f6dd56a351e308927.html"&gt;http://hi.baidu.com/kk126/blog/item/4927962f6dd56a351e308927.html&lt;/a&gt;&lt;/p&gt;</description></item><item><title>关于 NLB(Network Load Balancing) 会话状态--相似性的选择[摘]</title><link>https://yanbin.blog/nlbnetwork-load-balancing-session/</link><pubDate>Wed, 07 Jul 2010 01:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nlbnetwork-load-balancing-session/</guid><description>
but,在节点之间切换的话,web承载的论坛需要重新登录,SQL需要重新登录,Exchange需要,SMB会不会呢,全无状态信息,咋办?&lt;br/&gt;
&lt;br/&gt;
关于你说的状态信息我不清楚。但从NLB的角度来说的话是需要重新登录的。&lt;br/&gt;
&lt;br/&gt;
1. 你说的SMB，是说访问NLB上的共享文件么？这个也是需要重新登录的。&lt;br/&gt;
&lt;br/&gt;
2. 关于NLB的工作机制，其实NLB所做的，只是把网络流量平均分担到不同的NLB 节点。NLB的算法能保证来自于同一个IP和端口的流量总是由同一个NLB节点来响应。</description></item><item><title>WordPress Rewrite / Permalink内部过程分析[转]</title><link>https://yanbin.blog/wordpress-url-rewrite-permalink/</link><pubDate>Mon, 05 Jul 2010 08:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-url-rewrite-permalink/</guid><description>
本文说明WP 对URL rewrite并生成当前请求的过程.&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;关于Query Vars&lt;/strong&gt;这是WordPress全部代码中最重要的变量,所谓的query vars是一系列变量集合. WP通过解析URL设定query vars, 并通过分析query vars值决定显示那些文章,设定标志位等.所谓标志位是WP_Query类中一系列$is_xxx形式布尔成员变量,所有的is_xxx()形式template tag实际上都是返回$wp_query里对应成员变量值. 举例而言,如果当前页面是单篇文章, 则p这个Query Var(以下简称变量)值不为空.(在WP类里空的query var根本不存在,而WP_Query类里如果对应name的query var没有设置,$wp_query-&amp;gt;query_vars['varname']被填充为空值), 如果当前为搜索页, s变量值则为搜索关键字. 如果p和page两个变量都不为空值, 则当前为单篇文章分页页面, 依次类推. Query Vars在WP类($wp)里根据WP_Rewrite里的rewrite规则生成, 在WP_Query($wp_query)类里这些变量被用来建立主循环.</description></item><item><title>自定义 WordPress 插件如何对记录进行分页</title><link>https://yanbin.blog/admin-pagination/</link><pubDate>Mon, 05 Jul 2010 02:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/admin-pagination/</guid><description>
在我们为 WordPress 开发插件的时候，在涉及到列表显示许多记录的时候，肯定要考虑分页显示的问题。自然的，不管是简单性也好，还是统一性，直接参考下 WordPress 自己是怎么实现的，看看 Posts 或 User 管理页面的实现代码，比如 User 管理页面的实现代码在 wp-admin/includes/user.php 中的 WP_User_Search 类。默认实现其实是很好看的，如：&lt;img src="https://yanbin.blog/wp-content/uploads/2010/07/wordpress-post-paging.png" alt="" /&gt;。那我们如何在自己的插件里实现这样的效果呢，比如像这个：</description></item><item><title>让 Putty 保存密码，自动登陆的三种方法</title><link>https://yanbin.blog/putty-auto-login/</link><pubDate>Sun, 27 Jun 2010 07:03:44 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/putty-auto-login/</guid><description>
&lt;p&gt;Putty 基本是我在紧急时候用来登陆 Linux/Unix 终端的不二之选，因其小，开源，界面也非常实用。可是当你要在私有的机器上，
经常性的要登陆很多机器的时候就觉得烦琐了，不光打开一堆的窗口，还要一遍一遍的输入用户名和密码。当然登陆用户名是可以保存的，
我也是最近才注意到这一点的。Putty 不愿去保存密码是出于安全考虑，相信在多数时候确实是非常必要的。
&lt;/p&gt;
&lt;p&gt;于是我选择了 SecureCRT, 它能保存密码，而且是多 Tab 的，把 Session 改成 VT100 Linux 模式，用着一直很顺溜。然而今天却发现在
SecureCRT 终端中使用 vim 不能高亮显示语法，试过多种设置，在 Putty 中却行的，就这样又重新回到了 Putty 的怀抱。要相处得好，
必须更深层次的挖掘它的潜力，其实它也可让你实现自动登陆、甚至是多 Tab 的方式。
方法有三：</description></item><item><title>PHP 图版验证码，我选择 kcaptcha</title><link>https://yanbin.blog/php-image-validator-kcaptcha/</link><pubDate>Thu, 24 Jun 2010 07:44:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-image-validator-kcaptcha/</guid><description>
不管是防小人也好，还是防君子手误也罢，很多地方都需要用到图片验证码来加强安全性。在 PHP 站点上我选择了 Captcha，用起来很简单的。&lt;/p&gt;
&lt;br/&gt;
Captcha 从 &lt;a href="http://www.captcha.ru/en/kcaptcha/"&gt;http://www.captcha.ru/en/kcaptcha/&lt;/a&gt; 下站，当前版本是 &lt;a href="http://www.captcha.ru/kcaptcha.zip?PHPSESSID=emha4vmsich4lc7l0hmqt1rs76" target="_blank" rel="noopener"&gt;KCAPTCHA 1.2.6&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/php-image-validator-kcaptcha/kcaptcha.png"
/&gt;
&lt;br/&gt;
下载后，解压后，可以看到它自己带了例子，就是 index.php 用来产生图片，form_example.php 中引入了图片, 只是 &amp;lt;img src="./?&amp;lt;?php echo session_name()?&amp;gt;=&amp;lt;?php echo session_id()?&amp;gt;"&amp;gt; 的写法有点不好理解，要是写成 &amp;lt;img src="index.php/?&amp;lt;?php echo session_name()?&amp;gt;=&amp;lt;?php echo session_id()?&amp;gt;"&amp;gt; 就好看多了，至少知道 index.php 的内容是个图片。</description></item><item><title>asp:Menu 在 IE8/FireFox 下不正常显示子菜单的解决办法</title><link>https://yanbin.blog/aspmenu-ie8firefox-abnormal/</link><pubDate>Sun, 20 Jun 2010 13:30:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aspmenu-ie8firefox-abnormal/</guid><description>
在 Asp.Net 里用 Menu 控件，如果页面背景白色的，用 IE8 浏览感觉是无法展开子菜单。其实还是有展开的，只是一个白框，把页面背景设置了其他颜色就能看到。Menu 控件在 IE7 下没问题，FireFox 下也不行，反而 Opera 能正常显示。IE8 在趋于标准化的时候，显然是 Menu 控件不符合标准。&lt;/p&gt;
&lt;br/&gt;
有三个解决办法，请参考：&lt;a href="http://blogs.msdn.com/b/giorgio/archive/2009/02/01/asp-net-menu-and-ie8-rendering-white-issue.aspx" target="_blank" rel="noopener"&gt;ASP.NET Menu and IE8 rendering white issue&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
1. 重写 z-index 属性：标准模式下 z-index 默认为 auto。</description></item><item><title>Linq to XML 用一句话读出 RSS 文章列表</title><link>https://yanbin.blog/linq-to-xml-read-rss/</link><pubDate>Thu, 17 Jun 2010 03:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linq-to-xml-read-rss/</guid><description>
在页面的 Page_Load() 方法中写上这句话就行了：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;XDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://cnblogs.com/rss&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Descendants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XElement&lt;/span&gt;&lt;span class="p"&gt;[]{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;li&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;link&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;WriteTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;XmlTextWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OutputStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
效果是：</description></item><item><title>Asp.net 使用 System.Web.Routing 来实现 UrlRewrite</title><link>https://yanbin.blog/asp-net-routing-urlrewrite/</link><pubDate>Mon, 14 Jun 2010 01:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/asp-net-routing-urlrewrite/</guid><description>
不管是隐藏信息还是搜索引擎友好，UrlRewrite 还是很有用的。我们可以在应用服务层实现 UrlRewrite，比如 Apache 或 IIS；也可以在应用程序层实现 UrlRewrite。Java 有很好的组件实现，.Net 中可以用 &lt;a href="http://urlrewriter.net/" target="_blank" rel="noopener"&gt;UrlRewriter.NET&lt;/a&gt; 来实现。看 Asp.Net MVC 引入 Routing 的概念，不过幸运的是我们不必搞个 MVC 项目也能享受到 Routing，那就是 .Net 3.5 已经为 MVC 做好准备的 System.Web.Routing。&lt;/p&gt;
&lt;br/&gt;
差点忘了轻带一下什么是 UrlRewrite，比如说：</description></item><item><title>XSLT 中使用参数和变量以及 C# 向 XSLT 传入参数</title><link>https://yanbin.blog/xslt-csharp-use-parameter/</link><pubDate>Sun, 13 Jun 2010 15:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-csharp-use-parameter/</guid><description>
有时候我们需要向 XSLT 中设定或传入参数或变量，比如用 XSLT 从 RSS &lt;a href="http://www.blogjava.net/Unmi/rss"&gt;http://www.blogjava.net/Unmi/rss&lt;/a&gt; 取出前面若干条记录，那么这个记录数就应该作声明为参数。&lt;/p&gt;
&lt;br/&gt;
&lt;a href="http://www.blogjava.net/Unmi/rss"&gt;http://www.blogjava.net/Unmi/rss&lt;/a&gt; 的文档结构如下：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;&amp;lt;rss&amp;gt;&lt;br /&gt;
    &amp;lt;channel&amp;gt;&lt;br /&gt;
        &amp;lt;item&amp;gt;&lt;br /&gt;
        &amp;lt;title&amp;gt;写第一个 Wordpress 插件程序&amp;lt;/title&amp;gt;&lt;br /&gt;
        &amp;lt;link&amp;gt;http://www.blogjava.net/Unmi/archive/2010/04/27/319398.html&amp;lt;/link&amp;gt;&lt;br /&gt;
        &amp;lt;dc:creator&amp;gt;隔叶黄莺&amp;lt;/dc:creator&amp;gt;&lt;br /&gt;
        &amp;lt;author&amp;gt;隔叶黄莺&amp;lt;/author&amp;gt;&lt;br /&gt;
        &amp;lt;pubDate&amp;gt;Mon, 26 Apr 2010 17:06:00 GMT&amp;lt;/pubDate&amp;gt;&lt;br /&gt;
         ..............................................&lt;br /&gt;
        &amp;lt;/item&amp;gt;&lt;br /&gt;
        &amp;lt;item&amp;gt;&lt;br /&gt;
         .................................................&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
基于上面的需求，假定要前 5 条记录，XSLT 可以写成如下(c:\\Unmi_Rss_List.xslt):</description></item><item><title>XSLT 文件中使用 C#/JScript/VB 自定义函数</title><link>https://yanbin.blog/xslt-csharp-jscript-vb-function/</link><pubDate>Sat, 12 Jun 2010 13:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xslt-csharp-jscript-vb-function/</guid><description>
在用 XSLT 转换 XML 到其他格式时，不光是原数据搬到别处，还可能需要对数据进行一定的处理，比如一个标志位要 XSLT 转换为表义字符串(1-&amp;gt;True; 0-&amp;gt;False)，或者日期类型格式的转换等等。所以这时候我们在 XSLT 中要用到函数来处理这些细节上的转换。&lt;br/&gt;&lt;br/&gt;
XSLT 含有超过 100 个内建的函数，XQuery 1.0、XPath 2.0 以及 XSLT 2.0 共享相同的函数库。&lt;br /&gt;
这些函数用于字符串值、数值、日期和时间比较、节点和 QName 操作、序列操作、逻辑值，等等。&lt;br/&gt;&lt;br/&gt;
关于 XSLT 的内置函数请参数：&lt;a href="http://www.w3school.com.cn/xsl/xsl_functions.asp" target="_blank" rel="noopener"&gt;XSLT 函数参考手册&lt;/a&gt; 和 &lt;a href="http://www.w3school.com.cn/xpath/xpath_functions.asp" target="_blank" rel="noopener"&gt;XPath、XQuery 以及 XSLT 函数&lt;/a&gt;。</description></item><item><title>编写跨浏览器兼容的 CSS 代码的金科玉律[转]</title><link>https://yanbin.blog/browser-crossed-css/</link><pubDate>Wed, 09 Jun 2010 00:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/browser-crossed-css/</guid><description>
作为 Web 设计师，你的网站在各种浏览器中有完全一样的表现是很多人的目标，然而这是一个永远无法真正实现的目标，很多人认为，&lt;a href="http://dowebsitesneedtolookexactlythesameineverybrowser.com/"&gt;完美的跨 浏览器兼容并不必要&lt;/a&gt;，这样说虽然没错，但在很多情形，一种近似的兼容还是很容易实现的，本文讲的是各种跨浏览器兼容的 CSS 编码准则和技巧。&lt;br/&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/browser-crossed-css/browsers-css.jpg"
width="500px"
/&gt;</description></item><item><title>VS 2008中的jQuery Intellisense[转]</title><link>https://yanbin.blog/vs-2008-jquery-intellisense/</link><pubDate>Tue, 08 Jun 2010 21:52:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vs-2008-jquery-intellisense/</guid><description>
【原文地址】&lt;a title="VS 2008中的jQuery Intellisense" href="http://weblogs.asp.net/scottgu/archive/2008/11/21/jquery-intellisense-in-vs-2008.aspx"&gt;jQuery Intellisense in VS 2008 &lt;/a&gt;&lt;br/&gt;
【原文发表日期】 Friday, November 21, 2008 2:07 AM&lt;br/&gt;
&lt;br/&gt;
上个月，我在博客里宣布了&lt;a href="http://blog.joycode.com/scottgu/archive/2008/09/29/115287.aspx" target="_blank"&gt;微软将对jQuery提供支持&lt;/a&gt;。在过去的几个星期里，我们与jQuery开发团队合作，在Studio 2008 和 Visual Web Developer 2008 Express版本（免费的）中增加了很好的jQuery intellisense支持。现在这个支持可以下载使用了。&lt;br/&gt;
&lt;h3&gt;在VS 2008中启用jQuery Intellisense的步骤&lt;/h3&gt;
要在VS中启用jQuery的intellisense完成，你要遵循三个步骤：</description></item><item><title>使用 NAnt 构建 asp.net 项目并生成一个固定名字的动态库</title><link>https://yanbin.blog/nant-asp-net-fixed-dll-name/</link><pubDate>Sun, 06 Jun 2010 13:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nant-asp-net-fixed-dll-name/</guid><description>
在 Java 项目里多用 Ant 来自动构建项目，随着惯性思维，很容易就找到了 .Net 里也有类似的构建工具 NAnt。最该死的是连 Maven 在 .net 中的对应产物 NMaven 也都有了，&lt;a href="http://sourceforge.net/projects/nmaven/"&gt;http://sourceforge.net/projects/nmaven/&lt;/a&gt;。从 Ant 到 NAnt 自然会有一种驾轻就熟的感觉。其实 MS 也为我们提供了相应的构建工具，如早先的 nmake 和现在的 MSBuild，它们各自用特定的构建文件，只是纯粹的项目构建工具。&lt;/p&gt;
&lt;br/&gt;
NAnt 能让你完成许多的系统操作，并且是扩展的，它能独立的完成诸如取版本、编译、打包、发布、Email 通知等一系列过程。如果再让 NAnt 结合 MSBuild 便能制作出完全自动化，一劳永逸，简单化的构建方案。比如这里的例子讲述了如何用 NAnt 构建一个 WebSite 项目，并把生成的多个动态库，像：App_Web_j_5i4fnt.dll、App_Code.dll、App_global.asax.dll 用 aspnet_merge.exe 命令合成为一个固定名字的动态库，如 Unmi.Web.dll。这样非常有利于站点的部分更新。</description></item><item><title>C# 使用 iTextSharp(5.0.2) 生成 PDF 文档</title><link>https://yanbin.blog/csharp-itextsharp5-0-2-pdf/</link><pubDate>Sat, 22 May 2010 13:55:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-itextsharp5-0-2-pdf/</guid><description>
&lt;p&gt;前一篇 &lt;a id="Editor_Results_rprSelectionList_ctl01_LinkTitle" class="titlelink" href="https://yanbin.blog/csharp-itextsharp4-1-2-pdf/"&gt;C# 使用 iTextSharp(4.1.2) 生成 PDF 文档&lt;/a&gt; 写了一个用 iTextSharp 4.1.2 生成 PDF 文档的例子，由于新版 iTextSharp 5.0.2 变化太大，所以有必要再记录一下。比较两篇你可以很明显的感觉到它们之间 API 上的差异来。</description></item><item><title>C# 使用 iTextSharp(4.1.2) 生成 PDF 文档</title><link>https://yanbin.blog/csharp-itextsharp4-1-2-pdf/</link><pubDate>Sat, 22 May 2010 10:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-itextsharp4-1-2-pdf/</guid><description>
在 Java 里操作 PDF 文档的组件首当其充就是 iText，几乎是不二之选，惯性思维到 C# 里应该有一个相应的实现吧，对了，那就是 iTextSharp，这里来看看 iTextSharp 怎么生成一个 PDF 文档的。此篇所选版本是 iTextSharp 4.1.2，因与 iTextSharp 5 有较大的差别，所以分两篇来说明。&lt;br/&gt;
&lt;br/&gt;
这两个版本的主要区别有，iTextSharp 5 里不再有 iTextSharp.text.Table、HeaderFooter 类的，且实现了自己的 Font, Color，不再依赖于 System.Drawing 了；在设置页眉页脚实现也不一样。&lt;br/&gt;
&lt;br/&gt;
下面例子，用 OLDDB 从一个 Excel 里读取第一个 sheet，然后在 PDF 文档里生成张表格：</description></item><item><title>C# 使用 ExcelLibrary 读写 Excel 文件</title><link>https://yanbin.blog/csharp-excellibrary-handle-excel/</link><pubDate>Thu, 20 May 2010 14:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-excellibrary-handle-excel/</guid><description>
ExcelLibrary 是一个比较精致的 Excel 读写组件，还是由国人开发的，下载地址：&lt;a href="http://code.google.com/p/excellibrary/"&gt;http://code.google.com/p/excellibrary/&lt;/a&gt;。可以读写 xls 和 xlsx 格式的 Excel。支持简单的公式，可插入图片，对于格式的设置也不是很丰富，可设置单元格宽度，数据格式化显示。对字体，前景、背景色进行设置是它的局限性。虽比不上 NPOI，但作为小巧的用来读写纯数据内容的 Excel 还是很高效的。&lt;/p&gt;
&lt;br/&gt;
具体的例子，可以考看它的测试代码：ExcelLibrary.Test\SimpleTest.cs。这里</description></item><item><title>掌握一种 C#/.Net 模板技术 — Velocity</title><link>https://yanbin.blog/csharp-velocity-templat/</link><pubDate>Wed, 19 May 2010 11:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-velocity-templat/</guid><description>
&lt;p&gt;前面写过一篇关于 Java 中使用 Velocity 模板的文章：
&lt;a href="https://yanbin.blog/master-java-template-velocity/" target="_blank"&gt;掌握一种Java模板技术 -- Velocity&lt;/a&gt;，
现在要考究一下 C# 中怎么使用相应的 nVelocity 模板。&lt;/p&gt;
&lt;p&gt;当目光转移到了 C#/.Net 之后，但凡想要找个某个功能组件时都会联想到，曾经在 Java 中用过什么，.Net 中应该会有它相应的实现，
例如 Spring.Net, nHibernate, iTextSharp，还有这里的 nVelocity，特点就是组件名一般是 .Net 结尾或 n 字打头。&lt;/p&gt;
&lt;p&gt;还是依照原来的例子，从 &lt;a href="http://sourceforge.net/projects/nvelocity/files/"&gt;
http://sourceforge.net/projects/nvelocity/files/&lt;/a&gt; 下载 nVelocity 组件，当前版本是 NVelocity-0.4.2.8580。
下载后解压，在你的项目中引入 build 目录中的 NVelocity.dll，如果是 web 项目中用 nVelocity，可附加引入 NVelocity.Http.dll。</description></item><item><title>HTML 简史 [转]</title><link>https://yanbin.blog/html-history-review/</link><pubDate>Sat, 15 May 2010 11:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/html-history-review/</guid><description>
HTML 是 Web 统一语言，这些容纳在尖括号里的简单标签，构成了如今的 Web。1991 年，Tim Berners-Lee 编写了一份叫做 “HTML 标签”的文档，里面包含了大约20个用来标记网页的 HTML 标签。他直接借用 SGML 的标记格式，也就是后来我们看到的 HTML 标记的格式。本文讲述了 HTML 这门 Web 标记语言的发展简史。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;从 IETF 到 W3C: HTML 4 之路 &lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
HTML 1 并不曾存在，HTML 的第一个官方版本就是由 IETF （互联网工程任务组） 推出的 HTML 2.0。问世之前，这个版本中的很多细则已经被实现，比如，1994年的 Mosaic 浏览器已经实现了在文档中嵌入图片的方法，后来 HTML 2.0 便吸纳了 img 这个标签。</description></item><item><title>VS2008 发布网站时如何产生固定命名的 Dll 文件</title><link>https://yanbin.blog/vs2008-fixed-dll-name/</link><pubDate>Thu, 13 May 2010 12:11:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vs2008-fixed-dll-name/</guid><description>
VS2008 在发布网站时，bin 目录里为所有 cs 生成的 dll 文件每次都是随机命名的，像这样子的 App_Web_g2dnx-qj.dll, App_Web_s9nor8mv.dll，这样对更新 Live 站点很不方便，因为动态库名变了，涉及到每一个 aspx 文件每次都重新更改了对库的引用声明：&lt;br/&gt;
&lt;br/&gt;
&amp;lt;%@ page language="C#" autoeventwireup="true" inherits="_Default, App_Web_s9nor8mv" %&amp;gt;&lt;br/&gt;
&lt;br/&gt;
即使有一个 cs 的小改动，由于生成的动态库名称不一样，造成所有的 aspx 文件都得重传一遍。如果能固定动态库的文件名，更新网站时就省事多了，只改了 cs 的话，重新生成 dll 文件，只需要替换这一个 dll 文件就 OK 啦。我们可以找到几种做法：</description></item><item><title>Asp.Net 中一个控件关联多个验证器时，如何同时只出一个验证器的错误信息</title><link>https://yanbin.blog/asp-net-show-one-validitor-message/</link><pubDate>Thu, 13 May 2010 11:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/asp-net-show-one-validitor-message/</guid><description>
为一个控件绑定多个验证器那是很正常的，例如一个邮件输入框，首先它不能为空，其次不为空时邮件地址必须符合一定的规则，介于此，我们会依次放两个验证器，RequiredFieldValidator 和 RegularExpressionValidator。由于输入为空的时候对于 RegularExpressionValidator 是被认为通过的，所以同时只会显示其中一个验证器的错误信息。&lt;/p&gt;
&lt;br/&gt;
再看个需求，比如输入邮件地址和再次确认的邮件地址，这时需要为第二个邮件输入框加除上两之外的第三个验证器 CompareValidator 用于验证两次输入是否一致，那么三个验证器依次是：&lt;br/&gt;&lt;br/&gt;
RequiredFieldValidator   RegularExpressionValidator  CompareValidator</description></item><item><title>C# 程序中使用 SQLite 数据库</title><link>https://yanbin.blog/csharp-use-sqlite-db/</link><pubDate>Thu, 06 May 2010 23:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-use-sqlite-db/</guid><description>
差不多在 3 年前我就关注过 SQLite，也整理了一篇 SQLite 的介绍：&lt;a href="http://unmi.cc/embed-sqlite-introduction/" target="_blank" rel="noopener"&gt;开源嵌入式数据库 SQLite 简介&lt;/a&gt;, 还在我的一个查手机归属地软件中使用了 SQLite 数据库，&lt;a href="http://unmi.cc/csharp-use-sqlite-db/"&gt;http://unmi.cc/csharp-use-sqlite-db/&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
现在又要用到 C# 了，所以决意了解下 C# 中如何使用 SQLite，之所以选择 SQLite 作为自己的文件型数据，主要是看中它的非常&lt;strong&gt;小型&lt;/strong&gt;、&lt;strong&gt;单文件&lt;/strong&gt;、&lt;strong&gt;嵌入型&lt;/strong&gt;，更支持&lt;strong&gt;多进程&lt;/strong&gt;访问。&lt;br/&gt;&lt;br/&gt;
SQLite 官方网站：&lt;a href="http://www.sqlite.org"&gt;http://www.sqlite.org&lt;/a&gt;，你可以在那里下载到一个 sqlite3.exe，用命令行模式来管理你的数据库文件。或用其他比较好的管理工具：&lt;a href="http://sqliteman.com/" target="_blank" rel="noopener"&gt;sqliteman&lt;/a&gt;(也支持 UTF-8) 、&lt;a href="http://sqlitebrowser.sourceforge.net/" target="_blank" rel="noopener"&gt;sqlitebrowser&lt;/a&gt; 和 &lt;a href="http://sqliteadmin.orbmu2k.de/" target="_blank" rel="noopener"&gt;SQLite Administrator&lt;/a&gt;。我比较喜欢 sqlitebrowser，但更推荐可设置字符集的 &lt;a href="http://www.sqliteexpert.com" target="_blank" rel="noopener"&gt;SQLite Expert&lt;/a&gt;(可下载到免费的个人版)。</description></item><item><title>PHP 异步执行方法，模拟多线程</title><link>https://yanbin.blog/php-simulate-multithread/</link><pubDate>Wed, 28 Apr 2010 11:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-simulate-multithread/</guid><description>
PHP 本身没有多线程的东西，但可以曲线的办法来造就出同样的效果，比如多进程的方式来达到异步调用，只限于命令模式。还有一种更简单的方式，可用于 Web 程序中，那就是用 fsockopen()、fputs() 来请求一个 URL 而无需等待返回，如果你在那个被请求的页面中做些事情就相当于异步了。&lt;/p&gt;
&lt;br/&gt;
关键代码如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;fsockopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$errno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$errstr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;$errstr&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="si"&gt;$errno&lt;/span&gt;&lt;span class="s2"&gt;)&amp;lt;br /&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fputs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;GET another_page.php?flag=1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
上面的代码向页面 another_page.php 发送完请求就不管了，用不着等待请求页面的响应数据，利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。</description></item><item><title>打造自己的WordPress侧边栏[转]</title><link>https://yanbin.blog/wordpress-custom-sidebar/</link><pubDate>Wed, 28 Apr 2010 03:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-custom-sidebar/</guid><description>
倘若自己把一些需要的widget直接写进WordPress侧边栏sidebar.php里，在打开网页时，会比在后台添加的widget速度要快一些。道理很简单，如果在后台添加那些widget, 相当于dynamic_sidebar，然后去get一些option之类的函数，会比直接写进sidebar.php里多一些查询和调用的过程。&lt;br /&gt;
而且，大家也发现了，在后台的提供的widget里并非都是你想要的。所以有时候自己写一些widget，还是蛮有趣的。&lt;/p&gt;
&lt;br/&gt;
好比说，我现在想加一个友情链接（Blogroll）在侧边栏里，该怎么去写呢？ 很简单，在模板的sidebar.php里加上:&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!--blogroll--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;widget&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;友情链接&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;&amp;lt;?php wp_list_bookmarks(&amp;#39;orderby=rand&amp;amp;limit=10&amp;amp;categorize=0&amp;amp;title_li=&amp;#39;); ?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;div class="blockcode"&gt;
&lt;/div&gt;</description></item><item><title>关于 WordPress Shortcode 以及在 the_content 之外使用</title><link>https://yanbin.blog/wordpress-shortcode-location/</link><pubDate>Mon, 26 Apr 2010 13:54:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wordpress-shortcode-location/</guid><description>
Wordpress 中可以用 Shortcode 来创建宏代码，并且是高度可定制的，例如，有 PHP 代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [bartag foo=&amp;#34;foo-value&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bartag_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$atts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shortcode_atts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;no foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;default bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;$atts&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;foo = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$foo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;add_shortcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bartag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bartag_func&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
那么只要在你的 post 中输入，[bartag foo="foo-value"]，显示该文章的时候此处就被 bartag_func() 函数替换为：foo = foo-value。来个更实用的，比如欲定义一个[unmi_blog] 的标签，定义代码写成如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [unmi_blog]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;unmi_blog_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$atts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a href=&amp;#39;http://unmi.cc&amp;#39;&amp;gt;隔叶黄莺 The Blog of Unmi&amp;lt;/a&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;add_shortcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unmi_blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unmi_blog_func&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
那么在文章中的 [unmi_blog] 的会显示为：&lt;a href="http://unmi.cc"&gt;隔叶黄莺 Unmi Blog&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
还能用 Shortcode 打照更强大的自定义标签，如 [YouTube id='1234'] 就显示 id=1234 的视频等等，只是 Shortcode 似乎又太局限了。在文中 &lt;a href="http://codex.wordpress.org/Shortcode_API"&gt;http://codex.wordpress.org/Shortcode_API&lt;/a&gt; 详细介绍了 Shortcode 的用法，并且说在 the_content 显示的时候，Shortcode API 就会去解析已注册的 Shortcode，这就让 Shortcode 不能轻易的用在别处，不是在 post 里写 [unmi_blog] 将会原样显示出 [unmi_blog] 来，其实也就是在 Wordpress 系统里有:&lt;br/&gt;&lt;br/&gt;
add_filter('the_content', 'do_shortcode'); &lt;br/&gt;&lt;br/&gt;
但是人是活的，那些代码原本就是人编写的，我们可以假以巧妙的 Hack 让 Shortcode 应用在别处。&lt;br/&gt;&lt;br/&gt;
1. Customer Field 中应用 Shortcode &lt;a href="http://nspeaks.com/978/insert-shortcodes-into-your-custom-fields/" target="_blank" rel="noopener"&gt;Parse Shortcodes in your Custom Fields&lt;/a&gt;&lt;br /&gt;
2. Sidebar 里的 Text Widget 中 &lt;a href="http://englishmike.net/2008/07/07/wordpress-quick-tips-3adding-a-shortcode-to-a-sidebar-widget/" target="_blank" rel="noopener"&gt;Adding a Shortcode to a Sidebar Widget&lt;/a&gt;&lt;br /&gt;
3. 任意地方 $text = yourShortCodeFunction("[your shortcode tag here]");echo $text; 外大括号，里小括号&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*&amp;lt;?php wp_show_surveys(1);?&amp;gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;wp_show_survey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$survey_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$short_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[SURVEYS &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$survey_id&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apply_filters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show_survey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$short_code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show_survey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;do_shortcode&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;custom_show_survey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//echo &amp;#34;&amp;lt;script&amp;gt;alert(123)&amp;lt;/script&amp;gt;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//add_filter(&amp;#39;show_survey&amp;#39;,&amp;#39;custom_show_survey&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
参考: 1. &lt;a href="http://codex.wordpress.org/Shortcode_API" target="_blank" rel="noopener"&gt;Shortcode API&lt;/a&gt;</description></item><item><title>写第一个 Wordpress 插件程序</title><link>https://yanbin.blog/my-first-wordpress-plugin/</link><pubDate>Mon, 26 Apr 2010 12:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/my-first-wordpress-plugin/</guid><description>
本文旨在让像我这样的 Wordpress 初入门的开发者快速体验怎么写出一个最简单能工作的插件。至于插件的工作原理就不太多说，但基本的还是要懂得，插件中会用 add_action() 或 add_filter() 方法去挂住一些事件，如显示标题、显示内容等事件，然后执行一个函数。&lt;/p&gt;
&lt;br/&gt;
add_action() 意味着某件事情发生了，要做什么事情，add_filter() 意味着事情发生，并传来的数据可对数据进行处理。用 add_action() 还是 add_filter() 就看你实际需求了。&lt;br/&gt;&lt;br/&gt;
虽然刚开始就声明了不太想多讲插件的原理，但前面几句话却表明的差不多了。立即例子入手吧，比如我们来做一个在每篇文章内容后附加一句版权声明的话。</description></item><item><title>雅虎JavaScript架构师：网页开发技术安全优先[转]</title><link>https://yanbin.blog/yahoo-web-page-security/</link><pubDate>Mon, 26 Apr 2010 09:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/yahoo-web-page-security/</guid><description>
由于当前技术仍偏向功能强化为主要思维，指令码趋向庞杂的结果，容易产生更多安全漏洞，且失去快速因应攻击调整架构的灵活性。&lt;br/&gt;
&lt;strong&gt;身 为JavaScript网路技术重要推手的雅虎（Yahoo!）架构设计师Douglas Crockford表示，现行网页开发技术思维仍偏重多媒体功能或浏览效能的提升，未来应以安全为第一优先。&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
Crockford 现为Yahoo!资深JavaScript架构师，负责YUI（Yahoo! User Interface）的架构设计，并且担任ECMA JavaScript 2.0技术委员会成员，为JavaScript开发社群大师级人物，此次受邀来台参加OSDC（Open Source Developer's Conference Taiwan）进行专题演讲，向国内开发者介绍ECMA JavaScript的发展。</description></item><item><title>那些相见恨晚的 JavaScript 技巧[摘]</title><link>https://yanbin.blog/more-javascript-tips/</link><pubDate>Mon, 26 Apr 2010 09:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/more-javascript-tips/</guid><description>
JavaScript 的成功让人津津乐道，为 Web 网页编写 JavaScript 代码已经是所有 Web 设计师的基本功，这门有趣的语言蕴藏着许多不为人熟知的东西，即使多年的 JavaScript 程序员，也未能完全吃透。本文从7个方面讲述 JavaScript 中那些你不很熟知但非常实用的技巧。&lt;br/&gt;
&lt;br/&gt;
简略语句&lt;br/&gt;
&lt;strong&gt;JavaScript 可以使用简略语句快速创建对象和数组，比如下面的代码：&lt;/strong&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/more-javascript-tips/p1.gif"
/&gt;</description></item><item><title>PHP之中使用共享内存进行高速数据更新的一种方案[转]</title><link>https://yanbin.blog/php-share-memory-solution/</link><pubDate>Sat, 24 Apr 2010 15:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-share-memory-solution/</guid><description>
作者：&lt;a href="http://bbs.chinaunix.net/viewpro.php?uid=32193"&gt;&lt;span style="color: #000000; font-size: x-small;"&gt;HonestQiao&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;div&gt;    如果在你实际的应用之中,你确实需要高速数据更新的操作,那么我们认为你已经具备以下先决条件:&lt;br /&gt;
&lt;br/&gt;
    &lt;strong&gt;&lt;span&gt;能够按照实际应用的需要而调整服务器&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;br/&gt;
    关于这句话,大家自己可以明白意思的,不明白的自己想一想,如果没有达到这个要求,你是否有必要高速数据更新的操作, 或者先看看最后的附加说明.&lt;/div&gt;
&lt;br/&gt;
&lt;div&gt;&lt;span style="color: #008080;"&gt;先见的参考文档:&lt;br /&gt;
1. &lt;a href="http://w.yi.org/ftp/FAPM/PHP/php_manual_zh/ref.shmop.html" target="_blank" rel="noopener"&gt;http://w.yi.org/ftp/FAPM/PHP/php_manual_zh/ref.shmop.html&lt;br /&gt;
&lt;/a&gt;说明: PHP功能, 共享内存功能函数, 此处用于PHP直接操作内存, 提供高速数据操作&lt;br /&gt;
开启: 编译PHP的时候,加上参数: -enable-shmop&lt;br /&gt;
附注: 这个准备工作, 非常简单</description></item><item><title>PHP 进程间内存共享</title><link>https://yanbin.blog/php-share-memory-cross-proces/</link><pubDate>Sat, 24 Apr 2010 15:31:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-share-memory-cross-proces/</guid><description>
启用php的共享内存:&lt;br/&gt;&lt;br/&gt;
1).windows下，php.ini中取消 ;extension=php_shmop.dll 这行的行首分号，加载shmop扩展。&lt;br /&gt;
2) .linux/unix下,重新编译php,加入--enable--shmop 选项。&lt;br/&gt;&lt;br/&gt;
Shared Memory 的函数解释如下：&lt;br/&gt;&lt;br/&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-close.php"&gt;shmop_close&lt;/a&gt; — Close shared memory block&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-delete.php"&gt;shmop_delete&lt;/a&gt; — Delete shared memory block&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-open.php"&gt;shmop_open&lt;/a&gt; — Create or open shared memory block&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-read.php"&gt;shmop_read&lt;/a&gt; — Read data from shared memory block&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-size.php"&gt;shmop_size&lt;/a&gt; — Get size of shared memory block&lt;/li&gt;
&lt;li&gt;&lt;a href="http://php.net/manual/zh/function.shmop-write.php"&gt;shmop_write&lt;/a&gt; — Write data into shared memory block</description></item><item><title>PHP 的 MVC 框架参考实现</title><link>https://yanbin.blog/php-mvc-reference-implementation/</link><pubDate>Sat, 24 Apr 2010 14:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/php-mvc-reference-implementation/</guid><description>
MVC 模式在 Java 中表现的尤为出众，不光 Swing 是按照 MVC 来设计的，而且 Java 的 Web 框架也是 MVC1、MVC2 的。MVC 模式对于开发维护确有许多好处，所以 PHP 的框架，如 Zend、Symfony，PHP 的产品 Wordpress 和 Joomla 都应用了 MVC 模式。PHP 不像 Servlet 那样有成熟的规范，如 web.xml、servlet、filter 等，但变换着一些把式同样能实现出优雅的 MVC 模式。这里简单介绍一下 PHP 是如何实现 MVC 模式，参照了了 Zend 的实现，我觉得还有许多改进的地方。说明的时候会拿它的各部分与 Struts1 的 MVC 相比较。&lt;/p&gt;
&lt;br/&gt;
在 HTTP 环境中的 MVC 模式一句话描述就是：控制器根据 URI，把请求转给相应的 Action，由 Action 调用模型方法处理或得到数据，再选择相应的视图呈现界面。用过 Struts1 的请保留一些 Struts1 的实现原理，现在来看 PHP 的实现方式。&lt;br/&gt;</description></item><item><title>步步理解 JAVA 泛型编程(三)</title><link>https://yanbin.blog/understand-java-generic-3/</link><pubDate>Fri, 16 Apr 2010 04:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-generic-3/</guid><description>
前面介绍的无论是泛型类还是泛型方法，基本上都是把定义的类型参数作为一个整体来操作，放到数组或取出来，顶多就是调用了一下 hashCode() 方法，因为这是 Java 的根对象拥有的方法。比如说一个泛型数组，要得到其中的最小元素：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.unmi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 泛型，类型变量的限定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * @author Unmi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArrayAlg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smallest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smallest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smallest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;smallest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>实用的 JavaScript 测试及效验工具[转]</title><link>https://yanbin.blog/javascript-test-tools/</link><pubDate>Fri, 16 Apr 2010 00:15:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-test-tools/</guid><description>
&lt;strong&gt;JavaScript&lt;/strong&gt; 是一款强大的广泛运用于现代Web站点及应用的脚本语言。作为一个技艺精湛的&lt;strong&gt; Web 开发者&lt;/strong&gt;，掌握JavaScript可以增强用户的使用体验，提供交互及富客户端等功能。&lt;br/&gt;
尽管JavaScript 的语法非常简单，但对于写程序而言仍然是困难重重，就是因为它的运行环境：基于Web浏览器。&lt;br/&gt;
&lt;br/&gt;
以下您可以看到收集的&lt;strong&gt;&lt;a href="http://www.iwanna.cn/archives/2010/04/15/2723/"&gt;8个实用的 JavaScript 测试及效验工具&lt;/a&gt;&lt;/strong&gt;，它们都可以在不同环境下进行单元测试及校验测试您的脚本。&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://jslint.com/"&gt;JSLint&lt;/a&gt;&lt;br/&gt;
JSLint是基于Web的验证JavaScript错误代码的工具。它拥有的功能及特定的设置来使用您的需求，自定义你的验证算法。</description></item><item><title>步步理解 JAVA 泛型编程(二)</title><link>https://yanbin.blog/understand-java-generic-2/</link><pubDate>Thu, 15 Apr 2010 23:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-generic-2/</guid><description>
前面讲了泛型类的定义，你也可以在一个普通类中单单定义一个泛型方法。也就是说类能够带个类型参数，方法也可以带类型参数的。还是来看个例子(包括如何应用)，一个获得数组中间元素的方法，因为数组中存储元素的类型是不定的，所以把该方法定义成泛型的。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.unmi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * 泛型方法示例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * @author Unmi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArrayAlg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//这个就是在普通类 ArrayAlg 中定义的泛型方法&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getMiddle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Fantasia&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Kypfos&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//String middle = ArrayAlg.&amp;lt;String&amp;gt;getMiddle(names);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//上面那样写是可以，编译器可推断出要调用的方法，所以省去&amp;lt;String&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;middle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayAlg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMiddle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>步步理解 JAVA 泛型编程(一)</title><link>https://yanbin.blog/understand-java-generic-1/</link><pubDate>Thu, 15 Apr 2010 12:13:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-generic-1/</guid><description>
JDK 1.5 相对于 JDK 1.4 来说变化最大的部分就是泛型，甚至可以说自 Java 1.0 发布以来也是最大的一次语言变化，因为要涉及到编译器的大改动。很早的时候大家对泛型的呼声很大，正如 C++ 的模板，C# 的泛型确实是个值得借鉴的好特性。JDK1.5 这前，很多人对于泛型是急不可耐，在 JDK1.4 下搭配一个外挂的 Generic Java 编译器，通老实 -Xbootclasspath、-bootclasspath 同样能让你在 1.4 的 JDK 上使用泛型：&lt;/p&gt;
&lt;br/&gt;
javac -J-Xbootclasspath/p:JSR14HOME\gjcrt.jar -bootclasspath JSR14HOME\collect.jar;JDK141HOME\jre\lib\rt.jar -source 1.5 FileName.java&lt;br /&gt;
java -Xbootclasspath/p:JSR14HOME\gjc-rt.jar FileName</description></item><item><title>JMS 使用 ActiveMQ 传送文件</title><link>https://yanbin.blog/jms-use-activemq-transfer-file/</link><pubDate>Sun, 11 Apr 2010 10:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jms-use-activemq-transfer-file/</guid><description>
这里使用的 MQ 中间件是开源的 ActiveMQ，我们没有采用 BytesMessage 来按字节传送文件，而是 ActiveMQ 为我们提供了 &lt;a href="http://activemq.apache.org/maven/5.3.1/activemq-core/apidocs/org/apache/activemq/BlobMessage.html" target="_blank" rel="noopener"&gt;org.apache.activemq.BlobMessage&lt;/a&gt;，可以用它来传送大对象。&lt;a href="http://activemq.apache.org/maven/5.3.1/activemq-core/apidocs/org/apache/activemq/ActiveMQSession.html" target="_blank" rel="noopener"&gt;org.apache.activemq.ActiveMQSession&lt;/a&gt; 中有以下几个创建 BlobMessage 对象的方法：&lt;/p&gt;
&lt;br/&gt;
createBlobMessage(URL url)&lt;br/&gt;&lt;br/&gt;
createBlobMessage(URL url, boolean deletedByBroker)&lt;br/&gt;&lt;br/&gt;
createBlobMessage(File file)&lt;br/&gt;&lt;br/&gt;
createBlobMessage(InputStream in)&lt;br/&gt;&lt;br/&gt;
接收到 BlobMessage 消息后，可以调用其 getInputStream() 方法获得数据，然后写成磁盘文件，文件名、文件大小等可通过 Message 的 getXxxProperty("Property.Name") 取的。</description></item><item><title>JMS 基本知识及消息传送模型</title><link>https://yanbin.blog/jms-introduction-message-model/</link><pubDate>Sat, 10 Apr 2010 10:32:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jms-introduction-message-model/</guid><description>
&lt;p&gt;JMS(Java Message Service) 是 Java 为面向消息中间件(MOM)定义的接口。JMS 的通信管道就是消息队列，说到消息队列，历史就悠久，在 MS 系统中很早就有 MSMQ，譬如邮件、群组就是些消息队列。JMS 因其异步，所以可用来解决高并发的问题，分布式可对负载进行均衡。&lt;/p&gt;
&lt;p&gt;JMS 已成为 J2EE 规范中的一部分，所以在 J2EE 应用服务器中都有 JMS 核心部分 MQ 的实现，MQ 也有独立的产品，如 ActiveMQ、JBoss MQ(已更名为 JBoss Messaging)、WebSphere MQ 等。&lt;/p&gt;
&lt;p&gt;如果我们蒙着头来理解，JMS 消息通信中的主要角色应该有：&lt;strong&gt;消息生产者(Producer)&lt;/strong&gt;、&lt;strong&gt;消息消费者(Consumer)&lt;/strong&gt;、它们间的消息队列(Queue)、以及所传送的&lt;strong&gt;消息(Message)&lt;/strong&gt;。</description></item><item><title>Java 线程同步，对象锁与互斥规则</title><link>https://yanbin.blog/java-thread-sync-lock-mutex/</link><pubDate>Thu, 08 Apr 2010 20:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-thread-sync-lock-mutex/</guid><description>
&lt;p&gt;在前面一篇
&lt;a id="Editor_Results_rprSelectionList_ctl01_LinkTitle" class="titlelink" href="https://yanbin.blog/java-synchronized-method-codes/"&gt;
是同步方法还是 synchronized 代码？-- 详解多线程同步规则&lt;/a&gt;
其实已清楚讲述了这里想要是解的话题，只是之前的长篇大论，没个重点，读来实在会让人受累，故在此单列一专题，假以图示，也为自己加深对同步锁的理解。道中人觉得哪里说得有出入或有更妙的理解方式，尽管拍过来。
&lt;/p&gt;
&lt;p&gt;记得我初识多线程时，就是很简单化的去理解了同步：说到要同步，无外乎就是给方法加 synchronized 关键字，或者给代码块加上 synchronized(this)
括起来[注：jdk 1.5 后可用 Lock 来同步代码]，以为这样就万事大吉，就能保证一个线程执行时，另一个线程就进不来。
其实不尽然，因为静态同步方法与非静态同步方法是有区别的，而 synchronized(this) 括号中是用 this 还是要用别的对象，也是有讲究的，
这些问题可以阅读前一篇：
&lt;a id="Editor_Results_rprSelectionList_ctl01_LinkTitle" class="titlelink" href="https://yanbin.blog/java-synchronized-method-codes/"&gt;
是同步方法还是 synchronized 代码？-- 详解多线程同步规则
&lt;/a&gt;。</description></item><item><title>SLF4J 的几种实际应用模式--之三：JCL-Over-SLF4J+SLF4J</title><link>https://yanbin.blog/jcl-over-slf4j-slf4j/</link><pubDate>Wed, 07 Apr 2010 10:42:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jcl-over-slf4j-slf4j/</guid><description>
我们前面已经讲过了 SLF4J 的两种用法：SLF4J+Log4J  和 SLF4J+Logback，那是在比较理想的情况下，所用组件只使用了 SLF4J 这一种统一日志框架的时候。可是 JCL 一直影响深远，SLF4J 渐入佳境的时个，在你的项目中很可能所用的组件，它们分别用了 JCL 和 SLF4J 两种组件。比如说在项目中用了 Hibernate 3.5 和 Struts，或其他 Apache 的一些开源组件，你大约也不想用了 SLF4J 的组件日志信息输出到 A 处，用了 JCL 的组件日志输出到 B 处，那你自己写的代码中的日志信息该往哪儿写呢？&lt;/p&gt;
&lt;br/&gt;
中国人一直都愿追求大一统，不喜欢城邦制的便于分而治之。但说到日志输出还是得统一到单一通道中来，一方面多个通道浪费资源，另方面也便于配置和管理。那么既然 SLF4J 是趋势，当 SLF4J 和 JCL 被丢到一个坛子里，首先会让 SLF4J 为主，JCL 为辅，也就是要把 JCL 桥接到 SLF4J 上来，通过 SLF4J 统一输出日志信息。于是也就是这篇要介绍的 SLF4J 使用模式：JCL-Over-SLF4J+SLF4J。</description></item><item><title>SLF4J 的几种实际应用模式--之二：SLF4J+Logback</title><link>https://yanbin.blog/slf4j-logback/</link><pubDate>Wed, 07 Apr 2010 09:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/slf4j-logback/</guid><description>
前面讲的 SLF4J 的用法之一是 SLF4J+Log4J，而这里要推出的组合是 SLF4J+LogBack。不用 Log4J？难道还有比 Log4J 更好的日志实现吗？是的，答案就是 &lt;a href="http://logback.qos.ch/" target="_blank" rel="noopener"&gt;LogBack&lt;/a&gt;。假如你知道 LogBack 和 Log4J 是同出一位大师之手，你就不会觉得惊讶了。LogBack 对 Log4J 进行了增强，并投入了 SLF4J 的怀抱，它还被作者认定为 Log4J 的继任者。&lt;/p&gt;
&lt;br/&gt;
为什么叫做 Logback，我就不知道了，反正不要叫做 Brokeback 就行。这里是推崇用 Logback 替代 Log4J 的十几个理由：&lt;a href="http://logback.qos.ch/reasonsToSwitch.html" target="_blank" rel="noopener"&gt;Reasons to prefer logback over log4j&lt;/a&gt;，说的大至是更快；好测试；与 SLF4J 关系紧；文档丰富；能自动加载配置文件；多个 JVM 写一个日志文件，或其他 I/O 错误时不影响程序执行；配置文件中加入条件控制；强大的日志过滤；更强的日志切分功能；自动压缩、删除日志文件；异常栈中更多的数据信息。</description></item><item><title>SLF4J 的几种实际应用模式--之一：SLF4J+Log4J</title><link>https://yanbin.blog/slf4j-log4j/</link><pubDate>Wed, 07 Apr 2010 05:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/slf4j-log4j/</guid><description>
SLF4J(Simple Logging Facade for Java) 是一个通用的日志框架，不能何以谓之 Facade(门面)，所扮眼的角色相当于 Jakarta Commons Logging。就像 JCL 需要底层的日志实现，如 Log4J、java.util.logging、Simple Logger 等来完成具体的信息输出，事实上基本总是 JCL+Log4J 那么一个绝配。SLF4J 的原旨也是能支持多种下层日志框架实现，但最好的日志实现仍然是 Log4J，所以本篇讲述 SLF4J 的第一种用法 SLF4J+Log4J。&lt;br/&gt;
&lt;br/&gt;
需要的配置文件和组件包，下面三个 jar 文件和一个 properties 文件都是要放在项目的 ClassPath 上。&lt;br/&gt;
&lt;br/&gt;
1. slf4j-api-1.5.11.jar&lt;br/&gt;
2. slf4j-log4j12-1.5.11.jar&lt;br/&gt;
3. log4j-1.2.15.jar&lt;br/&gt;
4. log4j.properties(也可以是 log4j.xml，本例中用 log4j.propertes)</description></item><item><title>Java SE6 中的新功能特性简介</title><link>https://yanbin.blog/java-se6-new-features/</link><pubDate>Tue, 06 Apr 2010 11:52:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-se6-new-features/</guid><description>
1. String 新增了 isEmpty() 方法&lt;br/&gt;
     对于 String 不需要用 str.length() !=0  来判断&lt;br/&gt;
&lt;br/&gt;
2. Arrays 新增 copyOf() 从已知数组中拷贝直接返回一个新的数组&lt;br/&gt;
     不再需要，先准备一个数组，然后用 System.arrayCopy() 来从旧数组往新数组中拷贝数据&lt;br/&gt;
&lt;br/&gt;
3. Arrays 还新增了  copyOfRange() ，binarySearch(arr, fromIndex, toIndex, des) 方法，可在一个范围内查找。&lt;br/&gt;
&lt;br/&gt;
4. 可以用 Calendar 实例的 getDisplayNames() 和 getDisplayName() 获得区域化的日期格式显示&lt;br/&gt;
&lt;br/&gt;
5. System上新增 console() 获得 Console 实例，可用其的 printf()、readLine()、readPassword()、reader()、writer() 等方法，如</description></item><item><title>新一代通用日志框架 SLF4J 用户指南</title><link>https://yanbin.blog/new-common-logging-slf4j-guide/</link><pubDate>Tue, 06 Apr 2010 04:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/new-common-logging-slf4j-guide/</guid><description>
&lt;strong&gt;&lt;br /&gt;
&lt;span style="font-size: large;"&gt;SLF4J 用户指南&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
Simple Logging Facade for Java(简单日志门面--门面一词总觉有些别扭，还没找到更好的词)，简称 SLF4J，是作为各种日志框架的简单门面或者抽象的。比如，那些日志框架有 java.util.logging, log4j 和 logback 等，SLF4j 允许终端用户在部署的时候插入自己想要的日志框架。&lt;br/&gt;&lt;br/&gt;
Hello World&lt;br/&gt;&lt;br/&gt;
依照编程的传统，这个例子描绘了简单的方式来使用 SLF4j 输出 "Hello world"。它从获得一个名为 "HelloWorld" 的 logger 开始。接着使用这个 logger 输出 "Hello World" 日志消息。</description></item><item><title>做网站用UTF-8还是GB2312？[转]</title><link>https://yanbin.blog/web-utf-8-or-gb2312/</link><pubDate>Fri, 02 Apr 2010 10:58:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/web-utf-8-or-gb2312/</guid><description>
经常我们打开外国网站的时候出现乱码，又或者打开很多非英语的外国网站的时候，显示的都是口口口口口的字符，&lt;br/&gt;
&lt;br/&gt;
　　WordPress程序是用的UTF-8，很多cms用的是GB2312。&lt;br/&gt;
&lt;br/&gt;
　　● 为什么有这么多编码？&lt;br/&gt;
&lt;br/&gt;
　　● UTF-8和GB2312有什么区别？&lt;br/&gt;
&lt;br/&gt;
　　● 我们在国内做网站是用UTF-8编码格式还是GB2312编码格式好？ &lt;br/&gt;
&lt;h3&gt;　　一. 各种编码的来历&lt;/h3&gt;&lt;br/&gt;
　　可能很多同学一直对字符的各种编码方式懵懵懂懂，根本搞不清为什么他们有这么多编码。</description></item><item><title>你应该知道的几个CSS3技巧(请用 FireFox3.6浏览可看到所有效果)</title><link>https://yanbin.blog/css3-tips-use-firefox3-6/</link><pubDate>Fri, 02 Apr 2010 05:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/css3-tips-use-firefox3-6/</guid><description>
你们中有许多人可能已经听到过有关CSS3的不少传言，但是我们现在能真正用到的CSS3技巧又有哪些呢？本文，我将向你展示一些&lt;strong&gt;与众不同的 CSS3技巧&lt;/strong&gt;，
这些技巧在一些主要的浏览器中表现良好（如Firefox，Chrome，Safari，Opera浏览器）。这些效果会在不支持的浏览器中降级渲染（如IE浏览器）。使用浏览器特定的声明，许多提议的CSS3样式都可以马上拿来使用。&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://sixrevisions.com/css/css3-techniques-you-should-know/"&gt;&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2010/css3-tips-use-firefox3-6/14-01_css3_u_know_lead_image.jpg"
width="550px"
/&gt;
&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
如果你不知道浏览器的特定声明是什么，你只需要记住它们都是CSS样式属性前与内核提供商有关的特定前缀。因为CSS3目前还没得到全面的支持，所以我们需要使用这些特定声明。具体的形式如下：&lt;br/&gt;
&lt;ul style="margin-left: 40px;"&gt;&lt;br/&gt;
&lt;li&gt;Mozilla/Firefox/Gecko浏览器的前缀: &lt;span style="font-family: Courier New;"&gt;-moz-&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Webkit (Safari/Chrome)浏览器的前缀: &lt;span style="font-family: Courier New;"&gt;-webkit-&lt;/span&gt; （&lt;span style="font-weight: bold;"&gt;注意：&lt;/span&gt;一些Wbkit的前缀只能在Safari下使用，Chrome不支持。）&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
你可能已经看到了，使用这些声明的有一个缺点，那就是如果我们想在Firefox，Safari和Chrome中都得到CSS3的效果，我们需要使用到上面所有这些前缀。也不用惊讶，IE浏览器不支持CSS3，因此也就不像其他主要浏览器有一个特定的前缀声明。&lt;br/&gt;</description></item><item><title>各大浏览器 CSS3 和 HTML5 兼容速查表[转]</title><link>https://yanbin.blog/css3-html5-browsers-compatibility/</link><pubDate>Thu, 01 Apr 2010 02:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/css3-html5-browsers-compatibility/</guid><description>
支持 CSS3 和 HTML5 的浏览器越来越多，甚至包括最新版的 IE，当然，所谓支持仅仅是部分支持，因为 CSS3 和 HTML5 的W3C 规范都尚未形成。如果你现在就希望使用 CSS3 和 HTML5 创建你的站点，至少要对各个浏览器对这两种新技术的支持情况有一个全面了解。&lt;br/&gt;
&lt;br/&gt;
需要指出的是，即使同一个浏览器的同一个版本，在 Mac 和 Windows 两个平台，它们对 CSS3 和 HTML5 的支持也并不一致。本文是一份 Chrome， Safari， Firefox， Opera， IE 5 大浏览器，在 Mac 和 Windows 两个平台，对 CSS3 和 HTML5 各种功能的详细支持情况清单。</description></item><item><title>Logging tool for JDBC</title><link>https://yanbin.blog/logging-tool-for-jdbc/</link><pubDate>Tue, 30 Mar 2010 23:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/logging-tool-for-jdbc/</guid><description>
jdbcdslog 是一款知名用来跟踪 JDBC 的全功能工具.&lt;br/&gt;
&lt;br/&gt;
特征包括:&lt;br/&gt;
- 可挂接到数据库连接,&amp;nbsp;驱动或数据源上(XA,&amp;nbsp;连接池)&lt;br/&gt;
- 可记录 PreparedStatement&amp;nbsp;和 CallableStatement 调用所绑定的参数&lt;br/&gt;
- 可记录 ResultSet 对象中的查询记录行&lt;br/&gt;
- 可记录查询执行时间&lt;br/&gt;
- 支持多种日志引擎 (感谢 SLF4J, 你可以使用多数流行的日志组件库, 像&amp;nbsp;log4j, apache common logging, java logging&amp;nbsp;或者简单的输出到 System.out)</description></item><item><title>亲历基本 OSGI 实例，进入另番思维领域</title><link>https://yanbin.blog/osgi-sample-e/</link><pubDate>Tue, 30 Mar 2010 05:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/osgi-sample-e/</guid><description>
&lt;p&gt;软件的模块越来越插件化发展了，连硬件都处处热插拔，软件更当如此。记原来有个 JPF(Java Plugin Framework)，也能实现动态插件化，但要是有个业界标准的东西一般来说会更好的。于是轮到 &lt;a href="http://www.osgi.org" target="_blank" rel="noopener"&gt;OSGI&lt;/a&gt;(Open Service Gateway Initiative) 登台，OSGI 出来也有好几个年头了，应用也轰轰烈烈的，比如 Eclipse 3 开始不再使用原来的插件体系，完全用 OSGI 搭建。WebSphere 6.1 也全面改用 OSGI；JBoss、WebLogic、Spring DM，甚至是 BMW 车的控制系统中都得到了很好的应用。&lt;/p&gt;
&lt;p&gt;前面讲到可以用 OSGI 作为你的微内核，微内核的好处可以打个这样的比方：一台 Linux 服务器出故障了，应用程序坏了、某些服务不能访问等，但只要它还来连入网络，SSH 还是活的，我们就有办法进去修复它，想安装、卸载什么都行。</description></item><item><title>启动 Equinox 时出现 The System Bundle's start level can not be modified. 错误</title><link>https://yanbin.blog/equinox-bundles-start-level-not-modified/</link><pubDate>Mon, 29 Mar 2010 23:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/equinox-bundles-start-level-not-modified/</guid><description>
使用 Eclipse 的 OSGI 实现 Equinox，通过 config.ini 配置文件在命令行时启动 Equinox 时，可以进到 osgi&amp;gt; 是提示符，但是除 org.eclipse.osgi_3.5.2.R35x_v20100126 是 STARTING 外，其他的 Bundle 都是 RESOLVED 状态，没法进入到 ACTIVE 状态，osgi&amp;gt; start xxx 也不行。&lt;br/&gt;
&lt;br/&gt;
然后看目录 configuration 目录中生成的日志文件，是如下错误：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;!SESSION 2010-03-30 11:40:40.156 -----------------------------------------------&lt;br/&gt;
eclipse.buildId=unknown&lt;br/&gt;
java.version=1.6.0_18&lt;br/&gt;
java.vendor=Sun Microsystems Inc.&lt;br/&gt;
BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=zh_CN&lt;br/&gt;
Command-line arguments:  -console</description></item><item><title>是同步方法还是 synchronized 代码？– 详解多线程同步规则</title><link>https://yanbin.blog/java-synchronized-method-codes/</link><pubDate>Mon, 22 Mar 2010 11:55:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-synchronized-method-codes/</guid><description>
熟悉 Java 的多线程的一般都知道会有数据不一致的情况发生，比如两个线程在操作同一个类变量时，而保护数据不至于错乱的办法就是让方法同步或者代码块同步。同步时非原子操作就得同步，比如一个简单的 1.2+1 运算也该同步，以保证一个代码块或方法成为一个原子操作。&lt;br/&gt;
&lt;br/&gt;
简单点说就是给在多线程环境中可能会造成数据破坏的方法，做法有两种，以及一些疑问：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;&lt;u&gt;1. 不论是静态的或非静态的方法都加上 synchronized 关键字，那静态的方法和非静态的方法前加上 synchronized 关键字有区别吗？&lt;/u&gt;&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;u&gt;2. 或者在可疑的代码块两旁用 synchronized(this) 或 synchronized(someObject) 包裹起来，而选用 this 还是某一个对象--someObject，又有什么不同呢？&lt;/u&gt;&lt;br/&gt;</description></item><item><title>Java NIO 应用 -- 使用内存映射文件实现进程间通信</title><link>https://yanbin.blog/java-nio-memory-mapping-communicate/</link><pubDate>Fri, 19 Mar 2010 03:30:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-nio-memory-mapping-communicate/</guid><description>
一看到 Java NIO 的内存映射文件(MappedByteBuffer)，让我立即就联想到 Windows 系统的内存映射文件。Windows 系统的内存映射文件能用来在多个进程间共享数据，即进程间的共享内存，是通过把同一块内存区域映射到不同进程的地址空间中，从而达到共享内存。&lt;br/&gt;&lt;br/&gt;
Java NIO 的内存映射文件和 Windows 系统下的一样，都能把物理文件的内容映射到内存中，那么 MappedByteBuffer 是否能用来在不同 Java 进程(JVM) 间共享数据呢？答案是肯定的，这样在通常的 Socket 方式来实现 Java 进程间通信之上又多了一种方法。</description></item><item><title>共享内存在Java中实现和应用[转]</title><link>https://yanbin.blog/share-memory-for-java/</link><pubDate>Thu, 18 Mar 2010 23:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/share-memory-for-java/</guid><description>
&lt;strong&gt;1、共享内存对应应用开发的意义&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
对熟知UNIX系统应用开发的程序员来说，IPC（InterProcess Communication）机制是非常熟悉的，IPC基本包括共享内存、信号灯操作、消息队列、信号处理等部分，是开发应用中非常重要的必不可少的工具。其中共享内存IPC机制的关键，对于数据共享、系统快速查询、动态配置、减少资源耗费等均有独到的优点。&lt;br/&gt;
&lt;br/&gt;
对应UNIX系统来说，共享内存分为一般共享内存和映像文件共享内存两种，而对应Windows，实际上只有映像文件共享内存一种。所以Java应用中，也是只能创建映像文件共享内存。</description></item><item><title>Java NIO 入门学习(读写文件)</title><link>https://yanbin.blog/java-nio-access-fil/</link><pubDate>Thu, 18 Mar 2010 05:51:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-nio-access-fil/</guid><description>
我们用原有 IO 读写文件应该不会陌生了，顺带回顾一下，大致两种：
&lt;br/&gt;
1. 在 InputStream 或 OutputStream 上读写字节或字节数组，读 InputStream 时用是否返回 -1 来判断是否到达末尾。&lt;br /&gt;
2. 包装成 Reader/Writer 可以直接读写字符串，进一步包装到 BufferedReader/BufferedWriter 就可以按行读写了。readLine() 时看是否返回 null 断定是否读完了最后一行。&lt;br/&gt;&lt;br/&gt;
现在我们要用 NIO 来读写文件，肯定是要用到 Channel 和 Buffer 了。一句话描述过程就是从 FileInputStream 得到的 FileChannel 中读取数据到 Buffer 中，再处理 Buffer 中的数据。看代码：</description></item><item><title>Java NIO 入门学习(通道和缓冲区)</title><link>https://yanbin.blog/java-nio-channel-buffer/</link><pubDate>Thu, 18 Mar 2010 02:30:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-nio-channel-buffer/</guid><description>
&lt;p&gt;对于 NIO 来说，Channel(通道) 和 Buffer(缓冲区) 这两个东西是不得不提的，因为它们是 NIO 的核心对象，几乎每一个 I/O 操作都要它们。&lt;/p&gt;
&lt;p&gt;通道是对原 I/O 流的模拟，但有不同的是它是双向的，不再像流那样分 InputStream 和 OutputStream，通道的双向性更好的反映了底层操作系统的真识情况。也不能像原 I/O 那样可以直接往流里写或读数据，在通道了传输数据必须经由缓冲区。有点类似原 I/O 流通过过滤器流 BufferedInputStream 和 BufferedOutputStream 来读写数据。&lt;/p&gt;
&lt;p&gt;FileInputStream/FileOutputStream/RandomAccessFile/DatagramSocket/ServerSocket/Socket 的 getChannel() 方法可以获得相应类型的 Channel，Channel 的类型有 FileChannel/SocketChannel/SelectableChannel/DatagramChannel/ServerSocketChannel 等。</description></item><item><title>Java NIO 入门学习(过门)</title><link>https://yanbin.blog/java-nio-get-started/</link><pubDate>Wed, 17 Mar 2010 05:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-nio-get-started/</guid><description>
&lt;p&gt;Java NIO(New IO) 还是在 JDK 1.4 中引入的新特性。JDK 1.4 的另一重要特性正则表达式大家应该经常用到，但 NIO 就可能使用寥寥了，大概是缘于旧有 IO 体系仍然够用。有如 JDK1.5 早就泛型和注解了，但基于各种原因而无法亲力去实践泛型和注解一般，NIO 多数时候也只是与我们擦着肩而已。&lt;/p&gt;
&lt;p&gt;相比与 Old IO，NIO 在某些领域里确有其过人之处，所以现在让我们一起来对 NIO 有个更楚的认识。&lt;/p&gt;
&lt;p&gt;NIO 的相关 API，在 java.nio 中，它会与你纠缠的就是各种 &lt;span style="color: #ff0000;"&gt;Channel&lt;/span&gt; 和 &lt;span style="color: #ff0000;"&gt;Buffer&lt;/span&gt;。相关的应用在于处理通道和缓冲区、异步(非阻塞)的 IO、文件的锁定、字符集的处理上。</description></item><item><title>了解NoSQL的必读资料汇总[转]</title><link>https://yanbin.blog/nosql-materials/</link><pubDate>Tue, 16 Mar 2010 03:58:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/nosql-materials/</guid><description>
前记：看到 Twitter 和 Digg 相继用 NoSQL 替代了原有的关系型数据库 MySql。显然，NoSQL 定然有它的许多优势，且目前已有相当的 NoSQL 产品实现，所以有必要对 NoSQL 表示一定的关注，故找些资料来看。它也是非关系型的数据库，那它与著名的文档型数据库 Domino 又有何种关系呢，Java 程序又该如何实际应用 NoSQL 呢？&lt;br/&gt;
&lt;br/&gt;
NoSQL 的官网是：&lt;a href="http://nosql-database.org/"&gt;http://nosql-database.org/&lt;/a&gt;，里面有丰富的介绍&lt;br/&gt;
Wiki 百科上的条目：&lt;a href="http://en.wikipedia.org/wiki/NoSQL"&gt;http://en.wikipedia.org/wiki/NoSQL&lt;/a&gt;。以及如下文字：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #617b85;"&gt;NoSQL&lt;/span&gt; 是非关系型&lt;span style="color: #617b85;"&gt;数据&lt;/span&gt;存储的广义定义。它打破了长久以来关系型&lt;span style="color: #617b85;"&gt;数据库&lt;/span&gt;与&lt;a href="http://en.wikipedia.org/wiki/ACID" target="_blank"&gt;&lt;span style="color: #888888;"&gt;ACID&lt;/span&gt;&lt;/a&gt;理论大一统的局面。NoSQL 数据存储不需要固定的表结构，通常也不存在&lt;a href="http://en.wikipedia.org/wiki/Join_%28SQL%29" target="_blank"&gt;&lt;span style="color: #888888;"&gt;连接&lt;/span&gt;&lt;/a&gt;操作。在大数据存取上具备关系型数据库无法比拟的性能优势。该术语在 2009 年初得到了广泛认同。</description></item><item><title>用Route-Views看看全球路由表</title><link>https://yanbin.blog/route-views-globa/</link><pubDate>Fri, 29 Jan 2010 10:09:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/route-views-globa/</guid><description>
IPv4的地址数目高达40亿个，去除了部分私有的和保留的IP地址后，互联网上可能出现的公有地址数量依然相当可观（虽然这些地址仍然不够用），在经过汇总和合理的分配之后，互联网上的路由条目仍然有接近30万条！运行在互联网上的路由器为了可以选择最佳的路径达到全球目的地，有必要接收大部分甚至所有的路由条目，想必很多人会对这个包含全球路由的BGP路由表深感兴趣吧？Let‘s check it out！&lt;br/&gt;
&lt;br/&gt;
1.打开SecureCRT或者Putty甚至系统自带的telnet程序（不建议用系统自带的）&lt;br/&gt;
&lt;br/&gt;
2.telnet 这个地址：route-server.ip.att.net&lt;br/&gt;
&lt;br/&gt;
3.接着就会收到一些欢迎信息，和提示要求输入用户名，输入rviews</description></item><item><title>关于jQuery 不同选择器的性能比较[翻译]</title><link>https://yanbin.blog/jquery-selector-performance-compare/</link><pubDate>Tue, 24 Nov 2009 03:38:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jquery-selector-performance-compare/</guid><description>
&lt;p&gt;原文：&lt;a href="http://blog.dynatrace.com/2009/11/09/101-on-jquery-selector-performance/" target="_blank" rel="noopener"&gt;101 on jQuery Selector Performance&lt;/a&gt; by Andreas Grabner, Nov 09, 09&lt;/p&gt;
&lt;p&gt;上周我认真分析了一个 Web 页面，它在 onLoad 事件中处理一段定制脚本文件用了 4.8 秒。结果是其中 2.8 秒消耗在动态菜单库上(将会在博客中单独记录)，剩下的 2 秒花费在 jQuery 的选择器上。分析显示多数选择器不返回任何对象，而那些会返回对象的选择器可考虑用不同的选择器来改善性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关于 jQuery 选择器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有大量的日志文章论述了 &lt;a href="http://docs.jquery.com/Selectors" target="_blank" rel="noopener"&gt;jQuery 选择器&lt;/a&gt;及它们的&lt;a href="http://www.artzstudio.com/2009/04/jquery-performance-rules/" target="_blank" rel="noopener"&gt;性能影响&lt;/a&gt;。正如你所知，可以通过 ID, TagName 或 ClassName 选择元素。依赖于不同的选择器， jQuery 会使用浏览器本地方法，如 通过 ID 或标签来选择元素，或者在使用类名选择时须手工从 DOM 中遍历获得元素(因为在 IE 中不存在相应的 &lt;a href="http://ejohn.org/blog/getelementsbyclassname-speed-comparison/" target="_blank" rel="noopener"&gt;getElementsByClssName&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分析我的页面时间中这 2 秒&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 onLoad 处理器中对页面中某些特定的元素使用 jQuery 设置为隐藏，显示或改变样式表...。这里是一个代码片断：</description></item><item><title>Joomla 扩展使用手记：给 JCK Patched Editor 加添 Groovy 和 Scala 语法高亮支持</title><link>https://yanbin.blog/joomla-jck-patched-editor-groovy-scala-highlighter/</link><pubDate>Mon, 12 Oct 2009 22:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-jck-patched-editor-groovy-scala-highlighter/</guid><description>
&lt;p&gt;JCK Patched Editor 2.4.6.1 Beta1(&lt;a href="http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,fileinfo/id,289/"&gt;http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,fileinfo/id,289/&lt;/a&gt;) 能够很方便的支持语法加亮显示。目前它所支持的语言有 C++、C#、CSS、Delphi、Java、JavaScript、PHP、Python、Ruby、SQL、VB.NET、XML/HTML。因为它所采用的语法着色程序是 db.syntaxhighlighter，而我之前对 db.syntaxhighlighter 也有一定的研究，它对其他语法的支持扩展是很容易的，加把相应的刷子而已。&lt;/p&gt;
&lt;p&gt;这里先讲安装好 JCK Patched Editor 之后，如何让它支持 Groovy 和 Scala 语法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 下载 Groovy/Scala 刷子脚本&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在页面 &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/?r=92"&gt;http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/?r=92&lt;/a&gt; 下载到 shBrushGroovy.js 和 shBrushScala.js 这两个脚本，放到 Joomla 站点的目录 /plugins/content/joomler_syntaxhighlighter/Scripts/ 中。</description></item><item><title>Joomla 扩展使用手记：WYSIWYG 所见即所得编辑器比较</title><link>https://yanbin.blog/joomla-wysiwyg-editor-compare/</link><pubDate>Mon, 12 Oct 2009 13:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-wysiwyg-editor-compare/</guid><description>
&lt;p&gt;对 Joomla 1.5 下的几个常用的 WYSIWYG 编辑器进行简单比较，甄选出自己喜爱的一个。每个人的口味肯定也是不一样的，用着顺手，投己所好即可，比如，我一直蛮喜欢 BlogJava 现在用的这个 FreeTextBox 编辑器的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. TinyMCE&lt;br /&gt;&lt;br/&gt;
&lt;/strong&gt;
Joomla 自带默认的编辑器，Joomla 1.5.14 中所带的版本是 3.2.4.1，2009-05-25 发布的，当前版本是 2009-09-20 发布的 3.2.7。官网: &lt;a href="http://tinymce.moxiecode.com/"&gt;http://tinymce.moxiecode.com/&lt;/a&gt;。它是开源免费的，在 Joomla 1.5.14 的汉化版中，该插件没有被汉化，按钮提示为中文，也没有中文字体。一般我们技术性的文章也不会去设置特别的字体，调调字号，颜色足矣。&lt;/p&gt;
&lt;p&gt;按照按钮的多少，分为简易、高级、扩展三种设置，虽然默认为高级功能，但我还是觉得不够，所以设置为扩展功能。有默认、Office 2007 的蓝色、银灰和黑色三种皮肤设置。</description></item><item><title>Joomla 扩展使用手记：F!BB 论坛最新话题页话题内容提示不换行的解决</title><link>https://yanbin.blog/joomla-fbb-forum-nowrap/</link><pubDate>Fri, 09 Oct 2009 22:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-fbb-forum-nowrap/</guid><description>
在对 F!BB 和 Kunena Forum 这两个论坛组件进行对比时，很容易会发现，在最新话题页面中，鼠标悬停在贴子标题上，它们都会提示贴子内容。Kunena 的提示能正确换行，而 F!BB 的提示内容无法换行，全部挤在一团，实在丑陋。再继续看看它们的显示样式，Kunena 是用 &amp;lt;a&amp;gt; 的 title 属性来提示的，可以认识回车 Chr(10)；F!BB 却创新的使用了 &amp;lt;div&amp;gt; 来显示内容提示，所以无法把内容中的 Chr(10) 转换成显示时的 &amp;lt;br&amp;gt;。&lt;br/&gt;
&lt;br/&gt;
注意的，不管是 F!BB 也好，还是 Kunena Forum，都必须设置论坛模板为 default_ex 时才会最新话题贴子内容提示的效果。&lt;br/&gt;
&lt;br/&gt;
知道问题的症结所在，解决就容易了。&lt;br/&gt;
&lt;br/&gt;
先简述一下 Kunena Forum 内容提示的实现。它的最新话题页面</description></item><item><title>Joomla 扩展使用手记：F!BB 论坛引用回贴时的 Bug 解决</title><link>https://yanbin.blog/joomla-fbb-quote-bug/</link><pubDate>Fri, 09 Oct 2009 10:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-fbb-quote-bug/</guid><description>
安装了 Joomla 1.5 的论坛组件 F!BB(Frenzy Bulletin Board) 1.5.96 RC，前台在针对贴子点击 Quote 按钮引用回贴时会出现错误&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;Fatal error&lt;/strong&gt;: Call to a member function defaultPermCheck() on a non-object in &lt;strong&gt;E:\xampp\htdocs\joomla\components\com_fbb\template\default\post.php&lt;/strong&gt; on line &lt;strong&gt;1904&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
这是 F!BB  的一个 Bug，还好错误提示告诉了是哪段代码出问题(注：不同的版本可能指示的行号不一样)。打开那个 post.php 文件，找到错误是发生在方法 quote() 中&lt;br/&gt;
&lt;br/&gt;
$can_reply  = $FbCatPermissions-&amp;gt;defaultPermCheck($canpost,$permsArray['post_own'],$this-&amp;gt;firstPostUid,$this-&amp;gt;my_id,$permsArray['post_any'])?1:0;</description></item><item><title>为什么放在 BlogJava 中的文件不能下载了呢？</title><link>https://yanbin.blog/file-cannot-down-from-blogjava/</link><pubDate>Tue, 29 Sep 2009 20:15:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/file-cannot-down-from-blogjava/</guid><description>
&lt;p&gt;我在 BlogJava 后台传的文件为什么点击不能下载了，从上星期六开始的，比如地址为：&lt;a href="https://yanbin.blog/Files/Unmi/testrmi.rar"&gt;http://www.blogjava.net/Files/Unmi/testrmi.rar&lt;/a&gt;，点击后出现 403 - Forbidden: Access is denied. 如图：&lt;br /&gt;&lt;br/&gt;
&lt;/p&gt;
&lt;div align="center"&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2009/file-cannot-down-from-blogjava/AccessIsDenied.png"
/&gt;
&lt;/div&gt;
&lt;p&gt;我以为登录后就行，可是登录后仍然一样的提示，直接在后台“文件”中点击下载也是这样提示。&lt;br /&gt;&lt;br/&gt;
希望能快点好。&lt;/p&gt;</description></item><item><title>发布 Quartz Job Scheduling Framework 中文 PDF 版</title><link>https://yanbin.blog/quartz-job-scheduling-framework-chinese-pdf/</link><pubDate>Mon, 28 Sep 2009 02:22:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-chinese-pdf/</guid><description>
还是在一年前翻译完 Quartz Job Scheduling Framework 这篇手册、并做成了 CHM 文档，见此：&lt;a id="viewpost1_TitleUrl" href="https://yanbin.blog/quartz-job-scheduling-framework-chinese-chm-v1-0-0"&gt;Quartz Job Scheduling Framework［翻译］- 中文版 CHM 文件发布&lt;/a&gt;。&lt;br/&gt;
&lt;br/&gt;
Quartz Job Scheduling Framework 中文 PDF 版下载地址：&lt;a href="
/posts/2009/quartz-job-scheduling-framework-chinese-pdf/Quartz_Job_Scheduling_Framework_CN_V1.0.0.pdf
" target="_blank"&gt;Quartz Job Scheduling Framework 中文版 V1.0.0.pdf&lt;br/&gt;
&lt;/a&gt;&lt;br/&gt;
也应不少网友的要求，应该出一个 PDF 版本的文件来，一来可能多数人觉得 PDF 看着舒服，又适于打印；再则可能就是当初制作 CHM 文件时有几个问题未解决：&lt;br/&gt;
&lt;br/&gt;
1. BlogJava 上所用的字符集是 UTF-8、而我的那些文章转成 GBK 的 HTML 文件，有此地方会乱码，且版面会不同</description></item><item><title>Web开发工具大集合——每个浏览器都有份的！[转]</title><link>https://yanbin.blog/web-dev-tools-for-every-browser/</link><pubDate>Sat, 26 Sep 2009 02:42:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/web-dev-tools-for-every-browser/</guid><description>
作者：&lt;a href="http://www.klauskomenda.com/"&gt;Klaus Komenda&lt;/a&gt; ， 翻译：&lt;a href="http://www.butwho.net/"&gt;butwho &lt;/a&gt;&lt;br /&gt;
英文原文：&lt;a href="http://www.klauskomenda.com/archives/2008/02/16/collection-of-web-developer-tools-per-browser/"&gt;Collection of Web Developer Tools, per Browser&lt;/a&gt;&lt;br /&gt;
原文发现：&lt;a href="http://www.yeeyan.com/articles/source/24832_e18/Collection_of_Web_Developer_Tools_for_major_browse..."&gt;译言-Collection of Web Developer Tools for major browsers&lt;/a&gt;&lt;br /&gt;
中文译文：&lt;a href="http://www.butwho.net/2008/05/collection-of-web-developer-tools-per-browser.html"&gt;Web开发工具大集合——每个浏览器都有份的！&lt;/a&gt;&lt;/p&gt;
&lt;br/&gt;
网站开发者的一大苦恼，就是有太多的浏览器要应付。幸亏世界上有一些聪明的人，能够研究出一大些方便编辑和调试网页代码的好工具。不过，要从不计其数的小工具里面找出合适的可真不容易。这篇文章列出了几个最好用的web开发工具，并指导你快速地激活、安装、使用他们。 下面是工具列表：</description></item><item><title>Joomla 扩展使用手记：多模块叠放展示模块 JooTabs</title><link>https://yanbin.blog/joomla-use-jootabs/</link><pubDate>Thu, 24 Sep 2009 01:25:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-use-jootabs/</guid><description>
&lt;p&gt;把多个模块用 Tab 方式堆叠起来可让你更经济的利用有限的面积，于是有了各种 Tab 模块，见：&lt;a href="http://extensions.joomla.org/extensions/style-a-design/tabs-a-slides"&gt;http://extensions.joomla.org/extensions/style-a-design/tabs-a-slides&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我试用的第一款作品是 JooTabs，在 &lt;a href="http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,fileinfo/id,1133/"&gt;http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,fileinfo/id,1133/&lt;/a&gt; 处可下载到 1.0 中文版。它的特性我就直接从那个页面贴过来了：&lt;/p&gt;
&lt;p&gt;JooTabs 特色 :&lt;/p&gt;
&lt;p&gt;1.可选用鼠标点击激活标签还是鼠标指向激活&lt;br /&gt;&lt;br/&gt;
2.8 种预设标签风格模板&lt;br /&gt;&lt;br/&gt;
3.最多支持 10 个标签同时显示</description></item><item><title>Joomla 扩展使用手记：F!BB 与 Kunena 论坛组件之比较</title><link>https://yanbin.blog/joomla-fbb-kunena-forum-compare/</link><pubDate>Wed, 23 Sep 2009 03:08:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-fbb-kunena-forum-compare/</guid><description>
目的：要为 Joomla 1.5.x 选一款论坛组件。由于想在 Joomla 后台统一管理，不想用桥接器来整合像 phpBB3 和 SMF 那样优秀的论坛，所以倾向于找个 Joomla 1.5.x 原生态的论坛组件。初步定 F!BB(Frenzy Bulletin Board) 和 Kunena Forum，它们都是基于 FireBoard(已停止开发) 开发的，所以它们长得像是自然的。现在来对它们作一个简单的比较，以找到自己合适的。&lt;br/&gt;
&lt;br/&gt;
F!BB 版本为：1.5.96 RC；Kunena 的版本是：1.55，都是从 JoomlaGate  下载的汉化版。它们都有相应的模块，如最新贴，SEO 优化的，F!BB 还有热贴模块等。还是从后台到前台吧。&lt;br/&gt;
&lt;br/&gt;
它们安装完后，F!BB 在组件菜单中显示的是：Frenzy Bulletin Forum，Kunena 显示的是：Kunenna Forum。F!BB 的名称显得有点长，但可以自己修整，安装前或安装改名都行。</description></item><item><title>Joomla 扩展使用手记：给 SMF 论坛加上 WYSIWYG 编辑器</title><link>https://yanbin.blog/joomla-smf-forum-wysiwyg-editor/</link><pubDate>Thu, 17 Sep 2009 03:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-smf-forum-wysiwyg-editor/</guid><description>
目的：为 Joomla 1.5.x 配一款好论坛，当初想着安装一个论坛组件，现在想来还是装个独立的论坛程序，然后与 Joomla 桥接起来。第三方独立的论坛基本还是好过 Joomla 的论坛组件，而且更具生命力。看看 FireBoard 的历程就知道了：&lt;br/&gt;
&lt;br/&gt;
JoomlaBoard(1.1)   ---&amp;gt;  FireBoard(1.0.5RC)                ----&amp;gt; 分支一：F!BB(Frenzy Bulletin Board 1.5.96RC)&lt;br/&gt;
(已停止开发)     (开发停止,原生不支持 J1.5.x)       |----&amp;gt;分支二：Kunena Forum(1.5.5)&lt;br/&gt;
&lt;br/&gt;
F!BB 和 Kunena Forum 都是在 FireBoard 基础开发的，原生支持 J1.5.x，两支一分，恐怕不好说是丰富了用户的选择，而是造了个岔道口，也显现其气数。且它们用的都不是 WYSIWYG(所见即所得) 的编辑器，即使有个像 Joomla 发文章那种就满足了，现在是各种 RichEditor 横行的时候谁愿意记些 BBCode。</description></item><item><title>Joomla 扩展使用手记：ChronoComments 文章评论组件</title><link>https://yanbin.blog/joomla-chrono-comments/</link><pubDate>Wed, 16 Sep 2009 07:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-chrono-comments/</guid><description>
&lt;p&gt;目标：继续找寻符合自己要求的文章评论组件，一直还没说自己的需求呢，用了再说吧。&lt;/p&gt;
&lt;p&gt;这次要安装试用的是：Chrono Comments，支持 Joomla 1.5.x，它包括两部分：&lt;/p&gt;
&lt;p&gt;1. Chrono Comments 文章评论组件 1.2 多国语言版，文件：&lt;a href="http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,startdown/id,1103/" target="_blank" rel="noopener"&gt;ChronoComments_component_V1.2_J1.5_Chinese.zip&lt;/a&gt;&lt;br /&gt;&lt;br/&gt;
2. Chrono Comments 文章评论组件内容插件 1.2 多国语言版，文件：&lt;a href="http://www.joomlagate.com/component/option,com_remository/Itemid,48/func,fileinfo/id,1104/" target="_blank" rel="noopener"&gt;ChronoComments_plugin_v1.2_Chinese.zip&lt;/a&gt;（这个似乎是多余的）&lt;/p&gt;
&lt;p&gt;其中介绍说评论组件需要依赖于插件。在中国的站点面对的用户主要还是用汉语的，不像个人软件，你爱用什么语言，别人管不着，所以得找个汉化版，它们可以从 JoomlaGate.com 上下载到，不过只去付费会员开放。Chrono Comments 的官方网站是：&lt;a href="http://www.chronoengine.com"&gt;http://www.chronoengine.com&lt;/a&gt;，那上面还有另两个组件，ChronoForms 和  ChronoConnectivity。</description></item><item><title>Joomla 扩展使用手记：shoutbox 中文站点评论模块</title><link>https://yanbin.blog/joomla-shoutbox-comments/</link><pubDate>Wed, 16 Sep 2009 03:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-shoutbox-comments/</guid><description>
&lt;p&gt;目的：通过体验、对比、遴选，找到适合于自己的 Joomla 的扩展，包括组件、模块、模板。&lt;/p&gt;
&lt;p&gt;环境：Win7 + XAMPP 1.7.1 + Joomla 1.5.14。为了更切实的体验，每次试用某个扩展时都尽量在一个干净的 Joomla 环境中：安装 Joomla 后，导入示例数据，在没有其他第三方组件时安装某一扩展。&lt;/p&gt;
&lt;p&gt;shoutbox 是 shoutbox改写的中文评论模块（作者：ufonba），下载地址：&lt;a href="http://www.maycode.com/index.php/docman/doc_details/264-shoutboxufonba.html"&gt;http://www.maycode.com/index.php/docman/doc_details/264-shoutboxufonba.html&lt;/a&gt;，官网是：&lt;a href="http://joomla.batjo.nl/"&gt;http://joomla.batjo.nl/&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在后台用 "扩展-&amp;gt;安装/卸载" 前要对下载的这个压缩包处理一下，首先它要是一个 ZIP 格式的压缩文件，并且在压缩文件的根目录上应该是用 mod_shoutbox.xml 这样的文件才行。</description></item><item><title>本想做个 Scala 的推广，无奈本可注册的域名失了时机，望大家能出谋划策</title><link>https://yanbin.blog/promote-scala-miss-dn/</link><pubDate>Mon, 14 Sep 2009 14:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/promote-scala-miss-dn/</guid><description>
了解了下 Scala(&lt;a href="http://www.scala-lang.org"&gt;http://www.scala-lang.org&lt;/a&gt;) 语言的特性，也觉得颇有些搞头。
首先不失面象对象的特征，又能作泛函编程，并能像 Erlang 那样适用于多核、并发编程。
且看看这个语言的名称 &lt;strong&gt;Scala&lt;/strong&gt;ble，即可知它的志向之远大。力求以高定制、扩展性去应用于，无论是小软件还是大应用。&lt;br /&gt;&lt;br /&gt;
Groovy 之父说过：要是早知有 Scala，就不会再发明 Groovy。这两东西都是构筑在 JVM 平台的，要说 Groovy 发展至念今，所得奖项也不少，推广好像不咋样。
以及有“Scala 将成为现代 Web 2.0 的发起语言” 的论调，还有 Scala 实际在 Twitter 上的应用等等。&lt;br /&gt;&lt;br /&gt;
然而，国内好像对  Scala 的了解还不多，于是滋生了一个在国内对它进行推广的念头：建个站点、弄个域名、紧跟 Scala 的脚步，网友热心参与。
就像人名一样，域名也应该是要让人印像深刻的。前几天在万网上搜索时像 scalachina.com、scalacn.com、cnscala 等都没人用，可是工作忙了下，
耽搁了几天，那几个域名就被人占了去，并且也能看到人家的页面了。&lt;br /&gt;&lt;br /&gt;
没奈，时间上咱是差了一步，那我还是有信息在其他方面做得更好的啊。再说呢，也没过几个 什么什么中国 的网站做得特别出色的呀，
看看 JavaEye 是怎么后来崛起的就知道了。我的目标是想做出一个像 JavaEye 那样的东西，看过有个叫 PhpEye，如果再叫 ScalaEye 就有些老土了，
原本想的 Scala 中国 这样的名称就有些俗的。&lt;br /&gt;&lt;br /&gt;</description></item><item><title>Joomla! 1.5 安装程序完全导读【转】</title><link>https://yanbin.blog/joomla-1-5-installation-guide/</link><pubDate>Wed, 09 Sep 2009 23:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/joomla-1-5-installation-guide/</guid><description>
&lt;p class="copyright"&gt;本文来源于：&lt;a href="http://www.maycode.com/index.php/hotspot/39-joomla-inspect/526-joomla-15-installation-code-comprehend.html"&gt;&lt;span style="color: #2970a6;"&gt;http://www.maycode.com/index.php/hotspot/39-joomla-inspect/526-joomla-15-installation-code-comprehend.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p class="copyright"&gt;本文依照 Joomla! 1.5 Stable 版本的安装程序写作，文中所引用代码均来自实际程序文件。&lt;span id="more-104"&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;我们从安装程序入口文件 &lt;strong&gt;/installation/index.php &lt;/strong&gt;开始。&lt;/p&gt;
&lt;h2&gt;1. 常量定义&lt;/h2&gt;
index.php 文件起始处定义了若干常量：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;JPATH_BASE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="no"&gt;__FILE__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;DS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DIRECTORY_SEPARATOR&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;strong&gt;_JEXEC &lt;/strong&gt;是一个标志量，表示程序正常启动(通过合法入口 /index.php 访问)。Joomla! 在其他所有的 .php 程序起始处，均通过执行以下代码，阻止直接访问非入口程序的请求，这是出于安全的考虑：</description></item><item><title>iBatis 使用 OSCache 作缓存与 java.io.FileNotFoundException 异常</title><link>https://yanbin.blog/ibatis-oscache-java-io-filenotfoundexception/</link><pubDate>Wed, 19 Aug 2009 00:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ibatis-oscache-java-io-filenotfoundexception/</guid><description>
这种异常只会发生在 Windows 平台上，可以说是 iBatis 的一个 Bug，大约 iBatis 的开发测试人员都比较热衷于 Unix 族系的平台，而忽略了 Windows 下的兼容性测试。&lt;/p&gt;
&lt;br/&gt;
出现 FileNotFoundException 异常的情形是这样的：Windows7 + iBatis2.3.0.677 + OSCache2.4.1。&lt;br/&gt;&lt;br/&gt;
iBatis 中配置使用 OSCache 缓存，在映射文件 Product.xml 中有如下声明片断：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;cacheModel&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;OSCACHE&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;productCache&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;flushOnExecute&lt;/span&gt; &lt;span class="na"&gt;statement=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Product.insert&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;flushOnExecute&lt;/span&gt; &lt;span class="na"&gt;statement=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Product.delete&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/cacheModel&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;getById&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;cacheModel=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;productCache&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;parameterClass=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;int&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;resultClass=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Product&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; select id, name,description as desc1 from test_product where id = #value#
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
OSCache 的 oscache.properties 中指明用磁盘文件缓存数据，注意以下几个配置：</description></item><item><title>也说 Java 的双括符初始化, 其实就是令人费解的不规范代码</title><link>https://yanbin.blog/java-double-brackets-initial/</link><pubDate>Wed, 12 Aug 2009 03:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-double-brackets-initial/</guid><description>
初看 cgaolei 翻译的 &lt;a href="http://www.javaeye.com/topic/418542" target="_blank" rel="noopener"&gt;Java技巧之双括弧初始化&lt;/a&gt; 一文，走马观花，只知用法，未细看后面的解释。蔚为惊艳，心里想 Java 竟然有这么神奇的语法而一直未得知。因为在初始化集合时确实方便不少。原来做某些测试要初始化集合时会用到 commons-lang 包和 JDK 的 Arrays 工具类，现在知道可以这么用了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;QQ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1125535&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stooges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Larry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Moe&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Curly&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
看起来都是在一条语句里完成，而不需要分步骤写成:</description></item><item><title>使用 XStream 在 JavaBean 与 XML/JSON 之间相互转换</title><link>https://yanbin.blog/xstream-javabean-xml-json/</link><pubDate>Tue, 11 Aug 2009 08:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xstream-javabean-xml-json/</guid><description>
XML 和 JSON 是当今常用的两种数据描述与传输的格式，特别是涉及到 JS 时使用 JSON 颇为频繁。自然，在 Java  的世界里少不了完成 JavaBean 与这两种格式相互转换的组件，那就是 XStream 和 JSON-lib。这里我简单记下 XStream  的用法。&lt;/p&gt;
&lt;br/&gt;
其实相类似的工具早已有之。如果用过 DWR 的同志，一定有印像，DWR 进行远程方法调用时也能为你完成 JavaBean 和 JSON 格式的双向转换的，所依赖的是它的各种 Converter。再要是对 Struts1 的细节有所注意的话，Struts1 的 ActionServlet 在初始化 struts-config.xml 时是通过 &lt;a href="http://commons.apache.org/digester/" target="_blank" rel="noopener"&gt;commons-digester&lt;/a&gt; 来完成 XML 到 JavaBean 转换的。相应的 Apache 也有一个 &lt;a href="http://commons.apache.org/betwixt/" target="_blank" rel="noopener"&gt;commons-betwixt&lt;/a&gt; 实现了 JavaBean 到 XML 的生成。</description></item><item><title>Ext.data.Record 的基本法及其 API 帮助的一个 Bug</title><link>https://yanbin.blog/extjs-data-record-api-bug/</link><pubDate>Thu, 23 Jul 2009 03:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/extjs-data-record-api-bug/</guid><description>
&lt;strong&gt;基本用法(不涉及其他相关类)：&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
Ext.data.Record 实例就是对应一条记录，记录通常会放在数据集 Store 中，其中包含了数据的定义信息(字段名等) 和数据的值信息。这里不涉及与它相关的 Proxy、DataReader 和 Store，只单纯的用 Record 的方法。通常是两步，先调用 Record 的 create() 方法，返回一个类，然而 new 前面方法返回的类，代码过程如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//第一步，create() 返回一个 Record 的子类(定义了数据表现，有点像泛型)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="c1"&gt;//用对象数组指定每个字段名
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;//name 属性指定每个字段名
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//通过前面 create() 返回的 Person 类创建 record 实例
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="c1"&gt;//传入一个对象作为第一个参数，注意属性名对应
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Unmi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fantasia@sina.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//第二个参数指定记录的标识 ID，未指定 ID 则为你用整数进行编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//验证效果，只取 id 和其中的 email 属性，可试下前面无 {id:&amp;#39;name&amp;#39;}的情况
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;, email:&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
代码说明：</description></item><item><title>ExtJs 中使用 TaskRunner 和 DelayedTask 如何传参数到 run() 方法</title><link>https://yanbin.blog/extjs-taskrunner-delayedtask-run-method/</link><pubDate>Tue, 21 Jul 2009 09:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/extjs-taskrunner-delayedtask-run-method/</guid><description>
在 ExtJs 中支持多线程的类有 Ext.util.TaskRunner 和 Ext.util.DelayedTask。TaskRunner 提供了多线程的定时服务，DelayedTask 允许你延时多久在新建线程中执行一个任务。Ext.TaskMgr 是一个 TaskRunner 实例，在 TaskMgr.js 源码中可以看到最后一行是：&lt;/p&gt;
&lt;br/&gt;
Ext.TaskMgr = new Ext.util.TaskRunner();&lt;br/&gt;&lt;br/&gt;
实质上不管是 TaskRunner 还是 DelayedTask，它们都是通过 setInterval() 来执行任务的，TaskRunner 能多次重复的执行一个方法，而 DelayedTask 是延时执行完一次任务后就会调用 clearInterval() 来保证只执行一次。所以这里所说如何向任务的 run() 方法传递参数，本质上就是向 setInterval() 中方法传递参数。</description></item><item><title>如何无需密码进行 SSH 连接[翻译]</title><link>https://yanbin.blog/ssh-no-need-input-password/</link><pubDate>Mon, 20 Jul 2009 02:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ssh-no-need-input-password/</guid><description>
&lt;p&gt;译自：&lt;a href="http://www.vineetmanohar.com/2009/07/17/howto-ssh-without-password/"&gt;http://www.vineetmanohar.com/2009/07/17/howto-ssh-without-password/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个简单的教程讲解了如何无需输入密码 SSH 连接到远程机器。在你需频繁的登陆到同一机器，而又不得不一次次输入密码时就可以使用这一技术。这也很适于这样的场景：当你有一个脚本需要通过 SSH 从远程机器取文件或者在远程机器上执行一个任务，并且希望能自动的运行该脚本，而用不着人工去输入一个密码。&lt;/p&gt;
&lt;p&gt;这些指令在 Linux 和 Mac 下能正常工作。你可以在 Windows 下参照同样的方法来使用 Putty，但是我这里没有针对具体 Putty 指令作描述。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: medium;"&gt;目标：从本机登陆到远程服务器无需输入密码&lt;br /&gt;&lt;br/&gt;
&lt;/span&gt;&lt;/strong&gt;
&lt;strong&gt;步骤 1/2：在本机上：生成认证码&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;认证码是一个私钥公钥对。你的公钥也是一个 2-3 行的长长看不懂东西。公钥就像是你登录后的唯一标识。私钥就像是你的密码，但比一个规则的密码要长。你可以通过如下命令来产生你的公钥和私钥：</description></item><item><title>揭秘IT人才特点：中美印日四国程序员比较 [转]</title><link>https://yanbin.blog/it-programmer-compare/</link><pubDate>Thu, 16 Jul 2009 20:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/it-programmer-compare/</guid><description>
&lt;div&gt; 最近以裁判的身份参加了公司举办的编程大赛，发现高手云集，对公司内部的程序员能力也有了更深入的了解。我觉得编程能力对程序员而言，虽然很重要，但并不是全部。那么作为一个程序员，到底应该具备什么样的能力呢？这个话题显然太大。不过我觉得可以看看其它国家的程序员，也许可以得到一些借鉴。我有幸和中国，美国，印度和日本四国程序员有比较深入的合作过。虽然他们不一定有代表性，但我觉得他们的共性还是比较明显的。以下的比较纯属个人见解，欢迎指正。&lt;/div&gt;
&lt;div&gt; &lt;/div&gt;
&lt;div&gt;首先是日本程序员。他们的特点是非常仔细。我认为很主要的一个原因是日本公司的需求非常细致。细致到在网页上，连一个像素都不能偏差的地步。另外，日本人的执行力非常强，对老板的承诺比命还重要。一个项目可以做到连续3个月天天加班，每天只睡4个小时。然而，高执行力背后的代价是低创造力。在日新月异的互联网今天，很少听说日本工程师发明了哪些重要的技术。与其说这些特点是日本程序员的，不如说是大部分日本人的。因为在日本文化中，追求品质和遵守等级制度是根深蒂固的。另外，技术领域中的很多专业词汇是外来语，以英语(论坛)为主。这些专业词汇往往会被翻译成片假名。而片假名的发言有时候和英语大相径庭，导致沟通的困难。比如病毒一词在英语中是Virus，发音为歪儒斯，而日语的发音是味鲁斯。再例如服务器（Server）一词在日语中的发音是萨巴，和英文发言简直风牛马不相及。因此与日本程序员沟通是比较痛苦的，除非你懂日语。</description></item><item><title>ExtJs 中 xtype 与组件类的对应表</title><link>https://yanbin.blog/extjs-xtype-class-contrast/</link><pubDate>Thu, 16 Jul 2009 02:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/extjs-xtype-class-contrast/</guid><description>
我们在使用 ExtJs 创建组件时最容易理解的当然是用&lt;br/&gt;
 &lt;br/&gt;
new Ext.form.TextField({fieldLabel:'姓名', id:'name',width:120})；&lt;br/&gt;
&lt;br/&gt;
我们还可以直接用 xtype(比如 TextField 对应的 xtype 是 textfield) 的对象形式来创建组件，比如在面板的 items 属性中，尤其是多个组件或需要写许多的 ExtJs 相关代码时就更值得推荐。我们来对照如下形式就知道了：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;姓名&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;密码&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;passwd&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;生日&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;birth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Y年m月d日&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//替换成用 xtype 写法就如下(似乎只是免去了很多的 new ...)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;xtype&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;textfield&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;姓名&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;xtype&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;textfield&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;密码&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;passwd&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;xtype&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;datefield&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldLabel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;生日&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;birth&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Y年m月d日&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>创建兼容 IE/FireFox 的 event 及 event 的 srcElement、fromElement、toElement 属性</title><link>https://yanbin.blog/ie-firefox-event-srcelement-fromelement-toelement/</link><pubDate>Fri, 10 Jul 2009 06:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ie-firefox-event-srcelement-fromelement-toelement/</guid><description>
自然，我们都习惯了 IE，在 IE 中要在函数中获得各事件对象很容易，直接用 event、event.srcElemtn、event.fromElement、event.toElement 就行了。在 FireFox 中获得触发事件的元素可以用 event.target，但其他两个 fromElement 和 toElement 就要费些周折。&lt;br/&gt;&lt;br/&gt;
所以，为了保持一致的使用方式，也为了保持原有的使用习惯，我们加入以下 JS 代码(代码有些紧凑，未加注释，应该很好理解)：</description></item><item><title>考虑在本博客中换用另一种代码着色方式</title><link>https://yanbin.blog/change-syntx-highlight-method/</link><pubDate>Fri, 10 Jul 2009 01:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/change-syntx-highlight-method/</guid><description>
&lt;p&gt;原来是仿照 JavaEye 那样对 name 为 code 的 &amp;lt;textarea&amp;gt; 以指定的样式类进行格式化显示，效果如下图：&lt;/p&gt;
&lt;div&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2009/change-syntx-highlight-method/SyntaxHighlighter_Old.jpg"
width="750px"
/&gt;
&lt;/div&gt;&lt;p&gt;优点：1. 比较清爽&lt;br /&gt;         2. 可以选择局部进行复制，不会带上前面的标号&lt;br /&gt; 缺点：1. 使用 &amp;lt;textarea&amp;gt; 在其他浏览器上不利于输入和修改</description></item><item><title>创建兼容 IE/FireFox 的 insertAdjacentHTML 方法</title><link>https://yanbin.blog/ie-firefox-insertadjacenthtml/</link><pubDate>Wed, 08 Jul 2009 21:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ie-firefox-insertadjacenthtml/</guid><description>
在 IE 中我们可以用 insertAdjacentHTML 往元素的 beforeBegin,beforeEnd,afterBegin,afterEnd 处理插入新元素。而在非 IE 中没有该方法，因而我们要创造一个兼容的 insertHtml 方法来。&lt;br/&gt;&lt;br/&gt;
本方法从 Ext 2 中剥离出来的，在 ext-all-debug.js 中有以下方法：&lt;br/&gt;&lt;br/&gt;
Ext.DomHelper.insertHtml( String where, HTMLElement el, String html ) : HTMLElement&lt;br/&gt;&lt;br/&gt;
参数介绍：&lt;br/&gt;&lt;br/&gt;
where：插入位置。包括beforeBegin,beforeEnd,afterBegin,afterEnd。&lt;br /&gt;
el：用于参照插入位置的html元素对象&lt;br /&gt;
html：要插入的html代码</description></item><item><title>对SQL说不！NoSQL的数据库技术革命[摘]</title><link>https://yanbin.blog/no-sql-nosql/</link><pubDate>Tue, 07 Jul 2009 01:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/no-sql-nosql/</guid><description>
在NoSQL运动的最新一次聚会中，来自世界各地的150人挤满了CBS Interactive的一间会议室。&lt;br/&gt;
&lt;br/&gt;
就像当年波士顿的爱国者为反抗英国重税的行动一样，NoSQL的支持者们从各地涌来，分享他们如何推翻缓慢而昂贵的关系数据库的暴政，怎样使用更有效和更便宜的方法来管理数据。云计算可能会为数据库技术的发展带来新的契机，如果想在云中获得丰富而随需应变的可伸缩性，你需要一个非关系数据库。&lt;br/&gt;
&lt;br/&gt;
“关系型数据库给你强加了太多东西。它们要你强行修改对象数据，以满足RDBMS （relational database management system，关系型数据库管理系统）的需要，”Java工具提供商SpringSource公司的首席工程师Jon Travis说，他是本次集会的10位发言人之一，在他看来，基于NoSQL的替代方案“只是给你所需要的”。</description></item><item><title>关于 JavaBean 规范你还是应该知道的二三事(补充)</title><link>https://yanbin.blog/about-javabean-spec-plus/</link><pubDate>Thu, 02 Jul 2009 04:42:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-javabean-spec-plus/</guid><description>
&lt;p&gt;前面写过一篇：&lt;a id="viewpost1_TitleUrl" href="https://yanbin.blog/about-javabean-spec/" target="_blank"&gt;
&lt;span style="color: #000080;"&gt;关于 JavaBean 规范你还是应该知道的二三事&lt;/span&gt;
&lt;/a&gt;，发现还略受关注。其中有人对 boolean 型属性的 getter/setter 方法还有些想法，以及 JavaBean 的规范是根据属性名找相应的
getter/setter 方法，还是由 getter/setter 定位属性呢。本文主要就这两问题展开话题，原本想附中前篇中去，但考虑会让前文凌乱，所以另立新篇。
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 关于 boolean  型属性&lt;br /&gt; &lt;/strong&gt;&lt;br /&gt; 分别来看看 Eclipse(3.5) 和 NetBean(6.7) 的重构功能对
oolean student 和 boolean isStudent 生成什么样的 getter/setter 方法的。</description></item><item><title>关于 JavaBean 规范你还是应该知道的二三事</title><link>https://yanbin.blog/about-javabean-spec/</link><pubDate>Wed, 01 Jul 2009 09:19:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-javabean-spec/</guid><description>
作为 Java  程序员，对于 JavaBean 也许你会说再熟悉不过了，它贯穿在系统的多层中，不同的叫法有 PO、VO、DTO、POJO、DO(Domain Object)。然而它无外乎就是一个 Class 类，带上些属性和它们的 setter/getter 方法，set/get 后面那一个字母大写。虽然我们现在很少把 JavaBean 与那个古老的 2.0 的 EJB 搞混，但为什么明明用 IDE 为属性生成的 getter/setter 方法，应用一运行，还是报找不到某个 bean 属性的 setter 或 getter 方法呢？&lt;br/&gt;
&lt;br/&gt;
要知道，在 Sun 的网站上那个关于 JavaBean 规范的 PDF 文档可是有足足实实的 114 页啊。难免有些规则有点古怪，至使知名的 IDE 都难以应对，所以我们还是有必要了解其中二三，来规范我们的 JavaBean 和解释一些情形。&lt;br/&gt;
&lt;br/&gt;
Sun  的关于 JavaBean 规范见：&lt;a href="http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html"&gt;http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html&lt;/a&gt;，其中可下载到 JavaBean 规范的  PDF 文档。</description></item><item><title>RTX2009 Beta1对外发布[转]</title><link>https://yanbin.blog/rtx2009-beta1-release/</link><pubDate>Wed, 01 Jul 2009 06:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/rtx2009-beta1-release/</guid><description>
&lt;strong&gt;经过快速的开发和一定时间的小范围试用之后，RTX2009Beta1版本在今天发布了。目前RTX保持每个季度发布一个版本的节奏，使用户的反馈和需求能快速响应。
&lt;/strong&gt;RTX2009Beta1最重要的功能是实现了单套RTX可部署多台文件服务器，这个功能特别适合那些有分支机构的大型用户，在多人会话传文件时，
保证本地用户的文件传输都在本地文件服务器上中转，从而减少分支机构与总部之间的带宽占用，降低成本、提升体验。此功能与之前发布的企业集群结合使用，
几乎可以满足所有大型企业用户的最复杂部署，相应的介绍文档请参考：&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;RTX2009Beta1的更新内容如下:&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>Solaris 下使用 NFS 共享文件的几个链接</title><link>https://yanbin.blog/solaris-nfs-links/</link><pubDate>Thu, 25 Jun 2009 22:08:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/solaris-nfs-links/</guid><description>
1. &lt;a id="AjaxHolder_ctl01_TitleUrl" href="http://www.cnblogs.com/zdjxy/articles/377044.html" target="_blank"&gt;Solaris下NFS使用手册&lt;/a&gt;&lt;br /&gt;2. &lt;a href="http://blog.csdn.net/winterld/archive/2004/12/20/222780.aspx" target="_blank"&gt;Solaris NFS详解&lt;/a&gt;&lt;br /&gt;3. &lt;a href="http://www.mx68.com/caozuoxitong/2006-03-24/caozuoxitong_57627.shtml" target="_blank"&gt;Solaris nfs server &amp;amp; client配置&lt;/a&gt;&lt;br /&gt;4. &lt;a href="http://chenny.blog.ccidnet.com/blog-htm-do-showone-uid-2079-itemid-305929-type-blog.html" target="_blank"&gt;solaris下的NFS配置&lt;/a&gt;&lt;br /&gt;5. &lt;a href="http://yangtingkun.itpub.net/post/468/284160" target="_blank"&gt;Solaris NFS mount&lt;/a&gt;&lt;br /&gt;6. &lt;a href="http://blog.chinaunix.net/u2/88690/showart_1732894.html" target="_blank"&gt;solaris NFS 服务器&lt;/a&gt;&lt;br /&gt;7. &lt;a href="http://chandlewei.blogbus.com/logs/41355832.html" target="_blank"&gt;强制 umount nfs文件系统&lt;/a&gt;</description></item><item><title>vi 与 vim 的区别，及 vim 的语法加亮</title><link>https://yanbin.blog/vi-vim-difference-syntax-highlight/</link><pubDate>Wed, 24 Jun 2009 16:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-vim-difference-syntax-highlight/</guid><description>
之前每次终端连接到了 Linux/Unix 编辑文件，只知道用 vi，至于此时是 vi 还是 vim 就不得而知了，也不知如何去知。总以为 vi 就是 vim，vim 就是 vi，就没认真想过多出的 vim 是怎么回事。&lt;/p&gt;
&lt;br/&gt;
据说：vi - visual editor, very inconvenient, or virtually incomprehensible， vim - VI IMproved，俨然是个 vi 的加强版。&lt;br/&gt;&lt;br/&gt;
只是经常会登录同是 Redhat AS 4 的两台机器，同样是用 vi 启动编辑器，一个能进行语法加亮，v 可进入可视模式，空行前的波浪号是蓝色的(下称机器A)；另一个对于各种语法却是熟视无睹，v 无法进入可视模式，空行前的波浪号也是白色的(下称机器B)。这时才想起是否是 vi 与 vim  的区别，网上找来：</description></item><item><title>vi/vim 快捷键盘对照图（含中英文）</title><link>https://yanbin.blog/vi-vim-shortcuts/</link><pubDate>Wed, 24 Jun 2009 11:43:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-vim-shortcuts/</guid><description>
&lt;p&gt;对于经常要辗转于 Linux/Unix 终端的用户，时常要使用 vi 来编辑文本。Emacs 虽说功能强，但学习起来更为陡峭，
而况有些系统默认时没有 Emacs。 对于 vi 未有多份心者，也许使用它四五年时间，不过 i、o、dd、:wq、方向键移动光标尔耳，y、p 拷贝粘贴都极少用。&lt;/p&gt;
&lt;p&gt;总是想，既然常常用一个软件，那就尽量用好它，为工作多提升效率，不必多耗费时间，只每时多份心而已。故网上找来 vi/vim 的快捷键盘对照图，
有中英文对照，可打印出来，贴于你座位的隔板上，以助记忆习知。&lt;/p&gt;&lt;p&gt;若是对 vi/vim 有了好感，也想在 windows 一试 vi/vim  的身手，
你可以用 cygwin 里的 vi。而且 vi/vim 也像 Emacs  一样，有了 for windows 版的 vi/vim，并且一来就是俩：</description></item><item><title>DWR 的 Converter 实现原理简单分析及应用</title><link>https://yanbin.blog/dwr-converter-mechanism-application/</link><pubDate>Tue, 23 Jun 2009 04:43:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dwr-converter-mechanism-application/</guid><description>
我们在应用 DWR 调用远程方法时涉及到 JS 与 JAVA 之间参数和返回值的数据转换，例如：&lt;br/&gt;
&lt;br/&gt;
JS 的 123 与 Java 的 int  或 Integer、long 间的转换&lt;br/&gt;
JS 的 "2009-06-23" 与 Java 的 java.util.Date 之间的转换&lt;br/&gt;
JS 的 "[1,2,3]" 与 Java 的 int[] 间的转换&lt;br/&gt;
JS 的 "{id:123, name: 'Unmi'}" 与 Java 的 Class Person{int id; String name} 间的转换&lt;br/&gt;
&lt;br/&gt;
或者更复杂的嵌套类型( "{id:123, name: 'Unmi', blogs:['http://unmi.blogjava.net','http://blog.csdn.net/kypfos']}" ) 与 Java 类型间的转换，等等。那么这一切是怎么进行的呢？其实我们见识过很多组件的类型映射，如 Java 的 PropertyEditor、Hibernate(UserType)、iBatis(TypeHandler) 的类型映射，Struts1/2 中 Form/Model 用的 Converter 等。</description></item><item><title>请教一下 Servlet 中如何设定下载文件保存用的字符集</title><link>https://yanbin.blog/servlet-download-encoding/</link><pubDate>Thu, 18 Jun 2009 03:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/servlet-download-encoding/</guid><description>
&lt;p&gt;在前面一篇日志：&lt;a href="https://yanbin.blog/understand-apply-struts2-file-download/" target="_blank"&gt;
理解和灵活应用 Struts2 的文件下载功能
&lt;/a&gt; 还留下一个问题，在 Servlet 中支持文件下载功能时，客户保存下载文件所用的字符集总是 ANSI(ISO8859-1)，
如何能设定客户保存文件用的字符集呢？像 BlogJava 的备份数据功能下载后就是用的 UTF-8 字符集的。
自己作了一些尝试，总是无济于事，看如下代码是 Servlet 的 doGet() 方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;doGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpServletRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HttpServletResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServletException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// PrintWriter out = response.getWriter();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PrintWriter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PrintWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OutputStreamWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOutputStream&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContentType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/plain;charset=UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Content-Disposition&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;attachment;filename=down.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Servlet download file test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>使用 DWR 我是这样做简单分页的[自己记录一下]</title><link>https://yanbin.blog/dwr-simple-pagination/</link><pubDate>Wed, 17 Jun 2009 21:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dwr-simple-pagination/</guid><description>
DWR 本身未提供分页的功能，所以这部分要自己来实现。如果把后台的所有记录都拉到页面用 JS 分页，那是很不现实的。因此，虽说是 DWR 分页，但还得服务端来支持，由服务端的远程方法只返回当前页所显示的记录，DWR 自己要明确当前是第几页，是否有上一页、下一页。&lt;/p&gt;
&lt;br/&gt;
还是用代码来说明吧，分解为三部分代码：&lt;br/&gt;&lt;br/&gt;
1. 暴露给 DWR 调用的 Java 的远程方法&lt;br /&gt;
2. JS 的 DWR 调用&lt;br /&gt;
3. 显示分页导航栏&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;DWR 要调用的 Java 方法：&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>理解和灵活应用 Struts2 的文件下载功能</title><link>https://yanbin.blog/understand-apply-struts2-file-download/</link><pubDate>Wed, 17 Jun 2009 01:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-apply-struts2-file-download/</guid><description>
文件下载给我们最直接的概念就是，给个文件链接点击就下载。似乎太简单，然而 Struts2 却把它作为一个独立的学问来对待，理由有四：&lt;/p&gt;
&lt;br/&gt;
1. 文件名为中文时，直接点击下载，链接可能会走样(某些浏览器，URL 编码的问题)，致使无法下载。&lt;br /&gt;
2. 不总是从下载实际的文件，文件内容有时候是动态生成的，如数据库中的内容。&lt;br /&gt;
3. 对于知名的文件类型不让浏览器直接打开，而是出现下载对话框保存文件。例如，要下载的文件是 .txt 的，可能直接就在浏览器中显示其内容。&lt;br /&gt;
4. 需要授权才能下载文件时&lt;br/&gt;&lt;br/&gt;
当然对于以上若干问题，Servlet/JSP 都能通过正确的 URL 编码，响应头设置、权限代码控制解决，只是 Struts2 让我们处理起来更方便了，内部原理自然是一样的。</description></item><item><title>DWR 调用远程方法的错误和异常处理</title><link>https://yanbin.blog/dwr-remote-method-exception-handle/</link><pubDate>Mon, 15 Jun 2009 03:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dwr-remote-method-exception-handle/</guid><description>
记得以前通常我们在调用 DWR 的远程方法是这样写&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Remote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//服务端执行后客户端的逻辑，如
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;服务端执行后返回的数据：&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
第一个至倒数第二个参数为客户端传向服务端的数据，最后一个参数直接就是一个回调函数。因为服务端根据 Java 对象生成的 js 代码就是：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;Remote.method = function(p0, callback) {&lt;br/&gt;
    dwr.engine._execute(Remote._path, 'Remote', 'method', p0, callback);&lt;br/&gt;
}&lt;br/&gt;
&lt;/span&gt;&lt;br/&gt;</description></item><item><title>DWR 的 DWRUtil.addRows() 函数的使用</title><link>https://yanbin.blog/dwr-dwrutil-addrows-function/</link><pubDate>Fri, 12 Jun 2009 02:55:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dwr-dwrutil-addrows-function/</guid><description>
使用 DWR 一个常用的操作就是取到的数据往表格里填充，这就是 DWR 的 DWRUtil.addRows() 操作了，本篇就是以例子详细说明这个函数的用法。而另一个移除所有表格行的操作 DWRUtil.removeAllRows() 使用简单就不多说明了。&lt;/p&gt;
&lt;br/&gt;
开始的时候我们还是要对这个函数的原型加以说明，虽然有些俗套，但还是必要的：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;DWRUtil.addRows(id, array, cellfuncs, [options]);&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
描述:&lt;br /&gt;
向指定id的table元素添加行。它使用数组中的每一个元素(对象)在table中创建一行。然后用 cellfuncs 数组中的对应的函数创建一个列。单元格是依次用 cellfunc 根据没有数组中的元素创建出来的。&lt;br/&gt;&lt;br/&gt;
DWR1.1开始，addRows()也可以用对象(遍历对象的属性创建行)做为数据。如果你用一个对象代替一个数组来创建单元格，这个对象会被传递给cell函数。</description></item><item><title>通过 Squid 访问出现“确保报表服务器server.property设置了'proxyServer'参数”的解决</title><link>https://yanbin.blog/squid-server-property-proxyserver-error/</link><pubDate>Thu, 11 Jun 2009 22:07:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/squid-server-property-proxyserver-error/</guid><description>
内部网通过 Squid 代理访问国税的 税收调查网上直报(&lt;a href="http://218.18.101.150:8001/i/main.jsp"&gt;http://218.18.101.150:8001/i/main.jsp&lt;/a&gt;) -- &lt;a href="mailto:i@Report"&gt;i@Report&lt;/a&gt;通用 WEB网络报表平台 时页面只出现：&lt;br/&gt;
&lt;br/&gt;
XXX.XXX.XXX.xx透过反向代理访问服务器。请确保报表服务器server.property设置了'proxyServer'参数&lt;br/&gt;
&lt;br/&gt;
不走代理就没事，在网上通过上面的关键字没找到相应的解决办法，还查英文的报错信息：&lt;br/&gt;
&lt;br/&gt;
please esure property "proxyServer" in server.property was not empty&lt;br/&gt;
&lt;br/&gt;
找到一份答案：&lt;a href="http://bbs2.chinaunix.net/archiver/tid-1391943.html"&gt;http://bbs2.chinaunix.net/archiver/tid-1391943.html&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
原因是访问该网站时要求 Squid 对 请求 Header 部作些处理，上面链接中给了多种方法，有些不一定对你可行。最后我是在 squid.conf 文件后面加上以下两行：</description></item><item><title>Perl 使用 Log::Log4perl 输出日志的用法</title><link>https://yanbin.blog/perl-log4perl-user-guide/</link><pubDate>Wed, 10 Jun 2009 05:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-log4perl-user-guide/</guid><description>
用 Java 的很少有人对 log4j 感觉陌生，这是 Apache  上的项目。同时 Apache 上也有针对其他语言的 Log 组件，如 log4cxx、log4net、log4php。&lt;/p&gt;
&lt;br/&gt;
所用 Perl 时也会想想：Perl 该用什么日志模块呢？ log4perl？google 一下果然后，只不过它不是由 Apache 出品。&lt;br /&gt;
该项目原来在 &lt;a href="http://log4perl.sourceforge.net/"&gt;http://log4perl.sourceforge.net/&lt;/a&gt;，现已移到 &lt;a href="http://mschilli.github.com/log4perl/"&gt;http://mschilli.github.com/log4perl/&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;
下载地址：&lt;a href="http://search.cpan.org/~mschilli/Log-Log4perl/"&gt;http://search.cpan.org/~mschilli/Log-Log4perl/&lt;/a&gt; (要编译的)&lt;br /&gt;
使用文档：1. &lt;a href="http://www.perl.com/pub/a/2002/09/11/log4perl.html" target="_blank" rel="noopener"&gt;Tutorial(Retire your debugger, log smartly with Log::Log4perl)&lt;br /&gt;
&lt;/a&gt;             2. &lt;a href="http://search.cpan.org/dist/Log-Log4perl/lib/Log/Log4perl.pm" target="_blank" rel="noopener"&gt;Manual&lt;/a&gt;&lt;br /&gt;
             3. &lt;a href="http://search.cpan.org/dist/Log-Log4perl/lib/Log/Log4perl/FAQ.pm" target="_blank" rel="noopener"&gt;Frequently Asked Questions&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
当然，你也可以用 CPAN 和 PPM(如果安装的是 ActivePerl) 来安装 Log::Log4perl 模块。</description></item><item><title>五线谱速读方法和钢琴视奏思维方法[转]</title><link>https://yanbin.blog/music-staff-piano-study/</link><pubDate>Tue, 09 Jun 2009 05:10:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/music-staff-piano-study/</guid><description>
大家在练习新的钢琴曲时一定会经常遇到这样的问题:&lt;br/&gt;
&lt;br/&gt;
1 错音不断&lt;br/&gt;
2 双手的配合不协调,对位有问题&lt;br/&gt;
3 无法连贯的弹奏下来,总是断断续续,结结巴巴&lt;br/&gt;
4 视奏一个新曲子很困难,弹奏几遍都无法顺下来&lt;br/&gt;
&lt;br/&gt;
这些都是因为对于钢琴五线谱的读法不正确而造成的!&lt;br/&gt;
&lt;br/&gt;
下面我来给大家介绍一种新而巧的方法, 使您可以做到对五线谱的速读和100%的准确&lt;br/&gt;
&lt;strong&gt;&lt;br/&gt;
&lt;span style="font-size: large;"&gt;钢琴的五线谱速读方法包括四个原则 和三个要求&lt;/span&gt;&lt;br/&gt;
&lt;/strong&gt;</description></item><item><title>Emacs 快速指南[Emacs 在带的 Tutorial]</title><link>https://yanbin.blog/emacs-tutorial-carried-by-itself/</link><pubDate>Sun, 07 Jun 2009 02:51:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/emacs-tutorial-carried-by-itself/</guid><description>
【注意：位于【】之间的内容是译注，比如本行，下同。】&lt;br/&gt;
&lt;br/&gt;
Emacs 键盘命令通常包含 CONTROL 键（有时候以 CTRL 或 CTL 来标示）和META 键（有时候用 EDIT 或 ALT 来标示）。为了避免每次都要写出全名，我们约定使用下述缩写：&lt;br/&gt;
&lt;br/&gt;
 C-&amp;lt;chr&amp;gt;  表示当输入字符 &amp;lt;chr&amp;gt; 时按住 CONTROL 键。&lt;br/&gt;
          因此 C-f 就表示：按住 CONTROL 键再输入 f。&lt;br/&gt;
&lt;br/&gt;
 M-&amp;lt;chr&amp;gt;  表示当输入字符 &amp;lt;chr&amp;gt; 时按住 META（或 EDIT 或 ALT）键。&lt;br/&gt;
          如果你的键盘上没有 META 、EDIT 或 ALT 键，用下述方法也等效：&lt;br/&gt;
          先按一下 ESC 键然后放开，再输入 &amp;lt;chr&amp;gt;。我们用 &amp;lt;ESC&amp;gt; 来表示 ESC 键。&lt;br/&gt;
&lt;br/&gt;
重要提示：要退出 Emacs，请用 C-x C-c（两个连续的组合键）。下文中左边顶行的“&amp;gt;&amp;gt;”字样用来提示你尝试键盘命令。比如：&lt;br/&gt;
&lt;br/&gt;
&amp;gt;&amp;gt; 现在输入 C-v （查看下一屏文字）移动到下一屏。</description></item><item><title>Emacs 的入门操作</title><link>https://yanbin.blog/emacs-get-started/</link><pubDate>Sun, 07 Jun 2009 00:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/emacs-get-started/</guid><description>
&lt;strong&gt;1. 总体说明&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
M 键的替代，如 M-v，表示按住 META(EDIT 或 ALT 键)，再按 v。如果没有 META、EDIT 或 ALT 键，则先按下 ESC 键，然后放开，再输入 v。好在我们的键盘一般都有 ALT 键，那就是 ALT-v 吧。&lt;br/&gt;
&lt;br/&gt;
查看命令帮助的方法：C-h k C-f 就能在下方窗口中看到 C-f 的使用说明&lt;br/&gt;
&lt;br/&gt;
如果一行超过窗口的宽度，换行时会有反斜杠(控制台下)，或转弯箭头(图形系统)标示出。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2. C-x 系列操作&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
这个系列的命令多与 “窗格、文件、缓冲" 的控制操作有关。&lt;br/&gt;
后再加&lt;br/&gt;
&lt;br/&gt;
  C-c 退出   C-s(&lt;span style="color: #ff0000;"&gt;s&lt;/span&gt;ave) 保存&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;3. 光标的移动&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
C-v(助记：Mo&lt;span style="color: #ff0000;"&gt;v&lt;/span&gt;e) 下移一屏，M-v 上移一屏，C-l(助记：&lt;span style="color: #ff0000;"&gt;l&lt;/span&gt;oad/Re&lt;span style="color: #ff0000;"&gt;l&lt;/span&gt;oad) 重绘屏幕，并将光标置于屏幕中央，也可以用 PageDown 和 PageUp 的滚屏。</description></item><item><title>Vi 和 Emacs 使用点滴</title><link>https://yanbin.blog/vi-emacs-little-by-little/</link><pubDate>Sat, 06 Jun 2009 03:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vi-emacs-little-by-little/</guid><description>
&lt;strong&gt;1. Vi 中执行系统命令&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
命令模式下用 ! (Shell 命令),如&lt;br/&gt;
 &lt;br/&gt;
   :! perl test.pl&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2. Vi 中拷贝、剪切、粘贴操作&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
    v(逐字符)或V(逐行)进入可视模式(Visual mode),然后用方向键或hjkl等移动命令就可以选择了.&lt;br/&gt;
拷贝: 选择完后按y；剪切: 选择完后按d；粘贴: 移动到合适的地方, 按 p, 也可以用 P&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;3. 控制台下的 Emacs 支持语法加亮&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
在 ~/.emacs 文件(没有该文件则新建一个) 中加上&lt;br/&gt;
&lt;br/&gt;
(global-font-lock-mode t)&lt;br/&gt;
&lt;br/&gt;
或者你进一 Emacs 中执行 M-x global-font-lock-mode 也能看到语法加亮的效果&lt;br/&gt;
&lt;br/&gt;
有说在 ~/.emacs 中加上这个定制变量的</description></item><item><title>Linux下不同文件编码的转换[转]</title><link>https://yanbin.blog/linux-converter-file-encoding/</link><pubDate>Tue, 02 Jun 2009 04:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-converter-file-encoding/</guid><description>
一、什么是编码。&lt;br/&gt;
&lt;br/&gt;
字符编码(Character Encoding)可以说就是让某一字符序列匹配一个指定集合中的某一东西，常见的例子包括长短电键组合起来表示的摩斯电码(Morse Code)、Baudot code、Unicode和用二进制来表示的ASCII(American Standard Code for Information Interchange)码、这样便能够将文本在计算机中存储和通过通信网络发送出去。&lt;br/&gt;
&lt;br/&gt;
字符表(Character Set)和字符编码(Character Encoding)经常被混淆。实际上Character Set(字符表)只是指一组为特定的Encoding System(编码系统)使用的符号集合，而不包含他们的数字含义或者顺序。而Character Encoding(字符编码)则包括了Character Set（字符表）和每个字符在字符编码集中的表示方法。由于历史的原因，MIME(Multipurpos Internet Mail Extensions)和使用这种编码的系统使用属于“字符集(Charset)”来表示用于将一组字符编码成一系列8位字节数据的整个系统。&lt;br/&gt;
&lt;br/&gt;
摩斯电码(Morse Code)发明于1840年，用来编码字母、数字和拉丁字符，都用长短组合的电键来表示。&lt;br/&gt;
ASCII码发明与1963年，是由7位(7-bit)的二进制代码表示字符、数字、符号和固定长度的设备控制代码。同年，IBM使用8位(8-bit)的编码机制实现了ASCII的扩展EBCDIC(IBM's Extended Binary Coded Decimal Interchange Code)。</description></item><item><title>Ubuntu (Linux Mint 7)下安装 IE (ies4linux)</title><link>https://yanbin.blog/ubuntu-linux-mint-7-ies4linux/</link><pubDate>Mon, 01 Jun 2009 02:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ubuntu-linux-mint-7-ies4linux/</guid><description>
我安装的 Linux 是 Linux Mint 7，它是基于 Ubuntu 9.04 的发行版，它提供一种更开箱即用的用户体验。如去除了顶端的任务栏，安装后即包括了浏览器插件(如 Flash)，多媒体解码器，对 DVD 播放的支持，Java 和其他组件等。它与 Ubuntu 软件仓库完全兼容。&lt;br/&gt;
&lt;br/&gt;
免去游戏方面，Linux 基本能应付得了日常事物的。可偏偏有些网站与 IE 结缘太深，更不争气的还就是 BlogJava，它的编辑器在非 IE 浏览器上处理 &amp;lt;textarea&amp;gt; 不能正常，所以我要考虑装个 IE 在 Linux 上。&lt;br/&gt;
&lt;br/&gt;
所用的软件是 ies4linux，复数的，可以同时安装多个版本的 IE。它用样是由 wine 来模拟运行的，所以必须先安装 wine，你可以用新立得软件包管理器来安装 wine，也可以直接用 apt-get 来安装 wine。&lt;br/&gt;
&lt;br/&gt;
然后是 ie24linux 的安装，安装步骤如下：</description></item><item><title>WAS5.x 下使用 Log4j 为何没有日志输出的详细分析及解决办法</title><link>https://yanbin.blog/was5-x-log4j-no-output-in-log-file-resolve/</link><pubDate>Thu, 14 May 2009 22:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/was5-x-log4j-no-output-in-log-file-resolve/</guid><description>
&lt;p&gt;经常碰到有人使用了 Log4j 的项目在别处运行的好好的，一放到 WAS 下发现不能输出日志了：控制台文件 SystemOut.log 中没有 Log4j 本应输出的内容，
要么就只见磁盘上有一个设定的日志文件，却总是空空如也。&lt;/p&gt;
&lt;p&gt;本文就来以一个实际的例子分析为什么会出现这样的情况，以及告诉你应如何解决。这其中也是因为 ClassLoader 在作祟。
通常我们会在通用日志框架 jakarta-commons-logging 之下使用 Log4j，这也是合情理的，因为 Struts、Hibernate 等众多框架就是这么干的，
然而这却是一方面的根源。分析的过程应用了前面一篇介绍的
&lt;a href="https://yanbin.blog/eclipse-remote-debug-was/" target="_blank"&gt;Eclipse 远程调试 WebSphere Application Server (WAS)&lt;/a&gt;
方法。&lt;/p&gt;
&lt;p&gt;测试环境和工具是：WAS 5.1+Eclipse 3.3.2+MyEclipse 6.0.1，在 Eclipse 中建立 Web  项目 testlog(此处可下载：
&lt;a href="
/posts/2009/was5-x-log4j-no-output-in-log-file-resolve/testlog.zip
"&gt;testlog.zip&lt;/a&gt;)，
其中所用到的 jar 包只有 commons-logging.jar(V1.0.3)、log4j-1.2.11.jar。
再有为了知道是哪个 ClassLoader 加载了某个类，
参考了：&lt;a href="https://yanbin.blog/jwhich-class-where-to-load/" target="_blank"&gt;
利用 JWhich 掌握类路径,确定类路径中的什么类将被载入&lt;/a&gt;。</description></item><item><title>Apache Commons Logging 是如何决定使用哪个日志实现类的</title><link>https://yanbin.blog/apache-commons-logging-which-log-implementation/</link><pubDate>Thu, 14 May 2009 10:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-commons-logging-which-log-implementation/</guid><description>
Apache Commons Logging 像 SLF4J 一样，是个通用日志框架，广泛应用在各个开源组件中。说其通用，是因为它本身只提供了简单的日志输出的实现(org.apache.commons.logging.impl.SimpleLog和org.apache.commons.logging.impl.NoOpLog)，主要是为你统一使用其他专业日志实现(Log4j、jdk1.4 Logger、aavalon-Logkit)的方式，让你在程序中看不到具体日志实现的代码，以配置方式解藕。&lt;/p&gt;
&lt;br/&gt;
那么 commons-logging 是怎么决定程序执行时该使用哪个具体的日志实现呢？这里 commons-logging 有两个步骤要做：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #000080;"&gt;1. 定位 org.apache.commons.logging.LogFactory 的实现类(这一步是关键)&lt;br /&gt;
2. 定位到的 LogFactory 实现类决定使用哪个 org.apache.commons.logging.Log 实现&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
那现在我们把注意力主要集中在 commons-logging 如何定位 LogFactory 实现类上来。org.apche.commons.logging.LogFactory 是一个抽象类，所以需要一个 LogFactory 具体类。</description></item><item><title>了解 Java ClassLoader -- 为便于阅读整理做成了一个 CHM 文件</title><link>https://yanbin.blog/understand-java-classloader-chm/</link><pubDate>Wed, 13 May 2009 05:43:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-classloader-chm/</guid><description>
&lt;p&gt;Java 程序员还是应该对 Java ClassLoader 有所了解，曾经问过一个做 Java 的 JVM  是什么？结果是：没听过。
汗颜了吧，但也不少写 JSP 的甚至是 Java 代码的真的可能不了解 ClassLoader，所以对 Classpath 仍然费解。
JRE 本身就有一个 ClassLoader 层次，更别说在各种应用服务器中因为 ClassLoader 层次的因素产生了莫名其妙的问题。
例如，数据库驱动有时候应该放在哪个目录中，怎么应用却加载了一个旧版的 Jar 包等等。&lt;/p&gt;
&lt;p&gt;本篇的 Understand Java ClassLoader.chm 文件是我根据 IBM 开发者网站上的
&lt;a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial"&gt;
https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial&lt;/a&gt;
整理而成的。以前是用 Visual CHM 工具来制作 CHM 文件，而这个呢是用 FAR 生成的，感觉 FAR 的功能要强大些。</description></item><item><title>Eclipse 远程调试 WebSphere Application Server (WAS)</title><link>https://yanbin.blog/eclipse-remote-debug-was/</link><pubDate>Tue, 12 May 2009 03:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/eclipse-remote-debug-was/</guid><description>
我们用 Eclipse 开发 Web 项目时，多会用某个插件(如 MyEclipse) 来对 Tomcat 中的应用进行单步调试。而要调试 WAS 下的应用，MyEclipse 也是可以的，但在 MyEclipse 中启动 WAS 比较慢，且需要在本地安装一个 WAS。再有便捷点的方法是用 WSAD (Websphere Studio Application Developer) 或它的升级版 RAD (Rational Application Developer)，它们内置了对 WAS 很好的支持，不过也需要本机安装了 WAS，WSAD/RAD 肥大的身躯自是不必说。若要让 WSAD/RAD 进行远程调试，似乎还得在Server 上安装个 IBM Agent Controller。&lt;br/&gt;
&lt;br/&gt;
实际上 WAS 就支持远程调试的设置，打开它，你只需要用 Eclipse Debug 中的 Remote Java Application 功能连接到服务器上相应端口即行。设置很简单，这样的办法你甚至可以在闲时偷偷的调试一下生产环境的 WAS 应用以验证测试环境难以/无法重现的问题。具体做法如下(WAS 5.x 和 6.x 下的操作基本相同)：</description></item><item><title>Oracle 中使用 fetch bulk collect into 批量效率的读取游标数据</title><link>https://yanbin.blog/oracle-fetch-bulk-collect-into/</link><pubDate>Wed, 06 May 2009 10:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-fetch-bulk-collect-into/</guid><description>
通常我们获取游标数据是用 fetch some_cursor into var1, var2 的形式，当游标中的记录数不多时不打紧。然而自 &lt;span style="color: #ff1493; cursor: help;"&gt;Oracle 8i&lt;/span&gt; 起，Oracle 为我们提供了 fetch bulk collect 来批量取游标中的数据，存中即是合理的。它能在读取游标中大量数据的时候提升效率，就像 SNMP 协议中，V2 版比 V1 版新加了 GET-BULK PDU 一样，也是用来更高效的批量取设备上的节点值（原来做过网管软件开发，故联想到此）。&lt;/p&gt;
&lt;br/&gt;
fetch bulk collect into 的使用格式是：&lt;span style="color: #800080;"&gt;fetch some_cursor collect into col1, col2 limit xxx&lt;/span&gt;。col1、col2 是声明的集合类型变量，xxx 为每次取数据块的大小（记录数），相当于缓冲区的大小，可以不指定 limit xxx 大小。下面以实际的例子来说明它的使用，并与逐条取记录的 fetch into 执行效率上进行比较。测试环境是 Oracle 10g  10.2.1.0，查询的联系人表 sr_contacts 中有记录数 1802983 条，游标中以 rownum 限定返回的记录数。</description></item><item><title>查询Oracle正在执行的SQL语句</title><link>https://yanbin.blog/oracle-what-sql-executing/</link><pubDate>Mon, 04 May 2009 22:07:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-what-sql-executing/</guid><description>
支持左連接的 Oracle 9i 以上版本&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OSUSER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PROGRAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SCHEMANAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cpu_Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_TEXT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V$SESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V$SQL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_ADDRESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADDRESS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_HASH_VALUE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HASH_VALUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpu_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;desc&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 Oracle 8i 及以前的版本中&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OSUSER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PROGRAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SCHEMANAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_TEXT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V$SESSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V$SQL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_ADDRESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ADDRESS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SQL_HASH_VALUE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HASH_VALUE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;--后面可以加上你要的约束条件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OSUSER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Unmi&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USERNAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;APPS&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PROGRAM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;JDBC-1.0-Client&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
自行决定要查询出来的字段以及约束条件。</description></item><item><title>Memcache的使用和协议分析详解[转]</title><link>https://yanbin.blog/memcache-protocol-analysis/</link><pubDate>Mon, 04 May 2009 20:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/memcache-protocol-analysis/</guid><description>
作者：heiyeluren&lt;br/&gt;
博客：&lt;a href="http://blog.csdn.net/heiyeshuwu"&gt;&lt;span style="color: #336699;"&gt;http://blog.csdn.net/heiyeshuwu&lt;/span&gt;&lt;/a&gt;&lt;br/&gt;
时间：2006-11-12&lt;br/&gt;
关键字：PHP Memcache Linux 缓存&lt;br/&gt;
&lt;br/&gt;
Memcache是danga.com的一个项目，最早是为 LiveJournal 服务的，目前全世界不少人使用这个缓存项目来构建自己大负载的网站，来分担数据库的压力。（关于Memcache的更多信息请Google）&lt;br/&gt;
Memcache官方网站：&lt;a href="http://www.danga.com/memcached"&gt;&lt;span style="color: #336699;"&gt;http://www.danga.com/memcached&lt;/span&gt;&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="font-size: medium;"&gt;【安装Memcache服务器端】&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
我目前的平台，服务器是Fedora Core 1（内核：2.4.22），客户端是Windows XP SP2，需要安装的就是服务器的Memcached的守护进程和客户端的PHP扩展php_memcache两个东西。现在我分别来讲。&lt;br/&gt;
&lt;br/&gt;
服务器端主要是安装memcache服务器端，目前的最新版本是 memcached-1.2.0 。</description></item><item><title>div 中动态添加的内容超过它的高度时造成 div 显示下沉的解决</title><link>https://yanbin.blog/div-exceed-max-height-descend-issue/</link><pubDate>Tue, 28 Apr 2009 03:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/div-exceed-max-height-descend-issue/</guid><description>
有一个页面展示上的需求是：固定长宽的 &amp;lt;div&amp;gt; 中放置一个表格，点击按钮向表格中动态添加行，当表格显示大小超过 &amp;lt;div&amp;gt; 的设定时，&amp;lt;div&amp;gt; 出现滚动条(包括横向和纵向)，&amp;lt;div&amp;gt; 的大小和位置始终不变。&lt;/p&gt;
&lt;br/&gt;
可是这样简单的代码随性写来却有个问题，当表格的高度未超过 &amp;lt;div&amp;gt; 高度时正常，而超过到一定程度时会造成 &amp;lt;div&amp;gt; 在页面中显示下沉。&lt;br/&gt;&lt;br/&gt;
示例代码如下(点击 Load Data 按钮加载数据)：</description></item><item><title>java class文件格式解析(摘)</title><link>https://yanbin.blog/java-class-file-format/</link><pubDate>Sat, 11 Apr 2009 13:34:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-class-file-format/</guid><description>
&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: medium;"&gt;1．目的&lt;br /&gt; &lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大型软件系统开发时，某些Java组件可能涉及到多种数据库或中间件系统的连接和应用，例如一个数据传递组件需要从DB2中读取数据，
并将数据通过中间件WebSphere MQ发送到其他系统，这类组件功能单一，但却需要连接多种第三方产品，使得程序员的单元测试变的非常不便，
程序员不得不注视或修改部分源代码，或者在本地安装所需第三方产品。无疑这两种选择都是痛苦的。
&lt;/p&gt;
&lt;p&gt;基于以上的不便，本文开发了解析Java Class文件程序，目的是将第三方产品API的Class文件转换为Java源文件（不包括Java类的方法实现），
在源文件的各种程序所需的方法里实现一些简单的语句，例如数据库连接方法永远返回true，获得数据方法永远返回 ”Hello world” 等，
用JDK重新编译转换后的Java源文件，来替换真正的API 文件，这样程序员在UT测试时，无需修改源代码，也无需安装任何产品，
并且能通过修改替换的API Java源文件实施各种UT测试。</description></item><item><title>四、深入下package,import:(摘)</title><link>https://yanbin.blog/inside-package-import-4/</link><pubDate>Fri, 10 Apr 2009 11:19:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/inside-package-import-4/</guid><description>
注：因package,import涉及较多内容，另开一个帖子了，但为了保证此贴内容与标题相符，在此也把写上了该部分内容（措辞有整理）&lt;br/&gt;
 &lt;br/&gt;
&lt;strong&gt;深入下package,import:&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
    凡是和java设计相关的工具，都会用到package与import，到底这两个东东是做什么的，如何用，它们的内部机理又是如何呢，今仅就个人的理解谈谈看法，里面一些错漏，疑点也请朋友们指出：&lt;br/&gt;
&lt;br/&gt;
一， package,import引入原因：&lt;br/&gt;
&lt;br/&gt;
package:&lt;br/&gt;
    我们都熟悉超市，超市虽然庞大，东西繁多，但管理的井井有条，很容易找到某样东东，；之所以能如此，一个很重要的原因就是采用了分类放置，这样既方便了管理，又方便了寻找&lt;br/&gt;
    Package也是一个分类放置东东的区域，不过它放的不是商品而是java中的类。Java中有各种各样的类，&lt;br/&gt;
内容丰富，繁多，为了更好的管理，识别，就为每一类型的类建立一个区域，这个区域就是包</description></item><item><title>三、我对java中类路径的理解(摘)</title><link>https://yanbin.blog/understand-java-classpath/</link><pubDate>Fri, 10 Apr 2009 11:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-classpath/</guid><description>
Java中的类路径分“编译后的存放路径” 和 “运行时的查找路径”，下面分别谈谈&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1. java编译后的类存放路径，&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
分两种：“源文件被直接编译”和“源文件被间接编译”&lt;br/&gt;
        1-1源文件直接编译&lt;br/&gt;
          什么是源文件直接编译：即通过javac直接编译源文件&lt;br/&gt;
建立d:\my目录，在其目录下新建一个文件，如下：&lt;br/&gt;
&lt;br/&gt;
Public class HelloWorld{&lt;br/&gt;
    Public static void main(String[] args){&lt;br/&gt;
        System.out.println(“HelloWorld”);&lt;br/&gt;
    }&lt;br/&gt;
}&lt;br/&gt;
&lt;br/&gt;
在命令行输入： javac HelloWorld.java&lt;br/&gt;
&lt;br/&gt;
这时在d:\my这个目录下就产生了一个类文件HelloWorld.class</description></item><item><title>二、我对java中类装载的理解(摘)</title><link>https://yanbin.blog/understand-java-classloader/</link><pubDate>Fri, 10 Apr 2009 11:13:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-classloader/</guid><description>
1.Java中的所有类，必须被装载到jvm中才能运行，这个装载工作是由jvm中的类装载器完成的，&lt;br/&gt;
类装载器所做的工作实质是把类文件从硬盘读取到内存中&lt;br/&gt;
&lt;br/&gt;
2.java中的类大致分为三种：&lt;br/&gt;
    1.系统类&lt;br/&gt;
    2.扩展类&lt;br/&gt;
    3.由程序员自定义的类&lt;br/&gt;
&lt;br/&gt;
3.类装载方式，有两种&lt;br/&gt;
    1.隐式装载， 程序在运行过程中当碰到通过new 等方式生成对象时，隐式调用类装载器加载对应的类到jvm中，&lt;br/&gt;
    2.显式装载， 通过class.forname()等方法，显式加载需要的类&lt;br/&gt;
  隐式加载与显式加载的区别：&lt;br/&gt;
    两者本质是一样?，</description></item><item><title>一、我对java中编码的理解(摘)</title><link>https://yanbin.blog/understand-java-encoding/</link><pubDate>Fri, 10 Apr 2009 11:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/understand-java-encoding/</guid><description>
1. 编码的产生&lt;br/&gt;
    对电脑而言，只认识0，1； 而现实世界是由各种符合组成，要想让计算机解释现实世界，就必须建立一套现实世界中的符号 和 计算机能处理的符号之间的对应关系，这个对应关系就是编码&lt;br/&gt;
&lt;br/&gt;
2. 在一个编辑器中，当我们在键盘上敲入一个字符时，在该编辑器上就会显示对应的字符，这个过程用计算机执行步骤来解释大致如下：&lt;br/&gt;
    输入字符 –&amp;gt; 编辑器根据设定的编码格式把字符编成01格式 -&amp;gt; 编辑器再按编码规则对01解码–&amp;gt; 显示字符&lt;br/&gt;
&lt;br/&gt;
3.几种常见的编码格式&lt;br/&gt;
1. ASCII码： &lt;br/&gt;
    计算机中最早的一套编码格式，采用7位二进制表示一个常见的字符，我们知道，计算机是按照字节来处理数据的，一个字节8位，因此用一个字节就可以表示一个ASCII字符，且还有一个位空位，规定最高位不用，常见的把最高位设定为0。 7位二进制最多可以表示128个字符（2的7次方），ASCII码只能表示常见的英文字符，数字，和少量的符号（没办法，谁让计算机是人家老美先发明的啊，优先考虑本土语言，理解理解）&lt;br/&gt;
注： 由于ASCII最早定义，使用广泛，使得之后出现的新的”字符“(不是汉字喔)编码都尽量和它兼容</description></item><item><title>JVM 对 Java 异常的处理原理（try.catch 子句）</title><link>https://yanbin.blog/jvm-java-handle-try-catch/</link><pubDate>Tue, 07 Apr 2009 09:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jvm-java-handle-try-catch/</guid><description>
最初我们用 Java 写 JSP 的时候，几乎可以不触及异常，因为 Servlet 容器会把 API 抛出的异常包装成 ServletException 丢给容器去处理。再后来应用分层，代码中要处理的异常便多了，一般会转换成自定义的业务异常类，用 try-catch-throw customerException-finally。再到如今各种框架日臻成熟，代码中显式的异常处理又渐渐少了些，借助于 AOP 横行，异常对业务的影响描述被移入到了配置文件中了，例如，事物处理、权限的控制等。&lt;br/&gt;
&lt;br/&gt;
这颇有些像手机的发展，当通信技术不甚发达的时候，手里抓的是砖头，信号是模拟的。后来慢慢瘦身成两三根手指大小，甚至是就一支笔似的，可如今信息量大了，屏幕要大，再配上 QWERT 键盘，机身自然就肥硕了。&lt;br/&gt;
&lt;br/&gt;
当然与手机的个头变迁略有不同的是，任凭你怎么对待 Java 中异常，切入 AOP 也好，在 JVM 中处理异常的内在机制始终未变。</description></item><item><title>VBA 中发送邮件（三. 使用 XMLHttpRequest 组件）</title><link>https://yanbin.blog/vba-send-email-xmlhttprequest/</link><pubDate>Fri, 03 Apr 2009 03:36:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vba-send-email-xmlhttprequest/</guid><description>
w3 JMail 组件虽好用，但实际使用过程中还是碰到问题，多数机器上都表现正常，唯独在一台机器上，JMail 的错误信息总是：jmail The message was undeliverable. All servers failed to receive the message。根据经验把病毒软件、防火墙都关了，Telnet 到邮件服务器的 25 端口，或者 Outlook 收发都是正常的，这下有些没辙了，而同事真正又要在那台奇怪的机器上发邮件。&lt;br/&gt;&lt;br/&gt;
再想它法了，对，就是 XMLHttpRequest，把功能分离，Excel 只提交 Http 请求，由 JSP 来完成实际的邮件发送工作，当然也可以实现为其他的形式。所以也就有两部分实现代码，分别为：&lt;br/&gt;&lt;br/&gt;
JSP 代码，使用的是 Apache 的 commons-email 组件，它还需要用到 activation.jar 和 mail.jar，
关于 commons-email 的使用可参考前面的一篇：&lt;a href="https://yanbin.blog/apache-commons-email-send-mail/" target="_blank" rel="noopener"&gt;
用 apache commons-email 轻松发送无乱码邮件&lt;/a&gt;。
比如文件存为 sendmail.jsp，通过浏览器来访问时用的 URL 是
&lt;a href="http://192.168.1.100:8080/WebUtils/sendmail.jsp"&gt;http://192.168.1.100:8080/WebUtils/sendmail.jsp&lt;/a&gt;</description></item><item><title>VBA 中发送邮件（二. 使用 JMail 组件）</title><link>https://yanbin.blog/vba-send-email-jmail/</link><pubDate>Fri, 03 Apr 2009 02:26:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vba-send-email-jmail/</guid><description>
&lt;p&gt;在上一篇 &lt;a href="https://yanbin.blog/vba-send-email-outlook/"&gt;VBA 中发送邮件（使用 Outlook）&lt;/a&gt;中，VBA 通过 Outlook 来发邮件不仅频繁的弹出提示窗口要求确定，而且需要在 Outlook 中配置好，不能随意使用邮件服务器。对此，是否有相当于 JavaMail 那样的第三方组件呢？Google 帮忙，果然找到一个，名曰 w3 JMail，J 为首，其实与 Java 无任何关系。&lt;/p&gt;
&lt;p&gt;可以在网络上搜索 w3 JMail Personal V4.3，或者直接到官网 &lt;a href="http://www.dimac.net"&gt;http://www.dimac.net&lt;/a&gt; 去下载，w3 Jmail 的授权方式是免费的。默认位置安装好 JMail 之后，是在 C:\Program Files\Dimac\w3JMail4 可以看到 Examples 目录、jmail.dll、w3jmail4.pdf 和 readme.txt 文件。可以查阅w3jmail4.pdf 中的使用帮助。
我们所要的文件其实只是那个 jmail.dll，安装程序会自动帮你注册 jmail.dll。若未运行安装程序，只是拷贝了 jmail.dll，则要运行 regsvr32 jmail.dll 注册它即可。</description></item><item><title>VBA 中发送邮件（一. 使用 Outlook 组件）</title><link>https://yanbin.blog/vba-send-email-outlook/</link><pubDate>Thu, 02 Apr 2009 22:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vba-send-email-outlook/</guid><description>
&lt;p&gt;帮同事做了一个在 Excel 中用 VBA 发邮件的程序，逐行读取 Excel 表格记录中的待发邮件主题、内容及收件人进行发送。首先想到也是看看能否使用 Outlook 的组件，网上查了查，果然有，现把代码贴出来如下：</description></item><item><title>有关于 JVM 的垃圾收集(三)</title><link>https://yanbin.blog/about-jvm-gc-3/</link><pubDate>Wed, 01 Apr 2009 00:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-jvm-gc-3/</guid><description>
&lt;strong&gt;对象可触及时的生命周期&lt;/strong&gt;&lt;/p&gt;&lt;br/&gt;&lt;br/&gt;
在 JVM 1.2 之前，堆中的对象分为三种状态，分别是：&lt;br/&gt;&lt;br/&gt;
1. 可触及的 -- 从根节点开始可追踪到&lt;br /&gt;
2. 可复活的 -- 从根节点开始追踪不到，但有可能被终结方法触及并复活。不仅仅是那些声明了 finalize() 方法的对象，而是所有的对象都要经过可复活状态&lt;br /&gt;
3. 不可触及的 -- 以上两种可能性都不存在，可以真正回收它们所占据的内存了&lt;br/&gt;&lt;br/&gt;
版本 1.2 中，可触及按强弱进一步细分为：&lt;br/&gt;&lt;br/&gt;
1. 强可触及 -- 即原来的可触及，从根节点开始的任何直接引用，如一个局部变量或任何从强可触及对象的实例引用的对象&lt;br /&gt;
2. 软可触及 -- 表现为 SoftReference 所引用的对象&lt;br /&gt;
3. 弱可触及 -- 表现为 WeakReference 所引用的对象&lt;br /&gt;
4. 影子可触及 -- 表现为 PhantomReference 所引用的对象</description></item><item><title>用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（五. 结合 IronTrack SQL）</title><link>https://yanbin.blog/p6spy-view-executing-sql-irontrack/</link><pubDate>Tue, 31 Mar 2009 03:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/p6spy-view-executing-sql-irontrack/</guid><description>
&lt;p&gt;本想把 p6spy 结合 SQL Profiler 或 IronTrack SQL 的使用介绍掇凑于一块来写。简单点说，只是一贴上图样，篇幅便需拖拉难遂人愿，也好，
索性把它们分成两个篇章。一来每篇主旨鲜明，二来五篇成一系列比起四更来的自然且吉利。&lt;/p&gt;
&lt;p&gt;前面讲过 p6spy 本身就可利用 Log4j 的 SocketAppender 向远端发送日志，SQL Profiler 不过是在这个基础上作了进一步拓展。
而接下来要说的 IronTrack SQL 就略有不同了，看它带的 p6spy.properties 文件，里面有 IronTrack SQL 给 p6spy 定制的一个模块：
module.ibeam=com.irongrid.ibeam.server.IBeamFactory。它用到了 log4j-1.2.8.jar，不过还得研究下 Log4j 在其中所起的作来。
现在就来介绍 p6spy 结合 IrconTrack SQL 的使用，最好是你知道如何单独使用 p6spy。压缩包里有文档：是 IronTrackSQL\docs\index.html。&lt;/p&gt;</description></item><item><title>用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（四. 结合 SQL Profiler）</title><link>https://yanbin.blog/p6spy-view-java-executing-sql-profiler/</link><pubDate>Mon, 30 Mar 2009 20:34:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/p6spy-view-java-executing-sql-profiler/</guid><description>
&lt;p&gt;p6spy 虽好，但把 SQL 语句输出到文件或是控制台中看起来有些吃力。若能图形界面展示出来便可一目了然，亲切许多。有种方法是配置 p6spy.properties
使用 Log4j 的 SocketAppender，然后启动 Log4j 的 org.apache.log4j.net.SocketServer 界面，或是在 Eclipse log4j plug-in 中也能观察所执行的 SQL 语句。&lt;/p&gt;
&lt;p&gt;不过还有种更专业做法，本篇将介绍 p6spy 如何结合 Sql Profiler 或 IronTrack SQL 来使用，并附以贴图，来感受一下吧。也以此来完成关于 p6spy 的这个系列。
其实你到后面也会发现，即便是用 Sql Pofier 的实现过程也是借助了 Log4j 的 SocketApender，你可以从它自己带的 p6spy.properties 文件中的配置看出来，
即其中的 log4j.appender.SQLPROFILER_CLIENT=org.apache.log4j.net.SocketAppender 这么一个配置。</description></item><item><title>一篇没有听众的演讲（林语堂）</title><link>https://yanbin.blog/lecture-from-linyutang-no-audience/</link><pubDate>Mon, 30 Mar 2009 02:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lecture-from-linyutang-no-audience/</guid><description>
以前在哪儿说过，假如有人仿安徒生作“无色之画”，做几篇无听众的演讲，可以做得十分出色。这种演讲的好处，在于因无听众，可以少忌讳，畅所欲言，倾颇合 “旁若无人”之义。以前我曾在中西女塾劝女子出嫁，当时凭一股傻气说话，过后思之，却有点不寒而栗，在我总算掬愚诚，郊野叟献曝，而在人家，却未必铭感五内。假如在无听众的女子学校演讲，那便可尽情发挥了。比如在这样一个幻想的大学毕业典礼演讲，我们可以不怕校长难为情，说些时常敢怒而不敢言的话。在一个幻想的小学教员暑期学校，也可以尽情吐露一点对小学教育不大客气的话……婚姻的致词向来也是许多客套，没人肯对新郎新娘说些结婚常识而不免有点不吉利的老实话。因此我就以“婚礼致词”为题作例举隅：</description></item><item><title>9 个基于JavaScript 和 CSS 的 Web 图表框架</title><link>https://yanbin.blog/9-javascript-css-web-chart/</link><pubDate>Mon, 16 Mar 2009 22:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/9-javascript-css-web-chart/</guid><description>
jQuery, MooTools, Prototype 等优秀的 JavaScript 框架拥有各种强大的功能，包括绘制 Web 图表，使用这些框架以及相应插件，我们可以非常轻松地实现曲线图，圆饼图，柱状图等 Web 图表的绘制，而不必象以往那样通过复杂的 Flash 技术实现。&lt;strong&gt;本文介绍了9个优秀的基于 JavaScript 与 CSS  的 Web 图表框架。&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
jQuery, MooTools, Prototype 等优秀的 JavaScript 框架拥有各种强大的功能，包括绘制 Web 图表，使用这些框架以及相应插件，我们可以非常轻松地实现曲线图，圆饼图，柱状图等 Web 图表的绘制，而不必象以往那样通过复杂的 Flash 技术实现。本文介绍了9个优秀的基于 JavaScript 与 CSS 的 Web 图表框架。</description></item><item><title>用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（三. 定制输出）</title><link>https://yanbin.blog/p6spy-view-custom-java-executing-sql/</link><pubDate>Mon, 16 Mar 2009 05:52:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/p6spy-view-custom-java-executing-sql/</guid><description>
&lt;p&gt;既然提到 p6spy 的输出，那就有必要说明一下 p6spy 输出日志的格式了。从上一篇
&lt;a href="https://yanbin.blog/p6spy-view-java-executing-sql-tomcat/"&gt;用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（二. Tomcat 下的配置&lt;/a&gt;
中把输出的一段内容拿过来，如下：&lt;/p&gt;
&lt;p&gt;
&lt;span style="color: #800080;"&gt;
03-16-09 15:12:06:656|16|4|statement|SELECT * FROM OM_CUSTOMERS  WHERE CUSTOMER_ID=? ORDER BY CUSTOMER_ID ASC|SELECT * FROM OM_CUSTOMERS  WHERE CUSTOMER_ID=2194 ORDER BY CUSTOMER_ID ASC&lt;br /&gt;
03-16-09 15:12:06:671|15|3|statement|SELECT * FROM OM_ORDER_TYPE WHERE TYPE_ID=?|SELECT * FROM OM_ORDER_TYPE WHERE TYPE_ID=25&lt;br /&gt;
03-16-09 15:12:06:687|16|1|statement|select * from sys_lookups where lookup_type=?  and lookup_code=? |select * from sys_lookups where lookup_type='OM_ORDER_STATUS'  and lookup_code='70'&lt;br /&gt;
03-16-09 15:12:06:812|-1||resultset|select * from sys_lookups where lookup_type='OM_ORDER_STATUS'  and lookup_code='70' |meaning = 已安排生产</description></item><item><title>用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（二. Tomcat 下的配置）</title><link>https://yanbin.blog/p6spy-view-java-executing-sql-tomcat/</link><pubDate>Fri, 13 Mar 2009 02:15:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/p6spy-view-java-executing-sql-tomcat/</guid><description>
在前篇 &lt;a href="https://yanbin.blog/p6spy-view-java-executing-sql-first/" target="_blank" rel="noopener"&gt;用 p6spy 来观察 Java 程序中执行的所有 SQL 语句(一. 引子）&lt;/a&gt; 大略介绍了 p6spy，并且在 &lt;a href="http://www.p6spy.com/documentation/install.htm#install"&gt;http://www.p6spy.com/documentation/install.htm#install&lt;/a&gt; 也有 p6spy 在不同服务器下的安装方法。本文不打算依照官方的说明来做，我们让 Tomcat 的 Common 类加载器来加载 p6spy.jar 包，包含了 Tomcat 5/6 下的 p6spy 配置，数据库连接池实现用 C3P0，数据库为 Oracle，配置在一个与应用同名的单独的 xml 文件中，Tomcat 中是在应用的 META-INF/context.xml 文件中。步骤如下：&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. 软件准备&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
下载 Tomcat 5 或者 6 进行安装，不必多说。假设置 Tomcat 的目录为 $TOMCAT_HOME。&lt;br /&gt;
下载 &lt;a href="http://prdownloads.sourceforge.net/p6spy/p6spy-install.zip?download"&gt;&lt;span style="font-family: Verdana; font-size: x-small;"&gt;p6spy-install.zip&lt;/span&gt;&lt;/a&gt;，解压缩 p6spy-install.zip，其中有 p6spy.jar 和 spy.properties&lt;br /&gt;
准备好数据库的驱动包，比如 Oracle 的 classes12.jar，和 C3P0 实现包，如 c3p0-0.9.0.2.jar。</description></item><item><title>用 p6spy 来观察 Java 程序中执行的所有 SQL 语句（一. 引子）</title><link>https://yanbin.blog/p6spy-view-java-executing-sql-first/</link><pubDate>Wed, 11 Mar 2009 09:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/p6spy-view-java-executing-sql-first/</guid><description>
一个企业应用程序的性能瓶颈可能会在硬件配置、网络方面、程序代码、应用服务器配置、数据库配置、SQL 语句。这里我把本文的关注点 SQL 无意间放在了最后，其实它不并不意味着最后考虑的，而是过程中就要时刻留意的。&lt;br/&gt;
&lt;br/&gt;
SQL 语句的优化总得把所执行语句抓出来瞧瞧，分析分析。如果直接用 JDBC 或者是类 iBatis 的东西来访问数据库，那所执行的 SQL 语句是明确的，而现在的项目大多会用 ORM 组件，例如 Hibernate、JPA、CMP、TopLink 都有自己特定的查询语法，最终当然要转换成 SQL 语句的，所以会生成什么样的 SQL 语句就不甚明了，若人为的看着专有查询语句来相象出 SQL 语句并非易事。虽然 Hibernate 设置 show_sql=true 时也能打印出生成的 SQL(带？号参数)，配合详细的日志参数值也可以对上，不过挺麻烦的。</description></item><item><title>挖Linux中的古老缩略语[转]</title><link>https://yanbin.blog/linux-old-terms/</link><pubDate>Tue, 17 Feb 2009 02:58:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-old-terms/</guid><description>
Unix已经有35年历史了。许多人认为它开始于中世纪，这个中世纪是相对于计算机技术的产生和发展来说的。在过去的时间里，Unix和它的子分支Linux收集有许多的历史和一些完全古老的语言。在这篇技巧文章中，我们将介绍一少部分古老的语言和它们的目的和作用，以及它们真正的来源。&lt;br/&gt;
&lt;br/&gt;
　　RC&lt;br/&gt;
&lt;br/&gt;
　　在Linux中，最为常用的缩略语也许是“rc”，它是“runcomm”的缩写――即名词“run command”(运行命令)的简写。今天，“rc”是任何脚本类文件的后缀，这些脚本通常在程序的启动阶段被调用，通常是Linux系统启动时。如/etc/rc是Linux启动的主脚本，而.bashrc是当Linux的bash shell启动后所运行的脚本。.bashrc的前缀“.”是一个命名标准，它被设计用来在用户文件中隐藏那些用户指定的特殊文件;“ls”命令默认情况下不会列出此类文件，“rm”默认情况下也不会删除它们。许多程序在启动时，都需要“rc”后缀的初始文件或配置文件，这对于Unix的文件系统视图来说，没有什么神秘的。</description></item><item><title>Solaris 常用系统命令[转]</title><link>https://yanbin.blog/solaris-common-used-commands/</link><pubDate>Tue, 10 Feb 2009 01:51:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/solaris-common-used-commands/</guid><description>
&lt;h3&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;系统基本配置&lt;/span&gt;
&lt;span class="MsoBookTitle"&gt;
&lt;span lang="EN-US" style="FONT-VARIANT: normal! important"&gt;
&lt;?XML:NAMESPACE PREFIX = O /?&gt;
&lt;o:p&gt;
&lt;/o:p&gt;
&lt;/span&gt;
&lt;/span&gt;
&lt;/h3&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;如何查看日志信息&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;                &lt;/span&gt;dmesg | more /var/adm/messages*&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;收集&lt;/span&gt;
&lt;span lang="EN-US"&gt;exlpore&lt;/span&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;日志&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;   &lt;/span&gt;
&lt;span&gt;              &lt;/span&gt;/opt/SUNWexplo/bin/explorer -k -&amp;gt;/opt/SUNWexplo/output&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;系统的基本状况&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;    &lt;/span&gt;
&lt;span&gt;                       &lt;/span&gt;showrev&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;系统运行时间，平均负载&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;                                 &lt;/span&gt;uptime&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;版本信息&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;                                               &lt;/span&gt;uname -a&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span lang="EN-US"&gt;Update&lt;/span&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;版本&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;                                         &lt;/span&gt;more /etc/release&lt;span&gt;                 &lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;系统补丁&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;  &lt;/span&gt;
&lt;span&gt;     &lt;/span&gt;showrev -p&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;已安装的补丁信息&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;  &lt;/span&gt;patchadd 123-12 &lt;/span&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;打补丁&lt;/span&gt;
&lt;/p&gt;
&lt;p class="MsoNormal"&gt;
&lt;span style="FONT-FAMILY: 宋体"&gt;查看服务对应的端口&lt;/span&gt;
&lt;span lang="EN-US"&gt;
&lt;span&gt;  &lt;/span&gt;more /etc/services&lt;/span&gt;
&lt;/p&gt;</description></item><item><title>Oracle 存储过程中发送邮件，并支持用户验证、中文标题和内容</title><link>https://yanbin.blog/oracle-procedure-send-email/</link><pubDate>Fri, 06 Feb 2009 17:17:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-procedure-send-email/</guid><description>
在 Oracle 的存储过程执行中，我们可能希望它本身能完成邮件发送执行的结果，特别是在捕获到了异常时。不能总是依赖于调用存储过程的外部程序－－调用后，根据出口参数，发送执行结果。这一需求更迫切的表现在非人工参与的 Oracle Job 调用存储过程的情况下。&lt;/p&gt;
&lt;br/&gt;
所幸，Oracle 为我们提供了发送邮件的工具包 UTL_SMTP，它最早出现在 Oracle 8.1.7 版本中。下面是我从网络上搜索相关资料后、综合整理、多处修正、数次调试、排除万难而写出的一个发送邮件的存储过程。可支持需用户验证的邮件服务器，中文标题和中文内容无乱码，只还未支持附件的发送，相信这方面应用较少，需要的话再 Google 一下，且文后参考中有相应的链接。</description></item><item><title>Oracle 过程中执行动态 SQL 或 DDL 语句</title><link>https://yanbin.blog/oracle-procedure-execute-dynamic-sql-or-ddl/</link><pubDate>Thu, 05 Feb 2009 06:07:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-procedure-execute-dynamic-sql-or-ddl/</guid><description>
如果你用的是 Oracle 8i 及以上的版本，那简单，在过程中用 execute immediate sql_str 就行, sql_str 是一个拼凑的 SQL 语句，但这个动态语句中带参数，或 Select 的结果要 into 到变量中时就要稍加留心一下了。而在 8i 以前的版本(谁还用这么古老的玩艺，总有些不得已的地方，老系统考虑升级成本遗留下来的，应用软件所伴随着的等)，都没法用 execute immediate，就得使用 DBMS_SQL 包来实现了&lt;/p&gt;
&lt;br/&gt;
何谓动态 SQL 和 DDL 语句呢？通常在过程中要操作的表名、字段名都必须是明确的，否则编译过程时就要报错，但如果这两者也用变量名来表示就是动态的。DDL 就是数据库对象定义的操作，如 CREATE TABLE/VIEW/INDEX/SYN/CLUSTER....，及这些对象的删除、修改操作等等。</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第六部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix-6/</link><pubDate>Tue, 03 Feb 2009 01:51:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix-6/</guid><description>
&lt;strong&gt;十. 配置 Quartz 数据源&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
如果你在使用 JDBC &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;，就需定义要用到的数据源。而如果使用的是 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt;，你实际上需要定义两个数据源。数据源可由三种方式来配置：&lt;br/&gt;
&lt;br/&gt;
·在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中指定连接池属性，这样 Quartz 能亲自创建数据源。&lt;br/&gt;
&lt;br/&gt;
·指定应用服务器管理的数据源所在 JNDI 的位置，Quartz 直接使用它。&lt;br/&gt;
&lt;br/&gt;
·还可使用自定义的 &lt;span style="color: #800080;"&gt;org.quartz.utils.ConnectionProvider&lt;/span&gt; 实现类。</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第五部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix-5/</link><pubDate>Wed, 14 Jan 2009 07:57:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix-5/</guid><description>
&lt;strong&gt;九. 配置 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 提供了另一类型的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;，它能工作于一个关系型数据库之下。你能通过设置 &lt;span style="color: #800080;"&gt;org.quartz.jobStore.class&lt;/span&gt; 属性来选用 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt;：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
当使用 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 时，可设置一些附加属性，它们是表 A.5 所示的。</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix-4/</link><pubDate>Thu, 08 Jan 2009 07:23:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix-4/</guid><description>
&lt;strong&gt;八. 配置 &lt;span style="color: #800080;"&gt;JobStoreTX &lt;/span&gt;&lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
你可以像下面那样设定类名来选择 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 类：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
表 A.4 列出了配置 Quartz &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 的可用属性。只有少数几个属性是必须的，而且其他的都有合理的默认值。</description></item><item><title>Oracle 中重新编译无效的存储过程, 或函数、触发器等对象</title><link>https://yanbin.blog/oracle-recompile-procedure-etc/</link><pubDate>Tue, 06 Jan 2009 23:34:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-recompile-procedure-etc/</guid><description>
Oracle 中的存储过程在有些情况下会变成失效状态，在 PL/SQL Developer 中该存储过程的图标左上角显示一把小红叉叉。比如储过程所引用的对象失效，dblink 出问题啦都可能引起用到它的存储过程失效。再就我的存储过程经常会变成无效，至今原因都未查明。&lt;br/&gt;
&lt;br/&gt;
查询 dba_dependencies 视图可以看到存储过程所引用的对象，再就在 dba_objects 视图中可以看到对象的 created 和 last_ddl_time 时间。&lt;br/&gt;
&lt;br/&gt;
上面的那种无效的存储程，只要不是语法上有问题，重新编译一下又是可用的了。总不能每次发现时人工去编译的，所以要实现自动化，有以下两种方法(网上找到的所有的 &lt;a href="http://www.xland.com.cn/article/74/77/0803/28375.htm" target="_blank"&gt;在Oracle中重新编译所有无效的存储过程&lt;/a&gt; 代码排版都很混乱，所以主要是重新整理了)：</description></item><item><title>关于 Oracle 的数据导入导出及 Sql Loader (sqlldr) 的用法</title><link>https://yanbin.blog/oracle-exp-imp-by-sqlloader/</link><pubDate>Mon, 05 Jan 2009 21:15:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-exp-imp-by-sqlloader/</guid><description>
在 Oracle 数据库中，我们通常在不同数据库的表间记录进行复制或迁移时会用以下几种方法：&lt;/p&gt;
&lt;br/&gt;
1. A 表的记录导出为一条条分号隔开的 insert 语句，然后执行插入到 B 表中&lt;br /&gt;
2. 建立数据库间的 dblink，然后用 create table B as select * from &lt;a href="mailto:A@dblink"&gt;A@dblink&lt;/a&gt; where ...，或 insert into B select * from &lt;a href="mailto:A@dblink"&gt;A@dblink&lt;/a&gt; where ...&lt;br /&gt;
3. exp A 表，再 imp 到 B 表，exp 时可加查询条件&lt;br /&gt;
4. 程序实现 select from A ..，然后 insert into B ...，也要分批提交&lt;br /&gt;
5. 再就是本篇要说到的 Sql Loader(sqlldr) 来导入数据，效果比起逐条 insert 来很明显&lt;br/&gt;&lt;br/&gt;
第 1 种方法在记录多时是个噩梦，需三五百条的分批提交，否则客户端会死掉，而且导入过程很慢。如果要不产生 REDO 来提高 insert into 的性能，就要下面那样做：</description></item><item><title>VirtualBox 2.1.0 启动时提示 "虚拟电脑控制台 - 严重错误" 的处理</title><link>https://yanbin.blog/virtualbox-2-1-0-startup-fatal-error/</link><pubDate>Mon, 22 Dec 2008 23:18:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/virtualbox-2-1-0-startup-fatal-error/</guid><description>
&lt;p&gt;现属 Sun 旗下的虚拟机软件 VirtualBox 出了 2.1.0 版，看这篇文章对 2.1.0 版 VirtualBox 的介绍：
&lt;a href="http://www.cnbeta.com/articles/73057.htm"&gt;http://www.cnbeta.com/articles/73057.htm&lt;/a&gt;。
在 32 位的主机上可安装 64 位的客户系统，而且也加入了 OpenGL 3D。详见
&lt;a href="http://www.virtualbox.org/wiki/Changelog" target="_blank"&gt;查看官方的更新文档&lt;/a&gt;
这很值得体验一番。&lt;/p&gt;
&lt;p&gt;于是下载了 &lt;a href="http://download.virtualbox.org/virtualbox/2.1.0/VirtualBox-2.1.0-41146-Win_x86.msi" target="_blank"&gt;
VirtualBox-2.1.0-41146-Win_x86.msi&lt;/a&gt;
来安装，安装过程中很多的 未通过 Windows 微标的测试 的提示，不管这些，统统 Continue Anyway 过去。安装完了，一启动，弹出窗口报错了，
错误界面如下：</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix-3/</link><pubDate>Mon, 22 Dec 2008 01:58:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix-3/</guid><description>
&lt;strong&gt;六. 配置 Quartz RMI 选项&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
当通过 RMI 使用 Quartz 启动一个 Quartz 实例时，你需要把它配置为经由 RMI "导出" 服务。然后你就能创建客户端，配置它们的 Quartz Scheduler 作为 "代理" 工作而连接到服务端来。表 A.3 列出了可用的 RMI 设定。&lt;br/&gt;
&lt;table cellspacing="5" width="600"&gt;&lt;caption&gt;&lt;strong&gt;表 A.3. Quartz 使用 RMI 时的属性&lt;/strong&gt;&lt;/caption&gt;&lt;br/&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="140"&gt;&lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;
&lt;td width="70"&gt;&lt;strong&gt;必须&lt;/strong&gt;&lt;/td&gt;
&lt;td width="80"&gt;&lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;默认值&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.export&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Boolean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;false&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.registryHost&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;localhost&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.registryPort&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Integer&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;1099&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.createRegistry&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;never&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.serverPort&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Integer&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Random&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.scheduler.rmi.proxy&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Boolean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;false&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix/</link><pubDate>Sat, 20 Dec 2008 05:46:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix/</guid><description>
&lt;strong&gt;二. 配置 Quartz ThreadPool&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
表 A.2 列出了配置 Quartz ThreadPool 可用的属性。只有少些属性是必须的，剩下的都有合理的默认值。&lt;br/&gt;
&lt;table cellspacing="5" width="600"&gt;&lt;caption&gt;&lt;strong&gt;表 A.2. 配置 Quartz ThreadPool 的属性&lt;/strong&gt;&lt;/caption&gt;&lt;br/&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="140"&gt;&lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;
&lt;td width="70"&gt;&lt;strong&gt;必须&lt;/strong&gt;&lt;/td&gt;
&lt;td width="80"&gt;&lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;默认值&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.class&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;String&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;null&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.threadCount&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Integer&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;-1&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.threadPriority&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;Integer&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;5&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.makeThreadsDaemons&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;boolean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;false&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.threadsInheritGroup-&lt;br/&gt;
        OfInitializingThread&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;boolean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;true&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;org.quartz.threadPool.threadsInheritContext-&lt;br/&gt;
        ClassLoaderOfInitializingThread&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;boolean&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;false&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］附录 A. Quartz 配置参考 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-appendix-1/</link><pubDate>Fri, 19 Dec 2008 23:55:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-appendix-1/</guid><description>
&lt;strong&gt;附录 A. Quartz 配置参考&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
本附录编写作为配置一个 Quartz 应用的快速参考。尽管这些信息在 Quartz 文档中都有，但是这个附录提供了一种更快的方式来查找配置属性和它们可能的值。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;一. 主要的 Quartz 属性&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
表 A.1 列出了主要的 Scheduler 属性。它们用于声明和标识 Scheduler 和其他高层次的设置。</description></item><item><title>有关于 JVM 的垃圾收集(二)</title><link>https://yanbin.blog/about-jvm-gc/</link><pubDate>Mon, 15 Dec 2008 21:51:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-jvm-gc/</guid><description>
&lt;p&gt;
&lt;strong&gt;自适应收集器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在第一篇：&lt;a href="http://unmi.cc/about-jvm-1/" target="_blank"&gt;有关于 JVM 的垃圾收集(一)&lt;/a&gt;  中谈到过几种垃圾收集的算法，
然而我们的 JVM 启动之后并不要求彻头彻尾的死板的使用一种垃圾收集算法，固定的算法参数。因为某种情况下某些垃圾收集算法工作得更好，
而别外一些收集算法在另外的情况下工作得更好，所以自适应的垃圾收集技术应运而生。自适应算法监视堆中的情形，并且对应的调整为合适的垃圾收集技术。
或能是换一种垃圾收集算法，或者是调整当前算法参数，或者把堆划分为子堆，同时在不同的子堆中使用不同的算法。
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;简述火车算法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;垃圾收集一般都会停止整个程序的运行来查找和收集垃圾对象，它们可能在程序执行的任意时刻暂停，并且暂停的时间也无法确定。
垃圾收集也可能使得程序对事件响应迟钝，无法满足实时系统的要求。如果一种垃圾收集算法可能导致用户可察觉的到的停顿或者使得程序无法适合实时系统的要求，
这种算法被称作破坏性。垃圾收集算法的还有一个基本目标是使本质上的破坏性尽可能少，如果可能的话，尽可能消除这种破坏性。
&lt;/p&gt;</description></item><item><title>Linux 下仅用 iptables 实现为透明代理服务器</title><link>https://yanbin.blog/linux-iptables-transparent-proxy/</link><pubDate>Mon, 15 Dec 2008 00:15:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-iptables-transparent-proxy/</guid><description>
在网上 Google/Baidu  一下 “iptables 代理服务器” 多是讲的 iptables 结合 Squid 来实现透明代理的，用 iptables 把请求转发到 Squid  的代理端口上的，由于 Squid 有其局限性，不能支持 POP3、SMTP 和 P2P 协议。所以想在不影响多数人上网方式，集中控制的情况下，给自己单独开个小灶，用 iptables 在代理服务器上另辟一条溪径。&lt;/p&gt;
&lt;br/&gt;
代理服务器有两网卡：&lt;br/&gt;&lt;br/&gt;
eth0 10.1.0.1  连接内部的网卡&lt;br /&gt;
eth1 202.x.x.x       能上互联网的网卡&lt;br/&gt;&lt;br/&gt;
建立脚本，比如 /usr/local/transparent_proxy.sh，内容如下：</description></item><item><title>Unmi 学习 Groovy 之 Groovy 和 Swt</title><link>https://yanbin.blog/unmi-study-groovy-swt/</link><pubDate>Mon, 08 Dec 2008 23:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-swt/</guid><description>
&lt;p&gt;如上篇：&lt;a href="https://yanbin.blog/unmi-groovy-swing/" target="_blank" rel="noopener"&gt;Unmi 学习 Groovy 之 Groovy 和 Swing&lt;/a&gt;，Groovy 应用 Markup、命名参数、匿名构造和闭包很好的支持了 Swing 开发。然而 IBM 当初考虑到 Swing 的表现自行开发了一个界面框架，并已广受开发者青睐，也在 RCP 中得到了发展。Groovy 自然也看到了这一点，有了实际动作，见 &lt;a href="http://groovy.codehaus.org/GroovySWT"&gt;http://groovy.codehaus.org/GroovySWT&lt;/a&gt;。它通过 SwtBuilder 和 JFaceBuilder 不令提供了对 Swt 的支持，还支持了 Swt 更上一层的 JFace。&lt;/p&gt;
&lt;p&gt;要使用 GroovySWT，必须先下载到 &lt;a href="http://docs.codehaus.org/download/attachments/17022/groovy-swt-0.3.jar" target="_blank" rel="noopener"&gt;groovy-swt-0.3.jar&lt;/a&gt;，或用 Eclipse 自带的 swt.jar，或从 Swt 官方网站(&lt;a href="http://www.eclipse.org/swt/"&gt;http://www.eclipse.org/swt/&lt;/a&gt;) 上下载最新的 swt.jar。或者直接下载打包的 &lt;a href="http://docs.codehaus.org/download/attachments/17022/groovy-swt-0.3-including-Eclipse-libs.ZIP+" target="_blank" rel="noopener"&gt;groovy-swt-0.3-including-Eclipse-libs.ZIP&lt;/a&gt;，其中包含有 groovy-swt-0.3 和 swt/jface 等包。swt/jface 工程的 classpath 设置这里不多说，本文假定你对 Swt 有一定的了解，最好能理解 Swt 的 HelloWorld 程序。下面还是以例子进行说明吧。</description></item><item><title>Unmi 学习 Groovy 之 Groovy 和 Swing</title><link>https://yanbin.blog/unmi-groovy-swing/</link><pubDate>Tue, 25 Nov 2008 09:38:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-groovy-swing/</guid><description>
选择一种动态脚本语言，不仅仅是用它来执行一些控制台或是后台任务，还希望能快速建立 GUI 程序，开发自己的小巧实用的工具。而 Groovy 借助于 GroovyMarkup，并巧妙的结合了 Java 2 与生俱来的 Swing 来创建用户图形界面。当界面程序执行过程中，可直接修改脚本代码来调整显示和功能，无须再编译，比之 Java Swing 程序来的便捷。&lt;/p&gt;
&lt;br/&gt;
其实这还应该是 GroovyMarkup 的内容，本篇也可以叫之 《Unmi 学习 Groovy 之 GroovyMarkup (三)》&lt;br/&gt;&lt;br/&gt;
Groovy 提供了 SwingBuilder 标记类，它使得创建 Swing 用户界面比 Java 更快，代码更省。当然程序的执行性能在小程序方面已无所谓轻重了。还是以实际例子为见：</description></item><item><title>Unmi 学习 Groovy 之 GroovyMarkup (二)</title><link>https://yanbin.blog/unmi-study-groovy-markup-2/</link><pubDate>Thu, 20 Nov 2008 07:38:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-markup-2/</guid><description>
&lt;p&gt;在前一篇 &lt;a href="https://yanbin.blog/unmi-study-groovy-markup-1/" target="_blank"&gt;Unmi 学习 Groovy 之 GroovyMarkup (一)&lt;/a&gt;
引入了 GroovyMarkup 及介绍了其中的一个 Builder(MarkupBuilder) 的用法。本篇继续。&lt;/p&gt;&lt;p&gt;groovy.util.NodeBuilder，用来创建一般任意对象的树状结构。看到了，它不在 groovy.xml 包之下，但它是一个 Builder，所以秉承了 Builder 的语法规则，而且还可用路径(对象导航)的方式来访问这个 Builder 实例中的节点或属性值。还是来看例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;groovy.util.NodeBuilder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;someBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;NodeBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//只有这么一个构建方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;someBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;balance:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;]){&lt;/span&gt; &lt;span class="c1"&gt;//这一块的语法完全同 MarkupBuilder
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;gender:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;male&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Unmi&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Joe&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;item:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Book&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="s"&gt;//&lt;/span&gt;&lt;span class="err"&gt;因为&lt;/span&gt; &lt;span class="n"&gt;NodeBuilder&lt;/span&gt; &lt;span class="err"&gt;不预示着输出，所以需要显示的&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;看看上面程序的输出：</description></item><item><title>Unmi 学习 Groovy 之 GroovyMarkup (一)</title><link>https://yanbin.blog/unmi-study-groovy-markup-1/</link><pubDate>Wed, 19 Nov 2008 06:15:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-markup-1/</guid><description>
每一种语言都不能忽略了 XML 操作相关的 API，而 Groovy 把使用 XML 的美妙和简易性推向了极致，这就是 GroovyMarkup。它不仅简化了 SAX、DOM 操作，并且把这一理念引入到了 Swing、Swt 界面绘制领域中，Ant、Maven 构建脚本生成中；甚至铺散到更广阔的领域。&lt;/p&gt;
&lt;br/&gt;
Groovy 用闭包和命名参数给标记数据的操作创建通用的语法。GroovyMarkup 在实际的应用中表现为各种 Builder。例如在 Groovy 中内建了以下几个 Builder。&lt;br/&gt;&lt;br/&gt;
·groovy.xml.MarkupBuilder  --  将你的对象序列化成 XML 或 XHTML&lt;br /&gt;
·groovy.xml.SAXBuilder       --  可以用于现有的 SAX 处理器&lt;br /&gt;
·groovy.xml.DOMBuilder     --  创建并解析 DOM 文档&lt;br/&gt;&lt;br/&gt;
而且 GroovyMarkup 语法已经被公认为对操作特定于领域的对象结构很有价值，例如 Ant 文件和 Swing 用户界面。</description></item><item><title>oracle定时任务[转]</title><link>https://yanbin.blog/oracle-schedule-job/</link><pubDate>Tue, 11 Nov 2008 21:42:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-schedule-job/</guid><description>
&lt;p&gt;
DBMS_JOB系统包是Oracle“任务队列”子系统的API编程接口。DBMS_JOB包对于任务队列提供了下面这些功能：提交并且执行一个任务、改变任务的执行参数以及删除或者临时挂起任务等。&lt;/p&gt;
&lt;p&gt;
DBMS_JOB包是由ORACLE_HOME目录下的rdbms/admin子目录下的DBMSJOB.SQL和PRVTJOB.PLB 这两个脚本文件创建的。这两个文件被CATPROC.SQL脚本文件调用，而CATPROC.SQL这个文件一般是在数据库创建后立即执行的。脚本为DBMS_JOB包创建了一个公共同义词，并给该包授予了公共的可执行权限，所以所有的Oracle用户均可以使用这个包。</description></item><item><title>Quartz Job Scheduling Framework［翻译］- 中文版 CHM v1.0.0 文件发布</title><link>https://yanbin.blog/quartz-job-sche00pkk/</link><pubDate>Fri, 24 Oct 2008 09:26:48 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-sche00pkk/</guid><description>
&lt;table style="height: 192px;" width="682"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2008/quartz-job-sche00pkk/Quartz_Job_Scheduling_Framework_2.jpg"
width="138px"
/&gt;
&lt;/td&gt;
&lt;td align="center"&gt;&lt;strong&gt;Quartz Job Scheduling Framework&lt;/strong&gt;
&lt;strong&gt;中 文 版&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
Chuck Cavaness 著 Unmi(隔叶黄莺) 译&lt;/p&gt;
译者博客：&lt;a href="../"&gt;http://unmi.cc/&lt;br /&gt;
&lt;/a&gt;&lt;br /&gt;
下载地址：&lt;a href="
/posts/2008/quartz-job-sche00pkk/Quartz_Job_Scheduling_Framework_CN_V1.0.0.rar
"&gt;Quartz Job Scheduling Framework 中文版.chm&lt;br /&gt;
&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
&lt;strong&gt;写在最前面的&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
这一次的版本更新主要是因为创立了自己的独立博客 http://unmi.cc，从 blogjava.net 上脱离了开来，所以原来可以点击
&lt;img class="inline post-image"
src="https://yanbin.blog/posts/2008/quartz-job-sche00pkk/comment.jpg"
width="70px"
/&gt;
进行探讨的链接全都更新到这个博客来了，因为原链接已无效了。还有稍稍改了点原文中的一些差错。&lt;br/&gt;&lt;br/&gt;
Quartz 是什么，大概不需多加说明，简单讲就是一个纯 Java 实现的作业调度工具，相当于数据库中的 Job、Windows 的计划任务、Unix/Linux 下的 Cron，但 Quartz 可以把排程控制的更精细。也许大多数人听说 Quartz 是在学习或使用 Spring 的时候，也就是 Spring 整合了 Quartz。&lt;br/&gt;&lt;br/&gt;
同时感谢大家一路来的支持，也希望大家能提出宝贵意见，或有问题拿出来共同探讨。另外，每篇译章都有到达我的博客上 所对应页面的链接，所以若有疑问可点击页面上方或下方的的
&lt;img class="inline post-image"
src="https://yanbin.blog/posts/2008/quartz-job-sche00pkk/comment.jpg"
width="70px"
/&gt;
按钮进入到博客对应页面来&lt;strong&gt;&lt;em&gt;对本篇进行评论，或阅读他人的相关评论&lt;/em&gt;&lt;/strong&gt;。</description></item><item><title>简单例子演示如何进行类的热加载(Hot Deployment)</title><link>https://yanbin.blog/simple-java-hot-deployment/</link><pubDate>Tue, 21 Oct 2008 11:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/simple-java-hot-deployment/</guid><description>
&lt;p&gt;应用服务器一般都支持热部署(Hot Deployment)，更新代码时把新编译的确类替换旧的就行，后面的程序就执行新类中的代码。这也是由各种应用服务器的独有的类加载器层次实现的。那如何在我们的程序中也实现这种热加载功能呢？即要在虚拟机不关闭的情况下(比如一个)，换个类，JVM 就知道加载这个新类，执行新类中的逻辑呢？下面就简单演示这样一个热加载的例子，首先大致了解一下类加载器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标准 Java 启动器的类加载器层次&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1. 引导类加载器(bootstrap):   加载内核 API，如 rt.jar(java.lang、java.io 等)&lt;br /&gt;&lt;br/&gt;
2. 扩展类加载器(extension):   加载的默认扩展来自于 jre/lib/ext&lt;br /&gt;&lt;br/&gt;
3. 系统类加载器(system):       类路径上的类，如 com.unmi.*&lt;/p&gt;
&lt;p&gt;说明：这只是标准 Java 启动器运行程序时的类加载器层次，像应用服务器中的类加载器通常会多一两层，也是在这个基础上的延伸。上面的类加载层次存在自上而下的委托关系，委托加载不在这里细讲。</description></item><item><title>有关于 JVM 的垃圾收集(一)</title><link>https://yanbin.blog/about-jvm-1/</link><pubDate>Tue, 21 Oct 2008 01:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/about-jvm-1/</guid><description>
&lt;p&gt;Java 中使用 new、newarray、anewarray 和 multianewarray 指令来创建的对象，当这些对象不再使用时由垃圾收集来释放。
&lt;span style="color: #800080;"&gt;那么 反序列化等都是间接使用了前面的某个指令, clone()  是个本地方法？&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;JVM 规范不需要任何特定的垃圾收集技术，甚至也没要求有垃圾收集机制。
&lt;span style="color: #800080;"&gt;大概只是说不需要手工释放内存，具体怎么实现各 JVM 自行决定。&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;GC 除了释放不再被引用的对象，还要处理堆碎片，
整理出连续的空闲空间才能放得下新的对象。不至于出现总的空闲空间足够，但碎片太多而报出 "Out of Memory" 的异常。&lt;/p&gt;
&lt;p&gt;GC 有两个好处：
一个是提高了生产率，不用埋头于 Memory Link 的有时甚至是逐行的检查；二，GC 也是 Java 安全策略的一部分，有了它不至于因错误的释放内存而导至 JVM 崩溃。
但是 GC 的一个潜在缺陷影响了程序的性能，它需要一直在后台不时的做些事情，而且实时性也有所欠缺。</description></item><item><title>Maven 开发者手册翻译进行中......</title><link>https://yanbin.blog/maven-manual-translating/</link><pubDate>Thu, 16 Oct 2008 12:41:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maven-manual-translating/</guid><description>
&lt;p&gt;看到 Maven 不断的演进, 著名的  Appfuse 也摒弃了 Ant 而采用了 Maven 作为它的构建管理工具，各个 Java IDE 对于 Maven 的支持也是纷纷上马，所以急迫的感觉对于 Maven 的出现是不容忽视了。Ant 纵然是能满足于我们的需求，不过 Ant 的 build.xml 脚本总那么臃肿不堪，不利于阅读。&lt;/p&gt;&lt;p&gt;而 Maven 与 Ant 同出一族，为 Apache 门下的师兄弟。既然 Apache 会再发明这样一个新轮子，相比于 Ant 确有它的许多独到之处。Maven 依赖了 ant-x.x.x 包，但要说是建立在 Maven 之上的话恐有不太合适。&lt;/p&gt;&lt;p&gt;关于 Maven 与 Ant 之间比较的文章在网上能找到不少，如 &lt;a href="http://www.matrix.org.cn/thread.shtml?topicId=16836&amp;amp;forumId=31" target="_blank"&gt;maven和ant的比较&lt;/a&gt;。具体的不细说，略表几个 Maven 关键之处：1. 对依赖包的处理十分优雅；2. 有大量可用的插件支持；3. 便于与其他工具的支持；4. 适合于多项目的开发。本人也是接触不久，还有许多有待发掘。</description></item><item><title>Maven 1 的 Unknown error downloading; status code was: 301 错误解决</title><link>https://yanbin.blog/maven-1-301-unknown-error-downloading/</link><pubDate>Tue, 14 Oct 2008 02:10:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maven-1-301-unknown-error-downloading/</guid><description>
现在早已是 Maven 2 了，可还是有机会用了一下 Maven 1，所用的版本是 Maven 1.0.2，刚开始尝试的时候出现以下两个问题：&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1. 执行 maven genapp 时产生如下异常：&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;???????? commons-jelly-tags-interaction-20030211.143817.jar????&lt;br/&gt;
Error retrieving artifact from [http://www.ibiblio.org/maven/commons-jelly/jars/commons-jelly-tags-interaction-20030211.143817.jar]: java.net.ConnectException: Connection timed out: connect&lt;br/&gt;
?????? commons-jelly-tags-interaction-20030211.143817.jar ?????§°???&lt;br/&gt;
???????????????±???????¨??????????&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;commons-jelly-tags-interaction-20030211.143817.jar</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（七）</title><link>https://yanbin.blog/spring-in-action-notes-7/</link><pubDate>Tue, 07 Oct 2008 10:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-7/</guid><description>
&lt;ol&gt;
&lt;li&gt;Spring MVC 还提供了一个特别点的 Controller 类型就是 ThrowawayController，它自成一个接口，ThrowawayController 和 Controller 的关系是平行的。什么叫做 ThrowawayController 呢，中文叫做一次性控制器，也就像一次性筷子那样用完即丢，下次要用又拿新的。表现在实例上就是相应 Bean 配置为 singleton="false"，每次初始化一个新实例。与其他 Controller 的区别完全就是 WebWork 或 Struts2 的 Action 与 Struts1 的 Action 的区别。因为它是多例的，所以可以用实例变量来接受请求参数，执行方法无参数；而不像其他的 Controller ，因为共享实例，所以需要通过执行方法的来传递请求参数以保证线程安全（P273）&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
92. 在《Spring in Action》第一版 274 页说 DispatcherServlet 使用缺省 ControllerHandlerAdapter 时只会把控制权分发给 Controller 接口类型的类，而要配置 ThrowawayControllerHandlerAdapter 后，Dispatcherservlet 就把控制权分发给 ThrowawayController，配置如下：</description></item><item><title>Spring MVC 中的 MultiActionController 用法详解</title><link>https://yanbin.blog/spring-mvc-multiactioncontroller/</link><pubDate>Mon, 06 Oct 2008 01:54:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-mvc-multiactioncontroller/</guid><description>
Spring MVC 中 Controller 的层次实在是多，有些眼花缭乱了。在单个的基础上，再新加两三个叫做丰富，再多就未必是好事，反而会令人缩手新闻片脚，无从定夺。多数 Controller 都是只完成一个任务，不过也有一个像 Struts 的 DispatchAction 的那样的 Conntroller，org.springframework.web.servlet.mvc.multiaction.MultiActionController，意即在一个 Controller 中处理多个动作，如同一个业务的增、删、改可以放在一起了。不至于增、删、改各自为政，造成代码混乱、重复难以维护。&lt;/p&gt;
&lt;br/&gt;
本文中的 web.xml 的 org.springframework.web.servlet.DispatcherServlet 所处理的 url-pattern 是 *.html，如何搭建 Spring MVC 项目不细加说明，可参考我前面同系列的文章。&lt;br/&gt;&lt;br/&gt;
例如，下面的 UserController，有两个签名一样的 updateUser() 和 deleteUser() 方法</description></item><item><title>Unmi 学习 Groovy 之模板</title><link>https://yanbin.blog/unmi-study-groovy-templat/</link><pubDate>Sun, 05 Oct 2008 09:25:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-templat/</guid><description>
提起模板，在 Java 领域中无疑会让人想起 Velocity 和 FreeMarker，可也不要忽略了，和我们最早打交道的 JSP 其实也是一种模板。模板一般都用来作为数据显示分离的显示部分，即 MV 的 V。比如说我目前在 Web 展示和发送邮件时较多的用到模板。通常模板用占位符创建的一个文本，或者串文本、输入流等。这些占位符可以用自己的流程控制代码，所以相应的就有的各种模板语言，例如 VTL(Velocity Template Language)、FTL(FreeMarker Template Language)、自然 JSP 也有算上。&lt;br/&gt;&lt;br/&gt;
有了模板，再把模型数据和模板进行归并就能得到我们真正想要的数据展现了，这当中的工作就是叫做模板引擎的干的。Groovy 中包含了抽象的 groovy.text.TemplateEngine 类和 groovy.text.Template 接口。可见 Groovy 表现的很开放，它们使得将任何模板引擎插入到 Groovy 中成为可能，比如 Velocity 或者 FreeMarker 引擎，可惜现在还没有。</description></item><item><title>为 JDOM 解析 XML 文件成 Document 加速</title><link>https://yanbin.blog/speed-jdom-resolve-xml/</link><pubDate>Fri, 03 Oct 2008 23:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/speed-jdom-resolve-xml/</guid><description>
本篇讲述在 XML 中使用了 DTD 的情况下，用 JDOM 解析时如何加速，使用 Schema 验证 XML 的不在讨论之中。所用的是一个 Struts 1.3 的配置文件，大小为 102 K。&lt;/p&gt;
我们用 JDOM 解析 XML 最简单的代码莫过于以下两行代码，不过为了测试我们在其前后加上记录执行时间的代码：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SAXBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SAXBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;struts-config.xml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;耗时：&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; 毫秒.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
在这个 struts-config.xml 中的 DTD 声明如下：</description></item><item><title>Unmi 学习 Groovy 之 Groovlet</title><link>https://yanbin.blog/unmi-study-groovy-groovlet/</link><pubDate>Fri, 03 Oct 2008 01:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-groovlet/</guid><description>
在 Java 领域中我们结识过多个 XXXlet 的东西，比如 Applet、Servlet、Scriptlet，这里的 let 作为一个后缀，代表小的意思，或者引申为简短、精巧。&lt;/p&gt;
&lt;br/&gt;
Applet  ---- Application let 小应用程序&lt;br /&gt;
Servlet  ---- Service let 小服务程序&lt;br /&gt;
Scriptlet ---- Script let  一小段脚本，在 jsp 中用这样的标签 &amp;lt;jsp:scriptlet&amp;gt;&lt;br/&gt;&lt;br/&gt;
另外还有一种叫做 text/x-scriptlet 的 MIME 类型。&lt;br/&gt;&lt;br/&gt;
而本篇所要介绍的角色也用了 let 后缀，叫做 Groovlet，不能简单识作为 Groovy let ---- 小 Groovy 程序。不尽对，确切来说它是 GroovyServlet、进一步 Groovy Service let 的简称，没错，它实质上也是一个 Servlet，也就是 Servlet 的扩展。简单讲就是用 Groovy 脚本替代 Java 语言来写 Servlet 程序，再明白点讲就是：比如我们在浏览器中用 &lt;a href="http://localhost:8080/groovlet/hello.groovy"&gt;http://localhost:8080/groovlet/hello.groovy&lt;/a&gt; 访问某个脚本就像访问某个 php/perl 一样的效果。</description></item><item><title>用 Spring MVC 来处理向导式复杂表单</title><link>https://yanbin.blog/spring-mvc-wizard-page/</link><pubDate>Thu, 02 Oct 2008 04:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-mvc-wizard-page/</guid><description>
&lt;div&gt;&lt;strong&gt;&lt;span style="color: #0000ff; font-size: medium;"&gt;引子&lt;/span&gt;&lt;/strong&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;span style="line-height: 1.5em;"&gt;我们在网上经常会碰到一些向导式的表单页面，比如 第一步 &amp;gt;&amp;gt; 第二步 &amp;gt;&amp;gt; 第三步…、基本信息 &amp;gt;&amp;gt; 联系方式 &amp;gt;&amp;gt; 兴趣爱好…。它们都是按类型分到多个有序的页面要你来完成填写的。当然，你也可以把它们全部罗列在一个页面里，那样用户就会感觉茫茫然，分不清主次，可能不会很好的予以配合。&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
然而像 Struts、WebWork 那样的 MVC 框架未提供类似的实现，都需自己采用某种方式来实现向导，可以用层的隐现方式，或逐步把填写的部分数据放 Session 中，最后汇总处理。幸运的是 Spring MVC 考虑到了这种应用需求，它提供了一个 org.springframework.web.servlet.mvc.AbstractWizardFormController 来满足你。</description></item><item><title>Unmi 学习 Groovy 之多线程</title><link>https://yanbin.blog/unmi-study-groovy-multithread/</link><pubDate>Thu, 25 Sep 2008 20:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-multithread/</guid><description>
Java 的一个很值得称道的特性是在语言级支持多线程。定义了 Runnable 接口，并在根类 Object 中提供了 wait/notify 方法，还有 synchronized 关键字的支持。我们常说实现多线程的方式有两种：继承 Thread 和实现 Runnable，实质上工作者线程执行的都是 Runnable 接口中定义的 run() 方法，Thread 本身实现了 Runnable 接口，它不过是为线程的调度使用提供了许多有用的方法。&lt;/p&gt;
&lt;br/&gt;
而 Groovy 作为 Java 家族的皇储(安心做储君就行了)，多线程方面自然不会甘拜下风。Groovy 通过 MetaClass 对 &lt;span style="color: #800080;"&gt;java.lang.Thread&lt;/span&gt; 进行了扩展，即所谓的 &lt;a href="http://groovy.codehaus.org/groovy-jdk"&gt;GDK&lt;/a&gt; - Groovy methods added to Java SE classes。&lt;br/&gt;&lt;br/&gt;
在原 java.lang.Thread 类中增加了两个方法，分别是：</description></item><item><title>如何用正确的方法写出高质量软件的75条体会[转]</title><link>https://yanbin.blog/write-high-quality-code-75-methods/</link><pubDate>Wed, 24 Sep 2008 22:01:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/write-high-quality-code-75-methods/</guid><description>
&lt;strong&gt;1. 你们的项目组使用源代码管理工具了么？&lt;br/&gt;
&lt;/strong&gt;MVM：应该用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的选择是VSS。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2. 你们的项目组使用缺陷管理系统了么？&lt;br/&gt;
&lt;/strong&gt;MVM：应该用。ClearQuest太复杂，我的推荐是BugZilla。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;3. 你们的测试组还在用Word写测试用例么？&lt;br/&gt;
&lt;/strong&gt;MVM：不要用Word写测试用例（Test Case）。应该用一个专门的系统，可以是Test Manager，也可以是自己开发一个ASP.NET的小网站。主要目的是Track和Browse。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;4. 你们的项目组有没有建立一个门户网站？&lt;br/&gt;
&lt;/strong&gt;MVM：要有一个门户网站，用来放Contact Info、Baselined Schedule、News等等。推荐Sharepoint Portal Server 2003来实现，15分钟就搞定。买不起SPS 2003可以用WSS (&lt;a class="wordstyle" href="http://www.newasp.cn/" target="_blank"&gt;&lt;span style="color: #e9650e;"&gt;Windows&lt;/span&gt;&lt;/a&gt; Sharepoint Service)。</description></item><item><title>用 JFugue 给 Ant 构建完成分别在成功或失败时加上不同的声音提醒</title><link>https://yanbin.blog/java-jfugue-ant-build-sound/</link><pubDate>Tue, 23 Sep 2008 03:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-jfugue-ant-build-sound/</guid><description>
现在基本用 Eclipse，所有的操作都在无声无息中进行的，除了操作系统带来的咔嚓声还有你的呼哧声。怀念起使用 JBuilder 的时代了，在 JBuilder 中编译、执行程序或 Ant 脚本，在成功或失败时都有不同的声音提醒。&lt;br/&gt;&lt;br/&gt;
那我们不妨也来用 JFugue 让 Ant 构建成功或失败时发出不同的声音，关于 JFugue 的使用在 &lt;a href="https://yanbin.blog/java-music-jfugue/"&gt;让 Java 轻松乐动起来，使用 JFugue 制作自己的音乐&lt;/a&gt; 介绍过。这样我们就能在闭目养神中完成 Ant 的构建，听声音就知道成功还是失败。当然，敏捷过程中多是在晚上无人值守的时候完成自动构建，那晚上可要小心，别吓着鬼了。&lt;br/&gt;&lt;br/&gt;
实现原理，启动 ant 时用参数 -logger YourLoggerClass 指定你自己的 Logger 类(也是一个监听器)，在你的 Logger 类的 buildFinished(BuildEvent event) 时，发现无异常，表示成功，用 JFugue 发出一种声音，有异常时，表时失败，用 JFugue 发出另外一种声音。</description></item><item><title>Unmi 学习 Groovy 之数据库操作一</title><link>https://yanbin.blog/unmi-study-groovy-database1/</link><pubDate>Mon, 22 Sep 2008 04:44:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-database1/</guid><description>
不夸张的说，Groovy 在使用 JDBC 对数据库的操作上是简化到了极致。Groovy 中的 SQL 支持在 groovy.sql 包中，而其中最耀眼的明星就数 groovy.sql.Sql 了。&lt;/p&gt;
&lt;br/&gt;
现在我们就来体验一下 GroovySQL 给我们带来的快感。这里所用的示例数据库是 derbyDB, JDK 1.6 中附带了这个数据库(叫做 JavaDB)，MyEclipse 6.X 也带了 DerbyDB。假设我们把它安装在 C:\Java\JavaDB，那么在 C:\Java\JavaDB\demo\databases 下有一个 toursdb(目录) 数据库。我们就用这个，当然你可以使用你手边已有的或最拿手的数据库。</description></item><item><title>Unmi 学习 Groovy 之文件操作</title><link>https://yanbin.blog/unmi-study-groovy-file/</link><pubDate>Sun, 21 Sep 2008 19:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-file/</guid><description>
&lt;strong&gt;一：Java 与 Groovy 读文件操作比较&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
Groovy 对 java.io.File 进行了扩展，增加了一些接受闭包参数和简化文件操作的方法。作为对比，我们还是先来看看 java 中读取文件的两种常方法，分别是行读取和字节缓冲区读取：&lt;br/&gt;&lt;br/&gt;
&lt;table id="table1" style="border-collapse: collapse;" border="1" width="75%" align="center"&gt;
&lt;tbody&gt;
&lt;tr valign="top"&gt;
&lt;td width="50%"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//--BufferedReader 行读取
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;BufferedReader&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;br&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BufferedReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FileReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foo.txt&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLine&lt;/span&gt;&lt;span class="o"&gt;())!=&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileNotFoundException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;br&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//--借助于 buffer 缓冲区来读字节
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;InputStream&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FileInputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foo.txt&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;StringBuffer&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;StringBuffer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;))!=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileNotFoundException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
从上面可看到，采用 Java 传统方式来读取文件内容，不仅代码行多，而且还必须自己用 try/catch/finally 来处理异常和资源的关闭。现在马上来看看 Groovy 完成以上工作的代码是怎么的，只要一行代码：</description></item><item><title>让 Java 轻松乐动起来，使用 JFugue 制作自己的音乐</title><link>https://yanbin.blog/java-music-jfugue/</link><pubDate>Fri, 19 Sep 2008 04:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-music-jfugue/</guid><description>
学过或用过 Basic 的朋友大约还会记得，在 Basic 里要演奏(当时还是从 PC 喇叭里发出的，现在也能走声卡了)一段 &lt;span style="color: #0000ff;"&gt;哆来咪发唆拉西哆&lt;/span&gt; 可以写成：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-basic" data-lang="basic"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;PLAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;C D E F G A B O6C&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
直接用 JDK 可没有这么简单，虽然 JDK 1.3 开始就引入了 Java Sound API 处理 MIDI(Musical Instrument Digital Interface)，可是它的接口很难使用，
另外，Sun 也专为视、音频的捕获、回放、格式转换提供了
&lt;a href="http://java.sun.com/javase/technologies/desktop/media/jmf/" target="_blank"&gt;Java Media Framework API &lt;/a&gt;
(JMF)，但不能用来创作。有一个开源的组件 JFugue(&lt;a href="http://www.jfugue.org/"&gt;http://www.jfugue.org/&lt;/a&gt;)，
它能让你尽显音乐方面的天赋，给你一个高级易用的接口来操作 Java Sound，制作自己的 MIDI 音乐。</description></item><item><title>Unmi 学习 Groovy 之闭包与资源、异常处理</title><link>https://yanbin.blog/unmi-study-groovy-closure-resource-exception/</link><pubDate>Wed, 17 Sep 2008 22:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-closure-resource-exception/</guid><description>
&lt;p&gt;闭包还为我们提供了改善处理复杂 try/catch/finally 结构的方法。利用闭包，很容易编写正确处理资源和异常的代码。使用闭包的新方法已经添加到处理文件、进程和数据库连接的标准 Java 类中。当它们用在 Groovy 中的时候，不必处理和担心资源的关闭。首先我们来看看 Groovy 实现这一方式的原理。我们假设有这么一个资源处理类。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;resourceName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;ResourceException&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//open the resource
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;ResourceException&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//return data or false as the end marker
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="n"&gt;ResourceException&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//close the resource
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;那么我们的打开、读取和关闭资源的典型的 Java 代码看起来就像这样：</description></item><item><title>phpLDAPadmin 安装配置讲解，通过 Web 端来管理您的 LDAP 服务器</title><link>https://yanbin.blog/phpldapadmin-install-web-ldap-admin/</link><pubDate>Thu, 11 Sep 2008 06:51:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/phpldapadmin-install-web-ldap-admin/</guid><description>
做企业应用的也不时要与 LDAP 打交道的，例如组织架构、人员的管理，权限的分级控制等。以前管理 LDAP 都是用客户端工具 LDAP Browser 什么的，有时不甚方便。幸运的是，网上有一个现成的、功能完备的 LDAP Web 管理工具，那就是赫赫有名的 phpMyAdmin 的姊妹 phpLDAPadmin。&lt;/p&gt;
&lt;br/&gt;
Web 方式的 phpLDAPadmin 利于发布，随处使用，基本上它有 LDAP Browser 客户端工具的所有功能，诸如条目管理、属性管理、导入导出、条件搜索等。还有比 LDAP Browser 所没有的 Schema 信息明细展示功能。&lt;br/&gt;&lt;br/&gt;
phpLDAPadmin 的界面截图请见：&lt;a href="http://phpldapadmin.sourceforge.net/screenshots.php"&gt;http://phpldapadmin.sourceforge.net/screenshots.php&lt;/a&gt;&lt;br /&gt;
您也可以进到 phpLDAPadmin 的 Demo 站点：&lt;a href="http://thesmithfam.org/phpldapadmin-demo/htdocs/index.php"&gt;http://thesmithfam.org/phpldapadmin-demo/htdocs/index.php&lt;/a&gt; 先睹为快。</description></item><item><title>Unmi 学习 Groovy 之命名参数</title><link>https://yanbin.blog/unmi-study-groovy-named-paramter/</link><pubDate>Tue, 09 Sep 2008 00:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-named-paramter/</guid><description>
Groovy 中提供了一个减少输入的特性叫做命名参数(Named Parameter)。GroovyBean 可以通过在构造器调用中传递冒号隔开的属性名称和值进行构建。如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;BMW&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
其实类似的用法早已有之，这有如 C++ 中的初始化成员列表，VB、Python、Transact-SQL 中的命名参数，JavaScript 中的 JSON  写法，Ruby 中提供的 syntax sugar用 hash 模拟所谓的 keyword argument。&lt;br/&gt;&lt;br/&gt;
我们先详细介绍 Groovy 的命名参数的用法与本质，然而再附上其他一些语言类似用法的实例，纵向贯通以加深印象。&lt;br/&gt;&lt;br/&gt;
要说从外部表现上好像是先调用了空构造方法，然后是相应的 setter 方法进行设值。因此，我们所直接想像的应该相当于下列 Java 代码</description></item><item><title>Unmi 学习 Groovy 之闭包</title><link>https://yanbin.blog/unmi-study-groovy-closure/</link><pubDate>Wed, 03 Sep 2008 12:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-closure/</guid><description>
&lt;strong&gt;一. 认识闭包&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
将代码块作为方法参数进行传递，这种机制就叫做闭包。闭包可以引用在创建闭包的范围中可见的变量。最近关于闭包的讨论也比较多，闭包能使语言更具灵动性，在动态脚本语言中较广泛的支持，如 Perl、Python、Ruby、JavaScript，还有我们的 Groovy。&lt;br/&gt;&lt;br/&gt;
有些语言能把函数作为参数传递，如 JavaScript 的回调函数，Python，甚至是 C++ 的函数指针。而 Java 在这方面又略逊一筹，需搬动一个匿名的内部类来实现类似的功能，内部类只能访问外部声明为 final 的变量。不过有呼声要在 Java SE 7 中增加闭包特性，让我们试目以待吧。&lt;br/&gt;&lt;br/&gt;
Groovy 这回大概是从 Ruby 那儿偷得闭包的语法。前面说这么多，其实你看到了就会发现，其实闭包很简单的，不信，请看：</description></item><item><title>Unmi 学习 Groovy 之正则表达式</title><link>https://yanbin.blog/unmi-study-groovy-regex/</link><pubDate>Tue, 02 Sep 2008 21:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-regex/</guid><description>
一提到 Perl,很多人对它啧啧称道的就是它那强大的正则表达式。一段富含正则表达式的 Perl 代码能让人觉得眼花缭乱，不过一旦习惯了就会对此爱不释手。而 Java 是在 1.4(在之前的版本需要依靠 jakarta-oro 库来提供对正则表式的支持--也是参考了 Perl) 中才加入了正则表达式，但它的相关操作还是略显古板。&lt;/p&gt;
&lt;br/&gt;
当从 Java 分化出 Groovy 脚本语言一支，在处理正则表达式时也不想落后，从 Perl 那里学来了一些更为紧凑的语法，使用起来方便多了。虽相比 Perl 还有些差距，基本还是能知足了。下面来介绍 Groovy 中如何使用正则表达式，还是从 Java 的正式表达式说开，好有个对比。&lt;br/&gt;&lt;br/&gt;
比如在 Java 中要检查一段文本是否与某个模式相匹配，使用代码：</description></item><item><title>Unmi 学习 Groovy 之操作符重载</title><link>https://yanbin.blog/unmi-study-groovy-operator-overload/</link><pubDate>Tue, 02 Sep 2008 05:32:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-groovy-operator-overload/</guid><description>
Java 相比于 C++ 语法来说去除了指针及显式内存释放，受到不少赞誉，的确避免很多的出错的可能性，提高了生产率。可是把操作符重载也拿掉了，也没了条件编译。这两项特性在 C# 是有的。操作符在 C++ 中似乎不是很好理解，它可以带来很大的灵活性，和操作的直观性。Java 大约想的是过宽的灵活性怕带来过度的滥用，在大型项目会有所不利。&lt;/p&gt;
&lt;br/&gt;
因此由 Java 所衍生的 Groovy 脚本像 Perl、Python、Ruby 一样又有了操作符重载，脚本基于其应用领域需要更多的灵活性和自由度。&lt;br/&gt;&lt;br/&gt;
Groovy 中对操作的操作比起 C++ 中来说更简单，Groovy 中是把操作符映射到对应命名方法的调用，你只要重载了该命名方法便是重载了相应的操作符，比如说加号+ 映射到 &lt;span style="color: #800080;"&gt;plus(obj)&lt;/span&gt; 方法，你只要重载了 &lt;span style="color: #800080;"&gt;plus(obj)&lt;/span&gt; 即改变了加号+ 的形为。对于其他符号也是一样的。</description></item><item><title>汇集 developerWorks 的《精通 Groovy》成 chm 文件，方便阅读</title><link>https://yanbin.blog/developerworks-master-groovy-chm/</link><pubDate>Mon, 01 Sep 2008 11:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/developerworks-master-groovy-chm/</guid><description>
&lt;p&gt;由于与 Java 渊源，所以还是打算把 Groovy 作比较细致的了解。还是在 IBM developerWorks 上找到一份初级的 Groovy 教程，名之曰《精通 Groovy》，实则一份入门教程。为大家阅读方便－－免去了在线，还要一个 IBM ID 登陆后一页一页的翻，所以我把那些页面收集一下，制作成一个 CHM 文件，共享给大家。&lt;/p&gt;
&lt;p&gt;原始教程地址在：&lt;a href="https://www6.software.ibm.com/developerworks/cn/education/java/j-groovy/index.html"&gt;https://www6.software.ibm.com/developerworks/cn/education/java/j-groovy/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CHM 文件 Master Groovy.chm 下载地址：&lt;a href="
/posts/2008/developerworks-master-groovy-chm/Master_Groovy.chm.rar
"&gt;Master Groovy.chm.rar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;得承认 Ruby 在动态脚本语言领域很有其过人之处，只是本人一直不怎么情愿接受它。现在好了，终于觅得了几乎能与 Ruby 相媲美的 Groovy，应该可让其在某些时候大显身手的。&lt;/p&gt;</description></item><item><title>基于 dp.SyntaxHighlighter 写的代码着色工具(增加了对 Groovy 的支持)</title><link>https://yanbin.blog/dp-syntaxhighlighter-groovy-support/</link><pubDate>Sun, 31 Aug 2008 21:57:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dp-syntaxhighlighter-groovy-support/</guid><description>
&lt;p&gt;本程序是基于 dp.SyntaxHighlighter 写的代码语法着色的工具。支持的语言有&lt;br /&gt; java/xml/sql/jscript/groovy/css/cpp/c#/python/vb/perl/php/ruby/delphi。&lt;/p&gt;
&lt;p&gt;可方便用于你的博客中粘贴代码，只要自定相应的样式 (&lt;span style="color: #800080;"&gt;highlight.css&lt;/span&gt; 的内容，.Text 支持自定义样式或在模板里加上语法样式)，然后复制用这个工具生成的 HTML 代码就能让你的代码着高亮显示。&lt;/p&gt;
&lt;p&gt;可以加入更多语种的支持，本程序就是在 dp.SyntaxHighlighter 的基础上扩展了对 Perl 语言的支持，网上可以找到相应语法的 JS 代码和 CSS。语言扩展支持通过在 shCore.js 和 highlight.css 加入相应代码即可。</description></item><item><title>又是 Oracle JDBC 驱动版本太低引发的错误-rs.getByte() 的差异</title><link>https://yanbin.blog/oracle-jdbc-low-version-rs-getbyte/</link><pubDate>Thu, 28 Aug 2008 06:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-jdbc-low-version-rs-getbyte/</guid><description>
&lt;p&gt;前面写过一个因为 Oracle 驱动版太低引起的一在题：&lt;a id="viewpost1_TitleUrl" href="https://yanbin.blog/oracle-low-jdbc-version-unknow-chars/"&gt;Oracle 驱动版本引起的显示字段奇怪编码问题&lt;/a&gt;。
今天又遇着一个。&lt;/p&gt;
&lt;p&gt;本地写好的一段连接数据库的程序，一放到正式服务器上又不能正常工作了，原来的代码中没有打印出异常栈，都看不出什么问题来，后来加上异常栈输出，得到如下信息：&lt;/p&gt;
&lt;p&gt;&lt;span style="color: #ff1493;"&gt;Fail to convert to internal representation&lt;br /&gt;  java.sql.SQLException: Fail to convert to internal representation&lt;br /&gt;
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:114)&lt;br /&gt;
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:156)&lt;br /&gt;
        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:219)&lt;br /&gt;
        at oracle.jdbc.driver.OracleStatement.getLongValue(OracleStatement.java:3022)</description></item><item><title>Quartz Job Scheduling Framework 中文版 V0.9.0.CHM 文件发布</title><link>https://yanbin.blog/quartz-job-scheduling-framework-v0-9-0-chm-released/</link><pubDate>Mon, 25 Aug 2008 12:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-v0-9-0-chm-released/</guid><description>
&lt;p&gt;由本人翻译并制作的《Quartz Job Scheduling Framework 中文版》CHM 文件更新为 V0.9.0 版，上一版本是 V0.8.2。&lt;/p&gt;
&lt;p&gt;更新的特性有以下三点：&lt;/p&gt;
&lt;p&gt;1. 在原来版本的基础上加上了全文检索功能，方便于快速查找某个参考配置和定位到所关心的内容&lt;/p&gt;
&lt;p&gt;2. 去除了多余的索引标签，因为它和目录标签的内容就是雷同的&lt;/p&gt;&lt;p&gt;3. 加上了书签页，方便阅读时记录下所关注的主题</description></item><item><title>XML 的中阶知识巩固之 XML Schema 其一</title><link>https://yanbin.blog/xml-schema-lift1/</link><pubDate>Mon, 25 Aug 2008 04:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xml-schema-lift1/</guid><description>
用过 XML 的人都知道可用 DTD(Document Type Definition) 来验证语法，不过微软主导了新一代验证语法的 XML Schema，并且风头正劲，大有一统江湖之势。看看我们身边的几个变化就明白了。&lt;/p&gt;
&lt;br/&gt;
web.xml 从 web-app_2_3.dtd 到 web-app_2_4.xsd&lt;br /&gt;
spring 配置文件从 spring-beans.dtd 到 spring-beans-2.0.dtd 到 spring-beans-2.0.xsd，直至灵活的自定义 Schema&lt;br /&gt;
quartz_jobs.xml 配置文件从 job_scheduling_data_1_5.dtd 到 job_scheduling_data_1_5.xsd&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;XML Schema 简介&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
XML Schema 是一个比 DTD 具有更细致描述语法功能的验证技术，并且它本身也是一个 XML 文件，不像 DTD 用是是独有的语法。既然 XML Schema 和 DTD 都是用来验证 XML 的，并能大抢风头，固然有它值得称道的地方。所以先说说 DTD 的的不足和 XML Schema 的特点。</description></item><item><title>Quartz Job Scheduling Framework［翻译］- 中文版 CHM 文件发布</title><link>https://yanbin.blog/quartz-job-scheduling-framework-chinese/</link><pubDate>Sun, 24 Aug 2008 09:26:41 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-chinese/</guid><description>
&lt;table style="width: 682px; height: 192px;" width="682"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2008/quartz-job-scheduling-framework-chinese/Quartz_Job_Scheduling_Framework_2.jpg"
width="138px"
/&gt;
&lt;/td&gt;&lt;td align="center"&gt;&lt;strong&gt;&lt;span style="font-size: medium;"&gt;Quartz Job Scheduling Framework&lt;/span&gt;&lt;/strong&gt;&lt;strong&gt; 中 文 版&lt;br /&gt; &lt;/strong&gt;&lt;br /&gt; Chuck Cavaness 著 Unmi(隔叶黄莺) 译&lt;p&gt;译者博客：&lt;a href="http://unmi.cc/"&gt;http://unmi.cc/&lt;br /&gt; &lt;/a&gt;&lt;br /&gt;
下载地址：&lt;a href="
/posts/2008/quartz-job-scheduling-framework-chinese/Quartz_Job_Scheduling_Framework_CN_V1.0.0.rar
"&gt;Quartz Job Scheduling Framework 中文版.chm&lt;br /&gt; &lt;/a&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;写在最前面的&lt;br /&gt; &lt;/strong&gt;&lt;br /&gt; 以此篇权当译者序。再次翻看一下我的博客，最早的那篇《Quartz Job Scheduling Framwork》翻译是在 2007-10-17 02:17 ，距离今日那是一个造人的时间，足见这个翻译过程有多难产。一方面是源于语言水平，再者其间也发生了许多事情。&lt;/p&gt;&lt;p&gt;Quartz 是什么，大概不需多加说明，简单讲就是一个纯 Java 实现的作业调度工具，相当于数据库中的 Job、Windows 的计划任务、Unix/Linux 下的 Cron，但 Quartz 可以把排程控制的更精细。也许大多数人听说 Quartz 是在学习或使用 Spring 的时候，也就是 Spring 整合了 Quartz。而我也不例外，同样是在春天里得悉了这块石英。</description></item><item><title>JDOM 使用 XPath 查找带 NameSpace 的节点</title><link>https://yanbin.blog/jdom-xpath-seach-namespace/</link><pubDate>Fri, 22 Aug 2008 01:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdom-xpath-seach-namespace/</guid><description>
对于 XML 文件中不带命名空间(NameSpace) 的节点，如&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;&amp;lt;blog_Content&amp;gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
用 JDOM 的 XPath 来搜索这个节点时可用下面之中任一代码：&lt;br/&gt;&lt;br/&gt;
①   &lt;span style="color: #800080;"&gt;List&amp;lt;Element&amp;gt; elements = XPath.selectNodes(document,"//blog_Content");&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
②   &lt;span style="color: #800080;"&gt;XPath xpath = XPath.newInstance("//blog_Content");&lt;br /&gt;
       List&amp;lt;Element&amp;gt; elements = xpath.selectNodes(document);&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
但是如果是查找带命名空间的节点，如</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十四章. 工作流中使用 Quartz (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-14-4/</link><pubDate>Tue, 19 Aug 2008 02:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-14-4/</guid><description>
&lt;strong&gt;四. 创建一个工作流 Job&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
最后，我们需要介绍启动工作流的 Quartz Job 。当 Scheduler 调用了它，Quartz Job 就查找工作流的名字，并启动、运行相应的工作流。如果没有在 &lt;span style="color: #800080;"&gt;JobDataMap&lt;/span&gt; 中配置工作流的名字，Job 就会直接退出。&lt;br/&gt;&lt;br/&gt;
代码 14.13 显示了 &lt;span style="color: #800080;"&gt;WorkflowJob&lt;/span&gt;.</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十四章. 工作流中使用 Quartz (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-14-3/</link><pubDate>Mon, 18 Aug 2008 08:22:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-14-3/</guid><description>
&lt;strong&gt;四. Quartz 与 OSWorkflow 的集成&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
OSWorkflow 与 Quartz 集成的第一步是要改变关于 Job 的思维方式。当把 OSWorkflow 引入到你的 Quartz 应用时你需要以完全不同的方式来思考。那也不是说你当前的想法就是糟糕的或不正确的，只是与 Quartz 一同用工作流强迫你生发一些关于是什么组成 Job 的新的思维。你过去概念中的 Job 现成变成了一个 OSWorkflow 函数。你可以认为是你原有 Job 实质上存在的逻辑作为工作流中的步骤。你仍然需要使用 Quartz 的 Job，但是，当与 Quartz 框架集成工作流时，一个 Quartz Job 将用来初始化工作流。在工作流运行时，这个 Job 将会等待它直至结束。&lt;br/&gt;&lt;br/&gt;
在本章前面部分，当我们谈到串联 Job 时，每个 Job 代表了一个独立的任务。Jox X 执行后并完成一个任务，接着通知 Job Y 去执行一个有点关联却是独立的任务。在这两个任务间必须有一些依赖关系，否则你不能把它们链接在一起。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十四章. 工作流中使用 Quartz (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-4-2/</link><pubDate>Sun, 17 Aug 2008 22:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-4-2/</guid><description>
&lt;p&gt;&lt;strong&gt;三. OSWorkflow 快速入门&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;像 Quartz 一样，OSWorkflow 是一个完全由 Java 构建的开源项目，而且也是 OpenSymphony 家族项目的成员。还有许多的工作流项目，商业的或是开源的。OSworkflow 在设计上与 Quartz 有很多相似性，所以把这两个框架进行集成不用太费我们的心思。
&lt;/p&gt;
&lt;p&gt;OSWorkflow 工作在有限状态机的原则之上。一个工作流由一系列状态组成，包括一个开始状态和一个或多个结束状态。从某一状态迁移到另一状态，需要发生一次转换。实际上从某一特定状态可能会有多种转换，你也可以在同一时间从某一状态发生多种转换。选择什么转换依赖于环境，对状态的输入，和一些我们将在后面讨论的条件信息。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十四章. 工作流中使用 Quartz (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-14-1/</link><pubDate>Sat, 16 Aug 2008 11:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-14-1/</guid><description>
&lt;strong&gt;第十四章. 工作流中使用 Quartz&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 可以执行一个难以置信的 Job，来完成预计的任务。不幸的是，用来运行一个业务的 Job 经常比单一的 Job 或任务要稍稍复杂。每年百万计的美金花费到理解、设计和构建组织的业务流程。Quartz 框架包含一些设施用于把多个 Job 链接起来构建一个简单的业务流程模型　。本章讨论你能如何用 Quartz 连接 Job。为获得实际的工作流可操作性，你还需要一些来自于 Quartz 框架的东西。本章就来看为实现你的 Job 所构成的工作流可以如何扩展 Quartz 框架。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. 什么是工作流&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
Web 上聚集了个人或团体关于工作流的定义和实例。有人定义工作流为“自动化的后台管理系统”。另一些人使用“业务流程建模” 一语，并收取许多的咨询费用向你解释这个概念。对于本章的要义，我们使用如下的工作流定义：</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十一章. Quartz 集群 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-11-4/</link><pubDate>Mon, 11 Aug 2008 12:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-11-4/</guid><description>
&lt;strong&gt;四. 运行 Quartz 集群节点&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
在启动集群中的 Quartz 应用真的没什么差别。每个实例(或节点) 必须单独启动。启动时，实例连接到数据库，获取 Scheduler 信息，并开始部署 Job。&lt;br/&gt;
&lt;br/&gt;
因为 Quartz 使用了一个随机的负载均衡算法，你将会看到 Job 以随机的行方式由不同的实例执行。没有固定的模式或预告定义的节点来执行特定的 Job。&lt;br/&gt;
&lt;br/&gt;
下一节会讨论一些在处理集群环境中的 Quartz 较常见的问题和任务。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;五. Quartz 集群 Cookbook&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
本节旨在为开发者便于解决 Quartz 集群的具体问题而提供了资源。</description></item><item><title>Perl 语言概览，高屋建瓴、欲招人之耳目（四）</title><link>https://yanbin.blog/perl-overview-4/</link><pubDate>Mon, 11 Aug 2008 03:25:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-overview-4/</guid><description>
&lt;strong&gt;1.6 流程控制&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1.6.1 什么是真&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
  ·除了””和”0”，所有字符串为真&lt;br/&gt;
  ·除了 0，所有数字为真&lt;br/&gt;
  ·所有引用为真&lt;br/&gt;
  ·所有未定义的值为假．&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1.6.2 If 和 unless 语句&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
   if (case1) {...} elsif (case2) {...} elsif (case3) {...}  else {...}&lt;br/&gt;
&lt;br/&gt;
为节约版面，全写在一行了。if/elsif/else 后的大括号是必须的，这和 C/Java中不一样，elsif 同 pl/sql 的写法。&lt;br/&gt;
&lt;br/&gt;
象在英语中说“如果这不是真的，就做某事”，你可以使用 unless 语句：</description></item><item><title>Perl 语言概览，高屋建瓴、欲招人之耳目（三）</title><link>https://yanbin.blog/perl-overview-3/</link><pubDate>Sun, 10 Aug 2008 21:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-overview-3/</guid><description>
&lt;strong&gt;1.4 文件句柄&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
文件句柄只是你给文件，设备，网络套接字或管道起的一个名字，这样可以帮助你分清你正在和那个文件或设备通讯，同时掩藏了如缓冲等复杂性。&lt;br/&gt;
&lt;br/&gt;
你可以使用 open 创建并关联一个文件。open 函数需要至少两个参数：文件句柄和你希望与文件句柄关联的文件名。Perl 也给你一些预定义（并且预先打开）的文件句柄。STDIN 是我们程序的标准输入，&lt;br/&gt;
&lt;br/&gt;
STDOUT 是标准输出。STDERR 标准错误输出。&lt;br/&gt;
&lt;br/&gt;
因为你可以用 open 函数创建用于不同用途（输入，输出，管道）的文件句柄，因此你必须指定你需要哪种类型。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十一章. Quartz 集群 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-11-3/</link><pubDate>Fri, 08 Aug 2008 03:51:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-11-3/</guid><description>
&lt;strong&gt;三. 配置 Quartz 使用集群&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
为 Quartz 配置集群环境的步骤比设置类似的 J2EE 集群环境容易的多：&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1.&lt;/strong&gt;  配置每个节点的 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2.&lt;/strong&gt;  配置 JDBC &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;3.&lt;/strong&gt;  使用 Scheduler 信息(Job 和 Trigger) 装载数据库。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;4.&lt;/strong&gt;  启动每个 Quartz 节点。</description></item><item><title>Perl 语言概览，高屋建瓴、欲招人之耳目（二）</title><link>https://yanbin.blog/perl-overview-2/</link><pubDate>Thu, 07 Aug 2008 03:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-overview-2/</guid><description>
&lt;strong&gt;1.2.4 复杂数据结构&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
$wife{"Jacob"} = ("Leah", "Rachel", "Bilhah", "Zilpah");   # 错误&lt;br/&gt;
&lt;br/&gt;
$wife{"Jacob"} = ["Leah", "Rachel", "Bilhah", "Zilpah"];   # 正确&lt;br/&gt;
&lt;br/&gt;
用[] 中括弧来将一个列表转换成一个标量。&lt;br/&gt;
&lt;br/&gt;
$wife[0] = ["Leah", "Rachel", "Bilhah", "Zilpah"];  #正确&lt;br/&gt;
&lt;br/&gt;
$wife = ["Leah", "Rachel", "Bilhah", "Zilpah"]; #错误&lt;br/&gt;
&lt;br/&gt;
左边要写成 $wife{"Jacob"} 或 $wife[0] 表明 wife 是一个数组或散列，只有对数组或散列才能这样赋值。&lt;br/&gt;
&lt;br/&gt;
访问方式用 $wife{"Jacob"}[0] 或 $wife[0][0]</description></item><item><title>Perl 语言概览，高屋建瓴、欲招人之耳目（一）</title><link>https://yanbin.blog/perl-overview-1/</link><pubDate>Wed, 06 Aug 2008 08:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-overview-1/</guid><description>
&lt;strong&gt;1. 使用帮助手册&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
如果你是在 Linux/Unix 下，那么 Man 给我们准备好了各色点心，用 man 加上下面中的类别名就能查阅到对应的帮助，如 man perldata &lt;br/&gt;
&lt;table border="0"&gt;
&lt;tbody&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;
&lt;table style="border-collapse: collapse;" border="1" cellspacing="1" cellpadding="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;手册页&lt;/th&gt;&lt;br/&gt;
&lt;th&gt;内容&lt;/th&gt;&lt;br/&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perl&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;有些什么 perl 手册页&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perldata&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;数据类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlsyn&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;语法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlop&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;操作符和优先级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlre&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;正则表达式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlvar&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;预定义变量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlsub&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;子过程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;prelfunc&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;内建函数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlmod&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;如何令 Perl 模块工作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlref&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;参考手册&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlobj&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;对象&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlipc&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;进程间通讯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlrun&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;如何运行 Perl 命令，以及命令行开关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perldebug&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;调试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perldiag&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;诊断信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;table style="border-collapse: collapse;" border="1" cellspacing="1" cellpadding="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;手册页&lt;/th&gt;&lt;br/&gt;
&lt;th&gt;内容&lt;/th&gt;&lt;br/&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlfaq1&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;关于 Perl 的通用信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlfaq2&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;获取和学习 Perl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlfaq3&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;编程工具&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlfaq4&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;数据操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlfaq5&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;文件和格式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlfaq6&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;正则表达式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlfaq7&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;通用 Perl 语言信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffcc"&gt;perlfaq8&lt;/td&gt;
&lt;td bgcolor="#ffffcc"&gt;系统交互&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td bgcolor="#ffffff"&gt;perlfaq9&lt;/td&gt;
&lt;td bgcolor="#ffffff"&gt;网络&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
或分 FAQ 来分类手册，还能在手册中搜索内容，如&lt;br/&gt;
&lt;br/&gt;
man perlfunc split      --- 搜索 split 函数的使用&lt;br/&gt;
&lt;br/&gt;
man perlvar ARGV   --- 内置变量 ARGV 的帮助信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十一章. Quartz 集群 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-11-2/</link><pubDate>Wed, 06 Aug 2008 03:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-11-2/</guid><description>
&lt;p&gt;&lt;strong&gt;二. Quartz 中集群是如何工作的&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用，它又管理着其他的节点。意思是你必须对每个节点分别启动或停止。不像许多应用服务器的集群，独立的 Quartz 节点并不与另一其的节点或是管理节点通信。(将来的 Quartz 版本将会设计成让节点能与其他节点直接通信，而不是借助于数据库。) 取而代之的是，Quartz 应用是通过数据库表来感知到另一应用的。&lt;/p&gt;&lt;table style="border: 0px;" border="1" width="70%" align="center"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Quartz 集群仅能使用 JDBC JobStore 工作&lt;/strong&gt;
&lt;br/&gt;&lt;br/&gt;
因为集群中节点依赖于数据库来传播 Scheduler 实例的状态，你只能在使用 JDBC &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 时应用 Quartz 集群。这意味着你必须使用 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 或是 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 作为 Job 存储；你不能在集群中使用 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 的。在将来的释放版中非常可能移除这个需求，节点也将能直接与另一节点直接通过网络协议，可能使用 JGroup 进行通信。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;图 11.1 显示了每个节点直接与数据库通信，若离开数据库将对其他节点一无所知</description></item><item><title>DB2 登录数据库时的代码页转换错误 SQL0332N</title><link>https://yanbin.blog/db2-login-codepage-sql0332n/</link><pubDate>Tue, 05 Aug 2008 22:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/db2-login-codepage-sql0332n/</guid><description>
客户端连接到另一台机器上的 DB2 数据库，用 DB2 的控制中心连接没问题，但是用 Quest Central for DB2 来连接，输入用户名和密码，确定，出现提示窗口：&lt;br/&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;[IBM][CLI Driver] SQL0332N  没有从源代码页 "86" 至目标代码页 "819" 的转换。原因代码是 "DB2INST1"。  SQLSTATE=01539&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
无法登录，原因是本机的代码页(codepage) 与数据库的代码页不相符且无符完成又向转换。&lt;br/&gt;
&lt;br/&gt;
解决办法是在命令行下执行 db2set DB2CODEPAGE=819&lt;br/&gt;
&lt;br/&gt;
dos&amp;gt; db2set DB2CODEPAGE=819&lt;br/&gt;
&lt;br/&gt;
然后，再次用 Quest Central for DB2 来连接数据库就 OK 啦&lt;br/&gt;
&lt;br/&gt;
参考：1. &lt;a href="http://www.aiview.com/2006/03/sql0332n_reason_code_1.html"&gt;SQL0332N Reason Code 1&lt;/a&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十一章. Quartz 集群 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-11-1/</link><pubDate>Tue, 05 Aug 2008 21:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-11-1/</guid><description>
&lt;strong&gt;第十一章. Quartz 集群&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
不可避免的，我们还是要说到集群。虽然单个 Quartz 实例能给予你很好的 Job 调度能力，但它不能令典型的企业需求，如可伸缩性、高可靠性满足。假如你需要故障转移的能力并能运行日益增多的 Job，Quartz 集群势必成为你方言的一部分了。本章就告诉你如何使用 Quartz 的集群能力来更好的支持你的业务需求，并且即使是其中一台机器在最糟的时间崩溃了也能确保所有的 Job 得到执行。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;一. 集群对 Quartz 来说意味着什么？&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
集群扮演着运行一个组件或应用的多个实例，它们以透明的方式提供服务。集群是企业范畴的事物，而不局限于 Java 的世界里。当部署 J2EE 应用时，例如，供应商为应用服务器提供了集群的能力，以便于像 EJB、JNDI 和 Web 组件能获得高可用性。然当客户端请求这些服务时候，它们就能更可靠的提供服务。</description></item><item><title>想选一种动态语言＋跨平台界面组件的组合，希望大家给点意见</title><link>https://yanbin.blog/script-plus-crossed-widget/</link><pubDate>Thu, 31 Jul 2008 09:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/script-plus-crossed-widget/</guid><description>
原来只是关注过跨平台组件的选择对比，有 GTK、wxWidgets、Qt、AWT/SWING、SWT。&lt;br/&gt;
&lt;br/&gt;
GTK 是 GNOME 的实现组件&lt;br/&gt;
&lt;br/&gt;
wxWidgets 对于用过 MFC 的人来说用起来很顺手&lt;br/&gt;
&lt;br/&gt;
Qt 是 KDE 的实现组件&lt;br/&gt;
&lt;br/&gt;
AWT/SWING 无需多说，SWT 相信也为 Java 开发人员熟知了。&lt;br/&gt;
&lt;br/&gt;
曾得益于 VC 写的程序小巧且基本上拷个 EXE 文件到别人机器上就能运行，所以希望还是用 C++ 来编写界面程序，所以考虑过 wxWidgets 和 Qt。后来发现这两个组件的动态库的个头也不小，即使在客户端安装个 JRE 又何妨，何况 JAVA+SWT 的程序与 wxWidgets/Qt 的本地程序速度上差别已不大。综上，如果仅仅是跨平台的界面程序个人看重 JAVA+SWT 了，SWING 给我产生的欲望还不大。</description></item><item><title>JDBC 连接 Oracle 时，用 rs.absolute(n) 真的不如 n 次 next() 性能好</title><link>https://yanbin.blog/jdbc-oracle-rs-absoluten-or-next-n/</link><pubDate>Thu, 24 Jul 2008 09:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdbc-oracle-rs-absoluten-or-next-n/</guid><description>
前面写过一篇：&lt;a id="viewpost1_TitleUrl" href="https://yanbin.blog/oracle-low-jdbc-version-unknow-chars"&gt;Oracle 驱动版本引起的显示字段奇怪编码问题&lt;/a&gt;。讲到因 Oracle 8.0.5 不支持子查询排序，为改善原来那种每次翻页时都捋出所有数据成对象到 List 中，然后从中拣取页面实际要显示的记录的性能问题时，采用了 rs.absolute() 直接跳到起始记录游标的方法，但又引入了乱码问题，例如："无效"，变成了 "0xE697A0E69588"。&lt;br/&gt;&lt;br/&gt;
虽说，换个驱动，如 8.1.7.0.0 以上版本的驱动就能解决乱码的问题，但这一换又怕会影响到其他的应用。有朋友评论说，其实循环 next() 到某处比 absolute() 定位要好，乍一看，有些牵强，不过试试就知道了。下面就来做样一个测试，测试代码如下：</description></item><item><title>Quartz Job Scheduling Framework［翻译］第八章. 使用 Quartz 插件 (第五部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-8-5/</link><pubDate>Tue, 22 Jul 2008 08:45:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-8-5/</guid><description>
&lt;strong&gt;五. Quartz 工具插件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 框架包括几个你能用于你的应用中的几个插件。本节简单描述它们和它们的用途。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·&lt;span style="color: #800080;"&gt;JobInitializationPlugin&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
我们已经多次谈到过这个插件。它从一个 XML 文件中加载 Job 和 Trigger 信息(默认文件名是 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt;) 。你可以通过在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中为这个插件设定 &lt;span style="color: #800080;"&gt;filename&lt;/span&gt; 参数来配置文件名。假如你不需要数据库来存储你的 Job 或者是需要能快速测试特定 Job 的话，这个插件非常有帮助。</description></item><item><title>碰到一个不知如何解释的 Java 控制台程序的内存问题</title><link>https://yanbin.blog/explain-java-console-memory-leak/</link><pubDate>Tue, 22 Jul 2008 00:19:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/explain-java-console-memory-leak/</guid><description>
有一 Java 控制台程序，启动经过一段时间之后从 Windows 任务管理器里看它所占用的内存稳定在 540M 左右。&lt;/p&gt;
&lt;br/&gt;
启动参数是：-Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128&lt;br/&gt;&lt;br/&gt;
但只要你把那个控制台窗口最小化后，观察到的内存用瞬间下降到 100 多M，有时候甚至是几十M。然后不管是窗口保持最小化还是恢复了，它所占用的内存又以几十M几十M的上扬，直至先前的 540 M 左右。每次最小化窗口都可以观察到这种现象。&lt;br/&gt;&lt;br/&gt;
控制台窗口的参数：屏幕缓冲区大小：宽 120；高 300。窗口大小：宽 120；高 40。&lt;br/&gt;&lt;br/&gt;
不知道在控制台窗口最小化那时，JVM 做了些什么事情能让内存骤降下来，而复又升回去。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第八章. 使用 Quartz 插件 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-8-4/</link><pubDate>Mon, 21 Jul 2008 05:11:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-8-4/</guid><description>
&lt;strong&gt;四. 使用多个插件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
你喜欢多少个，就可以在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中注册多少个插件。然而，加载和初始化的顺序却不能保证，因为 Quartz 加载先把所有的属性到一个 Map 中，然后按照从 Map 中取出的顺序遍历插件。&lt;br/&gt;&lt;br/&gt;
为规避这一限制，你可以创建一个 Quartz 插件作为父插件，然后以给定的顺序加载其他多个插件。代码 8.6 显示了 &lt;span style="color: #800080;"&gt;ParentPlugin&lt;/span&gt; 长什么样子。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;代码 8.6. &lt;span style="color: #800080;"&gt;ParentPlugin&lt;/span&gt; 能以特定的顺序加载子插件&lt;/strong&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第八章. 使用 Quartz 插件 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-8/</link><pubDate>Sun, 20 Jul 2008 07:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-8/</guid><description>
&lt;strong&gt;三. 注册你的插件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
当 &lt;span style="color: #800080;"&gt;SchedulerFactory&lt;/span&gt; 首次初始化的时候，会从 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中搜寻你所配置的 Quartz 插件。它会通过 &lt;span style="color: #800080;"&gt;java.lang.Class&lt;/span&gt; 的 &lt;span style="color: #800080;"&gt;newInstance()&lt;/span&gt; 方法创建插件的实例。你的插件必须有一个无参的构造方法，像代码中 &lt;span style="color: #800080;"&gt;JobLoaderPlugin&lt;/span&gt; 所做的那样。&lt;br/&gt;&lt;br/&gt;
要在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中注册你的插件的话，需在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中使用如下的格式创建一个属性：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.plugin.&amp;lt;pluginName&amp;gt;.class=&amp;lt;fully_qualified_class_name_of_plugin&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
Quartz 找寻属性文件中所有含这个关键词的项：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.plugin.&amp;lt;pluginName&amp;gt;.class</description></item><item><title>Quartz Job Scheduling Framework［翻译］第八章. 使用 Quartz 插件 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-8-2/</link><pubDate>Sat, 19 Jul 2008 21:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-8-2/</guid><description>
&lt;strong&gt;二. 创建 Quartz 插件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
创建一个新的插件很简单。你所有要做的就是创建一个 Java 类(或重用一个现有的类)，让它实现 &lt;span style="color: #800080;"&gt;org.quartz.spi.SchedulerPlugin&lt;/span&gt; 接口。Scheduler 将会在启动期间创建这个插件的实例。这个插必须有一个无参的构造方法，很显然它不能是抽象的。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·&lt;span style="color: #800080;"&gt;JobInitializationPlugin&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
Quartz 框架有一个用来从 XML 文件中加载 Job 和 Trigger 信息的插件。这个插件就是 &lt;span style="color: #800080;"&gt;org.quartz.plugins.xml.JobInitializationPlugin&lt;/span&gt;，并且它在前面第三章 "Hello, Quartz" 中简略的讨论过。当你使用这个插件的时候，Quartz 框架就会搜寻一个叫做 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt; 的文件并试图从中加载 Job 和 Trigger 信息。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第八章. 使用 Quartz 插件 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-8-1/</link><pubDate>Fri, 18 Jul 2008 23:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-8-1/</guid><description>
&lt;strong&gt;第八章. 使用 Quartz 插件&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
Quartz 框架提供了几种用于扩展平台能力的方式。通过使用各种 "钩子" (通常指的就是扩展点)，Quartz 变得很容易被扩展和定制化来适应你的需要。其中一个最简单的扩展框架的方法就是使用 Quartz 插件。本章就来看看如何使用插件机制让 Quartz 进入到之前 Quartz 用户没去过的领域。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. 什么是插件？&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
假如你使用过其他的开源框架，例如 Apache Struts，你应该已经熟悉了插件的概念和它们的用法。非常简单，一个 Quartz 插件就是一个实现了 &lt;span style="color: #800080;"&gt;org.quartz.spi.SchedulerPlugin&lt;/span&gt; 接口的 Java 类，并且被作为插件注册给了 Scheduler。这个插件接口包含了三个方法，显示在代码 8.1 中。</description></item><item><title>Oracle 驱动版本引起的显示字段奇怪编码问题</title><link>https://yanbin.blog/oracle-low-jdbc-version-unknow-chars/</link><pubDate>Thu, 17 Jul 2008 07:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-low-jdbc-version-unknow-chars/</guid><description>
开门见山把产生问题的原因的解决办法列出来。&lt;/p&gt;
&lt;br/&gt;
我们一般获取 Statement 都是通过 conn.createStatement() 方法，很少传递参数给它的，所以其内置属性都取默认值的，取记录只用 while(rs.next()) 逐个取即可。然而有一个需求(Oracle 8i 之前的版本不支持子查询排序，所以无法用 rownum 取分页记录) 是通过如下代码来得到 Statement：&lt;br/&gt;&lt;br/&gt;
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);&lt;br/&gt;&lt;br/&gt;
由它获得的结果集可以 rs.absolute(n) 直接跳到第 n 行记录来获得值，但就这个用法出问题了，取出来的中文出现乱码了，如 "无效"，变成了 "0xE697A0E69588"</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第七部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-7/</link><pubDate>Sat, 12 Jul 2008 05:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-7/</guid><description>
&lt;strong&gt;八. 监听器中的线程使用&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
你看到了监听器接口中的方法后，你或许想知道是线程在调用监听器方法中饰演着什么样的角色。基实监听器方法是存在一个时序的，正如你看到方法名能想像到的那样。在一个 Job 执行的生命周期中，调用监听器方法以的顺序通常是固定的。图 7.2 描绘了监听方法的调用顺序和所涉及到的工作者线程。&lt;br/&gt;&lt;br/&gt;
&lt;div&gt;&lt;strong&gt;图 7.2. 监听器方法按某一特定的时序被调用&lt;/strong&gt;
&lt;br/&gt;&lt;br/&gt;
调用监听器方法的时序是固定的。如图 7.2 所示，在 Job 的执行前后，调用 Job 的 &lt;span style="color: #800080;"&gt;execute()&lt;/span&gt; 方法相同的线程被用于调用 &lt;span style="color: #800080;"&gt;JobListener&lt;/span&gt; 和 &lt;span style="color: #800080;"&gt;TriggerListener&lt;/span&gt; 的方法。假如你使用任何类类型的第三方线程管理工具或者打算实现你自己的线程池管理，知道这一点是很重要的。假如你在监听方法中实现了一个长运行逻辑时，这也会带来对性能上的负面影响。因为调用监听方法的线程和执行 Job 是同一个工作者线程，你不应该把监听方法实现的太复杂并要花费较长时间才能完成。保持它们的执行时间尽可能短。&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第六部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-6/</link><pubDate>Thu, 10 Jul 2008 07:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-6/</guid><description>
&lt;strong&gt;七. 在 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt; 文件中实现监听器&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
本章的所有例子告诉了你如何以编程的方式设置监听器。假如我们一个关于在 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt; 文件中以声明式配置监听器的例子都不提供本章就不能算是完结。&lt;br/&gt;&lt;br/&gt;
自 Quartz 1.5 开始，你能够在 Job 定义文件中指定监听器，当然就是知名的 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt; 文件了。代码 7.14 显示了一个使用全局监听器的例子。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;代码 7.14. Quartz 监听器能在 &lt;span style="color: #800080;"&gt;quartz_jobs.xml&lt;/span&gt; 文件中实现&lt;/strong&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第五部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-5/</link><pubDate>Wed, 09 Jul 2008 23:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-5/</guid><description>
&lt;strong&gt;六. 使用 &lt;span style="color: #800080;"&gt;FileScanListener&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 框架还包含一个我们未曾提及的监听器。这个监听器不像别的，因为它是为特定目的而设计的：同框架所带的一个工具 Job 一起用的。&lt;br/&gt;&lt;br/&gt;
这个监听器就是 &lt;span style="color: #800080;"&gt;org.quartz.jobs.FileScanListener&lt;/span&gt; 接口，它显式的设计为 &lt;span style="color: #800080;"&gt;FileScanJob&lt;/span&gt; 所用的，这一 Job 也在 &lt;span style="color: #800080;"&gt;org.quartz.jobs&lt;/span&gt; 包中。&lt;span style="color: #800080;"&gt;FileScanJob&lt;/span&gt; 检查某一指定文件的 &lt;span style="color: #800080;"&gt;lastModifiedDate&lt;/span&gt;。当某人改变了这个文件，这个 Job 就调用 &lt;span style="color: #800080;"&gt;FileScanListener&lt;/span&gt; 的 &lt;span style="color: #800080;"&gt;fileUpdated()&lt;/span&gt; 方法。&lt;br/&gt;&lt;br/&gt;
就像使用其他类型的 Quartz 监听器一样，你必须创建一个实现了 &lt;span style="color: #800080;"&gt;FileScanListener&lt;/span&gt; 接口的具体类。只有一个方法需要实现：</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-4/</link><pubDate>Tue, 08 Jul 2008 08:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-4/</guid><description>
&lt;strong&gt;五. 监听 Scheduler 事件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.SchedulerListener&lt;/span&gt; 接口包含了一系列的回调方法，它们会在 Scheduler 的生命周期中有关键事件发生时被调用。代码 7.9 列出了包括在 &lt;span style="color: #800080;"&gt;SchedulerListener&lt;/span&gt; 接口的方法。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;代码 7.9. &lt;span style="color: #800080;"&gt;org.quartz.SchedulerListener&lt;/span&gt; 接口中的方法&lt;/strong&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SchedulerListener&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobScheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobUnscheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;triggerFinalized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;triggersPaused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;triggersResumed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triggerGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobsPaused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobsResumed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobGroup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;schedulerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SchedulerException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;schedulerShutdown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Tomcat 使用 Log4j 输出详细系统日志信息，快速诊断启动故障</title><link>https://yanbin.blog/tomcat-log4j-system-logs/</link><pubDate>Mon, 07 Jul 2008 05:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tomcat-log4j-system-logs/</guid><description>
Tomcat 启动时的系统日志默认是由 JdkLog14Logger 打印出来的，如&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;2008-7-7 11:19:34 org.apache.catalina.core.StandardEngine start&lt;br /&gt;
信息: Starting Servlet Engine: Apache Tomcat/5.0.28&lt;br /&gt;
2008-7-7 11:19:34 org.apache.catalina.core.StandardHost start&lt;br /&gt;
信息: XML validation disabled&lt;br /&gt;
2008-7-7 11:19:34 org.apache.catalina.core.StandardHost getDeployer&lt;br /&gt;
信息: Create Host deployer for direct deployment ( non-jmx )&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
那我们能不能用 Log4J 来输出这种系统日志呢？我们知道像 Apache 那样的大部分开源框架、组件都是用通用日志组件 (commons-logging) 来输出日志的，因此如果把 commons-logging 和 log4j 搭配使用就能输出十分详尽的日志信息。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-3/</link><pubDate>Sat, 05 Jul 2008 22:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-3/</guid><description>
&lt;strong&gt;四. 监听 Trigger 事件&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
正如 &lt;span style="color: #800080;"&gt;JobListener&lt;/span&gt;, &lt;span style="color: #800080;"&gt;org.quartz.TriggerListener&lt;/span&gt; 接口也包含一系列给 &lt;span style="color: #800080;"&gt;Scheduler&lt;/span&gt; 调用的方法。然而，与 &lt;span style="color: #800080;"&gt;JobListener&lt;/span&gt; 有所不同的是， &lt;span style="color: #800080;"&gt;TriggerListener&lt;/span&gt; 接口还有关于 Trigger 实例生命周期的方法。代码 7.5 列出了 &lt;span style="color: #800080;"&gt;TriggerListener&lt;/span&gt; 接口的方法。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;代码 7.5. &lt;span style="color: #800080;"&gt;org.quartz.TriggerListener&lt;/span&gt; 接口的方法&lt;/strong&gt;</description></item><item><title>告诉所有在大陆使用中文的朋友，汉字排版又要回归成这样了</title><link>https://yanbin.blog/new-chinese-layout/</link><pubDate>Tue, 01 Jul 2008 04:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/new-chinese-layout/</guid><description>
魔高一尺，道应比其高一丈&lt;br/&gt;
&lt;div&gt;┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐&lt;br/&gt;
│　┆　┆　┆乃┆，┆四┆星┆瓦┆作┆。┆以┆天┆燎┆瓮┆言┆天┆　│&lt;br/&gt;
│　┆　┆　┆作┆吞┆夷┆文┆安┆“┆士┆万┆下┆原┆安┆封┆涯┆　│&lt;br/&gt;
│　┆　┆　┆此┆吐┆闻┆竖┆县┆不┆民┆计┆异┆之┆案┆事┆，┆红│&lt;br/&gt;
│　┆　┆　┆妇┆天┆而┆排┆”┆贱┆交┆，┆己┆势┆，┆者┆红┆朝│&lt;br/&gt;
│　┆　┆　┆人┆地┆轻┆体┆。┆省┆语┆禁┆者┆，┆传┆二┆朝┆笑│&lt;br/&gt;
│　┆　┆　┆小┆，┆之┆属┆时┆”┆作┆“┆，┆乃┆之┆十┆之┆话│&lt;br/&gt;
│　┆　┆　┆子┆包┆，┆文┆文┆，┆书┆贵┆天┆差┆海┆万┆稷┆一│</description></item><item><title>Oracle SQL优化［转］</title><link>https://yanbin.blog/oracle-sql-tuning/</link><pubDate>Mon, 30 Jun 2008 21:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-sql-tuning/</guid><description>
Oracle SQL的优化规则：&lt;br/&gt;
&lt;ul&gt;&lt;br/&gt;
&lt;li&gt;&lt;span style="font-weight: bold; color: #0000ff;"&gt;尽量少用IN操作符，基本上所有的IN操作符都可以用EXISTS代替&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br/&gt;
        用IN写出来的SQL的优点是比较容易写及清晰易懂，但是用IN的SQL性能总是比较低的，从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别：&lt;br/&gt;
       ORACLE 试图将其转换成多个表的连接，如果转换不成功则先执行IN里面的子查询，再查询外层的表记录，如果转换成功则直接采用多个表的连接方式查询。由此可见用 IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功，但对于含有分组统计等方面的SQL就不能转换了。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7-2/</link><pubDate>Sat, 28 Jun 2008 23:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7-2/</guid><description>
&lt;strong&gt;三. 监听 Job 事件&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;org.quartz.JobListener&lt;/span&gt; 接口包含一系列的方法，它们会由 Job 在其生命周期中产生的某些关键事件时被调用。&lt;span style="color: #800080;"&gt;JobListener&lt;/span&gt; 可用的方法显示在代码 7.1 中。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;代码 7.1. &lt;span style="color: #800080;"&gt;org.quartz.JobListener&lt;/span&gt; 接口中的方法&lt;br /&gt;
&lt;/strong&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;JobListener&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobToBeExecuted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JobExecutionContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobExecutionVetoed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JobExecutionContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;jobWasExecuted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JobExecutionContext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JobExecutionException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jobException&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第七章. 实现 Quartz 监听器 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-7/</link><pubDate>Sat, 28 Jun 2008 00:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-7/</guid><description>
&lt;strong&gt;第七章. 实现 Quartz 监听器&lt;br /&gt;
&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
在某个所关注事件发生时，监听器提供了一种方便且非侵入性的机制来获得这一通知。Quartz 提供了三种类型的监听器：监听 Job 的，监听 Trigger 的，和监听 Scheduler 自已的。本章解释如何应用每一种类型来更好的管理你的 Quartz 应用，并获悉到什么事件正在发生。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. 监听器作为扩展点&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
术语 "&lt;em&gt;扩展点&lt;/em&gt;" 在软件开发中用于指示框架或应用的某个位置，在这一位置在创建者期望用户扩展或定制这一框架来适合于他们的需要。(你也将会听到 &lt;em&gt;hook&lt;/em&gt;(钩子) 一词，是一样意思的)</description></item><item><title>Hibernate3调用存储过程用法</title><link>https://yanbin.blog/hibernate3-call-procedure/</link><pubDate>Mon, 23 Jun 2008 06:51:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate3-call-procedure/</guid><description>
直接以一个例子在说明，如DB2中有一个简单存储过程　selectAllUsers&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;procedure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;selectAllUsers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;DYNAMIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RESULT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SETS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp_cursor1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;CURSOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CLIENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OPEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp_cursor1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
映射文件中关于存储过程内容如下</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第七部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-7/</link><pubDate>Sat, 21 Jun 2008 23:22:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-7/</guid><description>
&lt;strong&gt;十三. 改善持久性 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 的性能&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
当在只有最少量时间做任何相关事情的时候，性能是一个广受人瞩目的课题。作为有经验的开发者，我们知道从项目之初它就成为一个需要考虑的事。&lt;br/&gt;&lt;br/&gt;
在使用 Quartz 的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 时，最大的关注面就是有关于与关系型数据库的交互。数据库 I/O(就像文件 I/O) 通常不是很快。你可以通过采取一些措施，如调优 SQL、增加索引和操作表和列等来改善性能。因为性能问题在写 Quartz 框架的时候就已有考虑到，而你又不想在未出现实际的性能问题时扎入到 Quartz 中做些手脚，那么可试图通过配置来解决它，或者尝试所有可能的方式，只要不是维护源代码。最好的消息是 Quartz 是开源的，你完全可以窥入其中了解它做了什么和如何实现的。假如你不喜欢它现有的查询数据库的方式，你有权去修正它。不过，在你采取行动之前，确定检查了 Quartz 论坛上的用户和开发者，看看是否其他人也遇到了相关的问题并浏览推荐的做法。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第六部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-6/</link><pubDate>Fri, 20 Jun 2008 21:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-6/</guid><description>
&lt;strong&gt;十二. 为 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 配置数据源&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
跟 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 一样，我们需要配置一个 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 才能使用 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt;。然而，&lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 需要两个 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 而不是像 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 只要一个。其中一个 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 和我们为 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 设置的类同：作为不受管理的数据源。同时呢，我们还需配置第二个数据源，是作为受管理的数据源，它由应用服务器来进行管理。&lt;br/&gt;&lt;br/&gt;
&lt;table style="border: 1px;" border="1" width="70%" align="center"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;为什么 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 需要两个 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 呢？&lt;/strong&gt;&lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 的原始作者，Jeffrey Wescott，设计 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 使用一个标准的 JDBC 连接来做它“自己的工作”，同时，代表客户端(如部署 Job) 的工作在执行时是使用一个在容器控制之下有自身事物的 JDBC 连接。即使 Quartz 处在一个大事物中，这种设计也允许用户与 Quartz 交互，而无需 JobStoreCMT 非得使用应用服务器的事物管理器(例如，经由 &lt;span style="color: #800080;"&gt;UserTransaction&lt;/span&gt;) 在做自己内部工作时(如处理已错过执行的 Trigger) 来创建和终止事物。如果是 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 使用 &lt;span style="color: #800080;"&gt;UserTransation&lt;/span&gt; 只给它配置一个数据源，从配置方面来看确实方便。然而，在相比于别的特性需求和改进的必要性时，作此变化并不会成为团队中首要的问题，因而 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt; 还是继续要两个数据源。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第五部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-5/</link><pubDate>Mon, 02 Jun 2008 08:58:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-5/</guid><description>
&lt;strong&gt;十. 使用数据库存储 Scheduler 信息&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·加载 Job 到数据库中&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
在前面有一节，"使用内存存储 Scheduler 信息"，我们谈到关于在使用 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 时如何加载 Job 和 Trigger 信息到内存中。那么  Job 和 Trigger 又是如何加载到数据库中的呢？存在以下几个方法把 Job 信息存入到数据库：&lt;br/&gt;&lt;br/&gt;
    · 在你的程序中加入 Job 信息&lt;br/&gt;&lt;br/&gt;
    · 使用 &lt;span style="color: #800080;"&gt;JobInitializationPlugin&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
    · 使用 Quartz Web 应用程序&lt;br/&gt;&lt;br/&gt;
我们在前面的 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 章节中讨论过前面两种途径。当它们用于 JDBC &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 时，并没有多大不同，只些许例外。首先，你需要知道，当使用这两个方法式，Job 信息是在数据库中的。甚至在你停止了程序后，这些信息仍然保留在数据库中。甚至是你不在你的程序中使用 &lt;span style="color: #800080;"&gt;JobInitializationPlugin&lt;/span&gt; 时，这些信息也还在数据库中。基于这一点，它是会从数据库中找寻 Job 信息。第八章涵盖了
&lt;span style="color: #800080;"&gt;JobInitializationPlugin&lt;/span&gt; 和常用的 Quartz 插件。</description></item><item><title>Struts2 action 的 ;jsessionid=xxx 使找不到页面 Bug 的解决</title><link>https://yanbin.blog/struts2-action-jsessionidxxx-page-not-found-bug/</link><pubDate>Mon, 02 Jun 2008 03:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-action-jsessionidxxx-page-not-found-bug/</guid><description>
在 Websphere Application Server(WAS，我用的还是 WAS 5.1，所以用的 Struts2 也是 j4 版，恐怕很难再找到像我这样在 JDK 1.4/Servlet 2.3/JSP 1.2 下用 Struts2 的了) 部署的第一个 Struts2 的应用没有出什么问题。可是在另一个 WAS 下部署的 Struts2 应用却有些问题(说明：Struts2 的版本都是 struts-2.0.11.1)，所有用了 &amp;lt;s:url url=…/&amp;gt; 或 &amp;lt;s:form action=…/&amp;gt; 来生成的 URL 都带了个 ;jsessionid=xxx，如：&lt;/p&gt;
&lt;br/&gt;
&amp;lt;script src="https://yanbin.blog/unmi/js/sys_cal.js;jsessionid=0001bosAgxaO_kxlHCbjS1AMxTu:11mijgptm"&amp;gt;&lt;br/&gt;&lt;br/&gt;
&amp;lt;img src="https://yanbin.blog/unmi/images/img_btn.gif;jsessionid=0001bosAgxaO_kxlHCbjS1AMxTu:11mijgptm"/&amp;gt;&lt;br/&gt;&lt;br/&gt;
&amp;lt;a href="https://yanbin.blog/unmi/storeSubinvRelate!toUpdate.action;jsessionid=0001bosAgxaO_kxlHCbjS1AMxTu:11mijgptm?model.id=162"&amp;gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-4/</link><pubDate>Fri, 30 May 2008 21:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-4/</guid><description>
&lt;strong&gt;八. 为 &lt;span style="color: #800080;"&gt;JobStroreTX&lt;/span&gt; 创建数据源&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
当使用持久性 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 时，Quartz 需要一个数据源。数据源扮演着产生数据库连接工厂的角色。在 Java 　中，所有的数据源要实现 &lt;span style="color: #800080;"&gt;java.sql.Datasource&lt;/span&gt; 接口。Quartz 自身并不提供 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 的所有功能；它只代表了那一职责。默认的，Quartz 能使用另一开源的框架，叫做 Commons DBCP，或者可以通过 JNDI 查找应用服务器中定义的 &lt;span style="color: #800080;"&gt;DataSource&lt;/span&gt;。&lt;br/&gt;&lt;br/&gt;
DBCP 是一个 Jakarta Commons 项目，网址是 &lt;a href="http://jakarta.apache.org/commons/dbcp"&gt;http://jakarta.apache.org/commons/dbcp&lt;/a&gt;。这个框架的二进制版包含在 Quartz 的发行版中，你应该把它加到你的 Quartz  应用中来。你还需要加入 Commons Pool 库，它也包含中 Quartz 发行版中，是 DBCP 要用到的。&lt;br/&gt;&lt;br/&gt;
使用 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 时，你必须在 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件中指定 Datasource 属性。这允许 Quartz 为你创建并管理 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt;。表 6.4 列示了使用 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 时需要的 &lt;span style="color: #800080;"&gt;Datasource&lt;/span&gt; 配置属性。</description></item><item><title>Struts2 的 struts.xml 配置中 namespace 的使用</title><link>https://yanbin.blog/struts2-struts-xml-understand-namespace/</link><pubDate>Mon, 26 May 2008 07:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-struts-xml-understand-namespace/</guid><description>
Struts2 的 struts.xml 中是分 package 配置的，可以为 package 设置 namespace 属性，如&lt;/p&gt;
&lt;br/&gt;
&amp;lt;package namespace="/secure"   ....&amp;gt;&lt;br /&gt;
    ......&lt;br /&gt;
&amp;lt;/package&amp;gt;&lt;br/&gt;&lt;br/&gt;
如果没有指定 namespace 属性，默认 namespace 是 ""。使用 namespace 可以方便于按不同目的规划对应用的访问规则。比如不同 namespace 下配置了不同的拦截器就可以实现权限的控制，如 "/secure" 下已登陆用户才能访问，"/public" 下可公开访问的。</description></item><item><title>Quartz Job Scheduling Framework［翻译］- 中文版目录总汇及内容提要</title><link>https://yanbin.blog/quartz-job-scheduling-framework-catalog/</link><pubDate>Sat, 24 May 2008 23:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-catalog/</guid><description>
&lt;ol&gt;
&lt;li&gt;&lt;a href="
/quartz-job-scheduling-framework-1" target="_blank" rel="noopener noreferrer"&gt;第一章. 企业应用中的作业调度&lt;/a&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 10px;"&gt;&lt;em style="color: #ff1493;"&gt;内容提要&lt;/em&gt;：什么是作业调度，作业调度为什么说是重要的，企业应用中的作业调度，非企业应用中的作业调度，作业调度与工作流，关于作业调度其他可选择方案&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="
/quartz-job-scheduling-framework-2" target="_blank" rel="noopener noreferrer"&gt;第二章. Quartz 起步&lt;/a&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 10px;"&gt;&lt;em style="color: #ff1493;"&gt;内容提要&lt;/em&gt;：本章对 Quartz 框架一个快速的入门介绍，同时也大略指导你从哪里下载，构建和安装这个框架&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="
/quartz-job-scheduling-framework-3-1" target="_blank" rel="noopener noreferrer"&gt;第三章. Hello Quartz (第一部分)&lt;/a&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 10px;"&gt;&lt;em style="color: #ff1493;"&gt;内容提要&lt;/em&gt;：建立 Hello Quartz 工程，并创建一个 Quartz Job 类 ScanDirectoryJob.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="
/quartz-job-scheduling-framework-3-2"&gt;第三章. Hello Quartz (第二部分)&lt;/a&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 10px;"&gt;&lt;em style="color: #ff1493;"&gt;内容提要&lt;/em&gt;：创建一个 Quartz Scheduler，关联上一个 Quartz Trigger 以编程方式调度前面编写的 ScanDirectoryJob 运行。&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="
/quartz-job-scheduling-framework-3-3" target="_blank" rel="noopener noreferrer"&gt;第三章. Hello Quartz (第三部分)&lt;/a&gt;&lt;br /&gt;
&lt;div style="margin-bottom: 10px;"&gt;&lt;em style="color: #ff1493;"&gt;内容提要&lt;/em&gt;：通过配置 quartz.properties、quartz_jobs.xml 以声明的方式调度 ScanDirectoryJob 运行。</description></item><item><title>Unmi 的 Struts2 学习笔记(十四)</title><link>https://yanbin.blog/unmi-study-struts2-14/</link><pubDate>Thu, 22 May 2008 08:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-14/</guid><description>
生活中常为一些无伤大体之事优柔寡断，譬如买羽毛球拍是方头的还是圆头的呢？电子产品是这款好还是那款好呢？拿此又放下，举彼又放下，最后靠概率来决定。取其一，少段时间内总有些戚戚然，而后全然无所谓。小事无碍，大事可就会后悔不及，无疑亦会让许多机会径直溜去。&lt;/p&gt;
&lt;br/&gt;
1. 从此开始学习 Struts2 对 Ajax 的支持。这也是 Struts2 新注入的元素。Struts2 的 Ajax 支持是建立在 Dojo 和 DWR 基础之上的。提供了 Ajax 的输入检验，表单提交；pub-sub 事件模型、自动完成以及与 JSON 的使用等功能。&lt;br/&gt;&lt;br/&gt;
2. 在 Struts2.0.6 和 Struts2.0.11 的 apps/struts-showcase-2.x.x.war 中的 dwr 包都还是 dwr-1.1-beta-3.jar，当前 DWR 最新版是 2.0，前一个稳定版是 1.1.4，所以正式应用可用 1.1.4 版，若用 2.0 DWR 需做充分的测试。Struts2 的 Ajax 支持是建立在 ajax 主题上的，ajax 是扩展了 xhtml 主题。</description></item><item><title>为什么用 http://unmi.blogjava.net 进不了我的博客，会转向到 cnblogs</title><link>https://yanbin.blog/unmi-blogjava-net-cannot-redirect/</link><pubDate>Wed, 21 May 2008 09:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-blogjava-net-cannot-redirect/</guid><description>
原来一直都用 &lt;a href="http://unmi.blogjava.net"&gt;http://unmi.blogjava.net&lt;/a&gt; 进我的博客的，
而不需输入 &lt;a href="http://www.blogjava.net/Unmi"&gt;http://www.blogjava.net/Unmi&lt;/a&gt;，但是最近不行了，
输入 &lt;a href="http://unmi.blogjava.net"&gt;http://unmi.blogjava.net&lt;/a&gt;
会转向到 &lt;a href="http://www.cnblogs.com"&gt;http://www.cnblogs.com&lt;/a&gt; 去。&lt;br/&gt;
&lt;br/&gt;
而在 &lt;a href="http://www.cnblogs.com"&gt;http://www.cnblogs.com&lt;/a&gt; 的博客仍可用
&lt;a href="http://unmi.cnblogs.com"&gt;http://unmi.cnblogs.com&lt;/a&gt; 的形式进去。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;这么久了，好像也没有人觉得有些不方便。&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
这里的管理员请麻烦及时看看怎么回事了，请尽快调整好，谢谢！</description></item><item><title>访问日志文件过大而导致启动 Apache 时出现 httpd could not be started 错误</title><link>https://yanbin.blog/apache-httpd-could-not-be-started-log-too-large/</link><pubDate>Mon, 19 May 2008 12:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-httpd-could-not-be-started-log-too-large/</guid><description>
正式应用又好久没有重启过了，所以先停了 IBM Http Server (IHS，Apache 1.3.28)，再把所有应用启动妥当后，最后把 Apache 启起来。终端下进到 IBMHttpServer 目录，然后敲入命令：&lt;span style="color: #800080;"&gt;bin/apachectl start&lt;/span&gt;&lt;span style="color: #000000;"&gt;，得到的错误是：&lt;/span&gt;bin/apachectl start: httpd could not be started。&lt;/p&gt;
&lt;br/&gt;
这下有点慌了神，以前从未碰到过，怀疑是配置文件 httpd.conf 有问题，但一般 httpd.conf 有问题有提示错误的，不管，还是用 bin/apachectl configtest 看下，返回 Syntax OK，没问题。&lt;br/&gt;&lt;br/&gt;
可能别的程序占用了 80 端口？netstat -na|grep 80 没发现什么。&lt;br/&gt;&lt;br/&gt;
翻看日志，打开 IBMHttpServer 下的 log.txt，好久以前的信息，打开 logs/error_log 也是很久以前的信息。</description></item><item><title>Unmi 的 Struts2 学习笔记(十三)</title><link>https://yanbin.blog/unmi-study-struts2-13/</link><pubDate>Sat, 17 May 2008 11:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-13/</guid><description>
柏杨在形容中国人不团结，喜欢窝里斗用了这样的话：每一个中国人都是一条龙,但是三个中国人加在一起──三条龙加在一起，就成了一条猪、一条虫，甚至连虫都不如--《&lt;a href="http://book.sina.com.cn/longbook/sal/1099295539_chouloudezhongguoren/5.shtml" target="_blank" rel="noopener noreferrer"&gt;丑陋的中国人&lt;/a&gt;》。可是他老人家走得走，要是能多有半个月的阳寿，不知作有何感想。&lt;/p&gt;
&lt;br/&gt;
1. &amp;lt;s:optiontransferselect.../&amp;gt; 正是考虑到通常使用而新加的用于创建两个下拉框，可以来回移动列表项。有属性设置是否显示 "选定左移"、"全部左移"、"选定右移"、"全部右移" 按钮以及各按钮上的文本，其他的 list/listKey/listValue/doubleList/doubleKey/doubleValue 等属性同 &amp;lt;s:doubleselect.../&amp;gt; 的是一样的。还可设置 allowUpDownOnLeft/allowUpDownOnRight 是否出现相应框的上下移动选择的按钮(true/false)。&lt;br/&gt;&lt;br/&gt;
2. &amp;lt;s:radio.../&amp;gt; 的用法与 &amp;lt;s:checkboxlist.../&amp;gt; 完全一样，只页面表现不一样，一个多选，一个单选。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-3/</link><pubDate>Fri, 16 May 2008 23:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-3/</guid><description>
&lt;strong&gt;七. 使用 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
我们首先要讨论的持久性 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 是 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt;。名字中的 "TX" 代表着 "事物"。我们在前面提过，&lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 是设计用于想要 Quartz 来管理事物的环境中。例如，假如你正构建一个 J2EE 应用，并且不使用到应用服务器，如 WebLogic 或者 JBoss 等，那么 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 会是持久性 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 正确的选择。&lt;br/&gt;&lt;br/&gt;
在之前章节中，我们看到配置 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 是多么的容易。我们提到 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 的其中一个优点就是易于配置。我们已经讨论过让数据库准备就绪该做的事情；现在我们讲述使 Quartz 应用支持 JDBC &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 需要对它配置些什么。</description></item><item><title>JSP 的 errorPage 指令异常转向错误页的实现机制及应用</title><link>https://yanbin.blog/jsp-errorpage-mechanism-application/</link><pubDate>Fri, 16 May 2008 11:42:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jsp-errorpage-mechanism-application/</guid><description>
如有 index.jsp 页，当出现后服务器端异异常时要转向到 errorPage.jsp，并在 errorPage.jsp 中把对应错误信息显示出来。我们需要在这两个页面分别加上指令 &lt;span style="color: #800080;"&gt;errorPage="errorPage.jsp"&lt;/span&gt; 和 &lt;span style="color: #800080;"&gt;isErrorPage="true"&lt;/span&gt;。&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;index.jsp&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt;&lt;span class="nd"&gt;@page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;errorPage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;errorPage.jsp&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;exception from jsp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>手工处理 Struts2 框架上传的文件</title><link>https://yanbin.blog/manual-struts2-file-upload/</link><pubDate>Thu, 15 May 2008 04:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/manual-struts2-file-upload/</guid><description>
在使用了 Struts2 框架的系统中，对于处理像下面这种表单上传文件时：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;...&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;enctype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;multipart/form-data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; 文件：&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;file&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;upload&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; 描述：&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;desc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;提交&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
自然而然的想法就是在 Action 中声明变量 File upload 和 String desc，请求提交到这个 Action 后，在 execute() 方法中就能直接使用 upload 和 desc 了，它们已被 Struts2 框架(&lt;span style="color: #800080;"&gt;org.apache.struts2.interceptor.FileUploadInterceptor&lt;/span&gt; 监听器) 赋上了相应的值了。</description></item><item><title>Websphere Application Server (WAS) 中应用的哪一个 web.xml 在起作用</title><link>https://yanbin.blog/websphere-application-server-was-which-web-xml/</link><pubDate>Tue, 13 May 2008 10:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/websphere-application-server-was-which-web-xml/</guid><description>
从 Tomcat 再到使用 Webshpere Application Server (WAS) 不光金钱上付出太多(这当然不用我操心)，精力上也费神的多，不过收获也不少，特别是商业产品在处理大用户量的并发访问能力是 Tomcat 无法比的。这里记述一个使用 WAS 给应用打补丁时遇到的问题--要更新哪一个 web.xml。&lt;/p&gt;
&lt;br/&gt;
为便于描述，我们假设已在 WAS 下部署了一个名叫 Prod.war 的包(你也可以部署一个 ear 包，部署的 ear 包后的目录名更明确一些)，WAS 的安装目录表示为 WAS_HOME，WAS 所在的主机名为 unmi，那么 Prod.war 部署后所在的目录就是&lt;br/&gt;&lt;br/&gt;
WAS_HOME/installedApps/unmi/Prod_war.ear/Prod.war&lt;br/&gt;&lt;br/&gt;
用过 WAS 的人都知道，如果是部署后要给应用打补丁的话，和 Tomcat 下的应用一样，也是把类或 JSP 拷到如上相应的目录中就行了，换了类，WAS 会自动重新加载。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-2/</link><pubDate>Sun, 11 May 2008 07:54:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-2/</guid><description>
&lt;strong&gt;四. 使用持久性的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
在很多方面，&lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 有用内存来存储的，还有些使用某种能长期持久的方式来共享相拟的特征。这不该有什么惊奇的，因为他们都服务于同一目的。&lt;br/&gt;&lt;br/&gt;
和 &lt;span style="color: #800080;"&gt;RAMJobStore&lt;/span&gt; 一样，特久性的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 有优点也有其缺点。在你选择持久性的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 之前应该认真理解其利与弊。这节就来解释它们的区别，以及在什么情况下你会希望使用持久性的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;。&lt;br/&gt;&lt;br/&gt;
目前，Quartz 提供了两种类型的持久性 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;，每一种类型都有其独特的持久化机制。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第六章. Job 存储和持久化 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-6-1/</link><pubDate>Sat, 10 May 2008 07:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-6-1/</guid><description>
&lt;strong&gt;第六章. Job 存储和持久化&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 用 &lt;span style="color: #800080;"&gt;JobStores&lt;/span&gt; 对 Job、Trigger、calendar 和 Schduler 数据提供一种存储机制。Scheduler 应用已配置的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 来存储和获取到部署信息，并决定正被触发执行的 Job 的职责。所有的关于哪个 Job 要执行和以什么时间表来执行他们的信息都来自于 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;。本章就来看 Quartz 中可用的各种类型的 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt;，和如何使用他们，以及哪一个能适应你的需求。&lt;br/&gt;&lt;br/&gt;
&lt;table border="0" width="70%" align="center"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;"罗马非一日建成"&lt;br /&gt;
&lt;/em&gt;&lt;br /&gt;
道格拉斯.亚当斯，《宇宙环游指南》&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;</description></item><item><title>Unmi 的 Struts2 学习笔记(十二)</title><link>https://yanbin.blog/unmi-study-struts2-12/</link><pubDate>Fri, 09 May 2008 22:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-12/</guid><description>
上有所好，下必甚之。君不见屠宰场的墙头“以三个代表指导我们的屠宰工作!” 发挥到极致；近来更是“和谐”泛滥，褒贬难分，诸如“创建人水和谐” 等比比皆是。程序开发更需要务实，任凭天花乱坠都糊弄不了梗直的机器。&lt;br/&gt;&lt;br/&gt;
1. 初见 Struts2 的表单标签有 tooltip 以及 tooltipConfig 下的 tooltipBgColor 等属性，有一种砰然心动的感，本能的以为例如一个输入框，鼠标悬停其中便出现一个 JS Tip 层，还可控制这个层的样式。用了才知道头脑其实有点过热，不过是在输入框前加下图标，鼠标要放在那个图标上才显示 tooltip 指定的内容，而且如果不给 form 加上 tooltipConfig="#{'jsTooltipEnabled':'true'}"，也就用 alt/title 来实现，没什么新意。加了tooltipConfig="#{'jsTooltipEnabled':'true'}" 的话还要人命的是 JS 提示层会出来，alt/title 也会迸出来，产生重叠。同时还必须加上 &amp;lt;s:head/&amp;gt; 否则出现 dojo 的错误，即使 jsTooltipEnabled 为 false 亦如此，没来由。</description></item><item><title>为何我的 Struts2 标签 tooltip 和 tooltipConfig 是这种效果呢？</title><link>https://yanbin.blog/struts2-tag-tooltip-tooltipconfig/</link><pubDate>Mon, 05 May 2008 10:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-tag-tooltip-tooltipconfig/</guid><description>
我的环境是 Tomcat 5.0.28 + Struts2.0.6 (web.xml 配置的是支持 Servlet 2.4)&lt;/p&gt;
&lt;br/&gt;
testTooltip.jsp 文件内容如下：&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;%@&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;text/html; charset=UTF-8&amp;#34;&lt;/span&gt; &lt;span class="nx"&gt;pageEncoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;%@&lt;/span&gt; &lt;span class="nx"&gt;taglib&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;s&amp;#34;&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/struts-tags&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="nx"&gt;Struts&lt;/span&gt; &lt;span class="nx"&gt;Tooltip&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;tooltipConfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#{&amp;#39;jsTooltipEnabled&amp;#39;:&amp;#39;true&amp;#39;}&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;textfield&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Name&amp;#34;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Enter your Name here&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;tooltipConfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;#{&amp;#39;tooltipFontColor&amp;#39;:&amp;#39;#ff0000&amp;#39;}&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
打开浏览器，浏览时鼠标移到输入框前面那个图标上稍呆会儿的效果是这样子的。</description></item><item><title>基于 dp.SyntaxHighlighter 写的代码语法着色工具</title><link>https://yanbin.blog/dp-syntaxhighlighter-hta-tool/</link><pubDate>Sat, 03 May 2008 04:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/dp-syntaxhighlighter-hta-tool/</guid><description>
本程序是基于 dp.SyntaxHighlighter 写的代码语法着色的工具。支持的语言有&lt;br /&gt;
java/xml/sql/jscript/css/cpp/c#/python/vb/perl/php/ruby/delphi。&lt;/p&gt;
&lt;br/&gt;
可方便用于你的博客中粘贴代码，只要自定相应的样式 (&lt;span style="color: #800080;"&gt;highlight.css&lt;/span&gt; 的内容，.Text 支持自定义样式或在模板里加上语法样式)，然后复制用这个工具生成的 HTML 代码就能让你的代码着高亮显示。&lt;br/&gt;&lt;br/&gt;
可以加入更多语种的支持，本程序就是在 dp.SyntaxHighlighter 的基础上扩展了对 Perl 语言的支持，网上可以找到相应语法的 JS 代码和 CSS。语言扩展支持通过在 shCore.js 和 highlight.css 加入相应代码即可。</description></item><item><title>VC++6.0 下搭建 wxWidgets 开发环境</title><link>https://yanbin.blog/vc6-0-wxwidgets-dev/</link><pubDate>Fri, 02 May 2008 13:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc6-0-wxwidgets-dev/</guid><description>
和 GTK、QT、SWT、SWING 一样，wxWidgets 也是一种开源跨平台的 GUI 框架。因 wxWidgets 和 MFC 有些渊源，有些亲近，故要体验一番 wxWidgets。现在最新版是 2.8.7。wxWidgets 程序还可运行于 Windows CE、Palm OS。不但包括 GUI，还有 Media、Socket、ODBC 等库。&lt;/p&gt;
&lt;br/&gt;
详情请见 wxWidgets 的官方网站是：&lt;a href="http://www.wxwidgets.org"&gt;http://www.wxwidgets.org&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
本篇介绍 VC++6.0 下如何搭建 wxWidgets 的开发环境，包括编译 wxWidgets 源码和配置 VC 环境。所用 wxWidgets 的版本是 2.8.7。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;第一步：下载并安装 wxWidgets&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
在 &lt;a href="http://www.wxwidgets.org/downloads/"&gt;http://www.wxwidgets.org/downloads/&lt;/a&gt; 下载到 wxWidgets 源码。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十三章. Quartz 和 Web 应用 (第五部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-13-5/</link><pubDate>Thu, 01 May 2008 09:50:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-13-5/</guid><description>
&lt;strong&gt;六. 介绍 Quartz Web 程序&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
早期的 Quartz 框架开发者意识到一个 GUI 对于某类用户群体是必需的。几年前，一个 Web 应用被创立，它可用于管理 Quartz 框架。虽说是历经了几年有相当投入的开发，但不得不说的，总是时断时续的。&lt;br/&gt;&lt;br/&gt;
近来出现有更多的要求对这个应用的更新与支持，因而又重新吸引了新的开发者自愿的工作并使之保持不断更新。这个应用就是知名的 Quartz Web 程序。(译者注：实际上这个应用程序基本驻步不前，当前版本还是 RC-1 2004-06-26 22:00 的，仅作参考来应用)</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十三章. Quartz 和 Web 应用 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-13-4/</link><pubDate>Thu, 01 May 2008 03:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-13-4/</guid><description>
&lt;strong&gt;五. 使用 &lt;span style="color: #800080;"&gt;ServletContextListener&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
很值得一提的是你可以配置和集成 Quartz 到 Web 应用的另一种方式。从 2.3 版本的 Servlet API 开始，你能创建监听器，由容器在其生命周期中的某个特定时间回调。其中的一个监听器接口叫做 &lt;span style="color: #800080;"&gt;java.servlet.ServletContextListener&lt;/span&gt;，它包括有两个方法：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;public void contextInitialized(ServletContextEvent sce);&lt;br /&gt;
public void contextDestroyed(ServeltContextEvent sce);&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
容器会在启动和关闭的时候相应的调用这两个方法。这就可以在 &lt;span style="color: #800080;"&gt;contextInitialized()&lt;/span&gt; 方法中初始化 Quartz Scheduler，并通过 &lt;span style="color: #800080;"&gt;contextDestroyed()&lt;/span&gt; 方法关闭它。代码 13.5 描述了这种用法：</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十三章. Quartz 和 Web 应用 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-13-3/</link><pubDate>Tue, 29 Apr 2008 12:26:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-13-3/</guid><description>
&lt;strong&gt;四. &lt;span style="color: #800080;"&gt;QuartzInitializerServlet&lt;/span&gt; 可谓救命草&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 框架包括一个叫做 &lt;span style="color: #800080;"&gt;org.quartz.ee.servlet.QuartzInitializerServlet&lt;/span&gt; 的 Java 类，它继承自标准的 &lt;span style="color: #800080;"&gt;HttpServlet&lt;/span&gt;。你可应用这个 servlet 于你的 Web 应用中，它将会创建一个 &lt;span style="color: #800080;"&gt;StdSchedulerFactory&lt;/span&gt; 实例并在你的程序后续中一直可用。通常的，它就是做了命令行版本的 Quartz 程序的 &lt;span style="color: #800080;"&gt;main()&lt;/span&gt; 方法所做的事性。&lt;br/&gt;&lt;br/&gt;
&lt;table style="border: 1px;" border="1" width="70%" align="center"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;br /&gt;
&lt;span style="color: #800080;"&gt;QuartzInitializerServlet &lt;/span&gt;在 Quartz 1.5 中有所改变&lt;/strong&gt;在 Quartz 的 1.5 发布版中，&lt;span style="color: #800080;"&gt;QuartzInitializerServlet&lt;/span&gt; 被修改为会存储 &lt;span style="color: #800080;"&gt;StdSchedulerFactory&lt;/span&gt; 实例到 Web 应用的 &lt;span style="color: #800080;"&gt;ServletContext&lt;/span&gt; 中。这就允许你的程序在任何地方都能访问到 Scheduler 实例，只要获取到了 &lt;span style="color: #800080;"&gt;HttpServletRequest&lt;/span&gt; 或 &lt;span style="color: #800080;"&gt;HttpSession&lt;/span&gt; 对象，调用工厂的 &lt;span style="color: #800080;"&gt;getScheduler()&lt;/span&gt; 就访问到了 Scheduler 实例。&lt;br/&gt;&lt;br/&gt;
还新增了一个 &lt;span style="color: #800080;"&gt;start-scheduler-on-load&lt;/span&gt; 的 Servlet 初始化参数。这一参数指定 Scheduler 是否随 &lt;span style="color: #800080;"&gt;QuartzInitializerServlet&lt;/span&gt; 启动或是别处启动。假如未设置时默认为 &lt;span style="color: #800080;"&gt;True&lt;/span&gt;，Sheduler 将随 &lt;span style="color: #800080;"&gt;QuartzInitializerServlet&lt;/span&gt; 起来。否则，你的应用将不得不自己去获得 Scheduler 实例然后调用 &lt;span style="color: #800080;"&gt;start()&lt;/span&gt; 方法。
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
当容器加载 &lt;span style="color: #800080;"&gt;QuartzInitializerServlet&lt;/span&gt;，该 Servlet 的 init() 方法将被调用。这个 Servlet 读取几个初始化参数，创建 &lt;span style="color: #800080;"&gt;StdSchedulerFactory&lt;/span&gt; 类的实例，并使用指定(或默认) 的 Quartz 属性文件来初始化 Scheduler。</description></item><item><title>Unmi 的 Struts2 学习笔记(十一)</title><link>https://yanbin.blog/unmi-study-struts2-11/</link><pubDate>Mon, 28 Apr 2008 11:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-11/</guid><description>
你尽可以做个良民，为群体效率而本份的遵循个人秩序，然而事与愿违，非但不能因此提升群体的效率，而你势必失去诸多的机会，不会有人褒奖你，更得不到同情，只得自觉的天真。&lt;/p&gt;
&lt;br/&gt;
1. 模板目录－＞主题目录－＞主题(模板文件) 这是模板/主题的目录组织方式。以实际为例，打开 struts2-core-2.x.x.jar 可以看到里面有一个 template，在 template 下有5个目录 ajax、css_xhtml、simple、xhtml 和 archive，其中前四个分别是 ajax、css_xhtml、simple、xhtml 主题的目录，每个主题目录中有各自的模板文件，主要是 ftl 文件，还有 css 和 js 文件。最后一个 archive 是归档的主题目录，其下又有 ajax、simple、xhtml、模板文件是 .vm 文件。由此可知 Struts2 大力推荐的模板语言是 FreeMarker，而不是 Velocity，以后要好好看看 FreeMarker，只知道 FreeMarkder 更 XML 化。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十三章. Quartz 和 Web 应用 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-13-2/</link><pubDate>Fri, 25 Apr 2008 10:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-13-2/</guid><description>
&lt;strong&gt;三. 在 Struts 框架中使用 Quartz&lt;br /&gt;
&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
第一步就是要下载 Apache Struts 并创建好你的 Web 应用的目录结构。Struts 框架可从 Apache Struts 站点 &lt;a href="http://struts.apache.org"&gt;http://struts.apache.org&lt;/a&gt; 上找到。也很欢迎你直接抓取到源代码来编译它，尽管你能够及时下载到最新版本的二进制版。&lt;br/&gt;&lt;br/&gt;
因为 Quartz 不直接依赖于 Struts 框架，所以你也就不必为该使用哪一版本的 Struts 而忧心。只管下载到当前的最新版本就是了。不过，你应该认识到，Struts 和 Quartz 框架共同依赖了一些第三方的包。实际上，在前面列出的 Quartz 所需要的库也是 Struts 框架所必须的。就是要留心混在一起不同的版本，如在最后一节的警告所注明的。</description></item><item><title>Unmi 的 Struts2 学习笔记(十)</title><link>https://yanbin.blog/unmi-study-struts2-10/</link><pubDate>Thu, 24 Apr 2008 11:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-10/</guid><description>
有一日，帮外公翻腾出他箱底的照片进行数字化留存，已是耄耋之年的外公端详起那些记忆来，看到都是一同革命的热情小伙，不时感慨：唉！好多人都不在啦！&lt;/p&gt;
&lt;br/&gt;
1. &amp;lt;s:action .../&amp;gt; 可以用来在 JSP 页面直接调用 Action，用 name 指定 Action，executeResult(默认为 false) 指定是否包含那个 Action 转向视图的内容；executeResult 会把视图的所有内容输出在标签位置。ignoreContextParams(默认为 false)，是否将本页的请求参数传到那个 Action。&lt;br/&gt;&lt;br/&gt;
2. &amp;lt;s:bean .../&amp;gt; 用来实例化 JavaBean，标签内使用 &amp;lt;s:param name="" value=""/&amp;gt; 来指定属性。在标签内，bean 实例位于 ValueStack 顶端，标签结束则自动清除。如果指定的 id 属性，还会把该实例放到 Stack Context 中。name 属性必须，指定 Bean 类的全限名称。不知道复杂的属性该如何用 &amp;lt;s:param .../&amp;gt; 或其他方式来指定。</description></item><item><title>利用 Ant 的 SQL Task 来实现自己的 Java 执行 SQL 脚本文件的功能</title><link>https://yanbin.blog/ant-sql-task-exec-sql-scripts/</link><pubDate>Thu, 24 Apr 2008 08:03:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ant-sql-task-exec-sql-scripts/</guid><description>
前面记载过一篇 &lt;a href="https://yanbin.blog/java-execute-sql-script-file/" target="_blank" rel="noopener noreferrer"&gt;Java 执行 SQL 脚本文件&lt;/a&gt;，这里边完全是由自己写代码来分离出脚本中的每一个 SQL 语句的，有不少缺陷。当时还不太清楚 ANT 本身提供了功能很强的执行 SQL 语句和脚本的 SQL Task 可用。以下依次简单介绍如何在 build.xml 中执行 SQL 语句或脚本；Java 代码中如何调用 ant 的 SQLExec 类执行 SQL 脚本，最后考虑  ant.jar 的个头说大也不小，1M 多，如果只用于执行 SQL 脚本，则绝大部分代码就是垃圾，所以从同抽离出需要的两个类 JDBCTask 和 SQLExec，完全去除了对 ant.jar 包的依赖。&lt;br/&gt;&lt;br/&gt;
有关 ant 的更详细的记录请参见，&lt;a href="http://ant.apache.org/manual/CoreTasks/sql.html"&gt;http://ant.apache.org/manual/CoreTasks/sql.html&lt;/a&gt; 。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十三章. Quartz 和 Web 应用 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-13-1/</link><pubDate>Wed, 23 Apr 2008 10:40:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-13-1/</guid><description>
&lt;strong&gt;第十三章. Quartz 和 Web 应用&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
到目前为止，我们与 Quartz 框架的交互主要还是通过命令行。对于有些使用者，比如我的一个大学计算机科学老教授(它曾每天都告诉我说，"GUI 是给能力差的人用的!")，使用命令行让他们很乐意接受。当应用程序被开发完成后，它们常要移交给终端用户或支持团队。在命令行应用程序上层架设一个 GUI 前端会非常有帮助也是很增值的。本章记述如何在 Web 应用中用 Quartz 来使得部署和维护 Job 更轻松。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. 在 Web 应用中使用 Quartz&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
至此，你已经看到过许多在 J2SE 环境中独立运行的 Quartz 的例子。在第十章，"J2EE 中使用 Quartz"，你也学到了 Quartz 良好的运作于 J2EE 环境中。但是，我们还没有向你介绍的是如何部署 Quartz 到一个 Java Web 应用(通常简写为 Web app)中。这就是本章唯一意图</description></item><item><title>Unmi 的 Struts2 学习笔记(九)</title><link>https://yanbin.blog/unmi-study-struts2-9/</link><pubDate>Mon, 14 Apr 2008 10:24:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-9/</guid><description>
中世纪的欧洲《礼仪书》中说：痰不要吐到桌子上，也不要飞过桌子。1774年，欧洲的《礼仪与基督教礼貌守则》中说：我们再也不能原谅那些把痰吐到窗户外、墙上或者家具上的人。可见，欧洲人的素质也是在他们工业化富了之后的事。至少那个时候，我们的老祖宗再不争气，也绝不会把痰吐到桌子上。&lt;/p&gt;
&lt;br/&gt;
1. OGNL 中有生成 List 和 Map 的表达式，分别是：{e1,e2,e3,...} 和 #{key1:val1,key2:val2,...}。对集合 OGNL 提供了两个操作符 in 和 not in，如：&lt;br /&gt;
   &amp;lt;s:if test="'foo' in {'foo','bar'}" ... &amp;lt;/s:if&amp;gt;            ---- not in 的用法当然是一样的了。&lt;br /&gt;
之外，OGNL 还允许通过某个规则取集合的子集&lt;br /&gt;
    ·? ：取出所有符合条件逻辑的元素&lt;br /&gt;
    ·^：取出符合条件的第一个元素</description></item><item><title>在 Windows 平台下从硬盘安装 Ubuntu Linux 7.10(alternate)</title><link>https://yanbin.blog/windows-ubuntu-linux-7-10-alternate/</link><pubDate>Sun, 13 Apr 2008 10:50:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-ubuntu-linux-7-10-alternate/</guid><description>
Ubuntu 8.0.4 已在万众企盼当中，本月 4 月 24 日正式发出，代码版本已封闭。继续写一个 Ubuntu Linux 7.10(alternate) 似乎十分多余，那就留给届时还会怀念 Ubuntu 7.10 的人士，也给自己留个底吧。&lt;/p&gt;
&lt;br/&gt;
当然，每写个东西，总是要负责的，非轻率地转抄，终以本人实践真实记录。前面有篇是讲 Ubuntu 7.10(Desktop) 的安装，&lt;a href="https://yanbin.blog/windows-ubuntu-linux-7-10-desktop/" target="_blank" rel="noopener noreferrer"&gt;在 Windows 平台下从硬盘安装 Ubuntu Linux 7.10(Desktop)&lt;/a&gt;，这里记载的是 Ubuntu 7.10(alternate) 的安装，是通过 grub4dos 来加上一个 Ubuntu 启动项，从而能从更盘引导。个人也更偏爱于用 alternate 版的光盘来安装，安装其他 Linux 发行版养成的习惯。</description></item><item><title>运行《Struts2 权威指南》 s-if.jsp 的异常引出对 EL 和 OGNL 的思考</title><link>https://yanbin.blog/struts2-s-if-jsp-el-ognl/</link><pubDate>Sun, 13 Apr 2008 01:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts2-s-if-jsp-el-ognl/</guid><description>
我用 MyEclipse 建了一个 Web 项目，配置了能支持 Struts2。在验证标签应用时，把 《Struts2权威指南--基于WebWork核心的MVC开发》一书的例子 10.3 controlTag 中的 s-if.jsp 拷入当前应用。该 jsp 文件的内容是：想像一下我访问它时得到了什么样的结果？&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;%@ page contentType=&amp;#34;text/html; charset=GBK&amp;#34; language=&amp;#34;java&amp;#34;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;%@taglib prefix=&amp;#34;s&amp;#34; uri=&amp;#34;/struts-tags&amp;#34;%&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;s:if标签测试&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;s:set&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;age&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;29&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;s:if&lt;/span&gt; &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${age &amp;gt; 60}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; 老年人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;s:if&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;s:elseif&lt;/span&gt; &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${age &amp;gt; 35}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; 中年人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;s:elseif&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;s:elseif&lt;/span&gt; &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${age &amp;gt; 15}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;wawa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; 青年人
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;s:elseif&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;s:else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; 少年
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;s:else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
然后把该应用 TestStruts2 部署到 Tomcat 5.0.28 下，启动 Tomcat，地址栏输入 &lt;a href="http://localhost:8080/TestStruts2/s-if.jsp"&gt;http://localhost:8080/TestStruts2/s-if.jsp&lt;/a&gt;，确定，执行结果让我傻眼了，任凭我百般刷新，也都是赫然显示着：&lt;br/&gt;</description></item><item><title>Unmi 的 Struts2 学习笔记(八)</title><link>https://yanbin.blog/unmi-study-struts2-8/</link><pubDate>Thu, 10 Apr 2008 13:32:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-8/</guid><description>
吾同人数进寺庙，却从未点过一柱香。概因精神世界里终无所寄托。既不为生儿还是女而筹算，也无须因落下亏心事要请得神灵庇佑。想来，真要的东西，阿弥陀佛也给不了。在物价飞涨之际，谁能给你加薪，何来民主，看看这辈子能不能有机会参与选一回国家领导人，少说几句了，就这点言论自由了。&lt;/p&gt;
&lt;br/&gt;
1. Struts 2 标签库以 OGNL 表达式为基础，对集合、对象的访问功能更强了。Struts 2 的标签用统一的前缀，一般为 "s"，不再像 Struts1 那样分 &amp;lt;html:.../&amp;gt;、&amp;lt;bean:.../&amp;gt;、&amp;lt;logic:.../&amp;gt; 等多个命名空间的标签。如果在 JSP 2.0 之前用过 JSTL，也就知道什么是 OGNL 表达式的。&lt;br/&gt;&lt;br/&gt;
2. Struts2 提供了很多 Struts1 标签没有的东西，如日历、树型控件、Tab页等；同时可借助 DWR，有标签支持 Ajax，除此还提供了主题、模板，还允许在页面中使用自定义组件。Struts2 标签都定义在 URI 为 "/struts-tags" 的命名空间下，Struts2 的标签库功能很强大，使用起来却更简单。</description></item><item><title>在 Windows 平台下从硬盘安装 Ubuntu Linux 7.10(Desktop)</title><link>https://yanbin.blog/windows-ubuntu-linux-7-10-desktop/</link><pubDate>Sun, 06 Apr 2008 10:37:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-ubuntu-linux-7-10-desktop/</guid><description>
真要看如何安装可直接跳到第三节，前两节权当胡诌。&lt;/p&gt;
&lt;br/&gt;
&lt;a href="#"&gt;此第一节与本篇旨意确有偏离，以免混淆视听，故此默认时隐去，有兴趣者可点击本行来展开阅读此节&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;
&lt;div id="firstg" style="display: none;"&gt;&lt;strong&gt;&lt;br /&gt;
&lt;span style="color: #0000ff;"&gt;一、&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;先前装过不少的 Linux 发行版，记得最早的还是 RedHat 5.1，那感觉真是非专业人员不可碰及，其实那时不过刚学步，当然要吃苦头的，一不小心便让硬盘数据倏忽全没了。&lt;/div&gt;
&lt;br/&gt;
现在 Linux 慢慢也走向平民化了，尤其是 Ubuntu Linux。与 Ubuntu 的机缘还是前年大约八月份的时候，有个同事还是从美国带回来的一张 Ubuntu 6.06 LTS 的 Live CD。当晚在自己机器上一启动，发现那傻不拉叽又愣黄的界面，实在是无法与 RedHat 和 Suse 相媲美，便把它沉之箱底。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十二章. Quartz Cookbook (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-12-3/</link><pubDate>Sun, 06 Apr 2008 00:30:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-12-3/</guid><description>
&lt;strong&gt;·替换已部署的 Job&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz 提供了对已部署 Job 进行修改的灵活性。它是通过允许用修改后的 &lt;span style="color: #800080;"&gt;JobDetail&lt;/span&gt; 替换已有的 &lt;span style="color: #800080;"&gt;
JobDetail&lt;/span&gt; 来支持这一特性的。为展未这一例子，让我们更新代码 12.4 中的 &lt;span style="color: #800080;"&gt;CheckEmailJob&lt;/span&gt; 类。
代码 12.4 是硬编码了邮件属性值到 Job 类中的。更好的做法是传入那些属性，如此则可以随意的改变它们；
那让我们改动 &lt;span style="color: #800080;"&gt;CheckEmailJob&lt;/span&gt; 来做到这一点。代码 12.7 显示的是那个 Job 的更新后的版本。&lt;br/&gt;&lt;br/&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十二章. Quartz Cookbook (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-12-2/</link><pubDate>Sat, 05 Apr 2008 02:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-12-2/</guid><description>
&lt;strong&gt;二. 与 Job 一同工作&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
本节为使用 Quartz 的 Job 提供了一些例子。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·创建一个新的 Job 类&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
创建一个新的 Job 类很简单。仅需创建一个类，让它实现 &lt;span style="color: #800080;"&gt;org.quartz.Job&lt;/span&gt; 接口即可。这个接口需要你实现 &lt;span style="color: #800080;"&gt;execute()&lt;/span&gt; 方法，它会在 Scheduler 决定 Job 要执行时被调用。&lt;br/&gt;&lt;br/&gt;
代码 12.4 演示了一个简单的 Job，它会为某个用户检查邮件服务器上是否有新的邮件。当 Scheduler 执行这个 Job 时，方法 &lt;span style="color: #800080;"&gt;execute()&lt;/span&gt; 被调用，然后其中的代码就会连接到邮件服务器并获取任何邮件消息。这一 Job 只简单的打印邮件是谁发的和邮件的主题。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十二章. Quartz Cookbook (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-12-1/</link><pubDate>Fri, 04 Apr 2008 12:52:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-12-1/</guid><description>
&lt;strong&gt;第十二章. Quartz Cookbook&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
本章的目的是为在构建 Quartz 应用时常遇到的情形提供一系列的例子和解决方案。本章也可作为对本书剩余部分的一个参考和补充。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. 与 Scheduler 一同工作&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
本节提供了使用 Quartz Scheduler 管理功能的几个例子。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·创建和启动 Scheduler&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
你能通过几种方式来启动 Quartz Scheduler，但是最简单的方式是使用两种 &lt;span style="color: #800080;"&gt;SchedulerFactory&lt;/span&gt; 实现中的一个。特别的，&lt;span style="color: #800080;"&gt;org.quartz.impl.stdSchedulerFactory&lt;/span&gt; 使用很简单，要执行对 Scheduler 的所有设置工作只需要调用 &lt;span style="color: #800080;"&gt;getDefaultScheduler()&lt;/span&gt; 这一静态方法即可，如代码 12.1 所演示的那般。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第五章. Cron 触发器及相关内容 (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-5-4/</link><pubDate>Thu, 03 Apr 2008 23:07:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-5-4/</guid><description>
&lt;strong&gt;七. Cron 表达式 Cookbook&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
此处的 Cron 表达式 cookbook 旨在为常用的执行需求提供方案。尽管不可能列举出所有的表达式，但下面的应该为满足你的业务需求提供了足够的例子。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·分钟的 Cron 表达式&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;table width="600" cellspacing="5"&gt;
&lt;caption&gt;&lt;strong&gt;表 5.1. 包括了分钟频度的任务计划 Cron 表达式&lt;/strong&gt;&lt;/caption&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;用法&lt;/strong&gt;&lt;/td&gt;
&lt;td width="150"&gt;&lt;strong&gt;表达式&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每天的从 5:00 PM 至 5:59 PM 中的每分钟触发&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;0 * 17 * * ?&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每天的从 11:00 PM 至 11:55 PM 中的每五分钟触发&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;0 0/5 23 * * ?&lt;br /&gt;
&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每天的从 3:00 至 3:55 PM 和 6:00 PM 至 6:55 PM 之中的每五分钟触发&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;0 0/5 15,18 * * ?&lt;br /&gt;
&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;每天的从 5:00 AM 至 5:05 AM 中的每分钟触发&lt;/td&gt;
&lt;td&gt;&lt;span style="color: #800080;"&gt;0 0-5 5 * * ?&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;</description></item><item><title>VMware+Linux AS4 为虚拟机新增了硬盘，该如何加载？[摘]</title><link>https://yanbin.blog/vmware-linux-as4-add-new-harddis/</link><pubDate>Mon, 31 Mar 2008 23:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vmware-linux-as4-add-new-harddis/</guid><description>
VMware 中安装了 Linux 后，又给这个虚拟机增加一个硬盘，假设选的类型是 SCSI， 进到虚拟机系统中应如何加载呢？&lt;br/&gt;
&lt;br/&gt;
其实这后 Linux 新加了一个物理硬盘是一回事，做法如下：&lt;br/&gt;
&lt;br/&gt;
在服务器上把硬盘接好，启动linux，以root登陆。&lt;br/&gt;
&lt;br/&gt;
比如我新加一块SCSI硬盘，需要将其分成三个区：&lt;br/&gt;
&lt;br/&gt;
#fdisk /dev/sdb&lt;br/&gt;
进入fdisk模式：&lt;br/&gt;
Command (m for help):p //查看新硬盘的分区&lt;br/&gt;
Command (m for help):n //创建新分区</description></item><item><title>Unmi 的 Struts2 学习笔记(七)</title><link>https://yanbin.blog/unmi-study-struts2-7/</link><pubDate>Sat, 29 Mar 2008 14:52:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-7/</guid><description>
小时候，大人们的谆谆教诲：做人要诚实。并真以此为做人原则。长大后才知道何谓社会。譬如530，再如艳照门，风声乍起之时，有人辟谣；直东窗事发后，道貌岸然者有之、恬不知耻者亦有之。原本成功就不属于规矩之人。纵观，无玄武门之血腥，何来一代宗皇；老毛若不有理而造反，一味守规矩，或为一介书匠耳。雅各一碗红豆汤便谋得以扫的长子权，再行骗去亚伯拉罕的祝福，并顺理成章让耶和华与他同在，可见上帝之“贤明”？。高等教育中有一句，顺利的是骗子，倒霉的是傻子，我是？不还有企业家的原罪吗？有时竟被社会所放任？&lt;br/&gt;&lt;br/&gt;
1. 前面讲的自定义类型转换器是基于 OGNL 的 DefaultTypeConverter 类并实现 convertValue() 方法，两个转换方向的逻辑都写在这一个方法中。而 Struts 2 为我们提供了一个 DefaultTypeConverter 的抽象子类 StrutsTypeConverter 来继承，并实现其中两个抽象方法 convertFromString() 和 convertToString()，这要简单易懂。对比 Struts 1 的转换器是要实现 org.apache.commons.beanutils.Converter 接口，以及它的 convert() 方法的。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第五章. Cron 触发器及相关内容 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-5-3/</link><pubDate>Fri, 28 Mar 2008 22:19:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-5-3/</guid><description>
&lt;strong&gt;四. 为 CronTrigger 使用起迄日期&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Cron 表达式是用来决定一个 Trigger 被触发执行一个 Job 的日期和次数。当你创建一个 &lt;span style="color: #800080;"&gt;CronTrigger&lt;/span&gt; 实例，假如没为它指定一个开始时间，这个 Trigger 当然就会假定是在依赖于 Cron 表达式尽早的被触发。例如，如果你用这个表达式&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;0 * 14-20 * * ?&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
这个 Trigger 会在每天的从下午 2 点到下午的 7:59 间的每分钟触发一次。一旦你运行了这个表达式的 &lt;span style="color: #800080;"&gt;CronTrigger&lt;/span&gt;，假如当前是下午 2 点后(不能超过 7:59 PM--译者注)，它将会立即触发。它会在每天无限期的被触发。</description></item><item><title>任务列表分派给多个线程的策略及方法(含完整代码和演示)</title><link>https://yanbin.blog/task-dispatch-strategy-demo/</link><pubDate>Tue, 25 Mar 2008 11:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/task-dispatch-strategy-demo/</guid><description>
多线程下载由来已久，如 FlashGet、NetAnts 等工具，它们都是依懒于 HTTP 协议的支持(Range 字段指定请求内容范围)，首先能读取出请求内容 (即欲下载的文件) 的大小，划分出若干区块，把区块分段分发给每个线程去下载，线程从本段起始处下载数据及至段尾，多个线程下载的内容最终会写入到同一个文件中。&lt;/p&gt;
&lt;br/&gt;
只研究有用的，工作中的需求：要把多个任务分派给多个线程去执行，这其中就会有一个任务列表指派到线程的策略思考：已知：1. 一个待执行的任务列表，2. 指定要启动的线程数；问题是：每个线程实际要执行哪些任务。&lt;br/&gt;&lt;br/&gt;
策略是：任务列表连续按线程数分段，先保证每线程平均能分配到的任务数，余下的任务从前至后依次附加到线程中--只是数量上，实际每个线程执行的任务都还是连续的。如果出现那种僧多(线程) 粥(任务) 少的情况，实际启动的线程数就等于任务数，一挑一。这里只实现了每个线程各扫自家门前雪，动作快的完成后眼见别的线程再累都是爱莫能助。</description></item><item><title>JAVA程序启动单一实例妙招[转]</title><link>https://yanbin.blog/java-startup-single-instance/</link><pubDate>Mon, 24 Mar 2008 06:54:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-startup-single-instance/</guid><description>
每日E读,间隔几天,显的每日E读不每了,新闻过后,精彩继续!
&lt;br/&gt;&lt;br/&gt;
今儿,想起来昨儿,解决的一问题,贴出来大家学习下,由于工作需要,学习,哈哈~!&lt;br/&gt;&lt;br/&gt;
手头的一个任务,是个java的SWT做的桌面应用程序,有一需求是,程序只能启动一个实例,二次启动会提示启动失败!网上转了一大圈几天也没找到比较理想的解决方法,在CSND等专业程序员网站上查找过,也没有找到!我正要放弃的时候,哈哈,被我找到了!&lt;br/&gt;&lt;br/&gt;
找了一写方案，总结起来就是，系统单一实例，就是查看共用资源，如果被占用，说明已经启动！有人说用单例模式，那是没有作用的！&lt;br/&gt;&lt;br/&gt;
网上找到的几种解决方案:</description></item><item><title>JavaScript高级培训－自定义对象[转]</title><link>https://yanbin.blog/javascript-advanced-training-custom-object/</link><pubDate>Sun, 23 Mar 2008 12:34:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-advanced-training-custom-object/</guid><description>
&lt;p&gt;&lt;strong&gt;&lt;br /&gt; &lt;span style="font-size: large;"&gt;一，概述&lt;/span&gt;&lt;br /&gt; &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在Java语言中，我们可以定义自己的类，并根据这些类创建对象来使用，在Javascript中，我们也可以定义自己的类，例如定义User类、Hashtable类等等。&lt;/p&gt;
&lt;p&gt;目前在Javascript中，已经存在一些标准的类，例如Date、Array、RegExp、String、Math、Number等等，这为我们编程提供了许多方便。但对于复杂的客户端程序而言，这些还远远不够。&lt;/p&gt;
&lt;p&gt;与Java不同，Java2提供给我们的标准类很多，基本上满足了我们的编程需求，但是Javascript提供的标准类很少，许多编程需求需要我们自己去实现，
例如Javascript没有哈西表Hashtable，这样的话在处理键值方面就很不方便。&lt;/p&gt;
&lt;p&gt;因此，我个人认为一个完整的Javascript对象视图应该如下：</description></item><item><title>IE的调试工具</title><link>https://yanbin.blog/ie-debug-tools/</link><pubDate>Sun, 23 Mar 2008 12:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ie-debug-tools/</guid><description>
FireFox 有 FireBug&lt;br /&gt;&lt;br /&gt;
而IE下有&lt;br /&gt;&lt;br/&gt;
1. MS 的Developer Toobar,&lt;br /&gt;
2. Instance Source&lt;br /&gt;
3. HttpWatch&lt;br /&gt;&lt;br /&gt;
&lt;a href="http://www.ieinspector.com/dominspector/download.html"&gt;http://www.ieinspector.com/dominspector/download.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
4. IE Dom Inspector&lt;br /&gt;
5. IE Http Analyzer&lt;br /&gt;6. AxScripter&lt;br /&gt;7. IE WebDeveloper&lt;br /&gt;&lt;br /&gt;
试一试它们的好处</description></item><item><title>SWT 中实现最小化到托盘图标，并只能通过托盘的弹出菜单关闭程序</title><link>https://yanbin.blog/swt-systray-close-via-tray/</link><pubDate>Sun, 23 Mar 2008 03:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/swt-systray-close-via-tray/</guid><description>
我们有些程序会想要托盘处显示图标，最小化到系统栏；关闭按钮不关闭程序，也是最小化到系统栏；点击托盘图标激活窗口，通过托盘图标的弹出菜单来退出程序。&lt;/p&gt;
&lt;br/&gt;
本段代码就是要完成这样的功能，是 SWT  来实现的。&lt;br/&gt;&lt;br/&gt;
直接代码给出，代码中有较详细的注释，说明了本程序的功能及实现。文中的任务栏和系统栏应该知道是指哪一段吧，微软就是这么定义的，用 spyxx 的 findwindow 窥探一下就知道了。</description></item><item><title>JSPWeaver消灭JSP开发中的“一回生”[转]</title><link>https://yanbin.blog/jspweaver-optimize-jsp/</link><pubDate>Mon, 10 Mar 2008 11:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jspweaver-optimize-jsp/</guid><description>
&lt;p class="info"&gt;之前整一星期忙得不可开交，关于技术已无暇顾及，这个星期总算有朝向正常化的迹象了。回到网络上多紧跟着技术变化的步伐，拣自认为好的东西摘记下来。&lt;/p&gt;
&lt;p class="info"&gt;原文：&lt;a href="http://www.infoq.com/cn/news/2008/02/JSPWeaver10"&gt;http://www.infoq.com/cn/news/2008/02/JSPWeaver10&lt;/a&gt;&lt;/p&gt;
作者 &lt;strong&gt;Charles Humble&lt;/strong&gt;译者 &lt;strong&gt;李剑&lt;/strong&gt; 发布于 2008年2月25日 上午4时46分&lt;br/&gt;
&lt;br/&gt;
&lt;dl class="tags2"&gt;
&lt;dt class="community"&gt;社区 &lt;a id="739" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="java" href="http://www.infoq.com/cn/java;jsessionid=676CA919BD6C684DDA2576F8360D490A"&gt;Java&lt;/a&gt;&lt;/dt&gt;
&lt;dt class="topics"&gt;主题 &lt;a id="832" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="dynamic_languages" href="http://www.infoq.com/cn/dynamic_languages;jsessionid=676CA919BD6C684DDA2576F8360D490A"&gt;动态语言&lt;/a&gt;,
&lt;a id="786" onclick="try {CategoryPopup.showPopup(this);} catch(e) {}; return false;" name="enterprise-architecture" href="http://www.infoq.com/cn/enterprise-architecture;jsessionid=676CA919BD6C684DDA2576F8360D490A"&gt;企业架构&lt;/a&gt;
&lt;/dt&gt;&lt;/dl&gt;ZeroTurnaround的&lt;a href="http://www.zeroturnaround.com/jsp-weaver/"&gt;JSPWeaver&lt;/a&gt;
是一个实时JSP解释器，它旨在消除因为服务器从JSP标记中创建和编译后台servlet而造成的“一回生
（&lt;strong&gt;译者注：&lt;/strong&gt;即第一个访问Web应用的JSP页面的人，响应时间会比别人长）”。</description></item><item><title>Unmi 的 Struts2 学习笔记(六)</title><link>https://yanbin.blog/unmi-study-struts2-6/</link><pubDate>Wed, 27 Feb 2008 10:06:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-6/</guid><description>
米洗毕，水适量，盖合上，心摇摆，总觉水多了一分或是少了几点，于是重又加上或倒出少许，其实这丁点份量完全无碍煮出香喷喷的饭来。及至将熟之际，亦有犹豫，心中默数至少10，如再有不定辄至20，掐火，20对最终质量来说或有所损。&lt;/p&gt;
&lt;br/&gt;
1. Struts2 的 Action 中可直接用属性来封装请求参数和处理结果，此谓之属性驱动。也可以像 Struts1 那样使用专门的 ActionForm 来处理请求参数和结果，只不过 Struts2 用的是一个更单纯的 POJO，这就做模型驱动；此时的 Action 需要实现 ModelDriven 接口，并实现其 getModel() 方法将 Action 与对应 Model 相关联。&lt;br/&gt;&lt;br/&gt;
2. 例子说明采用模型驱动的 Action (UserBean 是一个普通 JavaBean，其中定义了 username 和 password 两个属性)：</description></item><item><title>XML 的中阶知识巩固之命名空间</title><link>https://yanbin.blog/xml-knowledge-namespace/</link><pubDate>Tue, 26 Feb 2008 10:20:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/xml-knowledge-namespace/</guid><description>
&lt;strong&gt;&lt;br /&gt;
&lt;span style="color: #ff0000;"&gt;1. 命名空间 (Namespace) 的引入&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
比如有两份 XML 数据&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;labourer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Unmi&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;address&amp;gt;&lt;/span&gt;fantasia@sina.com&lt;span class="nt"&gt;&amp;lt;/address&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/labourer&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
和&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;labourer&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;Unmi&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;address&amp;gt;&lt;/span&gt;Gaoxin Nanyi Road, NanShan District, Shenzhen&lt;span class="nt"&gt;&amp;lt;/address&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/labourer&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
集成在一起时就是</description></item><item><title>在仅实现到 Servlet 2.3/JSP 1.2 规范、JDK为1.4 的容器中用 Struts 2 会有什么问题？</title><link>https://yanbin.blog/servlet23-jsp12-jdk14-struts2/</link><pubDate>Mon, 25 Feb 2008 08:31:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/servlet23-jsp12-jdk14-struts2/</guid><description>
Struts  的官方网站 &lt;a href="http://struts.apache.org/2.0.11/index.html"&gt;http://struts.apache.org/2.0.11/index.html&lt;/a&gt; 列出了 Struts 2 的平台要求有三：&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;· Servlet API 2.4&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;· JSP API 2.0&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;· Java 5
&lt;/strong&gt;&lt;br /&gt;
&lt;br/&gt;
但如果坚持要用 Java 4 也行，Struts 2 在 j4 目录下提供有用 retrotranslator 转换过的 struts2-core-j4-2.x.x.jar 和 xwork-j4-2.x.x 可用。&lt;br/&gt;&lt;br/&gt;
就是说换两个包可以 让 Struts 2 用 Java 4 环境中，但是未提及能不能用于 Servlet API 2.3/JSP API 1.2 的环境。</description></item><item><title>Unmi 的 Struts2 学习笔记(五)</title><link>https://yanbin.blog/unmi-study-struts2-5/</link><pubDate>Sun, 24 Feb 2008 00:30:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-5/</guid><description>
常有这样的梦：不知何来一本似乎是无字天书，而其中的字句却是慢慢地、渐显可辨。同时现实中的自己完全能察觉这是一个梦，极怕即刻醒来，想多记取那些天外之语。但总不能如愿，醒来时还留有某些字句痕迹，再过后就全然忘却。仿佛进入的是一个太虚幻境。&lt;/p&gt;
&lt;br/&gt;
1. 动态 Action：&amp;lt;action ../&amp;gt; 元素的一个前所未有的特性是，name 属性可以用通配符，class 和 method 属性中可以用 name 中的匹配参数，{0}/{1} 的形式。举两个例子说明：&lt;br /&gt;
    ① &amp;lt;action name="*Action" class="com.unmi.LoginRegistAction" method="{1}"&amp;gt;&lt;br /&gt;
         URL 是 registAction.action 时，会执行 LoginRegistAction 类实例的 regist() 方法&lt;br /&gt;
    ② &amp;lt;action name="*_*" class="actions.{1}Action" method="{2}"&amp;gt;&lt;br /&gt;
         URL 是 Book_save.action 时，会执行 actions.BookAction 类实例的 save() 方法</description></item><item><title>Quartz Job Scheduling Framework［翻译］第五章. Cron 触发器及相关内容 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-5-2/</link><pubDate>Fri, 22 Feb 2008 23:52:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-5-2/</guid><description>
&lt;strong&gt;三. cron 表达式的格式&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
Quartz cron 表达式的格式十分类似于 UNIX cron 格式，但还是有少许明显的区别。区别之一就是 Quartz 的格式向下支持到秒级别的计划，而 UNIX cron 计划仅支持至分钟级。许多我们的触发计划要基于秒级递增的(例如，每45秒)，因此这是一个非常好的差异。&lt;br/&gt;&lt;br/&gt;
在 UNIX cron 里，要执行的作业（或者说命令）是存放在 cron 表达式中的，在第六个域位置上。Quartz 用 cron 表达式存放执行计划。引用了 cron 表达式的 &lt;span style="color: #800080;"&gt;CronTrigger&lt;/span&gt; 在计划的时间里会与 job 关联上。&lt;br/&gt;&lt;br/&gt;
另一个与 UNIX cron 表达式的不同点是在表达式中支持域的数目。UNIX 给出五个域(&lt;span style="color: #800080;"&gt;分、时、日、月和周&lt;/span&gt;)，Quartz 提供七个域。表 5.1 列出了 Quartz cron 表达式支持的七个域。</description></item><item><title>Unmi 的 Struts2 学习笔记(四)</title><link>https://yanbin.blog/unmi-study-struts2-4/</link><pubDate>Fri, 22 Feb 2008 11:00:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-4/</guid><description>
在这个陌生的城市里，的确承受着巨大的压力。房东的驱赶与无理的加价，想置个家电须顾及到搬迁。也开始学会了算计着过活，两块五与三块的公交，宁愿等待与拥挤，就连省下一顿饭钱都能暗自庆幸。周末了，老老实实呆在家里吧！&lt;/p&gt;
&lt;br/&gt;
1. 按这个顺序加载 Struts2 的常量，struts-default.xml、struts-plugin.xml、struts.xml、web.xml；后加载的会覆盖前面的同名常量。强烈不推荐在 web.xml 中配置 Struts2 常量。&lt;br/&gt;&lt;br/&gt;
2. Struts1 中有一种 Action 类型是 ForwardAction，而在 Struts2 中相应的配置方式是 &amp;lt;action name="Showcase"&amp;gt;&amp;lt;result&amp;gt;/showcase.jsp&amp;lt;/result&amp;gt;&amp;lt;/action&amp;gt;，Showcase.action 直接映射到 /showcase.jsp。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第五章. Cron 触发器及相关内容 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-5-1/</link><pubDate>Thu, 21 Feb 2008 09:13:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-5-1/</guid><description>
&lt;strong&gt;第五章. Cron 触发器及相关内容&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
我们在上章中有承诺过会花更多时间来讲 Quartz 的 &lt;span style="color: #800080;"&gt;CronTrigger&lt;/span&gt;，所以不会让你失望的。&lt;span style="color: #800080;"&gt;SimpleTrigger&lt;/span&gt; 对于需要在指定的毫秒处及时执行的作业还是不错的，但是假如你的作业需要更复杂的执行计划时，你也就要 &lt;span style="color: #800080;"&gt;CronTrigger&lt;/span&gt; 给你提供更强更灵活的功能。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一. Cron 的快速一课&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
cron 这一观念是来自于 UNIX 世界。在 UNIX 中，cron 是一个运行于后台的守护程序，它负责所有基于时间的事件。尽管 Quartz 除相同的名字和相似的表达式语法外，并未分享到 UNIX cron 别的东西，我们还是值得花几个段落去理解 cron 背后的历史。我们这里的目标不是搞混 UNIX cron 表达式和 Quartz 的 cron 表达式，但是你应该了解 Quartz 表达式的历史，并探索为什么他们运作起来很像。这儿明显有许多有意图的相似性。</description></item><item><title>Unmi 的 Struts2 学习笔记(三)</title><link>https://yanbin.blog/unmi-study-struts2-3/</link><pubDate>Wed, 20 Feb 2008 10:20:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-3/</guid><description>
连续几日熬夜，以致左胸又起闷痛感，何来如此辛苦？不由自忖，人生至今，印象中从来就没有过安逸与幸福感。常要回避刺疼。&lt;/p&gt;
&lt;br/&gt;
1. WebWork 的核心控制器是 ServletDispatcher，映射到 *.action 的 &amp;lt;url-pattern&amp;gt; 即可。在 WebWork 与 Struts2 的 struts.xml 和 struts.properties 相对应的分别是 xwork.xml 和 webwork.properties，文件内容几乎一样，只几个关键字的差异。&lt;br/&gt;&lt;br/&gt;
2. Struts2 的 struts.xml 中 &amp;lt;action ../&amp;gt; 的 name 属性相当于 struts1 的 &amp;lt;action ../&amp;gt; 的 path 属性，但在这里不需要以 "/" 开始。&lt;br/&gt;&lt;br/&gt;
3. Action 中使用业务逻辑组件实例时，通常不直接 New，而会通过工厂方法或 IOC 容器。虽然 WebWork 有 IOC 容器，但 Struts2 通常会使用 Spring 的 IOC。</description></item><item><title>Unmi 的 Struts2 学习笔记(二)</title><link>https://yanbin.blog/unmi-study-struts2-2/</link><pubDate>Tue, 19 Feb 2008 09:33:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-2/</guid><description>
记笔记，尤其是在 Blog 里记，实则是浪费时间。让学习、阅读的速度以数量级的下降，收获时当时掌握的更细，却并不代表着真记得牢。一小段时间不用它，同样可以忘得很干净，也就是方便于以后能由此泛起些涟漪，如能抛砖引玉就更幸运了。入正题：&lt;/p&gt;
&lt;br/&gt;
1. 记得 Struts1 的 struts-config.xml 通常是放在 WEB-INF 目录下的，Struts2 的 struts.xml 却要放到 WEB-INF/classes 下的，傻眼了吧，反正我是。没看仔细之前，总给我报 There is no Action mapped for namespace / and action name Login. - [unknown location]  错误。因未在 web.xml 指定 struts.xml，所以用 ClassLoader 来加载它了。&lt;br/&gt;&lt;br/&gt;
2. 最简单的 Struts2 应用都要用到 freemarker-xxx.jar 包，因为 UI 标签要用到 FreeMarker ftl 的模板。&lt;br/&gt;&lt;br/&gt;
3. FilterDispatcher 要处理所有的 Web 请求，所以它的 url-pattern 是 /*，并自动对 *.action 转给业务 Action 处理。</description></item><item><title>Unmi 的 Struts2 学习笔记(一)</title><link>https://yanbin.blog/unmi-study-struts2-1/</link><pubDate>Mon, 18 Feb 2008 09:45:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unmi-study-struts2-1/</guid><description>
Struts 从1.1使用到1.3已有多年，都升成2了，却来了个大变脸，须重新审视一番了。从头学习，记下笔记，以要点式。&lt;/p&gt;
&lt;br/&gt;
1. Struts2 与 Struts1 似乎没什么血缘关系了，承自于 WebWork，直害 WebWork 不再推出新版本了，原先的 WebWork 使用者只需顺其然，即是水道渠成；相反 Struts1 的开发经验对于 Struts2 却没有太大的帮助。&lt;br/&gt;&lt;br/&gt;
2. Struts1 的核心控制器是 ActionServlet，而 Struts2 的核心控制器是 FilterDispatcher。Struts2 的业务 Action 会在运行时生成 Action 代理。&lt;br/&gt;&lt;br/&gt;
3. Struts1 的 Action 须继承抽象 Action 类，侵入了 Servlet API；而 Struts2 的 Action 可以实现 ActionSupport，或者只要一个纯净的 POJO 来充当，完全与 Servlet 绝缘。也因此带来测试性 Struts2 要比 Struts1 强，可把 Struts TestCase 扔到一旁去了。</description></item><item><title>MyEclipse Blue Edition: 针对 WebSphere 的廉价工具 [翻译]</title><link>https://yanbin.blog/myeclipse-blue-edition-websphere/</link><pubDate>Sun, 17 Feb 2008 10:13:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/myeclipse-blue-edition-websphere/</guid><description>
今夜，打开 TheServerSide.Com，看到 MyEclipse 又出新品 MyEclipse Blue Edition，专为 WebSphere 开发者打造的。WebSphere 用户也算是有福了，想想以前在 MyEclipse 里联用 WebSphere Server 是一件多么痛苦的事情啊，所以不得不在本地用 Tomcat 测试，一搬到正式环境难免要受到兼容性的折磨。WSAD 和 RAD 提供的功能自是不必说，但慢的难以忍受，快来体验一下 MyEclipse Blue Edition 吧。&lt;/p&gt;
&lt;br/&gt;
原文翻译如下：&lt;br/&gt;
&lt;hr /&gt;&lt;br/&gt;
Genuitec 今日宣布发布一款名为 &lt;a href="http://www.myeclipseide.com/blue.php"&gt;MyEclipse Blue Edition&lt;/a&gt; 的新产品，它针对的是 IBM Rational Application Developer (RAD) 和 WebSphere 的开发。</description></item><item><title>WAS 安全性中定制用注册表，通过 JDBC 验证用户实现 SSO</title><link>https://yanbin.blog/was-security-jdbc-sso/</link><pubDate>Sun, 27 Jan 2008 00:51:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/was-security-jdbc-sso/</guid><description>
通常 Websphere Application Server (WAS) 都是结合 LDAP 来对用户对行验证，实现单点登陆的。因为 LDAP 有着一个得天独厚的优势，它对查询进行了优化，因此 WAS 理所当然的提供了对 LDAP 十分完善的支持。从 WAS 控制台进 &lt;strong&gt;安全性&lt;/strong&gt;－＞&lt;strong&gt;用户注册表&lt;/strong&gt;－＞&lt;strong&gt;LDAP&lt;/strong&gt;，在类型里可以看到支持 IBM_Directory_Server、SecureWay、Sun ONE、Domino、Active_Directory、eDirectory 还可定制 LDAP 连接。&lt;br/&gt;&lt;br/&gt;
好，就此打住，我在这里要介绍的是如何配置 WAS 控制台和应用通过 JDBC 来验证用户。这一想法产生的背景是：公司原所有系统是通过 Portal 做的集成，配置 LDAP 便能实现 SSO，但有一个新的项目暂不能通过 LDAP 来验证，但也要能实现 SSO，于是就意思到要用 JDBC 来进行用户验证。你也许已注意到在 &lt;strong&gt;安全性&lt;/strong&gt;－＞&lt;strong&gt;用户注册表&lt;/strong&gt; 下除了 本地 &lt;strong&gt;OS&lt;/strong&gt; 和 &lt;strong&gt;LDAP&lt;/strong&gt; 外，还有一个 &lt;strong&gt;定制&lt;/strong&gt;。我们就是要在这个 "&lt;strong&gt;定制&lt;/strong&gt;" 上做文章的。下面详细具体步骤。</description></item><item><title>Java 执行 SQL 脚本文件</title><link>https://yanbin.blog/java-execute-sql-script-file/</link><pubDate>Tue, 22 Jan 2008 01:21:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-execute-sql-script-file/</guid><description>
假定Java 程序中要定期执行 SQL 语句，因需求变更应修改原有 SQL 语句或者加上更多的语句时，不得不修改源代码，然后再次编译。
要是把 SQL 语句写在单独的 SQL 脚本文件中，由 Java 程序来定时加载执行，那么每次改动时仅仅修改 SQL 脚本文件就行了。
&lt;br/&gt;
Java 没有提供现成的东西，所以自己写了一个这样的 SQL 脚本执行类 SqlFileExecutor。支持通用的 SQL 脚本文件，"--" 作为注释前导符，分号 ";"
分隔语句。不支持 MySQL 的 /*...*/ 形式的注释格式。对于 Windows 和 Linux/Unix 下编辑的脚本文件都测试通过。
这两个系统文件中的换行符不一样，Windows 是 "\r\n"，Linux/Unix 是 "\n"。</description></item><item><title>用 AOP 来记录每个方法的执行时间(Spring 或直接 AspectJ)</title><link>https://yanbin.blog/aop-spring-aspectj-method-invoke-time/</link><pubDate>Thu, 17 Jan 2008 23:45:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aop-spring-aspectj-method-invoke-time/</guid><description>
有时候我们要跟踪方法的执行时间，来观察系统的性能、时间分布。特别是要找出那些十分耗时的操作。如果是在每个方法中起始和结束位置记下时间相减，那是不太现实的，对代码的侵入性太过份，而且在产品环境中又得屏闭那部份代码。&lt;/p&gt;
&lt;br/&gt;
幸好现在有了 AOP，通过配置方式再加上外部辅助代码就能达到我们的要求，正式上线时只需要简单改个配置项拆卸下来即可。&lt;br/&gt;&lt;br/&gt;
下面介绍三种方式来打印每个方法的执行时间，分别是：&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. Spring 2.0 用 AspectJ 实现 AOP&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;2. Spring 通用的方法拦截&lt;br /&gt;
3. 直接用 AspectJ 实现&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;</description></item><item><title>为何不直接使用 Oracle 提供的连接池实现</title><link>https://yanbin.blog/use-oracle-carried-connection-pool/</link><pubDate>Wed, 16 Jan 2008 14:47:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/use-oracle-carried-connection-pool/</guid><description>
我在 Websphere Application Server (WAS) 下配置 JDBC 提供程序时，选择了 Oracle JDBC Driver 确定之后，看到最后一个选项是：oracle.jdbc.pool.OracleConnectionPoolDataSource。&lt;/p&gt;
&lt;br/&gt;
顾名思义，这是一个 DataSource 实现为，就像 DBCP 的 BasicDataSource 一样。那么能不能也像 BasicDataSource 那样，通过 new BasicDataSource()，然后设置各个必须的属性得到一个数据源 DataSource 呢？这个 OracleConnectionPoolDataSource 又是在哪个包里呢？</description></item><item><title>Quartz Job Scheduling Framework［翻译］第四章. 部署 Job (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-4-4/</link><pubDate>Tue, 08 Jan 2008 09:07:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-4-4/</guid><description>
&lt;strong&gt;七. 线程在 Quartz 中的用法&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
线程与 Quartz 来说尤为重要，因为 Quartz  就是设计为支持同时运行多个 Job。为达到此效果，Quartz 非常倚重于内建于 Java 语言的线程，借助于自己的类和借口还有所增强。你已经在本章或前面章节中看到过这方面的例子。&lt;br/&gt;&lt;br/&gt;
当 Quartz Schduler 首次由某个工厂方法创建时，工厂配置了 Scheduler 会在它的整个生命周期中用到的几个重要的资源。其中一些重要的资源是与线程相关的。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·主处理线程：&lt;span style="color: #800080;"&gt;QuartzSchedulerThread&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
Quartz 应用第一次运行时，&lt;span style="color: #800080;"&gt;main&lt;/span&gt; 线程会启动 Scheduler。&lt;span style="color: #800080;"&gt;QuartzScheduler&lt;/span&gt; 被创建并创建一个 &lt;span style="color: #800080;"&gt;org.quartz.core.QuartzSchedulerThread&lt;/span&gt; 类的实例。QuartzSchedulerThread 包含有决定何时下一个 Job 将被触发的处理循环。顾名思义，&lt;span style="color: #800080;"&gt;QuartzSchedulerThread&lt;/span&gt; 是一个 Java 线程。它作为一个非守护线程运行在正常优先级下。</description></item><item><title>Tomcat 5.0.28中的dwr出TransformerFactoryImpl not found错的处理办法</title><link>https://yanbin.blog/tomcat-5-0-28-dwr-transformerfactoryimpl-not-found/</link><pubDate>Fri, 04 Jan 2008 10:10:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tomcat-5-0-28-dwr-transformerfactoryimpl-not-found/</guid><description>
&lt;span style="color: #0000ff;"&gt;环境：dwr 1.1.4 + Tomcat 5.0.28 + jdk 1.6.0_03&lt;br /&gt;
&lt;/span&gt;配置了最基本的 web.xml，加了一个 web.xml 和 RemoteBean，部署后通过网址&lt;/p&gt;
&lt;br/&gt;
&lt;a href="http://localhost:8080/testdwr/dwr"&gt;http://localhost:8080/testdwr/dwr&lt;/a&gt; 访问&lt;br/&gt;&lt;br/&gt;
页面出现错误&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #ff1493;"&gt;javax.xml.transform.TransformerFactoryConfigurationError&lt;/span&gt;: Provider &lt;span style="color: #ff1493;"&gt;org.apache.xalan.processor.TransformerFactoryImpl not found&lt;/span&gt;&lt;br /&gt;
javax.xml.transform.TransformerFactory.newInstance(Unknown Source)&lt;br /&gt;
uk.ltd.getahead.dwr.convert.DOMConverter.&amp;lt;init&amp;gt;(DOMConverter.java:157)</description></item><item><title>Tomcat 和 OpenLDAP, 从配置到应用[翻译]</title><link>https://yanbin.blog/tomcat-openldap-config-application/</link><pubDate>Thu, 03 Jan 2008 09:50:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tomcat-openldap-config-application/</guid><description>
&lt;h2&gt;Tomcat 和 OpenLDAP, 从配置到应用&lt;/h2&gt;
by &lt;a href="http://today.java.net/pub/au/321"&gt;Darren Duke&lt;/a&gt;&lt;br /&gt;
05/31/2005&lt;/p&gt;
&lt;br/&gt;
几乎所有的 Java Web 应用都需要某种类型的安全性的控制, 并且这通常会借助于 &lt;a href="http://en.wikipedia.org/wiki/LDAP"&gt;Lightweight Directory Access Protocol&lt;/a&gt; (LDAP) 目录服务来实现.  作为一个开发者，很有必要在本地安装一个 OpenLDAP 和 Web 容器来提高开发效率. 配置 Tomcat 连接到 OpenLDAP 的过程假如你能理解的话，还是很直白的. 这篇文章详细描述了这一配置过程，并用 Apache Jakarta 项目下的标签库 (&lt;em&gt;taglib&lt;/em&gt;) 来演示简单的方式去测试这一安全机制.&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;内容&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#what_is_ldap"&gt;什么是 LDAP?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#openldap_setup"&gt;OpenLDAP 的安装和配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jxplorer"&gt;JXplorer: 可视化 LDAP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tomcat_configuration"&gt;Tomcat 为 OpenLDAP 的配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tomcat_server"&gt;Tomcat 服务器配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#web_app_config"&gt;Web 应用配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jakarta_taglibs"&gt;Jakarta 相应标签库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#conclusion"&gt;小结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#resources"&gt;资源&lt;/a&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第四章. 部署 Job (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-4-3/</link><pubDate>Sun, 30 Dec 2007 20:50:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-4-3/</guid><description>
&lt;strong&gt;5. 易失性、持久性和可恢复性&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
这三个属性有些类似的，由于它们影响的都是 Job 的运行时行为。我们下面依次讨论它们。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·Job 的易失性&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
一个易失性的 Job 是在程序关闭之后不会被持久化。一个 Job 是通过调用 &lt;span style="color: #800080;"&gt;JobDetail&lt;/span&gt; 的 &lt;span style="color: #800080;"&gt;setVolatility(true)&lt;/span&gt; 被设置为易失性的。&lt;br/&gt;&lt;br/&gt;
&lt;table style="border: 1px;" border="1" width="70%" align="center"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;当你需要持久化 Job 时不应使用 &lt;span style="color: #800080;"&gt;RamJobStore&lt;br /&gt;
&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;
&lt;span style="color: #800080;"&gt;RamJobStore&lt;/span&gt; 使用的是非永久性存储器，所有关于 Job 和 Trigger 的信息会在程序关闭之后丢失。保存 Job 到 &lt;span style="color: #800080;"&gt;RamJobStore&lt;/span&gt; 有效的使得它们是易失性的。假如你需要让你的 Job 信息在程序重启之后仍能保留下来，你就该考虑另一种 &lt;span style="color: #800080;"&gt;JobStore&lt;/span&gt; 类型，比如 &lt;span style="color: #800080;"&gt;JobStoreTX&lt;/span&gt; 或者 &lt;span style="color: #800080;"&gt;JobStoreCMT&lt;/span&gt;。它们会在第六章“作业存储与持久化”中讲到。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
Job 易失性的默认值是 &lt;span style="color: #800080;"&gt;false&lt;/span&gt;.</description></item><item><title>Quartz Job Scheduling Framework［翻译］第四章. 部署 Job (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-4-2-2/</link><pubDate>Thu, 27 Dec 2007 07:56:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-4-2-2/</guid><description>
&lt;strong&gt;3. 管理 Scheduler&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
除了启动 Scheduler, 在应用的生命周期中你也许还要执行 Scheduler 的别的一些操作。这些 Scheduler 操作包括查询、设置 Scheduler 为 standby 模式、继续、停止。很多情况下，当一个 Scheduler 启动后，除让它运行之外你不需要对它做任何事情的。在某些情形下，你也可能会要临时的终止 Scheduler 而转入到 standby 模式。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·启动 Scheduler&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
启动一个 Scheduler 也不总是一目了然的。当你有了 Scheduler 的实例，并得到正确的初始化，你的 Job 和 Triiger 也已注册上去了，你只需要简单的调用 &lt;span style="color: #800080;"&gt;start()&lt;/span&gt; 方法：&lt;br/&gt;</description></item><item><title>Quartz Job Scheduling Framework［翻译］第四章. 部署 Job (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-4-1/</link><pubDate>Tue, 25 Dec 2007 09:24:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-4-1/</guid><description>
&lt;strong&gt;第四章. 部署 Job&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
在上一章中，你首次尝试使用了 Quartz 来部署 Job。无可否认地，那些 Job 都不是很复杂，但这个不是重点。你应该轻松的对如何构造并部署 Job 有了相当的了解，更重要的是，由此热情的希望学得更多的东西。在本章中将会继续给你讲述。&lt;br/&gt;&lt;br/&gt;
第四章将带领你深入到 Quart 框架的核心部分。可证明的是，这一章对于阅读和理解本书是非常之重要的。调度器(Scheduler) 是此框架的心脏。本章关注于如何使用 Scheduler 来管理你的 Job；如何创建并关联触发器以使 Job 能被触发执行；以及如可选择 calendar 为给定的时程安排提供更多的灵活性。&lt;br/&gt;&lt;br/&gt;</description></item><item><title>利用 ANT 实现自动化部署管理 WebSphere Application Server 5.x 下的应用</title><link>https://yanbin.blog/ant-auto-deploy-application-was-5-x/</link><pubDate>Wed, 19 Dec 2007 00:49:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ant-auto-deploy-application-was-5-x/</guid><description>
题前说明：本文所做的测试是基于 WAS5.1 的，若是其他 WAS 版，请具体调整，或参考相应版本的红皮书。&lt;/p&gt;
&lt;br/&gt;
WebSphere Application Server (WAS) 确实给我们提供了一个很方便的管理控制台，可以手工很轻松的部署应用程序，管理服务器；有得亦有失，因为它不能像其他很多应用服务器那般拷贝文件的方式进行部署，所以给像 DailyBuild 那样全自动化的过程制造了一些障碍。&lt;br/&gt;&lt;br/&gt;
其实 WAS 也提供了接口（SOAP 和 RMI）可通过脚本来完成对服务器及应用程序的管理，只是使用起来稍显麻烦，还得钻研一番。你可以采用三种途径来使用 WAS 的接口：</description></item><item><title>调优 Linux 及Websphere 整理收集［转］</title><link>https://yanbin.blog/tune-linux-websphere/</link><pubDate>Wed, 12 Dec 2007 08:56:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tune-linux-websphere/</guid><description>
说明：以为转贴，原文 &lt;a href="http://tb.blog.csdn.net/TrackBack.aspx?PostId=1930730"&gt;http://tb.blog.csdn.net/TrackBack.aspx?PostId=1930730&lt;/a&gt;, 因见此文对目前项目帮助甚大，故直接转载文章内容，而不只是收藏，并保留原文格式，感谢作者分享的宝贵经验。&lt;/p&gt;
&lt;br/&gt;
&lt;h2&gt;调优 Linux&lt;/h2&gt;
您可能需要定制 Linux 系统，以提高服务器的性能。 下面，将向您介绍调整配置的技巧。 请牢记，这些系统可能会变化，从而导致这些建议过时，并导致您的结果有所不同。&lt;br/&gt;&lt;br/&gt;
在您为改善性能而进行任何更改之前，请确保已经对当前性能进行了度量。不管您是否关心事务执行速度、响应时间、最大并发用户数或其他一些性能条件，都需要在更改前后，进行足够准确地度量，以了解更改调优参数是否有效。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十章. J2EE 中使用 Quartz (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-10-2/</link><pubDate>Sun, 09 Dec 2007 07:55:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-10-2/</guid><description>
&lt;strong&gt;·在 J2EE 应用服务器中运行 Quartz&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
作为 J2EE 客户端运行 Quartz 比运行为外部 J2SE 应用程序稍显繁琐。这主要是因为部署一个应用到容器中有点了儿复杂，J2EE 规范对容器中的组件会有些约束。其中最大的原则之一就是涉及到该由谁来创建线程。因为 J2EE 容器有责任去管理所有资源，所以它并非允许谁想谁就能创建线程的。假如是这样的话，容器就会要更艰难的去管理环境和保证一切稳定性。Quartz 会创建自己的工作者线程，所以你必须依照一些步骤来保证它能正常的运转。&lt;br/&gt;&lt;br/&gt;
确保代像代码 10.1 那样的一个无状态会话 Bean 已部署到容器中。最简单的部署 Quartz 到容器中的方式是构建一个包含所必须文件的 WAR 包，然后使用管理工具或 Eclipse 部署这个 Web 应用到容器中。</description></item><item><title>JavaRebel 1.0 正式版发布，为应用服务器侦测类的变化[翻译，来自TheServerSide]</title><link>https://yanbin.blog/javarebel-1-0-java-hot-deploy/</link><pubDate>Sat, 08 Dec 2007 01:11:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javarebel-1-0-java-hot-deploy/</guid><description>
ZeroTurnaround 宣布 JavaRebel 1.0 最终正式版发布。&lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt; 通过即时重加载有改变的类，从而避免了应用服务器的重新部署。此次版本与第一个公开发行版加入了以下改进：&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;·简化了安装&lt;/strong&gt;。现在 Java 5 中安装 JavaRebel 只需要加上 "&lt;span style="color: #800080;"&gt;-noverify -javaagent:javarebel.jar&lt;/span&gt;" 到命令行中。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·优化了性能&lt;/strong&gt;。 此次版本关注了启动时间和后台 CPU 的使用率。一些用户报称启动应用服务器的时间比用之前版本快了 2-3 倍。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·改善了兼容性&lt;/strong&gt;。支持所有主流的容器和框架，在其他的之上也可能工作的很好。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·扩展了对 Java 1.4　的支持&lt;/strong&gt;。像 BEA Weblogic 8.X、Oracle OC 4J 9.x/10.X 和 Tomcat 4.x 也被支持。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第十章. J2EE 中使用 Quartz (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-10-1/</link><pubDate>Fri, 07 Dec 2007 11:19:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-10-1/</guid><description>
Java 2 平台企业版定义了基于组件的企业应用开发标准。不管你是否倾向于使用一种开源的 J2EE 服务器，比如 JBoss 或 Geronimo，或者你更希望得到可靠安全的商业服务支持，比如 WebSphere 和 WebLogic，你都能在那些应用服务器中使用几种不同的部署方式使用 Quartz。本章给你示范了在 J2EE 应用服务器中以不同策略部署 Quartz，你也会看到 Quartz 框架更加丰富了 J2EE。&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;一：假如我有 J2EE，为什么还需要 Quartz?&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
自从 20 世纪 90 年代末期 J2EE 首次登上舞台以来，开发人员就被某些规格决议和一些表面看来明显缺失的特征所困惑。这没必要去批判规格的制定者，更多的是说明了当有不同意见和议程的独立团体，在尝试达成统一的优先级时，就像联合国进行决议时那样，并不如所想像的那样好。许多必须的特征获得认可，但是一些关键的特征被略去留待以后加入。其中一个让早期规范遗漏的关键特征就是定时器服务。</description></item><item><title>找到了十多年前只听过一两遍的歌《我踏浪而来》</title><link>https://yanbin.blog/old-song-ten-years-ago/</link><pubDate>Tue, 04 Dec 2007 09:47:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/old-song-ten-years-ago/</guid><description>
&lt;table style="height: 100px;" border="0" width="700" cellspacing="0" cellpadding="0" align="center"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="320"&gt;&lt;table border="1" width="100%" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="font-size: 13px; background-color: whitesmoke;" height="22"&gt;&lt;strong&gt;&lt;br /&gt; &lt;a title="百度查找沈雁的所有歌曲" href="http://mp3.baidu.com/m?f=ms&amp;amp;tn=baidump3&amp;amp;ct=134217728&amp;amp;lf=&amp;amp;rn=&amp;amp;word=%C9%F2%D1%E3&amp;amp;lm=-1"&gt;沈雁&lt;/a&gt;&lt;br /&gt; &lt;a title="百度查找歌曲我踏浪而来" href="http://mp3.baidu.com/m?f=ms&amp;amp;tn=baidump3&amp;amp;ct=134217728&amp;amp;lf=&amp;amp;rn=&amp;amp;word=%CE%D2%CC%A4%C0%CB%B6%F8%C0%B4&amp;amp;lm=-1"&gt;我踏浪而来&lt;/a&gt;&lt;br /&gt; &lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td height="66"&gt;&lt;object width="100%" height="66" classid="clsid:6bf52a52-394a-11d3-b153-00c04f79faa6" codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"&gt;&lt;param name="name" value="Player" /&gt;&lt;param name="url" value="http://www.zuopin3.bxjyw.com/stwebs/chzh/003/MP3/tl.mp3" /&gt;&lt;param name="src" value="http://www.zuopin3.bxjyw.com/stwebs/chzh/003/MP3/tl.mp3" /&gt;&lt;embed type="application/x-mplayer2" width="100%" height="66" src="http://www.zuopin3.bxjyw.com/stwebs/chzh/003/MP3/tl.mp3" url="http://www.zuopin3.bxjyw.com/stwebs/chzh/003/MP3/tl.mp3" name="Player" /&gt;&lt;/object&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top" bgcolor="#ffffff" height="160"&gt;&lt;pre&gt;大海你来自何方&lt;br/&gt;
你又去哪里流浪&lt;br/&gt;
有谁知道你寂寞&lt;br/&gt;
有谁知道你惆怅&lt;br/&gt;
我踏着海浪而来&lt;br/&gt;
引着我也有我的方向&lt;br/&gt;
&lt;br/&gt;
浪花溅湿我衣裳&lt;br/&gt;
洗去我心中哀伤&lt;br/&gt;
看那无垠的大海&lt;br/&gt;
闪烁着是你希望&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="font-size: 13px; background-color: whitesmoke;" height="84"&gt;&lt;span style="color: #0000ff;"&gt;十多年前偶然听过一两遍却不记得名字的歌，唯一深刻的印像是一句歌词：“&lt;span style="color: #ff0000;"&gt;大海你来自河方，有谁知道你寂寞&lt;/span&gt;”，其间也据此在网上找过，未能如愿，今天有幸找来，听来别有一番滋味涌心头。右为歌唱者沈雁。一个年代，一个角度，悠悠山间小溪水的都快不再清澈。&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;td align="right"&gt;&lt;img src="http://unmi.cc/wp-content/uploads/2007/12/shenyan.jpg" alt="" /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</description></item><item><title>明明白白Unsupported major.minor version 49.0的错误</title><link>https://yanbin.blog/unsupported-major-minor-version-49-0-inside/</link><pubDate>Mon, 03 Dec 2007 13:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unsupported-major-minor-version-49-0-inside/</guid><description>
&lt;span style="color: #0000ff;"&gt;&lt;strong&gt;&lt;span style="font-size: medium;"&gt;一：要解决的问题&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
&lt;/span&gt;我们在尝鲜 JDK1.5 的时候，相信不少人遇到过 Unsupported major.minor version 49.0 错误，当时定会茫然不知所措。因为刚开始那会儿，网上与此相关的中文资料还不多，现在好了，网上一找就知道是如何解决，大多会告诉你要使用 JDK 1.4 重新编译。那么至于为什么，那个 major.minor 究竟为何物呢？这就是本篇来讲的内容，以使未错而先知。&lt;br/&gt;&lt;br/&gt;
我觉得我是比较幸运的，因为在遇到那个错误之前已研读过《深入 Java 虚拟机》第二版，英文原书名为《Inside the Java Virtual Machine》( Second Edition)，看时已知晓 major.minor 藏匿于何处，但没有切身体会，待到与 Unsupported major.minor version 49.0 真正会面试，正好是给我验证了一个事实。</description></item><item><title>VC窗口(控件)中显示指定 URL 对应图片</title><link>https://yanbin.blog/vc-show-url-image/</link><pubDate>Sat, 01 Dec 2007 21:04:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-show-url-image/</guid><description>
在上篇 &lt;a href="https://yanbin.blog/vc-ipicture-show-picture/"&gt;VC 中用 IPicture 在窗口中显示图片文件&lt;/a&gt; 讲到的是 VC 窗口(或控件)中显示本地图片文件，本文延伸这一话题，来演示如何显示网上的图片，即提供图片的 HTTP URL 地址，把它显示到 VC 的界面中来。&lt;br/&gt;&lt;br/&gt;
本文参考我原来写的一篇日志 &lt;a href="https://yanbin.blog/vc-cinternetsession-catch-web-page/"&gt;VC中使用CInternetSession抓取网页内容&lt;/a&gt; 来修改上篇代码中用来显示图片的函数：HRESULT ShowPic(&lt;span class="datatypes"&gt;char&lt;/span&gt; *lpstrFile,&lt;span class="datatypes"&gt;HWND&lt;/span&gt; hWnd)。把研究过的东西整理记下来总是好处多多，瞧，至少现在展开其他话题，可参考时便能信手拈来。&lt;br /&gt;
&lt;br /&gt;
重新实现的函数 HRESULT ShowPic(&lt;span class="datatypes"&gt;char&lt;/span&gt; *lpstrFile,&lt;span class="datatypes"&gt;HWND&lt;/span&gt; hWnd) 代码如下：</description></item><item><title>VC 中用 IPicture 在窗口中显示图片文件</title><link>https://yanbin.blog/vc-ipicture-show-picture/</link><pubDate>Fri, 30 Nov 2007 12:51:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-ipicture-show-picture/</guid><description>
想做个东西，用VC，希望是在程序界面上能把指定 URL 的图片显示出来，还没找到如何显示 HTTP 资源的图片，先还是让本地图片能在 VC 的窗口(包括各种控件)中显示出来吧。在网上搜索了一番，大家代码都如出一辙，我也不能生搬硬载，不由又亲手实践了一下，并连同原来学过的 Window SDK 做窗口程序也复习了一遍，没有使用到 MFC，只需存成单个源文件，甚至用 CL 命令来编译都无须打开 VC 的，在 VC6 中测试通过。&lt;br /&gt;
&lt;br /&gt;
效果图如下：</description></item><item><title>重拾久矣的 Perl 语言，力求扎实掌握这门动态脚本语言</title><link>https://yanbin.blog/perl-and-perl/</link><pubDate>Thu, 29 Nov 2007 08:46:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/perl-and-perl/</guid><description>
曾经也因项目中一时之用接触过好几种动态脚本语言，但无一不是忘得几无痕迹。PHP做过几个项目、TCL/TK 是制作 IPMI 驱动安装程序之用，它有较小的解释器，并具带着桌面工具集 TK，做界面程序也方便。&lt;/p&gt;
&lt;br/&gt;
提起 Perl，先前对它是有过两次的亲密接触，一次为自我修练，第二次为实现一个 Java 版的 Webmin 时，大量的参考了 Webmin 的 Perl 代码，最强烈的印象是彻底为它的正则表达式写法所征服了，从此对它也是藕断丝连。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第九章. 使用 Quartz 的远程方式 (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-9-3/</link><pubDate>Mon, 26 Nov 2007 10:50:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-9-3/</guid><description>
&lt;strong&gt;6. 创建 RMI 客户端&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
你需要创建一个客户端，用来调用远程 Quartz 调度器上的方法。客户端会同 RMI 注册服务器进行通信，进而定位到远程调度器对象，然后就能够调用其上的方法了。这些方法包括有暂停和停止调度器、部署和卸下 Job，和执行所有其他对与远程客户端可见的方法。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·配置 Quartz RMI 客户端&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
类似于表 9.1 所示服务端的配置，表 9.2 所列出的属性也是必须加到 Quartz RMI 客户端的。这两份属性列表必须分别应用到服务端和客户端的。</description></item><item><title>Windows 下也能创建硬链接和符号(软)链接</title><link>https://yanbin.blog/windows-create-soft-link/</link><pubDate>Sun, 25 Nov 2007 11:04:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-create-soft-link/</guid><description>
首先简单理解一下硬链接和符号(软)链接的区别(&lt;span style="color: #800080;"&gt;此文中的符号链接和软链接指同一概念&lt;/span&gt;)：&lt;br/&gt;
&lt;br/&gt;
硬连接指向的是节点(inode)，而软连接指向的是路径(path) 。&lt;br/&gt;
&lt;br/&gt;
最初的文件名与所有的硬链接地位是对等的，比如为文件 a 建立了硬链接 b、c、d。那么a、b、c、d之中只要有一个文件未删除，这个文件就可通未删除的名称访问的。你也可以认为每个文件都可认为至少有一个硬链接，就是说 a 也是一个硬链接。&lt;br/&gt;
&lt;br/&gt;
软链接特性上有些类似于快捷方式，比如为原文件 a 建立了软链接 b、c、d。删除b、c 或 d 访问到 a，但是只要删除了 a，软链接就不可用了。但是 windows 下的快捷方式只能在资源管理器中有用，它只是一个 lnk 文件，如果是一个目录的快捷方式，它是不能通过 cd 命令或路径进入。</description></item><item><title>［钱钟书系列01］［孙郁］从方鸿渐看钱钟书--看中国的文化与教育</title><link>https://yanbin.blog/fortress-besieged-says/</link><pubDate>Sat, 24 Nov 2007 01:39:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/fortress-besieged-says/</guid><description>
&lt;strong&gt;主讲人简介：&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
　　孙 郁：1957年出生于大连，毕业于沈阳师范学院，硕士研究生。曾任《北京日报》文艺周刊主编，现为鲁迅博物馆副馆长，党组书记。主要著作有《鲁迅与周作人》、《鲁迅与胡适》、《百年苦梦》等。&lt;br/&gt;
&lt;br/&gt;
　　&lt;strong&gt;内容简介：&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
　　《围城》是钱钟书先生1947年写就的一部以爱情、婚姻为主题的长篇小说，取意为“婚姻就像一座围城，城外的人想进来，城里的人想出去”。《围城》自1980年再版以来，赢得了读者的广泛赞誉，文中那些精妙绝伦的比喻和幽默辛辣的讽刺耐人寻味。小说通过主人公方鸿渐的爱情婚姻遭遇和生存境遇深刻刻画了某些旧中国知识分子的基本根性，他们的懦弱、虚荣、自私、功利以及欺骗性被钱钟书一一嘲讽。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第九章. 使用 Quartz 的远程方式 (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-9-2/</link><pubDate>Thu, 22 Nov 2007 10:13:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-9-2/</guid><description>
&lt;strong&gt;4. 创建 Quartz RMI 服务端&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
你务必按几个步骤来配置 Quartz 来使用 RMI。其中的一些步骤会在创建 Quartz RMI 服务端用到，还有些步骤会在 Quartz 客户端连接服务端。我们先来阐述服务端的配置步骤。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·配置 Quartz RMI 服务端&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
第一步就是修改要部署到 Quartz RMI 服务端的 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 文件。当在 Quartz 中使用 RMI，你还必须添加几个新的属性。表 9.1 包括了完整 RMI 属性列表。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第九章. 使用 Quartz 的远程方式 (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-9-1/</link><pubDate>Wed, 21 Nov 2007 10:20:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-9-1/</guid><description>
&lt;strong&gt;第九章. 使用 Quartz 的远程方式&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
以独立方式运行的 Quartz 应用程序，仅限于在 JVM 内部访问调度器。和其他任何 J2SE 程序一样，不使用其他某种机制的话，是决不允许从外部访问 JVM 中的对象的。&lt;br/&gt;&lt;br/&gt;
幸运的是，有几种技术(机制) 可让你做到这一点。Quartz 框架很好的支持其中一种机制－－远程方法调用(RMI)。本章就是关注于如何部署 Quartz 为一个 RMI 服务，以便于你能够从  JVM 外部访问到调度器。这样做有几个好处，这也是我们本章要讨论内容。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第三章. Hello Quartz (第四部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-3-4/</link><pubDate>Sat, 17 Nov 2007 08:16:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-3-4/</guid><description>
&lt;strong&gt;4. 打包 Quartz 应用程序&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
让我们最后简单讨论打包一个用到了 Quarts 框架的应用程序的流程，也以此来结束本章的内容。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·Quartz 第三方依赖包&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
从 1.5 版的发行包开始，你会看到一个 &lt;span style="color: #800080;"&gt;&amp;lt;QUARTZ_HOME&amp;gt;\lib&lt;/span&gt; 目录，在这个目录，你会发现几个子目录：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;    ·&amp;lt;QUARTZ_HOME&amp;gt;\lib\core    ·&amp;lt;QUARTZ_HOME&amp;gt;\lib\optional&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #800080;"&gt;&lt;span style="color: #800080;"&gt;    ·&amp;lt;QUARTZ_HOME&amp;gt;\lib\build</description></item><item><title>Quartz Job Scheduling Framework［翻译］第三章. Hello Quartz (第三部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-3/</link><pubDate>Sat, 17 Nov 2007 02:25:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-3/</guid><description>
&lt;strong&gt;3. 声明式部署一个 Job&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
前面我们讨论过，尽可能的用声明式处理软件配置，其次才才虑编程式。再来看代码  3.6，如果我们要在 Job 启动之后改变它的执行时间和频度，必须去修改源代码重新编译。这种方式只适用于小的例子程序，但是对于一个大且复杂的系统，这就成了一个问题了。因此，假如能以声明式部署 Quart Job 时，并且也是需求允许的情况下，你应该每次都选择这种方式。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;·配置 quartz.properties 文件&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
文件 &lt;span style="color: #800080;"&gt;quartz.properties&lt;/span&gt; 定义了 Quartz 应用运行时行为，还包含了许多能控制 Quartz 运转的属性。本章只会讲到它的基本配置；更多的高级设置将在以后讨论。在现阶段也不用太深入到每一项配置有效值的细节。</description></item><item><title>重定向System.out和System.err到JTextPane,分别用黑色红色显示(改进)</title><link>https://yanbin.blog/system-out-system-err-to-jtextpane-improved/</link><pubDate>Thu, 15 Nov 2007 10:41:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/system-out-system-err-to-jtextpane-improved/</guid><description>
在上一篇 &lt;a href="https://yanbin.blog/system-out-system-err-jtextpane/"&gt;重定向System.out和System.err到JTextPane,分别用黑色红色显示&lt;/a&gt;
 中讲了如何把 System.out 和 System.err 重定向到 JTextPane 上，并分不同颜色显示，其中用到了 PipedInputStream、
PipedOutputStream。那个例子还是参考的 《&lt;a href="http://www.exampledepot.com/?l=ex"&gt;The Java Developers Almanac 1.4&lt;/a&gt;》，
翻译出来叫做 《Java 开发者年鉴 1.4》，显得多么的权威啊，我当时还真把它看成官方最佳推荐实现了，太迷信了。&lt;/p&gt;
&lt;br/&gt;
可是现在看来，前面那个实现不仅代码繁琐，而且是 Bug 多多。现在重新对上回的 ConsolePane 来个新的更简洁高效的实现。代码如下：</description></item><item><title>Quartz Job Scheduling Framework［翻译］第三章. Hello Quartz (第二部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-3-2/</link><pubDate>Sun, 11 Nov 2007 07:33:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-3-2/</guid><description>
&lt;strong&gt;2. 调度 &lt;span style="color: #800080;"&gt;Quartz ScanDirectoryJob&lt;/span&gt;&lt;br /&gt;
&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
到目前为止，我们已经创建了一个 Quartz job，但还没有决定怎么处置它－－明显地，我们需以某种方式为这个 Job 设置一个运行时间表。时间表可以是一次性的事件，或者我们可能会安装它在除周日之外的每个午夜执行。你即刻将会看到，Quartz Schduler 是框架的心脏与灵魂。所有的 Job 都通过 Schduler 注册；必要时，Scheduler 也会创建 Job 类的实例，并执行实例的 &lt;span style="color: #800080;"&gt;execute()&lt;/span&gt; 方法。</description></item><item><title>重定向System.out和System.err到JTextPane,分别用黑色红色显示</title><link>https://yanbin.blog/system-out-system-err-jtextpane/</link><pubDate>Sat, 10 Nov 2007 07:52:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/system-out-system-err-jtextpane/</guid><description>
把 System.out 和 System.err 重定向到 JTextArea 的做法在网上能找到不少，由于 JTextArea 不能用不同的字体分别显示内容。但我还是希望能象 Eclipse 控制台那样，标准输出为黑色，错误信息为红色，于是选择了 JTextPane 作为输出目的地。线程之间通信息用到了 PipedInputStream 、PipedOutputStream 和　SwingUtilities.invokeLater(new Runnable()。  &lt;/p&gt;
&lt;br/&gt;
自定义了一个 JScrollPane，类名为 ConsolePane，写成的单例类；使用时只需要在你的面板上加上 ConsolePane组件，例如：&lt;span style="color: #ff3366;"&gt;&lt;strong&gt;getContentPane().add(ConsolePane.getInstance(), BorderLayout.CENTER);&lt;/strong&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
界面截图(黑色和红色分别显示 System.out 和 System.err 定向的输出内容)：</description></item><item><title>Eclipse中执行Ant出现Could not find the main class的问题及解</title><link>https://yanbin.blog/eclipse-ant-could-not-find-the-main-class/</link><pubDate>Thu, 08 Nov 2007 23:01:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/eclipse-ant-could-not-find-the-main-class/</guid><description>
这种情况通常是发生在换了另一份 Eclipse 拷贝之后。之前一般都能正常在 Eclipse 中执行 Ant 脚本，删除了原来的 Eclipse 之后换了另一个拷贝或者是新版本，在其中执行 Ant 脚本时弹出窗口出现以下错误：&lt;br/&gt;
&lt;br/&gt;
---------------------------&lt;br/&gt;
Java Virtual Machine Launcher&lt;br/&gt;
---------------------------&lt;br/&gt;
Could not find the main class. Program will exit.&lt;br/&gt;
---------------------------&lt;br/&gt;
确定  &lt;br/&gt;
---------------------------&lt;br/&gt;
&lt;br/&gt;
这种错误对用过 Java 的人来说还是很明白，找不到 Ant 的主类吗。不过说实话也困扰过我一段时间，也没去深究。说开了，症结就是 Eclipse 中的 ANT_HOME 指向不对。</description></item><item><title>对StrutsConfigHelper工具的小小升级－－增加代码着色显示功能</title><link>https://yanbin.blog/strutsconfighelper-improved/</link><pubDate>Tue, 06 Nov 2007 10:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/strutsconfighelper-improved/</guid><description>
StrutsConfigHelper 小工具方便你对一个或多个 StrutsConfig 文件按条件查找各要素。
在 &lt;a id="viewpost1_TitleUrl" href="https://yanbin.blog/strutsconfig-seach-tool/"&gt;《忙里找闲，自写了一个StrutsConfig配置文件条件查找工具》&lt;/a&gt; 一文章有对该工具的介绍。&lt;br/&gt;&lt;br/&gt;
软件为适宜之作，如果它确实对你毫无用处，你大可当它是垃圾，不因作者付出很大的苦劳，而勉强去接受；也许它还能给你带来些便利，那就体现了它的价值。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第三章. Hello Quartz (第一部分)</title><link>https://yanbin.blog/quartz-job-scheduling-framework-3-1/</link><pubDate>Fri, 02 Nov 2007 12:54:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-3-1/</guid><description>
[译者注：后面的章由于每章的内容较多，每章聚于一篇之中，过于臃肿，屏幕不比书本，三屏之后的内容一般不为读者乐意去阅读，此为第一部分]&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;第三章：Hello Quartz&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
多数读者都较容易从一个简明扼要的例子中明白一个东西。作为写作者，要注意避免把一章的内容精简地几乎什么都没了；作为读者呢，需要有耐心并且要进一步相信其后相关的章节应该去阅读，尽管这个例子看起来是如此之简单。&lt;br/&gt;&lt;br/&gt;
有了这种初衷，这一章将为你介绍如何用 Quartz 框架创建一个简单的应用程序，它展示了一个典型的应用。这个例子将让你领略到创建和执行一个简单应用的必要步骤。通过本章的学习，为你学习本书的后续章节打下了坚实的基础。 &lt;br/&gt;&lt;br/&gt;</description></item><item><title>忙里找闲，自写了一个StrutsConfig配置文件条件查找工具</title><link>https://yanbin.blog/strutsconfig-seach-tool/</link><pubDate>Tue, 30 Oct 2007 13:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/strutsconfig-seach-tool/</guid><description>
工作中有维护 Struts 项目，或看别人项目中代码时，要把访问页面链接与相应处理 Action 类及 FormBean等相关信息对应起来，每每都是打开 Struts 配置文件，Ctrl+F 进行查找，不易准确定位，尤其是项目中有多个 Struts 配置文件时就得这边倒来那边转去的，实为不便。于是乎就为自己写了一个可同时在多个 Struts 配置文件中根据不同条件查找配置信息的小工具，谓之：Struts Config Helper。&lt;/p&gt;
&lt;br/&gt;
这段时间工作上比较的忙，闲暇之间，一点一滴缓慢缀掇而成。也不知对于各位同路人是否有可借鉴之处，张贴出来，希望得到各位的指摘，多给我提些 Bug 出来，不胜感激！</description></item><item><title>Quartz Job Scheduling Framework［翻译］第二章. Quartz 起步</title><link>https://yanbin.blog/quartz-job-scheduling-framework-2/</link><pubDate>Sat, 20 Oct 2007 11:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-2/</guid><description>
&lt;strong&gt;第二章. Quartz 起步&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
本章对 Quartz 框架一个快速的入门介绍，同时也大略指导你从哪里下载，构建和安装这个框架&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;1. Quartz 框架的发展历程&lt;br /&gt;
&lt;/strong&gt;&lt;br /&gt;
和现今许多在用的开源项目一样，Quartz之初也只是为个人开发者提供了一个简单的实现方案。但是随着日益增多的关键人员的积极参与和慷慨的贡献，Quartz 已经成为了一个为众人所知，并且能帮助人们解决更大问题的框架。&lt;br/&gt;&lt;br/&gt;
Quartz 项目 是由 James House 创立的，它在1998年就有该框架最初的构思。包括作业队列的概念，使用线程池来处理作业，也许它最早的模型已不为现今的Quartz使用者所知了。</description></item><item><title>Quartz Job Scheduling Framework［翻译］第一章. 企业应用中的作业调度</title><link>https://yanbin.blog/quartz-job-scheduling-framework-1/</link><pubDate>Tue, 16 Oct 2007 13:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/quartz-job-scheduling-framework-1/</guid><description>
&lt;strong&gt;第一章.  企业应用中的作业调度&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
    ·什么是作业调度&lt;br/&gt;&lt;br/&gt;
    ·作业调度为什么说是重要的&lt;br/&gt;&lt;br/&gt;
    ·企业应用中的作业调度&lt;br/&gt;&lt;br/&gt;
    ·非企业应用中的作业调度&lt;br/&gt;&lt;br/&gt;
    ·作业调度与工作流&lt;br/&gt;&lt;br/&gt;
    ·关于作业调度其他可选择方案</description></item><item><title>命令行使用PuTTY的几个参数[转]</title><link>https://yanbin.blog/putty-parameters/</link><pubDate>Mon, 15 Oct 2007 01:28:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/putty-parameters/</guid><description>
使用ssh的朋友应该会知道这个工具，小巧，不需要安装，功能也不弱，是一个理想的ssh工具。&lt;br/&gt;
&lt;br/&gt;
在使用中，需要将PuTTY放到批处理文件中调用，自动登录上服务器，然后运行命令，退出。google，百度搜索了都没有相关的中文资料，于是直接看英文文档，使用到的几个参数做一个说明，抛砖引玉。欢迎补充转载，请注明出处。&lt;br/&gt;
&lt;br/&gt;
命令行格式&lt;br/&gt;
putty [-pw password] [-m file] user@ip_addr&lt;br/&gt;
&lt;br/&gt;
-pw password 使用ssh登录的用户的密码（明文，所以安全性请自己考虑）&lt;br/&gt;
-m file 要在服务器上运行的命令&lt;br/&gt;
user@ip_addr 使用ssh登录的用户名和服务器的ip地址</description></item><item><title>一个顶好用的 JavaDoc Api 查看软件 JavaDOCHelper</title><link>https://yanbin.blog/javadoc-html-view-javadochelper/</link><pubDate>Sun, 07 Oct 2007 15:40:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javadoc-html-view-javadochelper/</guid><description>
一般我们查阅 JDK API 都是用的 CHM 版的帮助文件，记得想想刚接触 Java 那时要在网上找个 CHM 版的 JDK 帮助文档都要费几番周折，甚至还经常在线查某个 AP I说明。现在好了，连中文的 JDK API 帮助都能及时出来，要找个 CHM 版的帮助也方便多了。&lt;br/&gt;
&lt;br/&gt;
可是对于大多数的 Java 组件都只提供 HTML 版的 JavaDoc API 帮助，查阅起来很不顺手。没有快速索引查找的功能，而且看不同组件的 API 帮助需要同时打开多个窗口。&lt;br/&gt;
&lt;br/&gt;
下面向大家介绍一款软件，JavaDOCHelper，它不仅能提供像 CHM 文件那般快速索引查找，而且提供一个界面里查阅多个 Java Doc API。该软件未注册版本，只限 30 次的查找功能的使用（不是指索引），现已对该软件完成了破解，并提供更换安装目录时更方便的提示功能，以及免安装，EXE 文件无控制台方式启动。</description></item><item><title>Java中类C/C++的sizeof()操作，知晓实例大小</title><link>https://yanbin.blog/java-sizeof/</link><pubDate>Fri, 28 Sep 2007 10:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-sizeof/</guid><description>
在C/C++中有sizeof()操作，可轻易获知某个类型或实例占用内存大小，sizeof(int) 或者 sizeof(new TestClass)。可是Java中可没有这么直观的方法可用。&lt;br/&gt;
&lt;br/&gt;
因本人看过不少人写代码总爱写成&lt;br/&gt;
&lt;br/&gt;
List userList = new ArrayList();   //注：声明时即初始化一个空 ArrayList&lt;br/&gt;
userList = userDao.getAllUsers();  //注：方法getAllUsers()中会生成一个ArrayList的&lt;br/&gt;
&lt;br/&gt;
上面就造成平白多了一个空的 ArrayList(),创建完后即刻就推向GC处置，我就在想这样一个空的 ArrayList 会占用多少内存，于是找来了 Optimizeit 观察后发现一个空的 ArrayList 要占去 24b 内存。那 Java 中有没有更便的捷的方法呢，于是在网上 google "java sizeof"，引出不少话题。</description></item><item><title>对iTracker的小修正，使之正确显示描述中的空格（解决代码退格显示）</title><link>https://yanbin.blog/itracker-show-blank/</link><pubDate>Thu, 27 Sep 2007 10:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/itracker-show-blank/</guid><description>
ITracker 是基于 J2EE 的 issue/bug 跟踪系统，支持多用户、多项目、邮件通知等，更多特征请查看&lt;a href="http://www.cowsultants.com/features.html"&gt;http://www.cowsultants.com/features.html&lt;/a&gt; 。这里还有一个在线 &lt;a href="http://www.cowsultants.com:8080/itracker"&gt;demo&lt;/a&gt; 可以试用，用户名和密码都是 guest。&lt;/p&gt;
&lt;br/&gt;
类似的产品还有 &lt;a href="http://scarab.tigris.org/"&gt;Scarab&lt;/a&gt;、&lt;a href="http://opinionplugin.sourceforge.net/begin.html"&gt;ELIPS&lt;/a&gt;、&lt;a href="http://sourceforge.net/projects/bugtracker/"&gt;Bugtracker&lt;/a&gt;、&lt;a href="http://www.gjt.org/pkg/bugrat/"&gt;BugRat&lt;/a&gt;、&lt;a href="http://abuky.sunsite.dk/index.html"&gt;Abuky&lt;/a&gt;、&lt;a href="http://trackit.sourceforge.net/"&gt;TrackIt&lt;/a&gt;、&lt;a href="https://jtrac.dev.java.net/"&gt;jTrac&lt;/a&gt;、&lt;a href="http://www.bugzilla.org/"&gt;Bugzilla&lt;/a&gt;、&lt;a href="http://jtrac.info/"&gt;JTrac&lt;/a&gt;、&lt;a href="http://pdune.sourceforge.net/"&gt;Project Dune&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;
iTracker确实是个好东西，但对一个东西过熟了，难免期望值就更高了。如果你用 iTracker 作过 Code Review 的话，你就会发现，你贴上去的代码，浏览显示出来的是代码中的空格少了，尤其是没了退格规范显示了。例如，你贴的代码是：</description></item><item><title>利用JDOM把两个XML合并生得到一个Document对象</title><link>https://yanbin.blog/jdom-combine-xml-one-document/</link><pubDate>Sun, 23 Sep 2007 03:58:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jdom-combine-xml-one-document/</guid><description>
通常一些应用框架都会用 XML 作为配置，而且很多都支持多个 XML 文件，例如 Struts 框架可以配置多个 struts-config-xxx.xml 文件，Spring
也允许你用多个 applicationContext-xxx.xml 文件，再比如 DWR 也是可以由多个 dwr-xxx.xml 依功能或其他方式分开来配置。我们知道，
这样的多个 XML 有相同的规范定义，那么程序如何一并解析它们呢？我看过 ActionServlet 是对 struts-config-xxx.xml 逐个解析的。
我这里介绍的一种方法是把那些有着相同规范定义的 XML 合成一个 Document 然后对这个 Document 对象进行处理，如 XPath 查找、进行 DOM 对象操作，
就不需要每次到多个 Document 中去查找一遍。&lt;/p&gt;
&lt;br/&gt;
众所周知，对 XML 的操作有两种方式，DOM：XML 映射在内存中一颗树；SAX：基于事件的方式。常用的 XML Java 解析组件有 DOM4J(Apache的)、JDOM、和JAXP(Sun的)，它们都提供了 DOM 和 SAX 实现和 Xpath 查找。</description></item><item><title>介绍几款所见即所得的WEB在线编辑器[转]</title><link>https://yanbin.blog/several-web-online-editors/</link><pubDate>Wed, 19 Sep 2007 00:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/several-web-online-editors/</guid><description>
转自：&lt;a href="http://homepage.yesky.com/62/2489062.shtml"&gt;http://homepage.yesky.com/62/2489062.shtml&lt;/a&gt;　作者： 猴年马月&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;1.FCKeditor 编辑器&lt;/span&gt;&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
FCKeditor is compatible with most internet browsers which include: IE 5.5+ (Windows), Firefox 1.0+, Mozilla 1.3+ and Netscape 7+.&lt;br/&gt;
&lt;br/&gt;
最新版本:FCKeditor 2.0&lt;br/&gt;
&lt;br/&gt;
语言环境:多国语言&lt;br/&gt;
&lt;br/&gt;
特性功能:所见所得,支持平台众多,支持XHTML 1.0,文本格式设置,常见的编辑,复制,粘贴,撤销,文件上传等功能</description></item><item><title>结合Apache和Tomcat实现集群和负载均衡</title><link>https://yanbin.blog/apache-tomcat-cluster-load-balance/</link><pubDate>Tue, 18 Sep 2007 10:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-tomcat-cluster-load-balance/</guid><description>
&lt;div&gt;&lt;span id="diary_group_textDIV" style="line-height: 150%;"&gt;&lt;br /&gt;
&lt;span id="diary_group_textDIV" style="line-height: 150%;"&gt;本文基本参考自 &lt;a href="http://hi.baidu.com/luodaijun/blog/item/5bbe4cfb5ffef864034f56a1.html"&gt;轻松实现Apache,Tomcat集群和负载均衡&lt;/a&gt;,经由实操经历记录而成，碰到些出入，以及个别地方依据个人的习惯，所以在一定程度上未能保持原文的完整性，还望原著者海涵。 &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;span id="diary_group_textDIV" style="line-height: 150%;"&gt;&lt;span id="diary_group_textDIV" style="line-height: 150%;"&gt;因原文中有较多的贴图，如若各位读者一时不想亲自动手而直想看到配置效果，可查看原文。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;一：软件环境&lt;br /&gt;&lt;br/&gt;
&lt;/span&gt;&lt;/strong&gt;   1. Apache: apache 2.0.55 (由&lt;a href="http://httpd.apache.org/"&gt;http://httpd.apache.org/&lt;/a&gt;进入下载)(&lt;a href="http://archive.apache.org/dist/httpd/binaries/win32/apache_2.0.55-win32-x86-no_ssl.msi"&gt;点击下载apache 2.0.55&lt;/a&gt;)&lt;br /&gt;
   2. Tomcat: Tomcat 5.5.25 (由&lt;a href="http://tomcat.apache.org/"&gt;http://tomcat.apache.org/&lt;/a&gt;进入下载)(&lt;a href="http://apache.mirror.phpchina.com/tomcat/tomcat-5/v5.5.25/bin/apache-tomcat-5.5.25.zip"&gt;点击下载Tomcat 5.5.25 zip版&lt;/a&gt;)&lt;br /&gt;
   3. mod_jk: 在页面 &lt;a href="http://tomcat.apache.org/"&gt;http://tomcat.apache.org/&lt;/a&gt;   Download 标题下找到 Tomcat Connectors 链接进入( &lt;a href="http://archive.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/win32/jk-1.2.15/mod_jk-apache-2.0.55.so"&gt;点击下载mod_jk-apache-2.0.55.so&lt;/a&gt;)，看起来像是个Unix/Linux下的动态库，实际应是个Win32 的 DLL 动态库，大概是为保持不同平台配置的一致性，才用了这个扩展名。</description></item><item><title>利用 JWhich 掌握类路径,确定类路径中的什么类将被载入</title><link>https://yanbin.blog/jwhich-class-where-to-load/</link><pubDate>Mon, 10 Sep 2007 09:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jwhich-class-where-to-load/</guid><description>
下面是我 2002 年刚开始学 Java，因不太理解 Classpath，常被它困扰时看的一篇文章，拿出来与初学者共勉。&lt;/p&gt;
&lt;br/&gt;
作者 Mike Clark&lt;br/&gt;&lt;br/&gt;
摘要&lt;br /&gt;
尽管 Java 类路径看上去是个很简单的概念，但它也经常是困惑和麻烦的源泉。本文将向您展示一个简单的工具，它可以清楚地确定类装载器从您的类路径中载入了什么 Java 类。（1,000 字）&lt;br /&gt;
开发人员在处理 Java 类路径时经常会遇到一些尴尬：他们不总是很清楚类装载器将要载入什么类，尤其是在应用程序的类路径被大量的路径和文件充斥的情况下更是如此。在本文中，我将介绍一个工具，它可以显示被载入的类文件的绝对路径。</description></item><item><title>一键复制弹出窗口中的文字内容</title><link>https://yanbin.blog/one-key-copy-msgbox-words/</link><pubDate>Sun, 02 Sep 2007 10:55:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/one-key-copy-msgbox-words/</guid><description>
我们用 Windows 时都遇到过系统报错，在不知道问题如何解决时一般都会去 Google、百度，根据弹出窗口中的报错内容搜索解决方法，通常都是一字一句键入，很是麻烦，其实有种方法是最简单的，就是按下 "Ctrl + C" 快捷键，窗口中的文字内容就被复制到系统的剪贴板上了：）&lt;br/&gt;
&lt;br/&gt;
如图，实际操作是打开记事本，其中随便输入内容，点击关闭就会弹出对话框来，只要按下 Ctrl + C，弹出窗口的内容就进入剪切板了，不信的话就立即关闭那个弹出窗口，在记事本中 Ctrl + V 就能体验到了：</description></item><item><title>在 Java 中操作 Windows 注册表</title><link>https://yanbin.blog/java-access-windows-registry/</link><pubDate>Sun, 02 Sep 2007 08:41:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-access-windows-registry/</guid><description>
想做个东西，要获IE的代理设置，看网上介绍基本都是读取注册表的方式，没提到说借助于特定的 Win32 API。而 JDK 提供操作 Windows 的 API 也就是 Preferences，因为这个 API 也是跨平台的，所功能比较弱，在 Win32 下只能用来操作 HKCU\Software\JavaSoft 和 HKLM\Software\JavaSoft 下及子节点的数据。&lt;/p&gt;
&lt;br/&gt;
自由访问注册表其他键的值光用 Java 是做不到的，必然方案就是 JNI，一开始也自己来实现这个 JNI 动态库，后来懒了一下，想着网上应该用现成的实现，Google 了一下，果然不出所望，就是 &lt;a href="http://www.trustice.com/java/jnireg/index.shtml"&gt;http://www.trustice.com/java/jnireg/index.shtml&lt;/a&gt; 下的 &lt;a href="http://www.gjt.org/download/time/java/jnireg/registry-3.1.3.zip"&gt;registry-3.1.3.zip&lt;/a&gt;(包含源代码)。可以利用它访问、修改、导出注册表项到文件等。解开 registry-3.1.3.zip，在 bin 目录中可以看到两个文件 ICE_JNIRegistry.dll 和 registry.jar，动态库就是本地代码实现。&lt;br/&gt;</description></item><item><title>java版本的escape和unescape函数</title><link>https://yanbin.blog/java-escape-unescape/</link><pubDate>Thu, 23 Aug 2007 00:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-escape-unescape/</guid><description>
有时候我们在处理页面提交过来的中文产生乱码不容易解决时，比如页面选择了别的编码，而 AJAX 是用的 UTF-8 字符集，我们可以对要发送到服务器的中文用 Javascript 的 escape 函数进行编码，然而 Java 中又没有相应的 unescape 函数。&lt;/p&gt;
&lt;br/&gt;
而且 Java 中的 java.net.URLDecoder/java.net.URLEncoder 也对应不上 javascript 的 encodeURI/decodeURI 和 encodeURIComponent/decodeURIComponent 函数。&lt;br/&gt;&lt;br/&gt;
所以我去网上找来了一段能够与 Javascript 的 escape/unescape 函数的代码。</description></item><item><title>Spring 整合 Hibernate 的一处简化配置［转］</title><link>https://yanbin.blog/spring-integrate-hibernate-simple/</link><pubDate>Mon, 20 Aug 2007 03:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-integrate-hibernate-simple/</guid><description>
在过去使用 Spring 整合 Hibernate 的时候，都是用这样的配置方式。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;bean&lt;/span&gt; &lt;span class="na"&gt;id =&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sessionFactory&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;lazy-init =&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;class =&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;org.springframework.orm.hibernate3.LocalSessionFactoryBean&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name =&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mappingResources&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&lt;/span&gt;resources/icustomer/Contact.hbm.xml&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&lt;/span&gt;resources/icustomer/Customer.hbm.xml&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; ...................
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>jsp:include page="" 和 include file="" 的区别</title><link>https://yanbin.blog/jsp-include-page-include-file/</link><pubDate>Fri, 10 Aug 2007 05:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jsp-include-page-include-file/</guid><description>
&lt;p&gt;下面用三个实验，更清淅的理解 &amp;lt;jsp:include page=""/&amp;gt; 和 &amp;lt;%@ include file="" %&amp;gt; 的区别。&lt;br /&gt;&lt;br/&gt;
&amp;lt;jsp:include page=""/&amp;gt; 包含被 include 文件的输出&lt;br /&gt;
&amp;lt;%@ include file="" %&amp;gt; 包含被 include 文件的代码&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;a.jsp&lt;/strong&gt; 内容：&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;jsp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b.jsp&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;strong&gt;b.jsp&lt;/strong&gt; 内容：(空)&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;_a.class&lt;/strong&gt; a.jsp编译成类的内容:</description></item><item><title>Hibernate 保存对象出现 org.hibernate.NonUniqueObjectException 及解决</title><link>https://yanbin.blog/hibernate-save-hibernate-nonuniqueobjectexception/</link><pubDate>Thu, 09 Aug 2007 11:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-save-hibernate-nonuniqueobjectexception/</guid><description>
Hibernate save 在 session 中已存在相同 OID(主键) 的对象，会出现异常，详细内容如下：&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #ff0000;"&gt;Exception in thread "main" &lt;span style="color: #3333ff;"&gt;org.hibernate.NonUniqueObjectException&lt;/span&gt;: a different object with the same identifier value was already associated with the session: [com.unmi.LoanDetail#1]&lt;br /&gt;
 at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:168)&lt;br /&gt;
 at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)&lt;br /&gt;
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)&lt;br /&gt;
 at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)&lt;br /&gt;
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient</description></item><item><title>Win32窗口程序生命周期思维导图</title><link>https://yanbin.blog/mind-map-of-win32/</link><pubDate>Sat, 28 Jul 2007 00:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/mind-map-of-win32/</guid><description>
看了Win32窗口程序的初始化及至结束过程，还是画一个思给导图清淅表述一下，
&lt;div style="text-align: center; margin-top: 20px;"&gt;
&lt;strong&gt;&lt;span style="color: #ff0000; font-size: small;"&gt;
&lt;a href="
/posts/2007/mind-map-of-win32/window_lifecycle.jpg
" target="_blank"&gt;点击看大图&lt;/a&gt;&lt;br/&gt;
&lt;/span&gt;&lt;/strong&gt;
&lt;/div&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2007/mind-map-of-win32/window_lifecycle.jpg"
width="755px"
/&gt;</description></item><item><title>概念图和思维导图二者概念区分的学习[转]</title><link>https://yanbin.blog/concepte-mind-map/</link><pubDate>Fri, 27 Jul 2007 11:34:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/concepte-mind-map/</guid><description>
概念图和思维导图是在欧美国家比较盛行的一种教学形式，也是一种有效的知识表示工具。二者在学习和工作中应用会提高效率。目前在我国还处于研究阶段，对二者的概念界定还在进行中；对于一线教育工作者，他们更关心的是如何更有效的运用二者实施教学。&lt;br/&gt;
&lt;br/&gt;
概念图（Concept Map）是康乃尔大学的诺瓦克（J.D.Novak）博士根据奥苏贝尔（David P.Ausubel）的有意义学习理论提出的一种教学技术。他认为，“概念图是用来组织和表征知识的工具。它通常将某一主题的有关概念置于圆圈或方框之中，然后用连线将相关的概念和命题连接，连线上标明两个概念之间的意义关系。”</description></item><item><title>概念图与思维导图的比较研究[转]</title><link>https://yanbin.blog/concept-mind-map/</link><pubDate>Fri, 27 Jul 2007 11:25:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/concept-mind-map/</guid><description>
&lt;p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.25pt;"&gt;&lt;b style="mso-bidi-font-weight: normal;"&gt;&lt;span style="font-family: 黑体; mso-bidi-font-size: 10.5pt;"&gt;摘要&lt;span lang="EN-US"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;/b&gt;&lt;br /&gt;
&lt;span style="font-family: 楷体_GB2312; mso-bidi-font-size: 10.5pt;"&gt;通过对概念图和思维导图的比较，分析它们的内涵，比较它们的共同点和差异，从而更好地理解概念图和思维导图各自的特点和用途。&lt;!--?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 21.25pt;"&gt;&lt;b style="mso-bidi-font-weight: normal;"&gt;&lt;br /&gt;
&lt;span style="font-family: 黑体; mso-bidi-font-size: 10.5pt;"&gt;关键词&lt;span lang="EN-US"&gt;:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;/b&gt;&lt;br /&gt;
&lt;span style="font-family: 楷体_GB2312; mso-bidi-font-size: 10.5pt;"&gt;概念图；思维导图；知识可视化&lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="mso-tab-count: 1;"&gt;&lt;span style="font-family: Times New Roman;"&gt;      &lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;概念图和思维导图是孪生兄弟，被介绍到到国内后，已有了一定规模的应用与研究。笔者在&lt;/span&gt;&lt;br /&gt;
&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;br /&gt;
&lt;span style="font-family: Times New Roman;"&gt;2006 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;年 &lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="font-family: Times New Roman;"&gt;9&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;月&lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="font-family: Times New Roman;"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;日通过 &lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="font-family: Times New Roman;"&gt;Google &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;的学术搜索引擎查了一下标题中含有“概念图”一词的文章有 &lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="font-family: Times New Roman;"&gt;220&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;篇，标题中含有“思维导图”一词的文章有 &lt;/span&gt;&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;&lt;span style="font-family: Times New Roman;"&gt;35 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: 宋体; mso-bidi-font-weight: bold; mso-hansi-font-family: 'Times New Roman'; mso-ascii-font-family: 'Times New Roman';"&gt;篇，大部分文章是关于在教育教学中的应用与实践。这说明概念图在教育教学中应用已较多，已初具规模，而思维导图在教育教学中的应用的研究还不够。本文试图通过对概念图和思维导图的比较，分析它们之间的共同点、差异、应用范围，以便使它们在教育教学中更好地应用。&lt;/span&gt;&lt;br /&gt;
&lt;span lang="EN-US" style="mso-bidi-font-weight: bold;"&gt;
&lt;/span&gt;</description></item><item><title>Windows下安装使用openldap</title><link>https://yanbin.blog/windows-install-openldap/</link><pubDate>Thu, 26 Jul 2007 13:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-install-openldap/</guid><description>
openldap 比起其他商业目录服务器(比如 IBM Directory Server)，特别的轻巧，十分适合于本地开发测试用，在产品环境中的表现也很优秀。&lt;/p&gt;
&lt;br/&gt;
openldap 软件在它的官方网站 &lt;a href="http://www.openldap.org"&gt;http://www.openldap.org&lt;/a&gt;, 不过下载过来是源代码，并没有包含 win32 下的 Makefile 文件，只提供了在 Unix/Linux 下编译用的 Makefile。所以相应的在网上介绍在 windows 下安装使用 openldap 的资料比较少，而在 Unix/Linux 下应用文档却很丰富。&lt;br/&gt;&lt;br/&gt;
本文实践了在 Windows 下安装配 openldap，并添加一个条目，LdapBrowser 浏览，及 Java 程序连接 openldap 的全过程。</description></item><item><title>如何让学来的技术不至于忘记的太快</title><link>https://yanbin.blog/knowledge-forget-too-quickly/</link><pubDate>Wed, 25 Jul 2007 12:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/knowledge-forget-too-quickly/</guid><description>
写这个的初衷也就是因为，有些技术我总是在一次次重复的去经历从入门到半精通的过程，颇感苦恼--白费了许多时间与精力。&lt;br/&gt;
&lt;br/&gt;
工作或项目中学来的需反复使用的技术，掌握的会比较牢固，即使隔上一两年脱手再使用，也能迅速回过神来。因为它们常常是有明确目的性去研究掌握的，并且工作中也不时的会用到，许多还是让你非常有成就感的东西。&lt;br/&gt;
&lt;br/&gt;
然而对于某个项目偶尔用过又抛弃，或者是自己一时兴起学来的一些技术，即便当时真正是理解了它，如果隔好一阵子对它不闻不问了，再次捡起，也会像未曾谋面一般，不得不又从头开始一点一滴的去熟悉，实是浪费时间。</description></item><item><title>Linux/Unix下读取lib目录下所有包生成classpath的Shell</title><link>https://yanbin.blog/linux-unix-lib-classpath-shell/</link><pubDate>Wed, 25 Jul 2007 08:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-unix-lib-classpath-shell/</guid><description>
做了一个 Java 程序放在 Solaris 下运行，用到许多的第三方包，有 *.zip 的和 *.ja r的，放在 lib 目录下，然后写了一个 shell 脚本来运行那个 java 程序，那就要在 classpath 中包含所有在 lib 中的第三方包。&lt;br/&gt;
&lt;br/&gt;
原来的做法是在 classpath中把lib目录中一个个的包文件名用冒号连缀起来，比如&lt;br/&gt;
classpath=.:lib/a.jar:lib/b.jar:lib/c.jar:lib/d.zip&lt;br/&gt;
java -classpath $classpath com.unmi.Main</description></item><item><title>VC使用C API 连接操作MySQL数据库</title><link>https://yanbin.blog/vc-c-api-access-mysql/</link><pubDate>Mon, 23 Jul 2007 12:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-c-api-access-mysql/</guid><description>
一切尽在代码中,代码中也太多了if/else,可以对它进行更好的函数及至类的封装，规范的处理好异常。</description></item><item><title>调用Windows外壳程序格式化磁盘</title><link>https://yanbin.blog/windows-call-shell-format-disk/</link><pubDate>Sun, 15 Jul 2007 02:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-call-shell-format-disk/</guid><description>
看资料介绍说 Windows 2000 以上的 SDK 提供了格式化逻辑驱动器的函数 SHFormatDrive, 这是一个外壳函数（Windows界面操作环境称之为外壳）,从 shell32.dll 库导出，调用后会弹出一个格式化对话框，只有用户单击"开始"按钮后格式化才开始，使用时应包含头文件 shlobj.h &lt;/p&gt;
&lt;br/&gt;
我都照做了，下载安装了 Windows XP SP2 的 SDK, 并配置在了 VC 的 include和lib目录中了，也把新SDK的目录调到了最上面，程序中也包含了 shlobj.h, 而且看到新的 SDK 的 shlobj.h 中确实有 SHFormatDrive 函数原型，可是当我使用 SHFormatDrive 函数编译时总提示我找不到这个符号。不知道发生什么问题了，留待以后有需求时再解决吧,反正就是预编译宏展开时，不是去 include 新的 SDK 的 shlobj.h 文件，include 的是 VC98 中的 shlobj.h 文件。</description></item><item><title>VC常用代码[转]</title><link>https://yanbin.blog/vc-code-collection/</link><pubDate>Sat, 14 Jul 2007 06:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-code-collection/</guid><description>
VC代码收集&lt;br/&gt;
一、改变视图背景色：&lt;br/&gt;
在CView的OnDraw函数中添写如下一段程序代码：&lt;br/&gt;
void CFileNameView::OnDraw(CDC＊ pDC)&lt;br/&gt;
{&lt;br/&gt;
    CFileNameDoc* pDoc = GetDocument();&lt;br/&gt;
    ASSERT_VALID(pDoc);&lt;br/&gt;
    CRect rectClient;&lt;br/&gt;
    CBrush brushBkColor;&lt;br/&gt;
    GetClientRect(rectClient);&lt;br/&gt;
    brushBkColor.createSolidBrush(RGB(255,0,0)); //颜色设置</description></item><item><title>神奇的CSS样式属性 text-overflow:ellipsis</title><link>https://yanbin.blog/css-text-overflow-ellipsis/</link><pubDate>Wed, 04 Jul 2007 11:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/css-text-overflow-ellipsis/</guid><description>
回顾上一日志&lt;a href="https://yanbin.blog/title-fixed-width/"&gt;较为完美的一种标题缩略显示方法&lt;/a&gt; 此时我越发觉得离完美还有些距离，重新试验了一下该日志中提到的 text-overflow:ellipsis 样式写法，我以自己的名义再次作个判定：用 text-overflow:ellipsis 才是一个更完美的标题缩略显示的方法，下面看例子&lt;br/&gt;&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;border:1px solid red;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;white-space:nowrap;width=170px&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;在VC6中使用正则表达式解析字符串&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;border:1px solid red;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;white-space:nowrap;width=170px&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;VC中使用CInternetSession抓取网页内容&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;border:1px solid red;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis;white-space:nowrap;width=170px&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;开源嵌入式数据库 SQLite 简介&lt;span class="ni"&gt;&amp;amp;lt;&lt;/span&gt;/div&lt;span class="ni"&gt;&amp;amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>较为完美的一种标题缩略显示方法</title><link>https://yanbin.blog/title-fixed-width/</link><pubDate>Wed, 04 Jul 2007 07:08:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/title-fixed-width/</guid><description>
通常在网页里都有一种这样的需求，为了界面整洁，在显示标题时需要把长标题以部分加省略号连缀显示，就比如中国博客网显示日志标题--下面那样&lt;/p&gt;
&lt;br/&gt;
·在VC6中使用正则表达式解析...(2007-06-24)&lt;br /&gt;
·VC中使用CInternet...(2007-06-23)&lt;br /&gt;
·开源嵌入式数据库 SQLit...(2007-06-23) &lt;br/&gt;&lt;br/&gt;
问题一是前面那部分字符串怎么来确定，上面很明显示的看得出来，因为标题中混杂着中英文，仍然没有达到初衷。</description></item><item><title>嵌入式数据库典型技术―SQLite和Berkeley DB的研究</title><link>https://yanbin.blog/embed-db-sqlite-berkeley/</link><pubDate>Mon, 02 Jul 2007 04:57:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/embed-db-sqlite-berkeley/</guid><description>
摘 要: 与常见的数据库相比，嵌入式数据库具有体积小、功能齐备、可移植性、健壮性等特点，本文分析和比较了典型的嵌入式数据库SQLite和Berkeley DB。首先从体系结构、子系统间调用关系、任务执行过程等角度对SQLite和Berkeley DB进行了详细分析，然后重点从数据类型、存储方式、模式、数据库引擎和错误处理及加密功能等方面讨论了SQLite和Berkeley DB的异同点，最后列举了一个基于ARM—Linux的SQLite应用实例。&lt;/p&gt;
&lt;br/&gt;
关键词: SQLite、Berkeley DB、SQL、虚拟数据库引擎（VDBE）</description></item><item><title>也写了一个手机号码归属地查询软件，晒一下</title><link>https://yanbin.blog/my-cellnum-locator/</link><pubDate>Mon, 25 Jun 2007 09:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/my-cellnum-locator/</guid><description>
网上有许多版本的手机号码归属地查询软件，很多还收费的，好奇怪，有些还具有流氓习气。看不过，所以自己亲自动手写了一个来玩玩，最初选用过数据存储引擎是 HSql 和 Derby，因为他们有些弊端很明显，数据文件大、多文件支持、例如初始化时特慢、查询效率也不高、占有内存多、数据安全性不高，最后按定了 SQLite。&lt;/p&gt;
&lt;br/&gt;
语言也是先用 Java 的 SWING 来实现的，后来觉得 Java 写的程序慢是一个方面，还要虚拟机才能玩得转，有碍于发布，所以选用了 VC 来写，它写出来的东西可方便，把用到的动态库和数据文件考到别的机器上就能运行，最后应用了一个皮肤美化一下。&lt;br/&gt;&lt;br/&gt;
本软件定名为：手机号码归属地查询（俗），英文名：Mobile Locator（名不符实），可查130-139及159段手机号码，数据我将会持续更新。绿色安装，不含任何恶意代码，解压执行 MobileLocator.exe　即可，运行时查询效率很高，占用内存不大，启动后 5M 疯狂点查询按钮最大占用内存也只是 7M，没有内存溢出。</description></item><item><title>在VC6中使用正则表达式解析字符串</title><link>https://yanbin.blog/vc6-boost-regex-string/</link><pubDate>Sat, 23 Jun 2007 11:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc6-boost-regex-string/</guid><description>
接前面一篇日志，当用 CInternetSession 抓到网页内容之后，就要从中按某种模式截取自己想要的东西，比如其中的电话区号，对于这种事情最擅长的莫过于正则表达式了，以及它的分组/后向引用功能。&lt;/p&gt;
&lt;br/&gt;
VC.net 倒是提供了一个很好的正则表达式类库 CATLRegExp (据说是全世界效率最高的正则表达式库)，可是却不能在VC6上使用，VC6可以使用VBS(Microsoft VBScript Regular Expression 5.5)这个 COM 来处理正则表达式，或者引入 vbscript.dll 的方式，最好的办法还是借助于第三方库来处理正则表达式，如 boost，更何况若是使用 COM 就不太值（COM给人的感觉总有些笨拙）。</description></item><item><title>VC中使用CInternetSession抓取网页内容</title><link>https://yanbin.blog/vc-cinternetsession-catch-web-page/</link><pubDate>Sat, 23 Jun 2007 07:10:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-cinternetsession-catch-web-page/</guid><description>
在 VC 中用 WinInet 的 CInternetSession::OpenURL(url),得到一个 CFile,读取其中的内容即可，详细代码如下&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;afxinet.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CInternetSession&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;HttpClient&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34; http://www.imobile.com.cn/simcard.php?simcard=1392658&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CHttpFile&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHttpFile&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;dwStatusCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pfile&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;QueryInfoStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwStatusCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwStatusCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;HTTP_STATUS_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CString&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CString&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pfile&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ReadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimRight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; &amp;#34;&lt;/span&gt; &lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="n"&gt;LPCTSTR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pfile&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;pfile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
其他如不从缓存中读取内容及如何使用代理连接现在就不说了，可以参考下面的链接，或者下次补上。另外不妨看看 Java 是如何读取 URL 内容的，更简单&lt;br/&gt;&lt;br/&gt;</description></item><item><title>开源嵌入式数据库 SQLite 简介[转]</title><link>https://yanbin.blog/embed-sqlite-introduction/</link><pubDate>Sat, 23 Jun 2007 01:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/embed-sqlite-introduction/</guid><description>
1、SQLite简介&lt;br /&gt;
SQLite 是 D. Richard Hipp 用 C 语言编写的开源嵌入式数据库引擎。SQLite第一个Alpha版本诞生于2000年5月. 至今已经有7个年头了.目前版本是 2007.6.18 出来没久的 SQLite 3.4.0&lt;/p&gt;
&lt;br/&gt;
其创建者保守地估计 SQLite 可以处理每天负担多达 100,00 次点击率的 Web 站点，并且 SQLite 有时候可以处理 10 倍于上述数字的负载。&lt;br/&gt;&lt;br/&gt;
下面是访问SQLite官方网站: &lt;a href="http://www.sqlite.org/"&gt;http://www.sqlite.org/&lt;/a&gt; 时第一眼看到关于SQLite的特性.</description></item><item><title>VC编程中常用快捷键【转】</title><link>https://yanbin.blog/vc-shortcuts/</link><pubDate>Fri, 22 Jun 2007 11:07:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-shortcuts/</guid><description>
忽又想，编写桌面软件 Java 始终是跟不上步伐，VC 能非常好的平衡开发与运行的效率。 掌握一个 IDE，想提高开发上的效率，记住常用的快捷键是很有必要的，真正的对战类游戏高手无不娴熟于键盘的微操作，那种叫嚣着真正的程序员是用记事本编程实在听来让人冷峻不经。于是也就有了下文，在网上搜来的 VC 常用快捷键</description></item><item><title>在 Visual Studio 或 Rational Rose 中使用 CVS</title><link>https://yanbin.blog/visual-studio-rational-rose-use-cvs/</link><pubDate>Thu, 21 Jun 2007 15:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/visual-studio-rational-rose-use-cvs/</guid><description>
&lt;p&gt;Eclipse 内置就对 CVS 很好的支持，而 Visual Studio(.net) 和 Rational Rose 等对 VSS 支持很好，因为他们遵循的是 Microsoft SCC API，
那么如何让 Microsoft SCC API 与 CVS 桥接起来呢？这就是现在要介绍的几个工具&lt;/p&gt;
&lt;p&gt;1.
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2007/visual-studio-rational-rose-use-cvs/jalindi.jpg"
width="560px"
/&gt;
Jalindi　这个程序完全免费，任你使用&lt;/p&gt;
&lt;p&gt;Jalindi 的网址&lt;br /&gt;
&lt;a href="http://www.devhood.com/tools/tool_details.aspx?tool_id=491"&gt;http://www.devhood.com/tools/tool_details.aspx?tool_id=491&lt;/a&gt;&lt;br /&gt;
或&lt;br /&gt;
&lt;a href="http://www.jalindi.com/igloo/index.html"&gt;http://www.jalindi.com/igloo/index.html&lt;/a&gt;</description></item><item><title>VC窗口程序中出现中文乱码的解决</title><link>https://yanbin.blog/vc-cannot-show-chinese/</link><pubDate>Wed, 20 Jun 2007 13:00:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc-cannot-show-chinese/</guid><description>
用 VC6 的 MFC AppWizard 生成的窗口程序，窗口标题或者标签用中文，编译执行发现中文成乱码了，奇怪，以前怎么没有出现这种问题呢？&lt;br/&gt;&lt;br/&gt;
在网上找找资料才知道，原来我现在用的是非中文 XP 操作系统，在用向导生成窗口程序的过程中，没有中文语言的选项。不知道为什么，我的操作系统的区域都是设置成的中国，语言也是设成的简体中体，都没错的，VC6 安装程序看我的界面不是中文就不给我装上中文语言支持的动态库。</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（六）</title><link>https://yanbin.blog/spring-in-action-notes-6/</link><pubDate>Sat, 16 Jun 2007 07:56:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-6/</guid><description>
&lt;ol&gt;
&lt;li&gt;Spring 提供了丰富的控制器层次，方便根据实际需求选择实现或继承那一种控制器。不像 Struts 和 WebWork 比较平坦的 Action 层次。比如 ThowawayController/MultiActionController/SimpleFormController 等。 (P254)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
82. 继承 AbstractController 要覆盖的方法是 handleRequestInternal(request,response)；new ModelAndView("counrseList","courses",courses) 第一个参数是 view 的逻辑名，第二第三个参数是传递给 view 的名称/数值对，那要向 View 传递多个参数就还是要用 request.setAttribute() 了。(P256)</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（五）</title><link>https://yanbin.blog/spring-in-action-notes-5/</link><pubDate>Sat, 16 Jun 2007 03:50:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-5/</guid><description>
&lt;strong&gt;第八章. 建立 Web 层&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;&lt;br/&gt;
71. Spring 的 MVC 和 Struts 的 MVC 基本一致&lt;br /&gt;
    Spring 的控制流程是：请求-&amp;gt;DispatcherServlet-&amp;gt;从 HandlerMapping 中查询到处理该请求的 Controller-&amp;gt; Controller 的 handleRequest 方法调用业务方法，最后返回 ModelAndView (ModelAndView告诉了 DispatcherServlet 转向到哪个视图)&lt;br /&gt;
    Struts 的控制流程是：请求-&amp;gt;ActionServlet-&amp;gt;从 ActionMapping 中查询到处理该请求的 Controller (Action类)-&amp;gt;Action 的execute 方法调用业务方法，最后返回 ActionForward (ActionForward告诉了 ActionServlet 该转向到哪个视图)(P243)</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（四）</title><link>https://yanbin.blog/spring-in-action-notes-4/</link><pubDate>Thu, 07 Jun 2007 10:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-4/</guid><description>
&lt;ol&gt;
&lt;li&gt;更为精彩的是自动代理的方式来总体配置各分散类中方法的事物属性，结合使用 DefaultAdvisorAutoProxyCreator, TransactionAttributeSourceAdvisor 和 TransactionInterceptor （我还需要详细理清楚）(P181)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br/&gt;
62. 当使用自动代理时，MethodMapTransationAttributeSource 就能很多的派上用场了，它的 methodMap 属性中可以指定哪个类的哪个方法，可以使用通配符(P182)&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;第七章. 访问企业服务&lt;/strong&gt;&lt;br/&gt;</description></item><item><title>掌握一种Java模板技术 – Velocity</title><link>https://yanbin.blog/master-java-template-velocity/</link><pubDate>Mon, 04 Jun 2007 09:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/master-java-template-velocity/</guid><description>
原来的项目中基本都是用 JSP+Tag  来展示内容，有时也有 Jasper Report 做报表，报表也算是一种模板技术，只是有些重量级。看过 Spring 的书籍，都会对 Velocity 和 FreeMaker 有介绍，带动了我对先前看过的 *.vm 文件的研究提上了日程，了解之后，已觉相见恨晚。&lt;/p&gt;
&lt;br/&gt;
下面讲利用 Velocity 怎么更好解决目前项目的一个简单问题：系统在进行支付了支付后，自己按设定的信息模板发送一条短信给责任人。信息模板是：&lt;br/&gt;&lt;br/&gt;
&lt;span style="color: #0000ff;"&gt;{单据责任人}：您的 {单据号} 号{单据类型}在 {操作日期} 日已支付&lt;/span&gt;</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（三）</title><link>https://yanbin.blog/spring-in-action-notes-3/</link><pubDate>Sun, 03 Jun 2007 12:11:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-3/</guid><description>
&lt;strong&gt;第十章. 使用其他 Web 框架&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;&lt;br/&gt;
41. Spring 提供了两种与 Struts 集成的方式&lt;br /&gt;
    1) 让你的 Action 继承 org.springframework.web.struts.ActionSupport&lt;br /&gt;
    2) 将请求委托给作为 Spring Bean 管理的 Struts action 来自理(P312)&lt;br/&gt;&lt;br/&gt;
42. 为了让 Struts 能访问 Spring 管理的 Bean，必须在 struts-config.xml 中注册一个知道 Spring 上下文的 ContextLoaderPlugIn，用的是 WebApplicationContext：(P312)</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（二）</title><link>https://yanbin.blog/spring-in-action-notes-2/</link><pubDate>Sun, 03 Jun 2007 09:06:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-2/</guid><description>
&lt;strong&gt;第四章. 征服数据库&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;&lt;br/&gt;
31. Spring 把数据访问流程中的固定部分和可变部分分开，分别映射成两截然不同的类，模板（Template）和回调（Callback），模板管事物控制、资源管理以及异常处理；回调实现特定于应用的部分--创建 statement、绑定参数、以及整理结果集。模板方法模式的优秀应用（P123）&lt;br/&gt;&lt;br/&gt;
32. JdbcTemplate template = new JdbcTemplate(myDataSource); 构造。 所有 Spring Dao 模板类是线程安全的，可以为每一个 DAO 配置一个 JdbcTemplate 属性，也可以让 DAO 类继承 JdbcDaoSupport，然后在 DAO 类中用 getJdbcTemplate() 获取到 JdbcTemplate 进行数据库操作。书中的做法是给每个 Dao 加一个 JdbcTemplate 属性，记录的日志略有不同，实际中注意(P127)</description></item><item><title>Spring 学习点滴，《Spring in Action》笔记（一）</title><link>https://yanbin.blog/spring-in-action-notes-1/</link><pubDate>Sun, 03 Jun 2007 00:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-in-action-notes-1/</guid><description>
&lt;strong&gt;第二章：装配 Bean&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;hr /&gt;
&amp;nbsp;&lt;br/&gt;
1. &amp;lt;ref&amp;gt;标签中的 bean, local, parent 三个属性的区别(P53)&lt;br/&gt;&lt;br/&gt;
2. &amp;lt;list&amp;gt;&amp;lt;value...&amp;lt;/list&amp;gt;和&amp;lt;set&amp;gt;&amp;lt;value...&amp;lt;/set&amp;gt;可以换着用都可以为 List, Set 以及数组属性赋值(P55)&lt;br/&gt;&lt;br/&gt;
3. &amp;lt;map&amp;gt;属性用 spring  进行装匹时 key 值只能是字符串类型，不过一般能满足要求(P55)&lt;br/&gt;&lt;br/&gt;
4. 装配 map 属性要用 &amp;lt;entry key="key1"&amp;gt;&amp;lt;value&amp;gt;foo&amp;lt;/value&amp;gt;&amp;lt;/entry&amp;gt;, 而装匹 properties 属性可以写成 &amp;lt;prop key="key1"&amp;gt;foo&amp;lt;/prop&amp;gt;, 这是因为 properties 的值总是字符串，而 map 中很随意(P55)</description></item><item><title>试图在不修改Log4J源码情况下,用静态方法打印正确日志信息失败</title><link>https://yanbin.blog/log4j-static-method-log/</link><pubDate>Tue, 22 May 2007 23:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/log4j-static-method-log/</guid><description>
在开源的项目中使用 Log4j一般 都是在类中添加一静态变量,如&lt;br /&gt;
 protected static Log log = LogFactory.getLog(RequestProcessor.class); //通用日志组件&lt;br /&gt;
或&lt;br /&gt;
 protected static Logger log = Logger.getLogger(RequestProcessor.class); //直接申明为Log4j的logger&lt;/p&gt;
&lt;br/&gt;
原来有一个项目是做了一个自定义了 MyLogger 类, 其中的 debug, info 等到方法直接就是调用 log4j 的 logger 的对应方法. 别的代码中调用 MyLogger 的静态方法打印日志时, 依据log4j.properties的配置显示 %l  定位信息就始终是 MyLogger&lt;br/&gt;&lt;br/&gt;
如： 2007-05-23 12:18:46,828 [DEBUG] com.unmi.MyLogger.debug(MyLogger.java:12) Hello MyLogger</description></item><item><title>提交多行数据到Struts的ActionForm的List属性中</title><link>https://yanbin.blog/multi-lines-struts-actionform-list/</link><pubDate>Tue, 22 May 2007 10:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/multi-lines-struts-actionform-list/</guid><description>
WEB 应用中一般都会处理主从表的信息, 或者称之为头层与行层的一对多的关系数据,如订单头/订单明细. 对于这种关系数据提交到后台的 Struts 的 ActionForm 的话, 这个 ActionForm 就要好好的设计一下, 不然会给自已带来许多额外的代码. 比如有的人的处理方法就是把页面提交到后台的毫无关系的散装数据非常吃力的拼凑一对多的关系对象出来.&lt;/p&gt;
&lt;br/&gt;
下面举一个如今非常现实的关于股票的例子, 简单的应用场景是: 记录某个帐户所持有的股票信息,提交到后台,然后显示出来. 输入页面如下图&lt;br/&gt;&lt;br/&gt;
帐户信息包括帐户名和资金帐号;持有股票的每一行信息包括股票代码, 股票名称, 成本价, 股票数量. 股票行可以动态增删.</description></item><item><title>Retrotranslator让你用JDK1.5的特性写出的代码能在JVM1.4中运行</title><link>https://yanbin.blog/retrotranslator-jdk15-run-in-jvm14/</link><pubDate>Thu, 17 May 2007 03:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/retrotranslator-jdk15-run-in-jvm14/</guid><description>
JDK1.5出来多年了(2004年10月正式发行)，就连6.0正式版在 &lt;a href="http://java.sun.com"&gt;http://java.sun.com&lt;/a&gt;上已是赫然在目，紧跟着的各应用服务器和 Java IDE 厂商的都准备就绪. 可是相信很多开发者跟我一样却碍于公司用的是老版本的应用服务器，如WebSphere Application Server,，WebLogic等只能支持到1.4的JDK，要升级应用服务器成本和风险都有担心，所以项目中只能用1.4 的JDK，一直无法体验到 JDK 1.5 的新特性带来的便利．&lt;/p&gt;
&lt;br/&gt;
有些同事机器里一直还是躺着 JDK 1.4，我可能比他们好一点就是直接装了一个 JDK 1.5，然后在 Java IDE 中设置编译器的 Compiler compliance level为 1.4（实质就是javac –target 1.4）．这样避免了用JDK1.5编译的Class 放在1.4的JVM中运行出现49.0的字节码版本太高的错误，这样做只不是50步和100步的差距，照例用不了JDK1.5 的新特性．</description></item><item><title>把中国博客日志同步到JavaEye上需要解决的几个问题</title><link>https://yanbin.blog/blogcn-javaeye/</link><pubDate>Mon, 07 May 2007 09:53:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/blogcn-javaeye/</guid><description>
1. 由于通过 RSS 方式，只能读取最新的 20 条记录，JavaEye 至今未支持从中国博客网的 HTML 方式导入，所以需要认为生成一个包含所有日志的 RSS 格式的 XML 文件。&lt;br/&gt;
2. 生成的 XML 文件要让 JavaEye 的导入工具能读取到，所以需要发布到公网上，这个问题已解决，用路由器的端口映射到我的电脑上即可。&lt;br/&gt;
3. 由于本地的日志有许多 &amp;lt;textarea name=code&amp;gt;.... 包含的源代码，在本地显示是能通过 syntaxhighlighter 转换显示，</description></item><item><title>iBatis 中 Like '%iBatis%' 的写法实现模糊查询</title><link>https://yanbin.blog/ibatis-query-with-like/</link><pubDate>Sun, 06 May 2007 06:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ibatis-query-with-like/</guid><description>
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unmi&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
iBatis 开发指南告诉我们，当 Person 对象的 name 属性不为 null 时启用 name 查询条件在映射文件 person.xml 中的配置为&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;getPersonsByName&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;resultClass=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;com.unmi.Person&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; select id as id,name as name,passwd as passwd from person
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;dynamic&lt;/span&gt; &lt;span class="na"&gt;prepend=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;WHERE&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;isNotNull&lt;/span&gt; &lt;span class="na"&gt;prepend=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;AND&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; (name like #name#)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/isNotNull&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/dynamic&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>如何在 iBatis 应用程序向 Oralce 数据表字段插入 NULL 值</title><link>https://yanbin.blog/ibatis-insert-null-into-oralce/</link><pubDate>Sun, 29 Apr 2007 10:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ibatis-insert-null-into-oralce/</guid><description>
用 iBatis 应用程序连接的数据库是 Oracle, 映射文件中的插入语句写成如下形式 &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 插入一条Person对应的记录到数据库中 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;insert&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;insertPerson&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;parameterClass=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;com.unmi.Person&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; INSERT INTO PERSON(ID,NAME,PASSWD) VALUES(#id#,#name#,#passwd#)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/insert&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>从 Hibernate 到 iBatis，初次体验 SqlMap，领略另番 OR-Mapping</title><link>https://yanbin.blog/hibernate-to-ibatis-or-mapping/</link><pubDate>Sat, 28 Apr 2007 12:20:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-to-ibatis-or-mapping/</guid><description>
用Hibernate也有一段时间了，项目中也用过CMP以及.net的DataSet，也想体验一下iBatis的SqlMap方式的魅力了，以前总是看iBatis介绍一的文章，现在应是亲自动手心临其境的时候了。&lt;/p&gt;
&lt;br/&gt;
&lt;img class="aligncenter post-image"
src="https://yanbin.blog/posts/2007/hibernate-to-ibatis-or-mapping/to-iBatis.jpg"
/&gt;
做这个实验基本是遵照　&lt;a href="http://ibatis.apache.org/docs/java/pdf/iBATIS-SqlMaps-2-Tutorial_cn.pdf"&gt;iBATIS SQL Maps 入门教程&lt;/a&gt;　中的例子做的，只是在原来的基础上还更简化了一些。&lt;br/&gt;&lt;br/&gt;
我用的开发工具是 Eclipse，如何建立你的工程和加入相应的 iBatis 包可不详叙。</description></item><item><title>Hibernate程序使用WAS连接池提交事务时出现 NullPointerException</title><link>https://yanbin.blog/hibernate-was-connection-pool-nullpointerexception/</link><pubDate>Sat, 21 Apr 2007 02:57:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-was-connection-pool-nullpointerexception/</guid><description>
Hibernate应用程序部署到WebSphere Application Server 5.1上,使用WAS的连接池,程序在处理了比较多的 session.save(object)操作之后,在执行提交事物 trans.commit()之时出现以下异常&lt;/p&gt;
&lt;br/&gt;
 &lt;span style="color: #ff3300;"&gt;java.lang.NullPointerException&lt;br /&gt;
        at oracle.jdbc.dbaccess.DBData.clearItem(&lt;span style="color: #0000ff;"&gt;DBData.java:431&lt;/span&gt;)&lt;br /&gt;
        at oracle.jdbc.dbaccess.DBDataSetImpl.clearItem(&lt;span style="color: #0000ff;"&gt;DBDataSetImpl.java:3528&lt;/span&gt;)&lt;br /&gt;
        at oracle.jdbc.driver.OraclePreparedStatement.clearParameters(&lt;span style="color: #0000ff;"&gt;OraclePreparedStatement.java:3401&lt;/span&gt;)&lt;br /&gt;
        at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.resetStatement(&lt;span style="color: #0000ff;"&gt;WSJdbcConnection.java:1719&lt;/span&gt;)</description></item><item><title>用UTF-8保存的网页出现Unterminated string constant 脚本错误</title><link>https://yanbin.blog/utf8-unterminated-string/</link><pubDate>Sat, 14 Apr 2007 06:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/utf8-unterminated-string/</guid><description>
我在EditPlus新建一个网页文件, 在其中只写下代码&lt;br/&gt;
&lt;br/&gt;
&amp;lt;script&amp;gt;&lt;br/&gt;
    alert("黄");&lt;br/&gt;
&amp;lt;/script&amp;gt;&lt;br/&gt;
&lt;br/&gt;
然后在浏览器中预览,结果出现运行时脚本错误</description></item><item><title>Hibernate应用程序部署在WebSphere AS 上出现记录死锁的分析与解决</title><link>https://yanbin.blog/hibernate-was-dead-lock/</link><pubDate>Fri, 13 Apr 2007 11:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-was-dead-lock/</guid><description>
有一个项目，是用Hibernate开发的部署在WebSphere Application Server 5.1下，总是会出现数据库记录被死锁的情况，即使是一个简单的更新动作，都有可能造成。&lt;/p&gt;
&lt;br/&gt;
这个问题，困扰我许久，检查过所有代码，事物一定要提交或者回滚，Session及时释放，怀疑过是否是因为是部署在集群环境，但好像那些措施总于事无补，百思不得其解。&lt;br/&gt;&lt;br/&gt;
就差没有使用容器提供的事物了。</description></item><item><title>Hibernate从2升级到3不支持Oracle8外连接(+)的解决办法</title><link>https://yanbin.blog/hibernate-2-to-3-support-plus/</link><pubDate>Sat, 07 Apr 2007 17:47:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-2-to-3-support-plus/</guid><description>
最近接手了一个要维护的项目，是用Hibernate2+Oralce8写成的，因为看到Hibernate3页出来这么久了，而且也感觉Hibernate3有它的许多新的特性，如批量删除和更新，新的HQL语法解析器AST。&lt;/p&gt;
&lt;br/&gt;
升级过程大致按照孙卫琴的那篇文章 如何把Hibernate2.1升级到Hibernate3.0？来做，该替换的替换完，该设置的设置完，程序一跑，当程序执行到向下面这种查询的时候(Oracle所特有的外连接查询)，报错。&lt;br/&gt;&lt;br/&gt;
语句为：（描述为类似语句，把项目中的实际表名隐去了）</description></item><item><title>如何杀掉(kill)Oracle中的会话(Session)</title><link>https://yanbin.blog/kill-oracle-session/</link><pubDate>Wed, 21 Mar 2007 18:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/kill-oracle-session/</guid><description>
有时，Oracle中造成一些表被死锁或者会话异常退出，如执行了更新记录操作后，既没有commit也没有rollback,网络就断开了，也会造表或记录被锁住，待到超时后才会被解开，那样都会造成应用操作被阻塞。&lt;br/&gt;&lt;br/&gt;
可以以Oralce管理员权限用户登录Oracle数据，查询到被锁的对象，然后杀除指定的会话。&lt;br/&gt;&lt;br/&gt;
用下面的语句查询被锁的对象，可以带上更多约束条件，如schemaname等更精确的匹配。</description></item><item><title>Struts下的MapForm [转]</title><link>https://yanbin.blog/struts-mapform/</link><pubDate>Wed, 28 Feb 2007 22:45:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/struts-mapform/</guid><description>
我们知道Struts的ActionForm一直被大家视为缺陷，觉得多余，但我个人认为ActionForm还是有它存在的理由。我们建立ActionForm通常和Web页面的Form元素绑定，用于数据的收集和校验等。ActionForm的属性必须声明，然后才能用于和Web页面中，我们经常遇到一些属性不需要全部声明，如查询条件等，而且ActionForm的属性太多时管理也是个问题，再另一些情况下，如采购单，使用master/detail方式，ActionForm的创建变的困难，好多属性均不确定，如采购明细为对条记录，这样处理比较麻烦，在这篇文章中，我们将向你讲述如何使用Struts的MapForm机制实现这样的功能。</description></item><item><title>Tomcat 的类/包加载顺序[转]</title><link>https://yanbin.blog/tomcat-class-package-load-sequence/</link><pubDate>Sun, 25 Feb 2007 18:59:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/tomcat-class-package-load-sequence/</guid><description>
1.最先是$JAVA_HOME/jre/lib/ext/下的jar文件。&lt;br/&gt;
&lt;br/&gt;
2.环境变量CLASSPATH中的jar和class文件。&lt;br/&gt;
&lt;br/&gt;
3.$CATALINA_HOME/common/classes下的class文件。&lt;br/&gt;
&lt;br/&gt;
4.$CATALINA_HOME/commons/endorsed下的jar文件。&lt;br/&gt;
&lt;br/&gt;
5.$CATALINA_HOME/commons/i18n下的jar文件。</description></item><item><title>Hibernate如何与Firebird数据库自增字段映射</title><link>https://yanbin.blog/hibernate-map-firebird-autoincrement/</link><pubDate>Fri, 09 Feb 2007 22:26:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hibernate-map-firebird-autoincrement/</guid><description>
Firebird 本身是没有自增型字段，但可以通过序列键产生器和触发器共同来实现，步骤如下：&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;primary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GENERATOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_generator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRIGGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_before_trigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;my_table&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BEFORE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GEN_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>一个JUnit测试用例比较标准模板(JBuilder所用)</title><link>https://yanbin.blog/junit-sample-from-jbuilder/</link><pubDate>Wed, 07 Feb 2007 19:12:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/junit-sample-from-jbuilder/</guid><description>
&lt;p&gt;这是当年用 JBuilder 的时候，创建测试用例自动建立的代码布局。</description></item><item><title>嵌入式数据库Firebird的使用</title><link>https://yanbin.blog/to-use-firebird-db/</link><pubDate>Tue, 06 Feb 2007 10:10:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/to-use-firebird-db/</guid><description>
firebird是一个十分小的开源数据库，可作为像Access一样使用的文件型数据库，单用户连接；也能够以服务方式启动，支持多用户访问，Hibernate有对该种数据库的支持，这里不详细对该数据库本身特性作介绍了。&lt;/p&gt;
&lt;br/&gt;
用jdbc连接嵌入式数据库Firebird最简单的使用是只需要两个动态库文件和一个(JDBC驱动)jar包&lt;br/&gt;&lt;br/&gt;
动态库是：&lt;br /&gt;
fbembed.dll   ---- firebird数据库提供&lt;br /&gt;
jaybird.dll   ---- 下载的jdbc驱动包提供，在FirebirdSQL-1.5.5JDK_1.4中</description></item><item><title>MyEclipse5.1生成映射文件出现Generating Artifacts错误</title><link>https://yanbin.blog/myeclipse51-generating-artifacts/</link><pubDate>Tue, 06 Feb 2007 05:30:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/myeclipse51-generating-artifacts/</guid><description>
使用 Eclipse3.2.1+MyEclipse5.1+classes12.jar+Hibernate3&lt;/p&gt;
&lt;br/&gt;
建立了 Hibernate3 的 Project, 使用上面那个 classes12.jar 的驱动，在 MyEclipse 的 DB Browser&lt;br/&gt;&lt;br/&gt;
连接上数据库，选择表，在上下文菜单中点击 Hibernate Reverse Engineering, 直接点 Finish 按钮，&lt;br/&gt;&lt;br/&gt;
生成映射文件是出现出错：&lt;br /&gt;
An internal error occurred during "Generating Artifacts"</description></item><item><title>用 apache commons-email 轻松发送无乱码邮件</title><link>https://yanbin.blog/apache-commons-email-send-mail/</link><pubDate>Wed, 06 Dec 2006 23:48:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/apache-commons-email-send-mail/</guid><description>
原来项目中都是自己拿到 mail.jar 和 activation.jar 两个包编写发送Email的代码，虽然不难，但也有些麻烦。&lt;/p&gt;
&lt;br/&gt;
现在好了，在 apache 站点有一个 jarkata/commons/email 子项目，也为我们实现了发送 Email 的功能，在 &lt;a href="http://jakarta.apache.org/commons/email/"&gt;http://jakarta.apache.org/commons/email/&lt;/a&gt; 把包 commons-email-1.0.jar 下来，自己要写的代码就十分少了，并且非常明了。&lt;br/&gt;&lt;br/&gt;
这个包的大小只有23K，也就是9个类而已，却能让您省不少事。</description></item><item><title>终于在SuSe10上成功安装上了VMware 5.5</title><link>https://yanbin.blog/install-suse10-under-vmware55/</link><pubDate>Mon, 04 Dec 2006 09:31:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/install-suse10-under-vmware55/</guid><description>
原来在 RedHat AS3 下安装VMware 4 还是 5 都能非常顺利的安装，可是前几天安装了 SuSe 10，安装 VMware 5 却出了些岔子。&lt;/p&gt;
&lt;br/&gt;
我用的是 VMware Workstation 5.5.0 build-18463 for Linux RPM包来进行安装，安装完成后，敲入命令 vmware 执行后，提示&lt;br/&gt;&lt;br/&gt;
unmi:/usr # vmware&lt;br /&gt;
vmware is installed, but it has not been (correctly) configured</description></item><item><title>Java Web应用因tld文件损坏出现的错误</title><link>https://yanbin.blog/java-webapp-tld-damage/</link><pubDate>Tue, 14 Nov 2006 03:40:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-webapp-tld-damage/</guid><description>
部署一个 Web 应用到 WebSphere Application Server　中，浏览 index.jsp 页面时而正常，时而出现500错误，查看服务器输出日志可以看到以下错误，&lt;/p&gt;
&lt;br/&gt;
&lt;span style="color: #ff0033;"&gt;E SRVE0026E: [Servlet Error]-[This absolute uri &amp;amp;#40;http://jakarta.apache.org/struts/tags-logic&amp;amp;#41; cannot be resolved in either web.xml or the jar files deployed with this application]: org.apache.jasper.JasperException: This absolute uri (http://jakarta.apache.org/struts/tags-logic) cannot be resolved in either web.xml or the jar files deployed with this application</description></item><item><title>关于依旋律在网上查找歌曲的一点遐想</title><link>https://yanbin.blog/search-music-by-heng/</link><pubDate>Tue, 31 Oct 2006 09:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/search-music-by-heng/</guid><description>
今晚，与我合租的一同学在打开某个网页听着一旋律，是钢琴演奏的，觉得很优美。此时我走过他门口，他问我有没有听过这首歌，觉得好熟悉，确着很好听，我眯着耳朵再听了一会儿，同时在脑海里搜寻着——想不起来，没法知道是什么歌……&lt;br/&gt;
&lt;br/&gt;
通常我们想在网上找一首歌的时候，总是会提供这首歌的某方面的文字描述信息，如歌名啦、专辑名啦、是谁演唱的、谁写的或其中歌词片段等等。可是当我所知道的仅仅是旋律(像MIDI，声音记录) 或者你耳感较强，还听出来是 C、D、E……调，或者 DO RE MI，该如何通过这一类信息在网上找到这首歌来，目前，据我所知没有哪个搜索引擎能办到。</description></item><item><title>用 Windows 脚本启停和监视应用程序</title><link>https://yanbin.blog/windows-script-mornitor-app/</link><pubDate>Mon, 23 Oct 2006 00:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/windows-script-mornitor-app/</guid><description>
如果一个程序运行后不会出现在任务管理的进程列表中，那么判断这个程序是否在运行当中该如何做呢？要是直接调用 Windows API 来获取当然是一点问题都没有，只不过需要用编译语言来写，动作大了点。下面介绍直接写 Windows 脚本的方法，据你熟悉而言可以用JS或VBS脚本，在脚本中须用到 WMI 查询。同时也顺带一下用 WMI 创建进程启动后台程序的脚本。&lt;/p&gt;
&lt;br/&gt;
1. 判断后台进程是否在运行，假设应用程序的名称为 UnmiProgram.exe，VBS脚本如下：</description></item><item><title>一条技巧：去除显卡的右键菜单项 [转]</title><link>https://yanbin.blog/content-menu-vagcard/</link><pubDate>Sun, 22 Oct 2006 00:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/content-menu-vagcard/</guid><description>
&lt;p&gt;安装显卡驱动以后，在桌面右键菜单上多了"另存为图式…"、"图形选项"、"显示模式"三项，我想去掉它们，应该怎么操作？&lt;/p&gt;
&lt;p&gt;答：要去掉显卡的桌面右键菜单，在"开始/运行"中输入"regsvr32 /u igfxpph.dll"运行即可；若要恢复使用，则用同样的方法输入"regsvr32 igfxpph.dll"运行。&lt;br /&gt;&lt;br /&gt;
摘自：&lt;a href="http://www.51windows.net/data/SiteNews.asp"&gt;http://www.51windows.net/data/SiteNews.asp&lt;/a&gt;　一条技巧 2006-10-13 21:39:33&lt;br /&gt;&lt;br /&gt;
安装了主板自带显卡的驱动一般就会在桌面右键菜单加上那几个项，真的很烦人，还影响右键菜单的弹出速度，原来还不知怎么拿掉它们，现在好了。&lt;br /&gt;&lt;br /&gt;
再参考：&lt;a href="http://hi.baidu.com/snoworld/blog/item/cfeb0afa23477e9d59ee90e8.html"&gt;http://hi.baidu.com/snoworld/blog/item/cfeb0afa23477e9d59ee90e8.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
上面的去除方法并不妥，首先相关注册表项还在，在桌面右击时系统还是会寻找这些菜单项，只不过是找不到内容而已（用Regmon监视一下就知道了，后面给出的注册表路径也是通过Regmon找到的）;
其次，反注册igfxpph.dll后，显卡的额外设置、高级选项（“显示 属性” －&amp;gt;“设置”-&amp;gt;“高级”）也会消失。&lt;/p&gt;
&lt;p&gt;个人的方法是：&lt;br /&gt;运行“regedit”打开注册表，进入[HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\]，
下面的子项igfxcui就是桌面右键菜单出现的Intel显卡驱动添加的内容。删除“igfxcui”这个子项即可（先导出以备份再删除）。这样上面的缺点就没有了。&lt;br /&gt;&lt;br /&gt;
参考：&lt;a href="http://hi.baidu.com/ejdfw/blog/item/e78a15dafb464c3c32fa1c60.html"&gt;http://hi.baidu.com/ejdfw/blog/item/e78a15dafb464c3c32fa1c60.html&lt;/a&gt; 介绍有更全面的办法。&lt;/p&gt;</description></item><item><title>隐藏BlogCN首页上显示的两个浮动广告 [原]</title><link>https://yanbin.blog/hide-two-float-ads/</link><pubDate>Thu, 19 Oct 2006 22:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/hide-two-float-ads/</guid><description>
一直看到BlogCN在首页上强加的两个浮动广告就想把它们去除掉。原来也琢磨过那两个玩艺儿不是 IFRAME 就是 DIV，那时没有继续下去，后来受好友老裴（minsurance）的启发（见&lt;a href="http://www.blogcn.com/gbook.asp?uid=minsurance&amp;amp;gid=43436046"&gt;http://www.blogcn.com/gbook.asp?uid=minsurance&amp;amp;gid=43436046&lt;/a&gt;）说&lt;br /&gt;
&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #949400;"&gt;在代码编辑中加入如下代码&lt;/span&gt;&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;:&lt;br /&gt;
&amp;lt;&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;STYLE type&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;=&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;text&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;/&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;css&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;&amp;gt;&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;iframe&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #ff0000;"&gt;{&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;position&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;: &lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;relative; width&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;: &lt;span style="word-break: break-all; line-height: 120%; color: #6400c8;"&gt;0&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;; height&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;: &lt;span style="word-break: break-all; line-height: 120%; color: #6400c8;"&gt;0&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #ff0000;"&gt;}&lt;br /&gt;
&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;&amp;lt;/&lt;span style="word-break: break-all; line-height: 120%; color: #000000;"&gt;STYLE&lt;span style="word-break: break-all; line-height: 120%; color: #804040;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
加这样的样式控制代码对一般的BlogCN页面还是有用的，但我的页面本来就有许多iframe是要显示的，如果只简单这样写势必把需显示的界面元素给隐藏了起来，于我的BlogCN就要另辟蹊径了。但却启示了我应该找到那两个 IFRAME　然后把它隐藏起来（可以把宽和高设置为零，也可以把 display 属性设置为 none）。&lt;br /&gt;</description></item><item><title>如何确保一个 Linux Shell 只有一个运行实例</title><link>https://yanbin.blog/only-one-shell-running/</link><pubDate>Thu, 12 Oct 2006 01:59:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/only-one-shell-running/</guid><description>
当我们在 Linux 下写了一个 Shell 做某些操作时，可能在一个终端运行了该脚本还没停止，然而又可能在另一个终端再一次执行了它，会有两个或多个实例在运行。或者是把这个 Shell 安排在 Cron 中，想让它每隔5分钟执行一次，有可能执行时间较长，正在执行当中，下一个5分钟的时刻到了，又在启动一次，这同样也会造成同一个 Shell会同时跑出多个实例来。&lt;br/&gt;&lt;br/&gt;
同时跑的多个实例就有可能对同一资源的操作造成数据的不可思异。这就要有一种方法来保证同一时刻同一脚本只能有一个实例在运行，借用很多软件的做法，比如 MySQL、Apache 等，在启动的时候生成一个临时的文件向后来者明确指示：有一个实例正在执行，不能执行第二个实例了。等到执行结束(可能被 Kill掉，被 Shutdown等)，就把临时文件删除，以后可以运行新的实例了。&lt;br/&gt;&lt;br/&gt;
可以把某个 Shell 的执行权当作一个独占资源，只有获取锁(没有临时文件，并且建立它)时才有权执行它，执行完后释放锁(把临时文件删除)。</description></item><item><title>读林锐大作《软件工程思想》有感 [转]</title><link>https://yanbin.blog/linrui-software-engineer-thinking/</link><pubDate>Tue, 19 Sep 2006 10:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linrui-software-engineer-thinking/</guid><description>
原址：&lt;a href="http://blog.sina.com.cn/u/4a8238ac01000638"&gt;http://blog.sina.com.cn/u/4a8238ac01000638&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
下面是我从书中抽取出来的，也希望能和你有共鸣&lt;br/&gt;
1.无论是什么好方法，贵在理解与灵活运用，而不可当成灵丹妙药，不象"吃了脑黄金或脑白金，就能使一亿人先聪明起来。&lt;br/&gt;
2.不要让人觉得程序员只管钻研技术，可以不懂世事并且应该自由散漫。程序员不该因为幼稚而显得单纯，应该是成熟了才变得单纯，才配得上这个充满活力的职业。</description></item><item><title>ORACLE 常用脚本（4）[转]</title><link>https://yanbin.blog/oracle-scripts-4/</link><pubDate>Tue, 19 Sep 2006 00:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-scripts-4/</guid><description>
rem  这需要 统计某个具体用户的"Table,index,column,constraits"&lt;br/&gt;
rem================================================================&lt;br/&gt;
rem   全部表-列定义 table_cols.txt&lt;br/&gt;
 set lin 110 pages 3000&lt;br/&gt;
 column table_name format a30&lt;br/&gt;
 column data_type  format a12&lt;br/&gt;
 column data_default  format a8&lt;br/&gt;
 column column_name format a22&lt;br/&gt;
 column Cid format  999</description></item><item><title>ORACLE 常用脚本（3）[转]</title><link>https://yanbin.blog/oracle-scripts-3/</link><pubDate>Tue, 19 Sep 2006 00:04:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-scripts-3/</guid><description>
&lt;strong&gt;&lt;br/&gt;
&lt;span style="font-size: medium;"&gt;用户的管理&lt;/span&gt;&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
&lt;strong&gt;一、ORACLE的安全域&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
1、TABLESPACE QUOTAS：表空间的使用定额&lt;br/&gt;
2、DEFAULT TABLESPACE：默认表空间&lt;br/&gt;
3、TEMPORARY TABLESPACE：指定临时表空间。&lt;br/&gt;
4、ACCOUNT LOCKING：用户锁&lt;br/&gt;
5、RESOURCE LIMITE：资源限制&lt;br/&gt;
6、DIRECT PRIVILEGES：直接授权</description></item><item><title>ORACLE 常用脚本（2）[转]</title><link>https://yanbin.blog/oracle-scripts/</link><pubDate>Tue, 19 Sep 2006 00:02:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-scripts/</guid><description>
&lt;strong&gt;一、ORACLE的表的分类：&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
1、REGULAR TABLE：普通表，ORACLE推荐的表，使用很方便，人为控制少。&lt;br/&gt;
2、PARTITIONED TABLE：分区表，人为控制记录的分布，将表的存储空间分为若干独立的分区，记录按一定的规则存储在分区里。适用于大型的表。&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;二、建表&lt;br/&gt;
&lt;/strong&gt;&lt;br/&gt;
1 CREATE TABLE 表名 （EMPNO NUMBER（2），NAME VARCHAR2（20）） PCTFREE 20 PCTUSED 50&lt;br/&gt;
STORAGE （INITIAL 200K NEXT 200K MAXEXTENTS 200 PCTINCREASE 0） TABLESPACE 表空间名称</description></item><item><title>ORACLE 常用脚本（1） [转]</title><link>https://yanbin.blog/oracle-scripts-1/</link><pubDate>Tue, 19 Sep 2006 00:01:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-scripts-1/</guid><description>
&lt;strong&gt;1、查看表空间的名称及大小&lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
    select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size&lt;br/&gt;
    from dba_tablespaces t, dba_data_files d&lt;br/&gt;
    where t.tablespace_name = d.tablespace_name&lt;br/&gt;
    group by t.tablespace_name;&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2、查看表空间物理文件的名称及大小&lt;/strong&gt;</description></item><item><title>管理常用SQL语句 [转]</title><link>https://yanbin.blog/management-sql/</link><pubDate>Mon, 18 Sep 2006 11:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/management-sql/</guid><description>
&lt;strong&gt;1. 查看数据库的版本&lt;/strong&gt;       &lt;br/&gt;
&lt;br/&gt;
　　 select @@version&lt;br/&gt;
&lt;br/&gt;
　　&lt;strong&gt;2. 查看数据库所在机器操作系统参数&lt;/strong&gt;       &lt;br/&gt;
&lt;br/&gt;
　　exec master..xp_msver&lt;br/&gt;
&lt;br/&gt;
　　&lt;strong&gt;3. 查看数据库启动的参数&lt;/strong&gt;        &lt;br/&gt;
&lt;br/&gt;
　　sp_configure</description></item><item><title>ADO.NET 访问数据库的性能改善方法集合 [转]</title><link>https://yanbin.blog/ado-net-access-db-performance/</link><pubDate>Thu, 14 Sep 2006 11:41:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ado-net-access-db-performance/</guid><description>
&lt;h3&gt;l  总的考虑方向&lt;/h3&gt;&lt;br/&gt;
    1) 根据数据使用的方式来设计数据访问层&lt;br/&gt;
&lt;br/&gt;
    2) 缓存数据，避免不必要的操作&lt;br/&gt;
&lt;br/&gt;
    3) 使用服务帐户进行连接&lt;br/&gt;
&lt;br/&gt;
    4) 必要时连接，尽早释放&lt;br/&gt;
&lt;br/&gt;
    5) 关闭可关闭的资源</description></item><item><title>在VB中进行条件编译</title><link>https://yanbin.blog/vb-conditional-compile/</link><pubDate>Wed, 13 Sep 2006 11:26:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vb-conditional-compile/</guid><description>
因为工作的需要，在某些时候又要拣一拣令我生厌的VB，是要在别人代码的基础上进行修改。看人家代码都是写连接字符串、用户名和密码都写两套，开发时用一套，注释另一套，发布时反过来，更加了VB还只能行注释。我一看就嫌这麻烦，宁愿多思考思考，看看有啥好办法能偷偷懒，当这事，条件编译了。&lt;/p&gt;
&lt;br/&gt;
以前就没怎么用过VB，更别提VB中条件编译了，Goole搜，发现VB中可以用几个预编译指令： #Const、#If .Then ...#ElseIf.Then...#Else...#End If&lt;br/&gt;&lt;br/&gt;
#Const 声明一个常量，相当于宏定义，用#Const定义的只能被当前模块识别。例如定义常 DebugVer =1，标明是调试版本：&lt;br/&gt;&lt;br/&gt;
#Const DebugVer = 1&lt;br/&gt;&lt;br/&gt;
然后，数据库连接串写成&lt;br/&gt;&lt;br/&gt;
#If DebugVer = 1 Then  '或者是#If DebugVer Then VB和C、C++也一样，非零为真&lt;br /&gt;
connStr = "Provider=OraOLEDB.Oracle;Data Source=Fraud-Test;User Id=Unmi;Password=testpwd;"&lt;br /&gt;
#Else&lt;br /&gt;
connStr = "Provider=OraOLEDB.Oracle;Data Source=Fraud;User Id=user;Password=pwd";&lt;br /&gt;
#End If&lt;br/&gt;&lt;br/&gt;
要发布到正式环境时编译前只需要把 #Const DebugVer = 1 改为 #Const DebugVer = 1 就 OK 了。</description></item><item><title>终于解决了自己的博客页面显示像蜗牛的问题</title><link>https://yanbin.blog/blog-too-slow/</link><pubDate>Fri, 08 Sep 2006 13:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/blog-too-slow/</guid><description>
今天晚上，忙了好一阵把中国博客网一多篇文章挪到博客天中去，因为几乎半年以来，我现在这个博客网打开页面都无比的慢，相信任何人都无法忍受，一直都是以为中国博客网就是很慢的，也没太在意为什么别人在中国博客网上的博客也很快的。&lt;br/&gt;
&lt;br/&gt;
也试过学着博客天下默认方式那样，每篇文章显示前约500字，再看看首页，并没明显示改善，况且进入完整内容页总还是如此之慢的，补充这里可能慢的概念，可能20分钟后还看不到内容。</description></item><item><title>ASP.NET 中使用 Log4Net 记系统日志</title><link>https://yanbin.blog/aspnet-log4net-logger/</link><pubDate>Wed, 30 Aug 2006 23:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/aspnet-log4net-logger/</guid><description>
Unmi原来一直是做 Java 的，凡是做 Java 的项目都会用到 Log4J 来记载日志，比如那些开源的框架，Struts/Hibernate/Spring 都有用到 Log4J, 系统日志成了一个必要的东西，没有它，软件出问题了很难去定位。&lt;/p&gt;
&lt;br/&gt;
最近无奈间接手了一个 .NET 项目，代码一片混乱，又没有错误日志，只在不很紧要处用 LogEvent 写了些东西到系统事件日志中。经常的客户有对这个项目提些 TroubleShooting, 基本只能对代码走查找到问题所在，实在是累啊，而且有些问题可就无能为力了。所以希望在新的版本上线时有记录错误日志信息，这样以后发生错误就能根椐日志信息迅速定位到代码行以及了解到发生了何种具体异常。</description></item><item><title>Spring MVC 初体验</title><link>https://yanbin.blog/spring-mvc-first/</link><pubDate>Tue, 29 Aug 2006 11:14:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/spring-mvc-first/</guid><description>
相信很多搞过Java的人都用过 Struts,或者转到用 Spring 框架后使用的仍然是Struts提供的MVC,那么是否想更多了解一下 Spring 本身的 MVC 呢，其实 Spring 的 MVC 还是很不错的，它×?Struts 中好的东西，还集 WebWork 的拦截器，下面来让我对 Spring MVC 作一个最简单的体验。 &lt;/p&gt;
&lt;br/&gt;
称之谓简单是我觉得要去体验 Spring 的 MVC 的这个例子已经是精小到极致了，应用中没有配置ContextLoaderServlet或者ContextLoaderListener。借鉴于 Struts 的概念，这个实例中的的请求处理过程依次是/helloSpring.unmi-&amp;gt;helloSpringController-&amp;gt;helloSping.jsp)</description></item><item><title>为何Java的有包名的类不能引用默认包中的类</title><link>https://yanbin.blog/java-cannot-import-default-package/</link><pubDate>Wed, 23 Aug 2006 11:26:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-cannot-import-default-package/</guid><description>
有两个java类，源代码如下：&lt;br/&gt;&lt;br/&gt;
&lt;table style="margin:auto; width: 750px; border-collapse: collapse;" border="1" cellspacing="2" cellpadding="2" bordercolor="#e7f7fe"&gt;
&lt;tbody&gt;
&lt;tr valign="top"&gt;
&lt;td width="50%"&gt; //没指定包名，也就是在默认包中public class B {&lt;br/&gt;
  public void print(){&lt;br/&gt;
    System.out.println("Printed By B Class");&lt;br/&gt;
  }&lt;br/&gt;
}&lt;/td&gt;
&lt;td width="50%"&gt; package com.unmi;&lt;br/&gt;
import B; //引用默认包中的B类&lt;br/&gt;
public class A {&lt;br/&gt;
  public static void main(String[] args) {&lt;br/&gt;
    B b = new B(); //在A中想要引用默认包中的B类&lt;br/&gt;
  }&lt;br/&gt;
}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
先不说在IDE中如何编译它们以及会提示什么语法错误，只是用命令javac来编译它们，先是把它们放在同一个目录下，注意的是，虽然A.java有package unmi.com;声明，我们还是先把它放在下B.java同一目录，希望由javac命令来生成相对应的包目录。</description></item><item><title>ASP.net从服务器端向客户端弹出alert对话框，但不使页面变成白板</title><link>https://yanbin.blog/asp-net-alert-from-server-side/</link><pubDate>Wed, 09 Aug 2006 01:12:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/asp-net-alert-from-server-side/</guid><description>
在做ASP.net的项目时，碰到在页面上输入一些数据后，然后点击提交按钮，然后服务器端校验数据，如果数据不合格，用&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;Response.Write("&amp;lt;script&amp;gt;alert(' 用户名不存在!')&amp;lt;/script&amp;gt;") &lt;/strong&gt;&lt;br/&gt;
&lt;br/&gt;
向客户端推出一个警告窗口提示，这时候会发现，显示警告窗口的时候，在警告窗口的背面页面是一片空白，非得你按了警告窗口上的确定按钮，主页面才会显示出需要的内容。&lt;br/&gt;
&lt;br/&gt;
为什么会出现一个页面白板呢？包括服务器端向客户端推出的 showModalDialog 模态窗口一样，用 Response.Wrtie()输出到客户端页面，可以通过查看生成页面的源文件，
就能发现是在页面的最前面加上了 alert() 或 showModalDialog() js函数，这种警告窗口或模态窗口弹出来之后，将会阻止页面显示接下来的内容，所以您看到的就是一片空白，
只有用户关了警告或模态窗口才会继续显示页面内容。那么怎么才能让显示 alert对话框或 showModalDialog 窗口时，主页面有内容显示呢？很简单，
需要让生成的 html 中 alert 和 showModalDialog 处在显示内容的后面。</description></item><item><title>用VS.NET开发asp.net时调试的时候出现错误，不能调试！</title><link>https://yanbin.blog/vs-net-asp-net-cannot-debug/</link><pubDate>Tue, 08 Aug 2006 10:10:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vs-net-asp-net-cannot-debug/</guid><description>
我用的是vs2003.net，原来本机安装的是IE6.0，现在换成了IE7.0，在vs2003.net中来调试C#写的ASP.net程序，启动时出现错误，不能调试&lt;br/&gt;
错误对话框是：&lt;br/&gt;
试图运行项目时出错：无法在Web服务器上启动调试。您不具备调试此应用程序的权限，此项目的URL位于Internet区域。有关更多信息，请单击"帮助"。&lt;br/&gt;
&lt;br/&gt;
在网络上搜索找到下面这篇比较全面的资料：&lt;a href="http://www.jxcn.cn/so/cnstu/3865/3865450.htm"&gt;http://www.jxcn.cn/so/cnstu/3865/3865450.htm&lt;/a&gt;&lt;br/&gt;
--------------------------------------------------------------------------------&lt;br/&gt;
看一下网站的文件夹安全属性.是不是有操作权限.还有就是IIS的安全设置.要启用windows验证密码&lt;br/&gt;
--------------------------------------------------------------------------------</description></item><item><title>C++的头文件中类声明后忘记加分号产生不可预期的编译错误</title><link>https://yanbin.blog/cpp-header-semicolon-expected/</link><pubDate>Tue, 08 Aug 2006 00:23:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/cpp-header-semicolon-expected/</guid><description>
&lt;table style="border-collapse: collapse;" border="1" width="100%" cellspacing="2" cellpadding="2"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="50%"&gt;头文件 Test.h 的内容是&lt;br /&gt;
class Test&lt;br /&gt;
{&lt;br /&gt;
public:&lt;br /&gt;
 virtual void test1();&lt;br /&gt;
}&lt;/td&gt;
&lt;td&gt;实现文件是 Test.cpp&lt;br /&gt;
#include "Test.h"&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
using namespace std;&lt;br /&gt;
void Test::test1(){  cout&amp;lt;&amp;lt;"Hello"&amp;lt;&amp;lt;endl; }&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br/&gt;
编译时出现错误&lt;br /&gt;
c:\program files\microsoft visual studio\vc98\include\errno.h(29) : error C2143: syntax error : missing ';' before 'string'</description></item><item><title>为什么Oracle的伪列rownum不支持&gt;,&gt;=,=,between...and，而都用&lt;比较</title><link>https://yanbin.blog/oracle-rownum-support-predicate/</link><pubDate>Thu, 03 Aug 2006 01:05:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-rownum-support-predicate/</guid><description>
对于 Oracle 的 rownum 问题，很多资料都说不支持&amp;gt;,&amp;gt;=,=,between...and，只能用以上符号(&amp;lt;、&amp;lt;=、!=)，并非说用&amp;gt;,&amp;gt;=,=,between..and 时会提示SQL语法错误，而是经常是查不出一条记录来，还会出现似乎是莫名其妙的结果来，其实您只要理解好了这个 rownum 伪列的意义就不应该感到惊奇，同样是伪列，rownum 与 rowid 可有些不一样，下面以例子说明&lt;/p&gt;
&lt;br/&gt;
假设某个表 t1(c1) 有 20 条记录&lt;br/&gt;&lt;br/&gt;
如果用 select rownum,c1 from t1 where rownum &amp;lt; 10, 只要是用小于号，查出来的结果很容易地与一般理解在概念上能达成一致，应该不会有任何疑问的。</description></item><item><title>Linux/Unix下定时任务系统--cron和crontab(拼凑)</title><link>https://yanbin.blog/linux-unix-cron-crontab/</link><pubDate>Mon, 31 Jul 2006 10:39:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/linux-unix-cron-crontab/</guid><description>
在 Linux 中，任务可以被配置在指定的时间段、指定的日期、或系统平均载量低于指定的数量时自动运行。红帽企业 Linux 预配置了对重要系统任务的运行，以便使系统能够时时被更新。譬如，被 &lt;tt class="COMMAND"&gt;&lt;span style="font-family: 新宋体;"&gt;locate&lt;/span&gt;&lt;/tt&gt; 命令使用的 slocate 数据库每日都被更新。系统管理员可使用自动化的任务来执行定期备份、监控系统、运行定制脚本等等。&lt;br/&gt;
&lt;br/&gt;
红帽企业 Linux 随带几个自动化任务的工具：&lt;tt class="COMMAND"&gt;&lt;span style="font-family: 新宋体;"&gt;cron&lt;/span&gt;&lt;/tt&gt;、&lt;tt class="COMMAND"&gt;&lt;span style="font-family: 新宋体;"&gt;at&lt;/span&gt;&lt;/tt&gt;、和 &lt;tt class="COMMAND"&gt;&lt;span style="font-family: 新宋体;"&gt;batch&lt;/span&gt;&lt;/tt&gt;。</description></item><item><title>安装 Microsoft AppLocale 后出现某些软件安装界面乱码问题的解决办法</title><link>https://yanbin.blog/microsoft-applocale-error/</link><pubDate>Thu, 13 Jul 2006 04:35:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/microsoft-applocale-error/</guid><description>
我用的是中文Windows XP，因为常常要看台湾的繁体字，有些是非Unicode，只是Big5码，又不想每次都去更改"控制面板 - 区域和语言选项 - 高级 - 非 Unicode 程序的语言"的设置，
所以前不久安装了 Microsoft AppLocale。但自从安装了 Microsoft AppLocale 之后，我发现很多软件的安装界面（如金山词霸），常常会乱码，还有MSDN98的主页面也出现乱码。
今天到网上搜索了一下，发现了以下的确实可行的解决办法，不用卸载 Microsoft AppLocale。</description></item><item><title>传统注音符号学习过门篇－－引子</title><link>https://yanbin.blog/phonetic-get-started/</link><pubDate>Wed, 12 Jul 2006 10:48:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/phonetic-get-started/</guid><description>
其实Unmi对注意符号一直都很有兴趣，因为打拿《新华字典》起（应该是很小的时候）就注意到那上面每个汉字后面除了汉语拼音外，还标了几个奇怪的字符（ㄅㄆㄇㄈ），后来看过日文，又感觉那还有点像是日文的假名。混迹社会之后才知道台湾那边就是用这种符号标识汉字读音来着，可还是不知道它与汉语拼音有什么渊源和异同点。&lt;br/&gt;
&lt;br/&gt;
前段时间，有时在公司中午休息的时候，没事看看&lt;a href="http://zh.wikipedia.org/"&gt;维基百科&lt;/a&gt;，增广增广见识，兴致一上来，瞧瞧里头的语言学，才明白许多年以前的印在头脑中的那些标识符是注音符号，而且它比汉语拼音还要传统的汉字语音注记符，还是汉语拼音的前身，因为汉语拼音的语音系统基本与注音符号一致，当然也有些许差别。也许您早已注意到在您的手机中也有这种注音输入法，</description></item><item><title>VB采用ADO的方式操作Excel表格</title><link>https://yanbin.blog/vb-ado-access-excel/</link><pubDate>Sun, 09 Jul 2006 11:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vb-ado-access-excel/</guid><description>
使用VB配搭着ADO可以像操纵Access数据文件一样操作Excel文件，对于如何用ADO操作Excel文件，首先应该建立一些基本概念。你需要将一个Excel数据文件想象为一个关系数据库，Excel文件中的每个工作表就对应该数据库中的一个表，而工作表中的每一列当然就是表的列了。&lt;/p&gt;
&lt;br/&gt;
代码如下：操作 book1.xls 的 sheet1 工作表,注意要写成 sheet1$ (加个美元符号)</description></item><item><title>大陆与台湾电脑用语之差异</title><link>https://yanbin.blog/computer-word-between-taipei-land/</link><pubDate>Thu, 06 Jul 2006 10:36:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/computer-word-between-taipei-land/</guid><description>
去过台湾公司的同志或是在网上经常看台湾那边软件技术类网站时，或与他们相关人等有交流时，会注意到两岸之间在电脑方面许多述语有较大的并别，但也非常有趣的。下面用表格列了一些出来：&lt;br/&gt;</description></item><item><title>关于代码复查和拥有代码</title><link>https://yanbin.blog/code-review-owner/</link><pubDate>Wed, 05 Jul 2006 21:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/code-review-owner/</guid><description>
代码复查是一个普遍遵循的程序设计实践，对任何项目都很有价值。在代码复查过程中一组程序员一行一行地检查一段代码。通常代码的作者也参加复查，帮助解释代码做了些什么。&lt;br/&gt;
&lt;br/&gt;
代码复查可以只在两三个程序员的范围内进行，也可以在整个小组范围内进行。小的、非正式的代码复查可以在开发过程的任何时候进行，而更正式一些复查被称为代码验收，是最终质量接收过程的一个重要部分。在开发过程中经常进行代码复查是很有用的，因为人们常常不会注意到自己的代码中的错误，他们与自己写的代码太接近了。</description></item><item><title>Javascript中的setTimeout和setInterval函数的区别</title><link>https://yanbin.blog/javascript-settimeout-setinterval/</link><pubDate>Tue, 04 Jul 2006 12:49:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/javascript-settimeout-setinterval/</guid><description>
从几年前(有5年多)一直到现在，在很多讲解Javascript的资料中看过setTimeout和setInterval这两个函数，一直就是以为它们俩的功能是完全一样的－－就是多久时间后执行一下了事，所以一直用的就是setTimeout函数，还从来在自己的代码中没用过setInterval函数，其实为什么就不再进一步想想，如果这两个函数功能完全一样，为啥有存在的合理性。&lt;/p&gt;
&lt;br/&gt;
到现在我才对两个函数的一致性表现出惯于怀疑来，到网上Google一下，才恍然大悟。原来是有区别的，首先从其中的两个单词来理解也可以看出一点名堂来：&lt;br/&gt;&lt;br/&gt;
Timeout：&amp;lt;主美&amp;gt;(球类比赛中的)暂时休息,(工作时的)工间休息；计算机里多为"超时"，或者"时间到了"的意思</description></item><item><title>在Java Swing中显示HTML网页，并能响应链接</title><link>https://yanbin.blog/java-swing-show-html/</link><pubDate>Fri, 30 Jun 2006 02:46:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-swing-show-html/</guid><description>
如果做过Java Swing开发的人应该知道，可以应用HTML标签来给控件增色，如
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//必须用&amp;lt;html&amp;gt;和&amp;lt;/html&amp;gt;包起来&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;JLabel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JLable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;html&amp;gt;&amp;lt;font color=red size=3&amp;gt;RED&amp;lt;/font&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
如果是完整一个HTML格式文件在Java Swing中应该如何显示出来呢？那就要用到强劲的编辑器控件JEditPane了。JEditorPane是Swing中一款非常强大的文本编辑控件，在JEditorPane中，我们完全可以将HTML文件或RTF格式的文件直接显示出来，但是它还不能完整地支持HTML的所有标准。支持HTML3.2标准的语法，对CSS和JavaScript就支持的不好，请掂量着使用CSS和JavaScript某些特性。&lt;br/&gt;&lt;br/&gt;
如果仅仅在JEditPane中显示网页，代码非常简单，只需以下四行代码：</description></item><item><title>Log4J 输出日志时是如何获知当前方法、行号的</title><link>https://yanbin.blog/log4j-get-line-number-method/</link><pubDate>Sun, 25 Jun 2006 05:43:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/log4j-get-line-number-method/</guid><description>
记得最早在项目中用Log4j的时候，也是觉得在每一个要记载日志的类中或者某个上层类中要声明一个Logger属性挺麻烦的&lt;/p&gt;
&lt;br/&gt;
注(2018-06-30)：本文所展示的 Log4J 如何定们位置信息可参考 Log4J 1.2.13 的源代码，新代码可能有出入。&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
或者&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
于是就写了一个自己的Logger类UnmiLogger，也就是把Log4j的Logger包装一下，直接调用Logger的日志输出方法，info、debug、warn等，只不过在UnmiLogger中把日志输出方法都改为静态的，让在别的代码中方便使用。</description></item><item><title>收集的一些有关中文分词的资源</title><link>https://yanbin.blog/chinese-analyzer-resources/</link><pubDate>Sat, 24 Jun 2006 07:27:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/chinese-analyzer-resources/</guid><description>
&lt;span id="showcontent"&gt; 1. &lt;a href="http://drupal.org/node/53886"&gt;Chinese Word Splitter(中文分词)&lt;/a&gt;  (*)&lt;br/&gt;
 2. &lt;a href="http://www.lietu.com/doc/index.htm"&gt;猎兔--中文分词技术文档&lt;/a&gt; (*)&lt;br/&gt;
 3. &lt;a href="http://www.jesoft.cn/posts/list/5.page"&gt;Lucene中文分词组件 JE-Analysis 1.3.1&lt;/a&gt; (*)&lt;br/&gt;
 4. &lt;a href="http://www.ieee.org.cn/dispbbs.asp?boardID=2&amp;amp;ID=25555"&gt;几个免费的中文分词模块&lt;/a&gt;&lt;br/&gt;
 5. &lt;a href="http://www.nlp.org.cn/project/project.php?proj_id=6"&gt;计算所汉语词法分析系统 ICTCLAS&lt;/a&gt; (*)&lt;br/&gt;
 6. &lt;a href="http://www.xml.org.cn/printpage.asp?BoardID=2&amp;amp;id=11502"&gt;fenci，Java 的 ICTCLAS&lt;br/&gt;
&lt;/a&gt; 7. AutoSplit，另一个 Java 的 ICTCLAS&lt;br/&gt;
 8. &lt;a href="http://www.donews.net/accesine"&gt;小叮咚中文分词　介绍页面&lt;/a&gt;</description></item><item><title>运行 Think In Java中的 RMI 示例可能产生的几种异常及解决</title><link>https://yanbin.blog/think-in-java-rmi-exception/</link><pubDate>Sat, 24 Jun 2006 04:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/think-in-java-rmi-exception/</guid><description>
在Think In Java里有一个示例 RMI 的代码 PerfectTime, 可是其中并没有把有些执行细节说明了，也就可以造成新手运行它会出某些异常而不知所措。下面将列出可能产生的异常，并说明解决办法，如读者在执行当中还遇到其他未列出的异常，可留言告知，吾将尽力而为。&lt;br/&gt;
&lt;br/&gt;
首先把代码PerfectTime和DisplayPerfectTime 中的&lt;span style="color: #004488;"&gt;//colossus:2005/PerfectTime&lt;/span&gt;改为&lt;span style="color: #004488;"&gt;//localhost:2005/PerfectTime&lt;/span&gt; ,因为colossus为机器名，所以改为localhost指向本机，不然找不到主机colossus的。&lt;br/&gt;
&lt;br/&gt;
已经用命令 RMIC 生成PerfectTime_Stub.class，并且执行了命令 rmiregistry 2005</description></item><item><title>JAVA RMI 快速入门实例</title><link>https://yanbin.blog/java-rmi-get-started/</link><pubDate>Sat, 24 Jun 2006 01:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-rmi-get-started/</guid><description>
本实例为参考多篇文章写就而成，网上及书上各类文章介绍如何使用RMI有多种实例可参考，譬如有：
&lt;br/&gt;
1. 用命令rmiregistry启动RMI注册服务的&lt;br /&gt;
2. 同时创建存根(stub)和骨架(skeleton)的&lt;br /&gt;
3. 只创建存根类的(jdk1.2以后版本)&lt;br /&gt;
4. 通过RemoteRef和rmi://协议字串方式的&lt;br /&gt;
5. 比较少讲到的用LocateRegistry直接在代码上启动RMI注册服务的。&lt;br/&gt;&lt;br/&gt;
以上描述并非明显分类，比如，你总是可以选择用rmiregistry或者代码LocateRegistry启动RMI注册服务&lt;br/&gt;&lt;br/&gt;
下面我将介绍一个完整的实例，让初学者能快速体验RMI的功用。&lt;br/&gt;&lt;br/&gt;
分为以下四个步骤</description></item><item><title>你应该知道的10件关于Java 6的事情 [转]</title><link>https://yanbin.blog/maybe-unknown-java6-10-thing/</link><pubDate>Thu, 22 Jun 2006 13:26:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/maybe-unknown-java6-10-thing/</guid><description>
&lt;a href="http://java.sun.com/developer/technicalArticles/J2SE/Desktop/mustang/beta2.html"&gt;http://java.sun.com/developer/technicalArticles/J2SE/Desktop/mustang/beta2.html&lt;/a&gt; 一文中描述了你应该知道的10件关于Java 6的事情，下面总结如下&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;1.Web Services.&lt;/strong&gt; 优先支持编写 XML web service 客户端程序。你可以用过简单的annotaion将你的API发布成.NET交互的web services. Mustang 添加了新的解析和 XML 在 Java object-mapping APIs中, 之前只在Java EE平台实现或者Java Web Services Pack中提供.&lt;br/&gt;
&lt;br/&gt;
&lt;strong&gt;2.Scripting.&lt;/strong&gt; 现在你可以在Java源代码中混入JavaScript了，这对开发原型很有有用，你也可以插入自己的脚本引擎。</description></item><item><title>JavaScript：世界上误解最深的语言 [转]</title><link>https://yanbin.blog/deeply-misunderstand-javascrip/</link><pubDate>Wed, 21 Jun 2006 00:31:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/deeply-misunderstand-javascrip/</guid><description>
&lt;a href="http://www.crockford.com/javascript"&gt;JavaScript&lt;/a&gt;, aka Mocha, aka LiveScript, aka JScript, aka ECMAScript, is one of the world's most popular programming languages. Virtually every personal computer in the world has at least one JavaScript interpreter installed on it and in active use. JavaScript's popularity is due entirely to its role as the scripting language of the WWW.&lt;br/&gt;
&lt;br/&gt;
&lt;a href="http://www.crockford.com/javascript"&gt;JavaScript&lt;/a&gt;，或者叫 Mocha，或者叫 LiveScript，或者叫 JScript，又或者叫 ECMAScript，是世界上最流行的编程语言之一。事实上世界上的每一台个人电脑都安装并在频繁使用至少一个JavaScript解释器。JavaScript的流行完全是由于他在WWW脚本语言领域中的地位决定的。</description></item><item><title>以一个最简单的例子把OO的JavaScript说明白[转]</title><link>https://yanbin.blog/simple-oo-javascript-sample/</link><pubDate>Wed, 21 Jun 2006 00:17:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/simple-oo-javascript-sample/</guid><description>
以一个最简单的例子把OO的JavaScript说明白 OO的JavaScript并不高深，麻烦就麻烦在google出来的国人介绍文章经常罗罗嗦嗦，而且之间的说法还各有不同，摆在一起就让人看了头大。&lt;br /&gt;
这里重拾简单主义，以一个最简单的例子把OO Javascript说明白。&lt;/p&gt;
&lt;br/&gt;
&lt;strong&gt;1.一个颇为精简的例子&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;
只需理解三个关键字:&lt;br /&gt;
第一个是function ，JS世界里Class的定义用'function'，function里面的内容就是构造函数的内容。</description></item><item><title>单元测试培训 笔记 2004-5-9</title><link>https://yanbin.blog/unit-test-training2004-5-9/</link><pubDate>Mon, 19 Jun 2006 21:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/unit-test-training2004-5-9/</guid><description>
培训日期：2004/5/9&lt;/p&gt;
&lt;br/&gt;
用系统测试的方法来测试单元&lt;br /&gt;
每30行代码要出一个测试用例 -- 单元测试&lt;br /&gt;
每50行代码要出一个测试用例 -- 系统测试&lt;br/&gt;&lt;br/&gt;
检视代码，测试用得好，设计是浪费时间&lt;br /&gt;
黑盒是基础，点80%用例，白盒是补充&lt;br/&gt;&lt;br/&gt;
代码覆盖应该有一个标准</description></item><item><title>使用Lucene的两段精要代码</title><link>https://yanbin.blog/lucene-two-code/</link><pubDate>Mon, 19 Jun 2006 21:16:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/lucene-two-code/</guid><description>
Lucene简介：&lt;br /&gt;
Lucene是一个基于Java的全文索引工具包。Lucene的发展历程：早先发布在作者自己的&lt;a href="http://www.lucene.com"&gt;www.lucene.com&lt;/a&gt;，后来发布在SourceForge，2001年年底成为APACHE基金会jakarta的一个子项目：&lt;a href="http://jakarta.apache.org/lucene/"&gt;http://jakarta.apache.org/lucene/&lt;/a&gt;.&lt;br /&gt;
Jive,Eyebrows,Cocoon,Eclipse中都使用了Lucene&lt;br /&gt;
全文检索和数据库应用最大的不同在于：让最相关的头100条结果满足98%以上用户的需求&lt;/p&gt;
&lt;br/&gt;
索引过程中可以看到：&lt;br /&gt;
    * 语言分析器提供了抽象的接口，因此语言分析(Analyser)是可以定制的，虽然lucene缺省提供了2个比较通用的分析器 SimpleAnalyser和StandardAnalyser，这2个分析器缺省都不支持中文，所以要加入对中文语言的切分规则，需要修改这2个分析器。或者建立自己的语言分析器</description></item><item><title>拾谈"用最有效率的方法算出2乘以8等於几?"</title><link>https://yanbin.blog/2x8-why-discuss/</link><pubDate>Sun, 18 Jun 2006 10:13:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/2x8-why-discuss/</guid><description>
这是网上流传的"&lt;a href="http://forum.gaya.cn/read.php?fid=12&amp;amp;tid=508"&gt;变态级JAVA程序员面试32问&lt;/a&gt;"的其中一题（二十八题），然后下面给出来的答案是&lt;br/&gt;
&lt;br/&gt;
第二十八，编程题: 用最有效率的方法算出2乘以8等於几?&lt;br/&gt;
有C背景的程序员特别喜欢问这种问题。&lt;br/&gt;
&lt;br/&gt;
2 &amp;lt;&amp;lt; 3&lt;br/&gt;
&lt;br/&gt;
粗看似乎很在理，大致想来2&amp;lt;&amp;lt;3会是移位操作，在Java的字节码中移位指令是ishl（右移），而在CPU上的硬件指令可能就会是shl（算术右移指令）。其实不然，如果熟悉汇编语言，还考虑过编译优化，2&amp;lt;&amp;lt;3根本不会使用移位操作，而是在编译时就优化计算出16来了。&lt;br/&gt;
&lt;br/&gt;
但如果是写成这样的方式（int i = 2; int j = i&amp;lt;&amp;lt;2;），又是不一样的，大家可以从代码不同写法生成的Java操作指令或汇编指令看出个端倪。</description></item><item><title>怎样成为优秀的软件模型设计者[摘]</title><link>https://yanbin.blog/pretty-soft-model-designer/</link><pubDate>Fri, 16 Jun 2006 06:33:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/pretty-soft-model-designer/</guid><description>
我们期待自己成为一个优秀的软件模型设计者，但是，要怎样做，又从哪里开始呢？将下列原则应用到你的软件工程中，你会获得立杆见影的成果。&lt;br/&gt;
&lt;br/&gt;
1. 人远比技术重要&lt;br/&gt;
&lt;br/&gt;
你开发软件是为了供别人使用，没有人使用的软件只是没有意义的数据的集合而已。许多在软件方面很有成就的行家在他们事业的初期却表现平平，因为他们那时侯将主要精力都集中在技术上。显然，构件（components），EJB（Enterprise Java Beans）和代理（agent）是很有趣的东西。但是对于用户来说，</description></item><item><title>白话软件架构与架构师 [转]</title><link>https://yanbin.blog/talk-software-architec/</link><pubDate>Fri, 16 Jun 2006 06:29:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/talk-software-architec/</guid><description>
架构一词是舶来品，是architecture的中文翻译, 其英文的本意是来源于建筑行业的建筑艺术、建筑（风格）和结构，引入到软件领域里面来以后，并没有一个统一的定义。有的人将架构定义为：功能+设计+构造手段，我们可以通俗的理解为：总体设计和总体结构。&lt;br/&gt;
&lt;br/&gt;
买过房子的人都知道5层以下的楼房一般是砖混结构，而高层和小高层的楼房都是框架结构，楼层越高对结构要求越高。软件也是一样，系统越庞大，生命周期越长，结构的重要性就越明显。因此，随着人们对软件工程的深刻理解，将架构进行充分的强调是很自然的，正如人们越来越强调系统的需求分析，从而有了领域工程师和领域专家的概念一样。其实强调软件架构的最主要的目的有3个：</description></item><item><title>C# 及其特性</title><link>https://yanbin.blog/csharp-properties/</link><pubDate>Sat, 03 Jun 2006 23:18:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-properties/</guid><description>
1.C#是一种从C++和Java继承而来的,简单的,现代的,面向对象的语言.&lt;br /&gt;
2.它的目标是综合Visual Basic高产和C++底层高效的特性.&lt;br /&gt;
3.它是Microsoft Visual Studio7.0 的一部分.&lt;br /&gt;
4.Visual studio支持Vb,VC++,C++,Vbscript,Jscript.所有这些语言提供对Microsft .NET平台的访问.&lt;br /&gt;
5..NET包括一个Common Execution引擎和一个丰富的类库.&lt;br /&gt;
6.Microsofts JVM eqiv就是Common language(通用语言)运行库(CLR).&lt;br /&gt;
7.CLR支持不止一种语言,诸如 C#,VB.NET,Jscript,ASP.NET,C++.&lt;br /&gt;
8.源代码---&amp;gt;中间语言代码(IL)---&amp;gt;(JIT 编译器)机器码.</description></item><item><title>C#调用非托管动态库中的函数</title><link>https://yanbin.blog/csharp-call-unmanaged-library/</link><pubDate>Sat, 03 Jun 2006 17:32:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/csharp-call-unmanaged-library/</guid><description>
C#如何调用一个非托管动态库中的函数呢，比如用VC6写的动态库，总之C#调用动态库的过程是比Java调用DLL动态库方便快捷多了，下面举例说明这个过程。&lt;/p&gt;
&lt;br/&gt;
1。创建一个非托管动态库&lt;br/&gt;&lt;br/&gt;
代码如下：&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;   &lt;span class="c1"&gt;//这一句是声明动态库输出一个可供外不调用的函数原型.  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;   &lt;span class="kd"&gt;extern&lt;/span&gt;   &lt;span class="s"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;  &lt;span class="n"&gt;__declspec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dllexport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;  &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;   &lt;span class="p"&gt;{&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;        &lt;span class="c1"&gt;//实现这个函数returna+b;  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;   &lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
注意上面代码，一定要加上　extern"C"　，不能生成的动态库中的导出函数名就不会是add，而是像　?add@@YAHHH@Z　样子，后面只是通过函数名 add 来定位函数入口就会出问题。&lt;br /&gt;</description></item><item><title>使用JNative,在Java中传递一个C/C++结构参数到动态库中</title><link>https://yanbin.blog/jnative-java-pass-struct/</link><pubDate>Thu, 18 May 2006 10:38:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jnative-java-pass-struct/</guid><description>
&lt;p&gt;用C++写的动态库的导出方法的代码如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;TestStruct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;C&amp;#34;&lt;/span&gt; &lt;span class="kr"&gt;__declspec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dllexport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;TestStruct&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ShowStruct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestStruct&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;testStruct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;在动态库中打印：传入动态库中的结构成员a的值为:&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;testStruct&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//改变传入结构成员a的值为100
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;testStruct&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;testStruct&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>Java调用动态库最简便方法和最好用的组件</title><link>https://yanbin.blog/java-jnative-dll-concise/</link><pubDate>Wed, 17 May 2006 13:09:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-jnative-dll-concise/</guid><description>
记得以前 Java 要调用C/C++写得动态库都是用的 JNI 方式，还需要自己写不少 C/C++ 代码。比如说要在Java中调用已有的动态库，如 Windows 的 user32.dll 的方法 MessageBoxA，具体步骤是 Java 中声明一个 native 方法，然后用 javah 命令生成JNI样式的头文件，再自己实现头文件中声明的方法，在实现方法中装载动态库 user32.dll，调用 MessageBoxA 方法，需要把自己写的这部分 C/C++ 代码封装成一个动态库，如Sample.dll，最后在 java  中装载 Sample.dll，然后执行其中所声明的本地方法。&lt;br/&gt;
&lt;br/&gt;
可见，用老实的JNI方式，我们在调用一个已知动态库的时候还需要额外生成一个符合JNI规则的动态库作为桥梁，显得有点多余了。</description></item><item><title>VC6.0编译时出现"Error spawning cl.exe"及解决方法</title><link>https://yanbin.blog/vc6-errorn-spawning-cl-exe/</link><pubDate>Wed, 17 May 2006 10:37:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vc6-errorn-spawning-cl-exe/</guid><description>
我安装了VC　6.0后，写一段简单的"Hello World"程序，点击 Build 按钮，信息栏里只出现"&lt;strong&gt;Compiling...,Error spawning cl.exe&lt;/strong&gt;"错误提示。原来那么久可没碰到过这种错误，一下子有点蒙了，没法，很容易就有产生重新安装VC来解决。&lt;/p&gt;
&lt;br/&gt;
重新安装的话太麻烦，还是先到网上找找有没有相应的解决方案，把出错提示放网上google一下，就查到&lt;a href="http://www.mscenter.edu.cn/blog/k_eckel/articles/3016.html"&gt;http://www.mscenter.edu.cn/blog/k_eckel/articles/3016.html&lt;/a&gt;　的一篇文章提供了方法。</description></item><item><title>管理和IT的对话</title><link>https://yanbin.blog/management-it-conversation/</link><pubDate>Tue, 16 May 2006 06:25:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/management-it-conversation/</guid><description>
有一个人坐热气球在天空中飘浮，不觉间迷了路，他把热气球下降少许，向地面上的一位路人问路。&lt;br/&gt;
「对不起！你能否告诉我，我现在身处何方吗？」&lt;br/&gt;
「你现在正坐在热气球上，离地面约3米。」路人说。&lt;br/&gt;
「先生，我猜你一定是从事IT行业的。」热气球上的人说。&lt;br/&gt;
「对啊！你为甚么会知道的？」&lt;br/&gt;
「因为你给我的答复很技术性，但完全没有用。」&lt;br/&gt;
「先生，我也猜猜你的职业吧！你一定是做管理的。」路人说。&lt;br/&gt;
「对呀！你为甚么会知？」&lt;br/&gt;
「Well！因为你不知自己在哪？也不知自己应往哪方走，但你却希望我帮你解决问题，你现在的处境和先前没有两样，但责任已归咎在我身上。」</description></item><item><title>在VS.NET的VC++中运行控制台程序后暂停</title><link>https://yanbin.blog/vs-net-vc-console-pause/</link><pubDate>Fri, 12 May 2006 22:21:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vs-net-vc-console-pause/</guid><description>
在VC++6.0中运行控制台程序后，执行完后控制台会出现       Press any key to continue . . .  按任意键，控制台才消失。&lt;br/&gt;
&lt;br/&gt;
但是换到在VC++7.0 (VS.NET的VC++)中编写的控制台程序，点击运行后，默认情况下马上把控制台关了，让你看不到输出结果。那么在这个IDE下如何让 Press any key to continue . . . 再次出现呢？&lt;br/&gt;
&lt;br/&gt;
C++中实现"任意键暂停"对于要直接查看输出结果时很重要的，比如你用的是BC编辑器，不能像VC那样自己实现程序运行完后暂停，而是跳回到编辑状态，根本看不到运行结果。&lt;br/&gt;
&lt;br/&gt;
有的人会用#include&amp;lt;conio.h&amp;gt;加上getch();来实现，其实完全不需要的……&lt;br/&gt;
&lt;br/&gt;
用VC或BC编辑C++程序，不用像C语言中加入头文件conio.h ，然后用getch()来实现任意键暂停。&lt;br/&gt;
&lt;br/&gt;
用system("pause");这个语句就可以，不过如果你想改变文字还是要用上面的办法。&lt;br/&gt;
&lt;br/&gt;
其实在C语言中的stdlib.h的头文件中就有这个函数。</description></item><item><title>Portal 的概念和内涵 [转]</title><link>https://yanbin.blog/portal-concept/</link><pubDate>Wed, 26 Apr 2006 16:01:00 -0500</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/portal-concept/</guid><description>
Portal一词原来是"门户网站"的意思，如YAHOO！、AltaVista、SINA这样的网站。而对于企业信息化而言，它扮演的角色则有所不同。对企业来说，建立Portal的意义在于可以使员工共享各种系统和信息资源，并对其进行统一的管理。对员工来说，通过Portal可以了解和掌握有关企业的各种信息、参加讨论、协同工作等。Portal自动将分散于企业各处的信息资源整理并发送给员工，真正实现信息找人的目的。&lt;br/&gt;
&lt;br/&gt;
Portal现在已经成为企业最关注的领域之一，是实现企业整合的第一步。其重要价值在于它是企业现有投资与新投资的集成节点，使用户能够与人、内容、应用和流程进行个性化的、安全的、单点式的互动交流。</description></item><item><title>软件业中的编码（Coding）与开发（Development）的区别</title><link>https://yanbin.blog/it-coding-development/</link><pubDate>Sat, 25 Mar 2006 09:21:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/it-coding-development/</guid><description>
跑到一个新公司，忽然发现自己真变成IT民工了，那里边管没有一官半职的开发人员称为做Coding的&lt;br/&gt;
Coding是什么？Coding就是代码工人，难道他们所谓的做Coding的真的不需要半点儿创造力吗？&lt;br/&gt;
除非你把详细设计做的非常好，那直呆下去，只要我们做重复性的事情吗，那我更不愿干了&lt;br/&gt;
先前对软件事业的狂热难道就此被打压下去吗？&lt;br/&gt;
&lt;br/&gt;
编码（Coding）有一种"把已经存在的设计机械化地翻译成计算机语言"的意味&lt;br/&gt;
而开发（Development）并不都这么机械化的，需要可观的创造力的判断力</description></item><item><title>常用log4j配置</title><link>https://yanbin.blog/log4j-common-config/</link><pubDate>Thu, 23 Mar 2006 10:35:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/log4j-common-config/</guid><description>
常用log4j配置，一般可以采用两种方式，.properties和.xml,下面举两个简单的例子：
&lt;br/&gt;&lt;br/&gt;
最简单的配置是只要取下面的log4j.properties文件中带下划线的四行就行，它完成日志输出到控制台。&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;一、log4j.properties&lt;/strong&gt;</description></item><item><title>关于 Log4j 比较全面的配置</title><link>https://yanbin.blog/log4j-full-config/</link><pubDate>Thu, 23 Mar 2006 10:16:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/log4j-full-config/</guid><description>
LOG4J的配置之简单使它遍及于越来越多的应用中了：Log4J配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。择其一二使用就够用了</description></item><item><title>不自觉又喜欢上了NetBeans IDE5.0(Build 200601251500)</title><link>https://yanbin.blog/like-netbeans-ide/</link><pubDate>Tue, 07 Mar 2006 11:05:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/like-netbeans-ide/</guid><description>
曾经是Eclipse的拥趸,今天晚上在网上Down了一个NetBean 5.0，安装到电脑上，打开试用了一下，确实比以前版本快很多，而且功能也非常的丰富，直接支持EJB开发，AJAX，JSF1.1，Struts都到1.2.7了，还要看看Struts低版本是如何支持的，WebService的开发；还有把源文件输出为HTML格式的功能，导出要展示的代码很有用的。&lt;br/&gt;
&lt;br/&gt;
NetBean也是免费的，虽说Eclipse也是免费的，但Eclipse还得倚仗着盗版的MyEclipse才能做实际的开发；NetBean也有插件啊。看来一被忽略的NetBean需要好好去发掘，以后在本机学习、测试、验证代码的话我就在NetBean下进行了。</description></item><item><title>使用Java的反射调用方法应注意的异常处理</title><link>https://yanbin.blog/java-reflect-exception-handle/</link><pubDate>Tue, 07 Mar 2006 10:07:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-reflect-exception-handle/</guid><description>
先看下面的代码，看看程序执行会是什么样的结果：
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.lang.reflect.Method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * @author Unmi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExceptionTest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;foo1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Exception Type: MyException&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Exception Type: Exception&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ExceptionTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDeclaredMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;foo2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//注意调用foo2时，foo2方法会抛出MyException异常&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;foo2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//foo2方法直接抛出异常&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//一个自定义的异常&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;</description></item><item><title>纯Java程序读取DBF文件型数据库</title><link>https://yanbin.blog/pure-java-access-dbf/</link><pubDate>Fri, 03 Mar 2006 11:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/pure-java-access-dbf/</guid><description>
还记得那个古老的Foxbase和Foxpro产生的数据库吗？与access（MDB）类似的文件型数据库，可是有些地方有人还在用它。DBF数据库一般都是在Foxbase或Foxpro中或由它们生成的应用程序来访问，也可以通过建立ODBC的方式来访问。&lt;/p&gt;
&lt;br/&gt;
Java要访问什么数据库第一个念头就会是有没有相应的驱动啊，有驱动那好办，若是没有驱动那能不能通过配置ODBC，用JDBC-ODBC桥来访问呢？当然Java访问DBF文件是可以通过JDBC－ODBC的方式，但这样的话是不是纯Java实现还说，倒是得为要访问的DBF文件建一个数据源麻烦，尤其是程序移植时。</description></item><item><title>Every day English（2005-12-16–2006-01-25）</title><link>https://yanbin.blog/everyday-english-2005-12-16/</link><pubDate>Wed, 25 Jan 2006 03:53:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-12-16/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-12-16&lt;br/&gt;
&lt;/span&gt;&lt;br/&gt;
    1) Are all the utilities included?/这房子免付水电费？&lt;br/&gt;
    2）Are pets allowed in the apartment?/可以饲养宠物吗？&lt;br/&gt;
    3）I can't afford a moving company to help me move./我请不起搬家公司帮我搬家。&lt;br/&gt;
    4）When will it be available for me to move in?/我什么时候可以搬进来？&lt;br/&gt;
    5）I prefer a furnished apartment over an unfurnished one./含家具和不含家具的房子，我选前者。</description></item><item><title>IBM软件倾力打造5朵小花 10年完成45项收购 [转]</title><link>https://yanbin.blog/five-flowers-of-ibm/</link><pubDate>Mon, 23 Jan 2006 21:52:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/five-flowers-of-ibm/</guid><description>
当郭士纳(Lou Gerstner)在Comdex大会上用一场划时代的讲演，展望以Web为中心的计算技术的前景后，IBM软件集团应运而生。从1995年成立到今年，IBM软件集团已经走过了10个年头，并且，IBM软件集团已经成为全球第二大软件实体。&lt;br/&gt;
&lt;br/&gt;
　　上世纪90年代中期，是IBM历史上的一道分水岭。以PC为代表的信息产业革命根本改变人们的工作和生活方式，一大批IT企业抓住机遇，崛起于阡陌之间。然而那时的蓝色巨人却似乎已经失去了往日容光，IBM固守着它的硬件，在新的信息产业革命的潮流到来之际，驻足不前。在爆炸般成长的新生市场中，IBM几乎没有取得令人印象深刻的成功。</description></item><item><title>Oracle中的Java存储过程的操作权限</title><link>https://yanbin.blog/oracle-java-permissions/</link><pubDate>Mon, 16 Jan 2006 00:47:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle-java-permissions/</guid><description>
当我们在Oracle中用Java写的存储过程方法中，如果访问了本地文件系统、连接了网络、或者运行系统命令，就必须为执行该过程的模式赋予相应的权限。不然执行该存储过程就会报无该操作权限的异常。&lt;/p&gt;
&lt;br/&gt;
本文所面对的是哪些会用Java写存储过程的朋友，可能你们在用Java写Oracle的存储过程也遇到过类似问题。希望对他们能有所帮助。关于如何用Java写Oracle的存储过程请参考我前面的一篇文章：&lt;a href="https://yanbin.blog/oracle8i-develop-java-procedure/"&gt;Oracle8i中使用Java语言来开发存储过程&lt;/a&gt;，其中有相应的代码。</description></item><item><title>Oracle8i中使用Java语言来开发存储过程</title><link>https://yanbin.blog/oracle8i-develop-java-procedure/</link><pubDate>Sun, 15 Jan 2006 22:12:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/oracle8i-develop-java-procedure/</guid><description>
在Oracle8i之前，开发人员只能使用PL/SQL来开发存储过程。而在Oracle8i之中，不仅可以使用原有的PL/SQL开发存储过程，而且也可以使用Java语言来开发存储过程。本篇文章将简单介绍关于这方面的知识，包括以下内容：(灰色显示的部分在此将不作详细介绍)&lt;/p&gt;
&lt;br/&gt;
本文是我参考网上资料，整理出来的一份，文中所有代码语句均真实可用，因只是作测试，所以代码未表现实际业务操作。&lt;br/&gt;&lt;br/&gt;
&lt;a href="#1"&gt;一. 存储过程简介&lt;/a&gt;&lt;br /&gt;</description></item><item><title>Every day English（2005-12-5–2005-12-13）</title><link>https://yanbin.blog/everyday-english-2005-12-5/</link><pubDate>Sat, 14 Jan 2006 01:31:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-12-5/</guid><description>
&lt;span style=" 10.5pt; font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"&gt;&lt;span style="color: #ff00ff;"&gt;2005-12-5&lt;br/&gt;
&lt;/span&gt;&lt;br/&gt;
&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;" lang="EN-US"&gt;    1) Should we order separately or as a group?/&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;"&gt;分开点还是合着点菜？&lt;br/&gt;
    &lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;" lang="EN-US"&gt;2&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;"&gt;）&lt;span lang="EN-US"&gt;It is economical to eat out with friends./&lt;/span&gt;跟朋友一起到外面吃比较经济。&lt;br/&gt;
    &lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;" lang="EN-US"&gt;3&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;"&gt;）&lt;span lang="EN-US"&gt;More rice, pls!/&lt;/span&gt;请加饭。&lt;br/&gt;
    &lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;" lang="EN-US"&gt;4&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;"&gt;）&lt;span lang="EN-US"&gt;Can we have the dishes in small portions?/&lt;/span&gt;菜的份量弄小份的可以吗？&lt;br/&gt;
    &lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;" lang="EN-US"&gt;5&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-family: Arial;"&gt;）&lt;span lang="EN-US"&gt;I will bring home the left overs in a box./&lt;/span&gt;我要把剩菜打包回家。&lt;br/&gt;
    &lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: Arial;" lang="EN-US"&gt;6&lt;/span&gt;&lt;span style=" 10pt; color: black; font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: Arial;"&gt;）&lt;span lang="EN-US"&gt;Don't pick your teeth publicly./&lt;/span&gt;别在公开场合剔牙。</description></item><item><title>强烈推荐几个比较好的Java代码查询网站 [转]</title><link>https://yanbin.blog/java-code-search/</link><pubDate>Wed, 11 Jan 2006 19:56:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/java-code-search/</guid><description>
[原文出处：&lt;a href="http://blog.csdn.net/javamxj/archive/2006/01/05/571368.aspx"&gt;http://blog.csdn.net/javamxj/archive/2006/01/05/571368.aspx&lt;/a&gt;]&lt;br/&gt;
&lt;br/&gt;
这些是我经常使用的一些Java代码查询网站，希望在这里能起到抛砖引玉的效果，大家群策群力，一起分享Java快乐。&lt;br/&gt;
 &lt;br/&gt;
  1. java2s  &lt;a href="http://www.java2s.com/"&gt;http://www.java2s.com/&lt;/a&gt;&lt;br/&gt;
   这个网站非常好，分成三大类，分别是Example、Products、 Articles，每个大类下又分别设许多小类，还有搜索功能，这样查找起来非常方便。。比如，如果要学习SWT/JFace，只要把Example下的SWT JFace Eclipse 研究一下也就可以了。另外，这个网站还有JavaScript DHTML、 C# / C Sharp、 C / ANSI-C、 SQL / MySQL等类。总之，非常好。</description></item><item><title>由StringBuffer和StringBuilder的区别而衍生的</title><link>https://yanbin.blog/stringbuffer-and-stringbuilder/</link><pubDate>Wed, 11 Jan 2006 10:31:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/stringbuffer-and-stringbuilder/</guid><description>
认识StringBuilder大概是在一年前的八九月份里，还是在JDK1.5从未正式发布始，一直都是关注的它的几大显著特性--泛型、静态引入、装拆箱和不定参等，某些比较细微的差别却没注意到。&lt;/p&gt;
&lt;br/&gt;
    大家都知道处理字符串连接时StringBuffer比String效率高，因为从编译的字节码中也可以看到String的相加也是要转换为StringBuffer来进行的。&lt;br/&gt;&lt;br/&gt;
    比如对于这几句代码  &lt;span style="color: #ff0000;"&gt;①&lt;/span&gt;String s = &lt;span style="color: #0066ff;"&gt;"Hello"&lt;/span&gt;;  &lt;span style="color: #ff0000;"&gt;②&lt;/span&gt;s=s+&lt;span style="color: #0066ff;"&gt;" Java"&lt;/span&gt;;  &lt;span style="color: #ff0000;"&gt;③&lt;/span&gt;s=s+&lt;span style="color: #0066ff;"&gt;" World!"&lt;/span&gt;; 反编译之后从字节码可以看出虚拟机实际是如何处理的（具体字节码这里就略去了，对于详细字节码的分析我想如果需要的时候以后会单独写一篇来介绍）：&lt;br /&gt;</description></item><item><title>Every day English（2005-11-25–2005-12-2）</title><link>https://yanbin.blog/everyday-english-2005-11-25/</link><pubDate>Wed, 11 Jan 2006 01:27:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-11-25/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-11-25&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
    1) Don't go out by yourself at night!/晚上别一个人出门。&lt;br/&gt;
    2）This is truly an &lt;span style="color: #ff0000;"&gt;eye opening experience&lt;/span&gt; for me./这经历真是令我大开眼界。&lt;br/&gt;
    3）Hold the &lt;span style="color: #ff0000;"&gt;saliva&lt;/span&gt;!/不要把口水喷出来！&lt;br/&gt;
    4）I am &lt;span style="color: #ff0000;"&gt;out of breath&lt;/span&gt;!/我喘不上气了！&lt;br/&gt;
    5）You are singing &lt;span style="color: #ff0000;"&gt;off-key&lt;/span&gt;./你唱歌走调。</description></item><item><title>精彩开发网站链接</title><link>https://yanbin.blog/wonderful-dev-links/</link><pubDate>Mon, 09 Jan 2006 23:35:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/wonderful-dev-links/</guid><description>
&lt;table style="border-collapse: collapse;" border="1" width="100%"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
一、技术门户类&lt;br/&gt;
1、微软开发者网络(MSDN)：&lt;a href="http://msdn.microsoft.com/"&gt;http://msdn.microsoft.com/&lt;/a&gt;&lt;br/&gt;
2、IBM开发者网络：&lt;a href="http://www.ibm.com/developerworks/"&gt;http://www.ibm.com/developerworks/&lt;/a&gt;&lt;br/&gt;
3、Borland开发者网络(BDN)：&lt;a href="http://bdn.borland.com/"&gt;http://bdn.borland.com/&lt;/a&gt;&lt;br/&gt;
4、SUN开发者网络(SDN) ： &lt;a href="http://developers.sun.com/"&gt;http://developers.sun.com/&lt;/a&gt;&lt;br/&gt;
5、塞迪网：&lt;a href="http://www.ccidnet.com/"&gt;http://www.ccidnet.com/&lt;/a&gt;&lt;br/&gt;
6、计算机世界网：&lt;a href="http://www.ccw.com.cn/"&gt;http://www.ccw.com.cn/&lt;/a&gt;&lt;br/&gt;
7、中国软件网：&lt;a href="http://www.csdn.net/"&gt;http://www.csdn.net/&lt;/a&gt;
&lt;/td&gt;
&lt;td rowspan="2"&gt;二、软件工程类&lt;br/&gt;
1、UML播种机：&lt;a href="http://www.umlchina.com"&gt;http://www.umlchina.com&lt;/a&gt;&lt;br/&gt;
2、UML软件工程组织：&lt;a href="http://www.uml.org.cn"&gt;http://www.uml.org.cn&lt;/a&gt;&lt;br/&gt;
3、IT之源：&lt;a href="http://www.iturls.com"&gt;http://www.iturls.com&lt;/a&gt;&lt;br/&gt;
4、中国系统分析员协会网站：&lt;a href="http://www.csai.cn"&gt;http://www.csai.cn&lt;/a&gt;&lt;br/&gt;
5、软件工程专家网：&lt;a href="http://www.51cmm.com"&gt;http://www.51cmm.com&lt;/a&gt;&lt;br/&gt;
6、系统/软件工程研究与实践论坛：http:www.seforum.net/&lt;br/&gt;
7、软件分析与软件工程：&lt;a href="http://www.sa-se.com/"&gt;http://www.sa-se.com/&lt;/a&gt;&lt;br/&gt;
8、系统分析员之窗：&lt;a href="http：/www.sawin.cn/"&gt;http：//www.sawin.cn/&lt;/a&gt;&lt;br/&gt;
9、极限编程总站：&lt;a href="http://www.extremeprogramming.org/"&gt;http://www.extremeprogramming.org/&lt;/a&gt;&lt;br/&gt;
10、XP中国：&lt;a href="http://chinaxp.org"&gt;http://chinaxp.org&lt;/a&gt;&lt;br/&gt;
11、敏捷中国：&lt;a href="http://www.agilechian.org/"&gt;http://www.agilechian.org/&lt;/a&gt;&lt;br/&gt;
12、松耦合空间：&lt;a href="http://lcspace.nease.net"&gt;http://lcspace.nease.net&lt;/a&gt;&lt;br/&gt;
13、中构在线：&lt;a href="http://cbdonline.com.cn/"&gt;http://cbdonline.com.cn/&lt;/a&gt;&lt;br/&gt;
14、阎宏博士网站：&lt;a href="http://www.webendshere.com"&gt;http://www.webendshere.com&lt;/a&gt;&lt;br/&gt;
15、透明思考：&lt;a href="http://gigix.cool2u.net/"&gt;http://gigix.cool2u.net/&lt;/a&gt;&lt;br/&gt;
16、软件过程解决方案：&lt;a href="http://www.chinaspis.com"&gt;http://www.chinaspis.com&lt;/a&gt;&lt;br/&gt;
17、北京SPIN组织：&lt;a href="http://www.pkspin.org"&gt;http://www.pkspin.org&lt;/a&gt;&lt;br/&gt;
18、软件工程研究中心：&lt;a href="http://www.sercenter.com/"&gt;http://www.sercenter.com/&lt;/a&gt;&lt;br/&gt;
19、测试管理中心：&lt;a href="http://www.testmanager.com.cn"&gt;http://www.testmanager.com.cn&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;四、网络&lt;br/&gt;
1、中华蓝盟：&lt;a href="http://www.cn-lan.com/"&gt;http://www.cn-lan.com/&lt;/a&gt;五、安全&lt;br/&gt;
1、看雪学院：&lt;a href="http://www.pediy.com"&gt;http://www.pediy.com&lt;/a&gt;六、流媒体&lt;br/&gt;
1、搜新流媒体：&lt;a href="http://www.souxin.com/"&gt;http://www.souxin.com/&lt;/a&gt;&lt;br/&gt;
2、媒体：&lt;a href="http://www.liumeiti.cn/"&gt;http://www.liumeiti.cn/&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
七、其他&lt;br/&gt;
1、软件人生俱乐部：&lt;a href="http://www.winlic.com/"&gt;http://www.winlic.com/&lt;/a&gt;&lt;br/&gt;
&lt;br/&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;三、软件开发类&lt;br/&gt;
综合类&lt;br/&gt;
1、DPG联盟论坛：&lt;a href="http://www.xhstudio.net/"&gt;http://www.xhstudio.net/&lt;/a&gt;&lt;br/&gt;
2、软件编程资讯网：&lt;a href="http://www.cty99.com/"&gt;http://www.cty99.com/&lt;/a&gt;&lt;br/&gt;
C++&lt;br/&gt;
3、VC知识库：&lt;a href="http://www.vckabase.com"&gt;http://www.vckabase.com&lt;/a&gt;&lt;br/&gt;
4、Visual C++开发指南网站：www.vchelp.net&lt;br/&gt;
5、代码实验室：&lt;a href="http://wwwcode-labs.com/"&gt;http://wwwcode-labs.com/&lt;/a&gt;&lt;br/&gt;
6、程序家园：&lt;a href="http://www.vcfan.com/"&gt;http://www.vcfan.com/&lt;/a&gt;&lt;br/&gt;
7、C视点：&lt;a href="http://www.c-view.org/"&gt;http://www.c-view.org/&lt;/a&gt;&lt;br/&gt;
8、侯捷网站：&lt;a href="http://jjhou.csdn.net/"&gt;http://jjhou.csdn.net/&lt;/a&gt;&lt;br/&gt;
JAVA&lt;br/&gt;
9、J道：&lt;a href="http://www.jdon.com/"&gt;http://www.jdon.com/&lt;/a&gt;&lt;br/&gt;
10、Java中文站：&lt;a href="http://www.java-cn.com/"&gt;http://www.java-cn.com/&lt;/a&gt;&lt;br/&gt;
11、Java研究组织：&lt;a href="http://www.javareseach.org/"&gt;http://www.javareseach.org/&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
12、中国java手机网：&lt;a href="http://www.cnjm.net/"&gt;http://www.cnjm.net/&lt;/a&gt;&lt;br/&gt;
13、J2ME专业站点：&lt;a href="http://www.sowork..com/j2me/"&gt;http://www.sowork..com/j2me/&lt;/a&gt;&lt;br/&gt;
DELPHI&lt;br/&gt;
14、大富翁论坛：&lt;a href="http://www.delphibbs.com/"&gt;http://www.delphibbs.com/&lt;/a&gt;&lt;br/&gt;
15、黄昏狼制作室：&lt;a href="http://www.swaysoft.com/"&gt;http://www.swaysoft.com/&lt;/a&gt;&lt;br/&gt;
16、Delphi窑洞：&lt;a href="http://www.51delphi.com/"&gt;http://www.51delphi.com/&lt;/a&gt;&lt;br/&gt;
17、Delphi盒子：&lt;a href="http://www.2ccc.com/"&gt;http://www.2ccc.com/&lt;/a&gt;&lt;br/&gt;
中间件&lt;br/&gt;
18、灰狐动力：&lt;a href="http://www.huihoo.com/"&gt;http://www.huihoo.com/&lt;/a&gt;&lt;br/&gt;
WEB应用&lt;br/&gt;
19、web应用网：&lt;a href="http://www.webasp.net/"&gt;http://www.webasp.net/&lt;/a&gt;&lt;br/&gt;
20、中国JSP技术网站：&lt;a href="http://www.cnjsp.org/"&gt;http://www.cnjsp.org/&lt;/a&gt;&lt;br/&gt;
21、极限技术网：&lt;a href="http://www.alltips.com"&gt;http://www.alltips.com&lt;/a&gt;&lt;br/&gt;
专项开发网&lt;br/&gt;
22、驱动开发网：&lt;a href="http://www.driverdevelop.com/i"&gt;http://www.driverdevelop.com/i&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>如何查找JBoss所发布的JNDI资源</title><link>https://yanbin.blog/jboss-locate-jndi-resource/</link><pubDate>Mon, 09 Jan 2006 10:22:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jboss-locate-jndi-resource/</guid><description>
J2EE容器分为Servlet容器和EJB容器，例如Tomcat就是一个Servlet容器，WebLogic，WebSphere Application Server，JBoss就是EJB容器。他们都提供JNDI的支持，你可以把任何资源（如DataSource、JMS、Queue、Mail甚至是URL资源）都绑定到JNDI上下文中，这样可以降低组件间的耦合性。&lt;/p&gt;
&lt;br/&gt;
通常的Servlet容器（如Tomcat、Resin）中的JNDI资源只能被容器中的程序查到、使用，而不能在容器外引用，也就是只能被容器所以进程所用；而EJB容器中的JNDI资源却可以在容器外，或者是另一台机器上的程序查找到并透明使用，因为EJB本来就是要为分布式服务的。EJB容器中的JNDI资源可以通过JNP、RMI、IIOP、T3或文件引用的方式发布出去。</description></item><item><title>刚开始学习使用JBoss就被绊了一下</title><link>https://yanbin.blog/first-step-to-jboss/</link><pubDate>Sun, 08 Jan 2006 11:53:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/first-step-to-jboss/</guid><description>
刚开始碰到的一个问题是我JDK安装的是1.5，启动jboss-3.2.3就有问题，出现的错误提示是&lt;br /&gt;
&lt;br/&gt;
Failed to boot JBoss:&lt;br /&gt;
javax.management.InstanceNotFoundException: JMImplementation:service=LoaderRepository,name=Default&lt;br /&gt;
        at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(Unknown Source)&lt;br /&gt;
        at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(Unknown Source)&lt;br /&gt;
        at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(Unknown Source)&lt;br /&gt;
        at org.jboss.system.server.ServerImpl.initBootLibraries(ServerImpl.java:499)&lt;br /&gt;
        at org.jboss.system.server.ServerImpl.doStart(ServerImpl.java:335)</description></item><item><title>Every day English（2005-11-17–2005-11-24）</title><link>https://yanbin.blog/everyday-english-2005-11-17/</link><pubDate>Sun, 08 Jan 2006 01:18:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-11-17/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-11-17&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
老外不说：&lt;br/&gt;
&lt;br/&gt;
1）What time is convenient for you?(fault)  What time is &lt;span style="color: #ff0000;"&gt;good&lt;/span&gt; for you?(correct)/什么时间你有空？&lt;br/&gt;
&lt;br/&gt;
2）I slept early.(fault)  I &lt;span style="color: #ff0000;"&gt;went to bed&lt;/span&gt; early./我睡得早。&lt;br/&gt;
&lt;br/&gt;
词组：&lt;br/&gt;
&lt;br/&gt;
1）go jogging/慢跑（不说"run")</description></item><item><title>研究 WebSphere MQ (Workflow) 时在网上幸遇IBM技术支持工程师</title><link>https://yanbin.blog/websphere-mq-workflow-ibm-support/</link><pubDate>Fri, 06 Jan 2006 20:03:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/websphere-mq-workflow-ibm-support/</guid><description>
在公司加入了一个新的项目组，被分配去研究 WebSphere MQ Workflow，Workflow是有些基本的了解，但是MQ和JMS还不是很清楚，更主要的是环境不知道怎么去搭建，也不知道MQ与Workflow有什么关系。&lt;br/&gt;&lt;br/&gt;
    最开始只知道有一个软件是WebSphere MQ Workflow，于是安装它吧，没想我无论是选择安装服务器端还是客户端都提示我需要MQSeries这个东西，我该到哪儿去找这个东西呢？而此时我旁边的同事却把Workflow给装上了，怎么回事呢？他说他的添加删除程序里有一个WebSphere  MQ,他也不知道是几时安装的，查查安装路径才知道是WAS5.1自己带的一个MQ。于是我也装上WAS 5.1的MQ部件，果然Workflow也能安装上了。</description></item><item><title>在网上找到了一款非常好用的吉他调音器</title><link>https://yanbin.blog/guitar-sound-variator/</link><pubDate>Fri, 06 Jan 2006 08:32:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/guitar-sound-variator/</guid><description>
&lt;p&gt;
许久没摸过吉他了，忽一拿来连调音时耳朵都不那么顺了，于是就想：应该有一种电脑调音软件，只要我把声音输入到电脑，就会显示输入声音的音高，
这样岂不是非常简单又实用。Google一下"电脑 吉他 调音软件"，真找到了，是Dasein Guitar Tuner，一用，真是精彩超乎想象。 &lt;br /&gt;
&lt;br/&gt;
&lt;img src="http://images.blogcn.com/2006/10/22/9/unmi,2006102217399.jpg" align="right" border="0" /&gt;
下载页面：&lt;a href="http://web.my8d.net/gita2/soft.htm"&gt;http://web.my8d.net/gita2/soft.htm&lt;/a&gt;&lt;br /&gt;
下载地址1：&lt;a href="http://web.my8d.net/gita/download/dgt.zip"&gt;http://web.my8d.net/gita/download/dgt.zip&lt;/a&gt;&lt;br /&gt;
下载地址2：&lt;a href="http://web.my8d.net/gita2/download/dgt.zip"&gt;http://web.my8d.net/gita2/download/dgt.zip&lt;/a&gt;&lt;br /&gt;
下载地址3：&lt;a href="http://web.my8d.net/gita2/download/Ctuner2e.zip"&gt;http://web.my8d.net/gita2/download/Ctuner2e.zip&lt;/a&gt;&lt;br /&gt;
&lt;br/&gt;
地址1和2是英文版，释放Crack中文件到安装目录，执行其中的sdkill.exe就行了，地址3是一个中文乱码版，我用MagicWin都没能成功转码。
Dasein Guitar Tuner的使用界面如左边，多好啊，弹一下就知道是什么调，我用的是电箱琴，吉他直接连电脑，不用经MIC拾音。
看看左边我的E弦可是标准音了(指针指到正中央)。还有示范音，提供多种定调，如FADGBE、DADGBE和自定义方式。
就是你没有声卡都能从PC Speaker中发出声来，还有多种声音的显示方式，如Needle、Ellipse、Skyline。在某些有噪音背景下都能正常使用，真是考虑周到。快去体验一下吧！&lt;br /&gt;
&lt;br/&gt;
        六根弦全指向中央零，琶一下C和弦试，嗯！真是乐耳，这把破吉他恐怕是头一回让们所有的弦调到了标准音，感觉真是美妙极了。
以前的话别说每根弦上标准音，就连六根空弦音之间正确的间距度数都没达不到要求。&lt;br /&gt;
 　　我还想，要是找不到类似的软件，我自己也可以试着开发一个这样的东西来玩玩，应该不难吧，对声卡采集到声音进行声谱分析，再深入一下，未必就很简单，还是需要一点点的专业知识的。&lt;br /&gt;
在上面那个下载页面中还有两个节拍器软件可以下载：&lt;br /&gt;　　一个是：超方便的　&lt;a href="http://web.my8d.net/gita2/download/lala.ZIP"&gt;
http://web.my8d.net/gita2/download/lala.ZIP&lt;/a&gt;&lt;br /&gt;　　
另一个是：功能齐全的　&lt;a href="http://web.my8d.net/gita2/download/metrotim.zip"&gt;http://web.my8d.net/gita2/download/metrotim.zip&lt;/a&gt;
&lt;/p&gt;</description></item><item><title>Every day English（2005-11-8–2005-11-14）</title><link>https://yanbin.blog/everyday-english-2005-11-8/</link><pubDate>Thu, 05 Jan 2006 23:34:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-11-8/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-11-8&lt;/span&gt;    出席会议/attending a meeting&lt;br/&gt;
&lt;br/&gt;
    1) I would like you to attend the meeting./我希望你能参加这个会议。&lt;br/&gt;
    2）The president wish to see you at the meeting./总裁让你出席这次会议。&lt;br/&gt;
    3）I think I can attend./我想我能出席。&lt;br/&gt;
    4）I must be present./我一定出席。&lt;br/&gt;
    5）I'm terribly sorry, but I can't attend it since I have to be with a client./非常抱歉，我无法参加，因为到时候我得陪一位客户。&lt;br/&gt;
    6）Can I let others attend on my behalf?/我让别人代表我出席行吗？</description></item><item><title>一个切实可用的CheckStyle的build.xml文件</title><link>https://yanbin.blog/checkstyle-build-xml/</link><pubDate>Wed, 04 Jan 2006 09:13:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/checkstyle-build-xml/</guid><description>
网上找了好些个关于CheckStyle的文章都是抄抄抄一大通，却没有一个build.xml着实可运行,&lt;br /&gt;
在这里我也是参考着一编，改改成能成功运行并能生成正确报告，发送 Email 的build.xml文件&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="ni"&gt;&amp;amp;lt;&lt;/span&gt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;gb2312&amp;#34;?&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- ANT make file checkstype --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- See &amp;lt;a href=&amp;#34;http://jakarta.apache.org/ant&amp;#34; data-mce-href=&amp;#34;http://jakarta.apache.org/ant&amp;#34;&amp;gt;http://jakarta.apache.org/ant&amp;lt;/a&amp;gt; for info about ANT --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 网上下来的CheckStyle解压在d:/javalib/CheckStyle/checkstyle-4.1中 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;default=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;basedir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;d:/javalib/CheckStyle/checkstyle-4.1&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- CheckStyle配置,这里你替换成你实际的环境 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project.docs.dir&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/contrib&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 源代码的目录是e:/eclipseworkspace/talupdate/src --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project.src.dir&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;e:/eclipseworkspace/talupdate/src&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 建立了build目录在其中生成报告 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;project.checkstyleReport.dir&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/build&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.jar&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/checkstyle-all-4.1.jar&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 使用SUN的代码规范，可替换成公司自己的规范 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.config&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/sun_checks.xml&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.report.style&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${project.docs.dir}/checkstyle-noframes.xsl&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.result&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${project.checkstyleReport.dir}/checkstyle_result.xml&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.report&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${project.checkstyleReport.dir}/checkstyle_report.html&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 定义发送邮件列表 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mail.list&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(User1)user1@xxx.com,(User2)user2@xxx.com&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;target&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;init&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;tstamp/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!--CheckStyle脚步--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;taskdef&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyletask.properties&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;classpath=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.jar}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;target&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;depends=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;init&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;对java源代码进行检查并产生检查报告. &amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;checkstyle&lt;/span&gt; &lt;span class="na"&gt;config=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.config}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;failOnViolation=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;failureProperty=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.failure&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;formatter&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;xml&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;tofile=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.result}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${project.src.dir}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;includes=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;**/*.java&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/checkstyle&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 生成报告,其格式取决于${checkstyle.report.style} --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;style&lt;/span&gt; &lt;span class="na"&gt;in=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.result}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;out=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.report}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.report.style}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 当有不规范的情况发生时将检查结果发送到 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;target&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle-nightly&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;depends=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;if=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;checkstyle.failure&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sends email if checkstyle detected code conventions violations.&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 如果邮件服务器需要验证，则加上 user 和 password 属性 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;mail&lt;/span&gt; &lt;span class="na"&gt;from=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;(Admin)admin@xxx.com&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;tolist=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${mail.list}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;mailhost=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mail.xxx.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;subject=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; checkstyle result from project reports&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;files=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${checkstyle.report}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br/&gt;
&amp;lt;继续补充内容&amp;gt;</description></item><item><title>使用EMMA生成代码测试覆盖率报告简单应用</title><link>https://yanbin.blog/emma-code-coverage-report/</link><pubDate>Wed, 04 Jan 2006 08:41:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/emma-code-coverage-report/</guid><description>
Java测试代码覆盖率工具有Clover, Jcoverage和Emma等,其中只有Emma才算是彻底免费开源的，用起来也比较方便。&lt;br /&gt;
而Jcoverage使用起来就有些繁琐，必须一步步按照规定的方式走，步骤为 &lt;span style="color: #0000ff;"&gt;debug方式compile-&amp;gt;instrument-&amp;gt;test-&amp;gt;coverage&lt;/span&gt;。&lt;/p&gt;
&lt;br/&gt;
下面将简单介绍emma的使用，以下方法告诉你如以以命令的方式来生成代码测试覆盖率，当然你也可以写成 Ant 任务的方式，这就要求自己写几个 Ant 用的Task类，其实也不难。&lt;br/&gt;&lt;br/&gt;
emma的下载地址是：&lt;a href="http://emma.sourceforge.net/downloads.html"&gt;http://emma.sourceforge.net/downloads.html&lt;/a&gt;</description></item><item><title>用Ant进行单元测试并生成测试报告</title><link>https://yanbin.blog/ant-junit-report/</link><pubDate>Tue, 03 Jan 2006 23:33:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ant-junit-report/</guid><description>
关键Ant的build文件如下(已加上比较详细的说明)&lt;/p&gt;
&lt;br/&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;basedir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;.&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;default=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;main&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;excute TestCase and build test report&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 测试报告存放目录 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;build.reports.dir&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/report&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;target&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 删除测试报告数据，重新生成 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;delete&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/report&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;*.*&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/delete&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;junit&lt;/span&gt; &lt;span class="na"&gt;fork=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;yes&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;printsummary=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 生成的class目录以及执行TestCase所依赖的库
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; 无论是用&amp;lt;test&amp;gt;还是&amp;lt;batchtest&amp;gt;都要这个配置 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;classpath&lt;/span&gt; &lt;span class="na"&gt;location=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/bin&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 生成报告数据的格式，可能多个，支持xml/brief/plain --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;formatter&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;xml&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;formatter&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;brief&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;usefile=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;false&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 可以用&amp;lt;test&amp;gt;也可以用&amp;lt;batchtest&amp;gt;,但两种的设置有一些区别
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; 以下&amp;lt;test&amp;gt;和&amp;lt;batchtest&amp;gt;三种形式用某一种就可以的 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- name指定Class的名称，如CatTest或com.unmi.CatTest --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;test&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;CatTest&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;todir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 注意其中&amp;lt;fileset&amp;gt;的dir属性及&amp;lt;include&amp;gt;的name属性指代的意义 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;batchtest&lt;/span&gt; &lt;span class="na"&gt;todir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- dir属性指定TestCase类的源代码的路径 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/src&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- name属性指定TestCase源文件规则 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;**/*Test.java&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/batchtest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 上面的&amp;lt;batchtest&amp;gt;还可以写成如下形式，&amp;lt;fileset&amp;gt;按指定为class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt; 注意其中&amp;lt;fileset&amp;gt;的dir属性及&amp;lt;include&amp;gt;的name属性指代的意义 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;batchtest&lt;/span&gt; &lt;span class="na"&gt;todir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- dir属性指定TestCase类的路径 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${basedir}/bin&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- name属性指定TestCase类文件规则 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;**/*Test.class&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/batchtest&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/junit&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 用执行以上TestCase生成的报告数据生成测试报告 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;junitreport&lt;/span&gt; &lt;span class="na"&gt;todir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;fileset&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;TEST-*.xml&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/fileset&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- 指定生成测试报告的格式frames/noframes,和报告存放目录 --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;report&lt;/span&gt; &lt;span class="na"&gt;format=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;frames&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;todir=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${build.reports.dir}&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/junitreport&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
下面以后也会加上测试报告的贴图的。</description></item><item><title>Every day English（2005-10-31–2005-11-7）</title><link>https://yanbin.blog/everyday-english-2005-10-31/</link><pubDate>Tue, 03 Jan 2006 19:46:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-10-31/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-10-31&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
    Talking about Studies&lt;br/&gt;
&lt;br/&gt;
    1) What's your background? /你是什么学历&lt;br/&gt;
&lt;br/&gt;
    2）You can take English Conversation as an &lt;span style="color: #ff0000;"&gt;optional&lt;/span&gt; course. /你可能选修英语对话课。&lt;br/&gt;
&lt;br/&gt;
    3）Nobody can get him in physics. /物理谁也比不过他。&lt;br/&gt;
&lt;br/&gt;
    4) He has &lt;span style="color: #ff0000;"&gt;no equal&lt;/span&gt; in English study. /论英语学习他没有对手。</description></item><item><title>发表日志操作时发现的一些问题及应该注意的地方</title><link>https://yanbin.blog/some-problems-about-posting/</link><pubDate>Tue, 03 Jan 2006 11:07:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/some-problems-about-posting/</guid><description>
说明：本篇日志将记录一些平时使用Blog时发现的一些问题以及应该注意的地方。因为是日积月累而成，所以而是每当发现一些问题，又会附加上内容的，附加内容将在前面加上日期。&lt;br/&gt;
&lt;br/&gt;
通常我都是用可视化编辑方式来添加日志，时不时也会在 "HTML源码编辑器" 和 "&lt;span style="color: #333333;"&gt;HTML图文编辑器&lt;/span&gt;" 两种模式间来回切换，有时候切换到 "HTML源码编辑器" 下看到日志的末尾有一大堆的&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/font&amp;gt;......。起初都不明白是怎么回事，以为编辑器有这个毛病，总爱在后面加一堆垃圾；后来终于是明白了，因为这里要求标答应该成对出现，可能你粘贴或书写时&amp;lt;font&amp;gt;只写了半边，如&amp;lt;font&amp;gt;内容......，而没有结束部分&amp;lt;/font&amp;gt;，需然这样写显示是没问题的，</description></item><item><title>用JCoverage生成测试覆盖率报告(二 build文件)</title><link>https://yanbin.blog/jcoverage-report-build-file/</link><pubDate>Tue, 03 Jan 2006 10:45:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jcoverage-report-build-file/</guid><description>
下面的那一大段build.xml文件内容主要也是参考下载过来的JCoverage中的一个例子中的build文件，只是稍稍作了点修改，用下面这个build文件前，需要把JCoverage用到的 jar (下载的JCoverage中都有) 包拷到工程目录下lib子目录中，请注意理解其中的注解，以后必要时会加上更详细的中文注释。</description></item><item><title>用JCoverage生成测试覆盖率报告(一 介绍)</title><link>https://yanbin.blog/jcoverage-report-introduction/</link><pubDate>Tue, 03 Jan 2006 09:45:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/jcoverage-report-introduction/</guid><description>
JCoverage不是免费的(对商业不是免费,对open source是免费的
&lt;br/&gt;&lt;br/&gt;
JCoverage主页:&lt;a href="http://www.jcoverage.com"&gt;http://www.jcoverage.com&lt;/a&gt; &lt;br/&gt;&lt;br/&gt;
JCoverage的允许需要JUnit和Log4j、bcel、jakarta-oro、getopt类库,这些类库在下载的JCoverage中都包含了,JCoverage主要也是和Ant结合在一起使用, &lt;br/&gt;&lt;br/&gt;
注意事项: &lt;br/&gt;&lt;br/&gt;
1 : 初始化是最好删除当前目录下的jcoverage.ser文件,这个文件就是测试的结果,如果其在当前目录下找到这个文件将不会进行测试,或者重新测试 &lt;br/&gt;&lt;br/&gt;
2 : 在进行instrument时,一定要保证其class是debug模式下编译的,否则将不能注入jcoverage指令到被测试类的二进制文件中,建议从新用debug模式编译一个在当前目录</description></item><item><title>Every day English（2005-10-24–2005-10-28）</title><link>https://yanbin.blog/everyday-english-2005-10-24/</link><pubDate>Tue, 03 Jan 2006 04:46:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/everyday-english-2005-10-24/</guid><description>
&lt;span style="color: #ff00ff;"&gt;2005-10-24&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
    Telephone conversation:  &lt;br/&gt;
&lt;br/&gt;
    1) hold the line/请稍候  Pls hold the line and I'll call her.&lt;br/&gt;
&lt;br/&gt;
    2) put sb. through/转接  I will put you through.&lt;br/&gt;
&lt;br/&gt;
    3) be engaged/占线  The line is engaged/busy.&lt;br/&gt;
&lt;br/&gt;
    4) extension/分机  May I have ext.1000?</description></item><item><title>使用Ant进行ssh和scp操作</title><link>https://yanbin.blog/ant-ssh-scp/</link><pubDate>Tue, 03 Jan 2006 02:41:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/ant-ssh-scp/</guid><description>
现在我们安装Linux的时候通常考虑到安全因素（默认情况下）是不打开telnet服务的，而ssh服务是有的，ant很早就支持telnet，但要求我们在Linux下要安装telnet-server,并要启用该服务。&lt;/p&gt;
&lt;br/&gt;
还好自Ant1.60开始支持了SSH 及SCP 操作了，早在Ant之前若要支持SSH、SCP、SFTP等任务就必须下载j2ssh的j2ssh-ant.jar和j2ssh-core.jar（在&lt;a href="http://www.sourceforge.net"&gt;http://www.sourceforge.net&lt;/a&gt;的j2ssh下有下载）。现在可以使用Ant提供的Sshexec和scp任务，由$ANT_HOME/lib/ant-jsch.jar提供支持，但是同样你也要在&lt;a href="http://www.jcraft.com/jsch/index.html"&gt;http://www.jcraft.com/jsch/index.html&lt;/a&gt;下载一个依赖包jsch-0.1.24.jar(文件名因版本而不同)，jsch同样也是&lt;a href="http://www.sourceforge.net"&gt;http://www.sourceforge.net&lt;/a&gt;下的一个项目。&lt;br/&gt;&lt;br/&gt;
你需要把下载的jsch-0.1.24拷贝到$ANT_HOME/lib下，如果是Eclipse下的Ant环境必须在Window-&amp;gt;Preferences-&amp;gt;Ant-&amp;gt;Runtime-&amp;gt;Classpath中加入jsch-0.1.24。</description></item><item><title>维也纳在等着你----世上没有加班这回事</title><link>https://yanbin.blog/vienna-waiting-for-you/</link><pubDate>Mon, 02 Jan 2006 19:49:00 -0600</pubDate><author>yabqiu@gmail.com (Yanbin Qiu)</author><guid>https://yanbin.blog/vienna-waiting-for-you/</guid><description>
&lt;p&gt;&lt;em&gt;维也纳在等着你&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;几年前，我与南加州一个大项目的项目经理交流各自的艰辛历程。他开始叙述将项目和疯狂的时间表压到他下属的身上产生的影响。一是发生的两宗离婚案，
其中原因可以直接追溯到与他的人经常加班有关；再者就是一个员工的孩子吸毒，其中原因可能是由于在过去的一年里，孩子的父亲太忙，未能尽到做父亲的责任，
最后，测试团队的负责人又神经崩溃。 在他继续叙述这些可怕的事情的时候，我开始意识到，此人一直在炫耀他这种奇怪的工作方式。
你也许怀疑，如果再有另个一两个人离婚和一个人自杀，这个项目原本会取得圆满的成功，至少在他的眼里是这样的?&lt;/em&gt;&lt;br/&gt;
&lt;em&gt;――TDM&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;每当谈起&amp;quot;聪明地工作&amp;quot;这一话题时，一个很普遍的感觉就是，现实世界中的管理就是在更大程度上以员工的生活为代价，让他们更努力、
更长时间地工作。经理们总是不停地吹嘘他们的员工的加班时数和能从这些人身上榨取更多时间的小把戏。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;使用西班牙人理论的管理&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;很久以前，历史学家们从各种不同的价值理论中形成了一个抽象的结论：西班牙人的理论坚持认为地球上只有一个固定数量的价值，
因此通向积累财富的道路就是学会从土地或者从人身上更有效地榨取财富。而英国人的理论认为价值可以通过天才和技术创造出来。
因此英国就产生了工业革命，而西班牙人就转动起了车轮，开始开拓疆土和剥削在新大陆的印第安人。他们从海上运回大量的黄金，
他们的所有努力带来的却是通货膨胀（太多的金钱追逐太少的有用货物）。&lt;/p&gt;
&lt;p&gt;西班牙人的价值理论依然生机勃勃地活在各处的经理们的心中，每当他们谈到生产力的时候，你就看到这一点。生产力应该意味着用一小时的工钱榨取更多利润，
两者之间有很大的不同。持西班牙人的理论的经理梦想通过不付钱要求员工加班加点的简单手段达到新的生产力水平，他们将一周所要做的任何工作除以40小时，
而不是员工实际投入的80或90小时。&lt;/p&gt;
&lt;p&gt;确切地说那不是生产力――那更像是欺骗――但对美国的经理来说这是一种艺术境界。他们威逼或哄骗他们的下属延长工作时间，他们给下属的印象是交货日期是多么重要
（即使这个日期可能是很武断的；地球不会因为你的项目迟了一个月完成而停止转动）。他们欺骗手下人接受毫无希望实现的时间紧迫的进度表，
让他们带着愧疚的心理，牺牲一切以满足这个进度表的要求，并且竭尽所能地使手下人更长时间、更努力地干活。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;来自后方的一句话&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;虽然你的员工在办公室也许被&amp;quot;更长时间、更努力地工作&amp;quot;这句话所影响，但从他们家里听到的是完全不同话语。在家里听到的是&amp;quot;生活已离你而去，
你要洗的衣服堆满了壁橱，你的小宝宝没人抱了，你的配偶开始朝其他地方张望。旋转木马的人生只剩下一圈了，只有最后一次中奖机会了，
如果你把你的生活用到COBOL上……&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;但是你知道，当实情被告知之时，&lt;br/&gt;
你或许能得到你想要的或者你只能变老，&lt;br/&gt;
在你还未走到半道之时，你就要被淘汰出局，&lt;br/&gt;
你何时意识到――维也纳在等着你？&lt;br/&gt;
――《陌生人》（The Stranger）比利•乔尔&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;维也纳在等着你，在比利的歌词里，就是你人生之路的最后一站。当你到达那里时，一切都完了。如果你认为你的项目组成员对如此沉重的话题一点也不担心，
请你再想想，你的下属很清楚上帝赐予每个人的生命都是短暂的，他们也非常清楚有比他们正在从事的这种愚蠢的工作更为重要的事情。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;世上没有加班这回事&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;对于领薪水的员工而言，加班是天真的经理想像出来的虚构之物。噢，为了在星期一的最后期限完成工作而在星期六加班几个小时可能会有点好处，
但是那总是伴随着一个相等时段的补偿性&amp;quot;减班&amp;quot;，弥补上他们生活中因加班失去的部分。对应于1个小时的加班，或多或少会有一个小时的减班。
从短期而言，这种抵消的方法可能对你有好处，但是从长远角度看，这些好处会被抵消掉。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;疯狂的孩子你慢一点，&lt;br/&gt;
把电话摘下然后消失一会儿，&lt;br/&gt;
对了，你可以放松一两天，&lt;br/&gt;
你何时意识到……维也纳在等着你？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;正因为持西班牙理论的经理们很大程度上对不付工资的加班视而不见（这些人总是计算1周40小时工时，而不考虑人们实际投入了多少时间），
因此对减班也视而不见。你从未在任何人的时间表上看到这一点，那是花在打电话、闲谈或干脆是休息上的时间，没有人能真正工作超过40小时，
至少不是连续地和以创造性工作所需要的强度水平进行工作。&lt;/p&gt;
&lt;p&gt;加班就像冲刺：在马拉松赛跑中，对那些还有剩余能量的人来说，最后几百码是非常重要的，但是如果你在第一个1英里开始冲刺，你只是在浪费时间。
对经理而言，如果试图让人冲刺得太多，结果只会失去人的尊重。最好的员工已经经历过这种事情了：当经理哆嗦说工作必须在4月份完成时，
他们知道保持沉默并且转动着眼睛就足够了。然后当他们能够补休时他们就补休，并且以每周实际工作40小时而告终。优秀员工以那种方式做出反应；而其他所有员工则是工作狂。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;工作狂&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;工作狂将加班加点，不需要补休。虽然或许他们的工作效率在降低，但是他们会工作过多的时间。如果给他们施加足够的压力，
他们会在损害个人生活的道路上走很长一段路，但是这只是一时的。这样的信息迟早会传到甚至最专心的工作狂的耳朵里：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;慢一点，你做得很好了，&lt;br/&gt;
在你死前你不可能完成一切心愿，&lt;br/&gt;
虽然今晚在边缘线上非常浪漫，&lt;br/&gt;
但是何时你意识到……维也纳在等着你？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;一旦体会到这一点，在完成了这个项目后，你就完全失去了这个员工。意识到一个人为了不重要的价值（工作）牺牲了更重要的价值（家人、爱情、家庭、青春），
这种意识是破坏性的，它会使得一个曾经不知情地做出了牺牲的人寻求报复。他不会去找老板平静而理智地解释在将来事情必须改变――他只是辞职了，
这是另外一种耗尽热情和精力的情况。无论如何，他已经离开了。&lt;/p&gt;
&lt;p&gt;工作狂是一种疾病，但不是像酗酒那样，只影响几个不幸的人。工作狂更像感冒：每个人都不时地碰到一回。
我们在此写这些内容的目的不是为了过多地讨论它的根源和根治方法，而是为了讨论这个更简单的问题：你，作为经理，应该如何与你的工作狂打交道。
如果你用典型的西班牙理论的方法最大限度地剥削他们，你最终将失去他们。无论你是多么拼命地需要他们把所有的时间都投入到项目中，
你不能让他们以个人生活为代价来这样做。失去一个好人是不值得的，这一点已超过了狭隘的工作狂范畴，进入到更加复杂的主题：有意义的生产Α?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;生产力：赢得战役和输掉战争&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;下次当你听某人谈到生产力时，仔细听一听说话的人是否用了&amp;quot;人员流动&amp;quot;一词，很大的可能性是他（或她）没有提到这个词。多年来，
从听到的关于&amp;quot;生产力&amp;quot;的讨论或看到的数以百计的关于这方面的文章中，我们从没有遇见一个专家谈到有关&amp;quot;人员调整&amp;quot;这个主题的任何事情。
然而只谈论一个而不谈论另外一个有什么意义呢？下面评价一下公司在改进生产力方面通常要做的一些事情：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强迫人们加班加点&lt;/li&gt;
&lt;li&gt;产品开发过程的机械化&lt;/li&gt;
&lt;li&gt;在产品质量上的妥协&lt;/li&gt;
&lt;li&gt;生产过程的标准化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些措施中的任何一个都会潜在地降低工作的趣味性和满意度。因此，改进生产力的过程是在冒险使人员流动辐度加剧。这不是说不付出人员流动的代价，
就不能改进生产力。这只是说，无论何时开始达到更高的生产力都需要把人员流动考虑进去。否则，完成的&amp;quot;改进&amp;quot;会与关键人才的损失相抵消。&lt;/p&gt;</description></item></channel></rss>