qiubo's life

Stay Hungry, Stay Foolish


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

don't worry, be happy!

发表于 2015-11-22 | 分类于 java

情绪地雷区避雷方案:

  1. 安排B计划 (比如等人吃饭,提前准备点事做,别人没到就做自己的事)
  2. 公开自己的情绪死穴 (比如提前给人说哪些是不喜欢的,如果你这样做了,我也避免不了我不发脾气)

情绪管理在生活与工作中的应用

  1. 正向思考

    凡事往正面的角度去想,选取对自己最有利的注解

  2. 转移注意力

    去做能带来好情绪的活动或事情。比如吃自己最喜爱的食物

  3. 让开放的肢体动作带动情绪

    比如去爬山,去跑步,去旅游。

  4. 透过问句引导自己改变认知

    a.对方是故意的还是无心的?
    b.这件事还有没有其他角度与意义可以让我感觉更好?
    c.如果我希望自己的情绪变好,现在我应该做什么?
    d.这样做是真的会让情绪变好还是会有后遗症?
    e.我可以寻求谁的帮助来改善自己的情绪?
    
  1. 发现自己情绪有失控的感觉或倾向,必要时离开现场
  2. 把不满、愤怒、失意等负面情绪写在纸上,然后烧掉、冲掉、寄出或者收藏
  3. 了解自身的有点,积极表现自己,但求尽心尽力
  4. 多看别人的好,适度予以宽容的对待
  5. 接近拥有心灵智慧的良师益友

    智慧不起烦恼,慈悲没有敌人

  6. 提醒自己:世间事都只是过程而非结果

    每件事最终都会过去,包括我自己(凡人必有一死)

  7. 全力以赴后,放下得失心,因为你没有办法决定所有的事一定如你所愿

    战胜对手只是人生的赢家,战胜自己才是命运的强者

哈佛大学推荐20个快乐的习惯

  1. be grateful (学会感恩)

    让自己变慢脚步,看看你的四周,关注生活中的细微之处:人行道上淡紫色的花,美丽的日落,洗去你一天疲惫的淋浴,伴侣眼中的笑容。当你的感恩之心能够欣赏生活的美,思考和祝福,你自然就充满了幸福感。

  2. choose your friedns wisely

    如果你想变得开心的话,要选择和乐观的朋友在一起,他们能欣赏你真实的自己,让你的生活变得更丰富,快乐,有意义。

  3. cultivate compassion (培养同情心)

    当我们代替别人,站在另一个角度看问题,我们更能用同情心,客观和有效的处理问题。生活中就会少一些冲突,多一点快乐。

  4. keep leaning

    学习让我们保持年轻,梦想让我们充满活力。我们运用大脑,进行运作的时候,我们就不大会想不开心心的事情,我们会变得更开心和满足。

  5. become a problem solver (学会解决问题)

  6. do what you love
  7. live in the present

    你感到沮丧,是因为你活在过去。你会感到担忧和焦虑,是因为你活在未来。但是当你感到满足,开心和平和时,你才是活在当下。

  8. laugh often

     笑是对抗生气或沮丧最有力的的东西。不要把生活看的太严肃。要学会在每日的奋斗中寻找幽默感和笑声。  

  9. practice forgiveness

    憎恨和生气是对自我的惩罚。当你释怀的时候,事实上你是在对自己施以善意。最重要的是,学会原谅自己。每个人都犯错。只有通过我们的错误,我们才慢慢学会如何成为一个更强大,更好的人。

  10. say thanks often

    对生活中的祝福要学会欣赏。向那些让你生活变好的人,无论或大或小,表达出你的欣赏之情也同样重要。

  11. create deeper connections (学会深交)

    我们的幸福感会在和另一个人的深交中不断猛增。专注聆听是加强这种关系纽带和把幸福感带给自己和别人的两个最重要的方面。

  12. keep you agreement

    我们的自尊是建立在我们对自己守承诺的情况下。高度的自尊和幸福感有直接关联。所以要对自己和别人遵守承诺。

  13. meditate (冥想)

  14. focus on what you’re doing

    当你全身心投入一件事的时候,你就会处于一个开心的状态。当我们处于这种状态,你就不大会关心别人对你怎么看,不大会被不大重要的事情干扰。结果呢?更幸福,当然啦!

  15. be optimistic

    对于开心的人来说,玻璃都一直是半满的。每当你面对一个挑战时,如果你倾向于想象最坏的想法,那就自我转换这种情况。告诉你自己一个状况中的好处或者你从中学到的东西。乐观肯定能驱动成功和幸福感。

  16. love unconditionally

    没人是完美的,接受你自己所有的不完美,也要这样对待别人。无条件的爱一个人并不意味着你要花所有的时间和他们在一起,或者帮助他们解决问题。无条件的爱意味着接受真实的他们,以他们自己的步伐,让他们自己摸索。

  17. don’t give up

    没有完成的方案和不断的失败不可避免的会削弱你的自尊。如果你决定做某事,做完它。

  18. do you best and then let go

    每个人都有局限性。而且有时候尽管我们很努力做一件事情,但是总会事与愿违。所以做最好的自己,然后放手。当你尽了全力,你就没有遗憾了。

  19. take care of yourself

    一个健康的身体是幸福的关键。如果你身体不好,你无论如何努力,都很难快乐。确信自己吃得好,做锻炼,找点时间休息。好好照顾你的身体,大脑和精神。

  20. give back (学会给予)

    做好事是最能确保你心情好的方法之一。根据哈佛,人们做好事,他们的大脑变得活跃,就好像当你经历别的奖励时,大脑所受的刺激。所以,那些关心别人的人要比不大关心别人的人更开心。

2015年11月Reading Notes

发表于 2015-11-01 | 分类于 java

Next Generation Session Management with Spring Session

http://www.infoq.com/articles/Next-Generation-Session-Management-with-Spring-Session

在大规模集群的场景,无状态的应用能减少运维成本、缩短应用恢复时间。spring session主要解决了web应用session持久化的问题。把session存储在应用外部,让应用无状态。

spring session主要提供如下能力:

  1. 把session的存储逻辑抽取出来,可以选择rdis,或者自己实现session存储
  2. 使用websocket也能保持会话
  3. 非web应用也能使用会话
  4. 支持多个session(可以登陆多个用户)
  5. Restful API也能通过http header维护会话

spring session 1.1版本支持HttpSessionListener.如果使用的redis,spring session通过key过期时的过期事件+redis消息推送来实现。可惜我们用的是codis,我们实现了DisabledRedisMessageListenerContainer把这个能力屏蔽了。

更多参考:spring session 官方文档 Add HttpSessionListener Support

The Twelve-Factor App

http://12factor.net

The twelve-factor app is a methodology for building software-as-a-service apps.

  1. Codebase:One codebase tracked in revision control, many deploys

    one codebase per app(all tracked in revision control), but there will be many deploys of the app(one or more staging use same code,use multi config file or configuration management system for different stage ).

  2. Dependencies:Explicitly declare and isolate dependencies

    declares all dependencies via a dependency declaration manifest,and use dependencies check strategy to ensure that no implicit dependencies.

  3. Config:Store config in the environment

    use mutli config file (build env-awared app) or use configuration management system (spring cloud config)

  4. Backing Services:Treat backing services as attached resources

    app makes no distinction between local and third party services(both are attached resources). A deploy should be able to swap out a local MySQL database with one managed by a third party (such as Amazon RDS) without any changes to the app’s code.

  5. Build, release, run:Strictly separate build and run stages

    app uses strict separation between the build, release, and run stages. For example, it is impossible to make changes to the code at runtime, since there is no way to propagate those changes back to the build stage(we can put dynamic part code into db ).

  6. Processes:Execute the app as one or more stateless processes

    Twelve-factor processes are stateless and share-nothing. Any data that needs to persist must be stored in a stateful backing service, typically a database.Sticky sessions should never be used or relied upon.

  7. Port binding:Export services via port binding

    The twelve-factor app is completely self-contained and does not rely on runtime injection of a webserver into the execution environment to create a web-facing service. The web app exports HTTP as a service by binding to a port, and listening to requests coming in on that port.

  8. Concurrency:Scale out via the process model

    processes are a first class citizen.Processes in the twelve-factor app take strong cues from the unix process model for running service daemons. Using this model, the developer can architect their app to handle diverse workloads by assigning each type of work to a process type.

  9. Disposability:Maximize robustness with fast startup and graceful shutdown

    Processes should strive to minimize startup time.

    Processes shut down gracefully ( ceasing to listen on the service port,thereby refusing any new requests, allowing any current requests to finish, and then exiting.)when they receive a SIGTERM signal from the process manager.

    For a worker process, graceful shutdown is achieved by returning the current job to the work queue.Implicit in this model is that all jobs are reentrant, which typically is achieved by wrapping the results in a transaction, or making the operation idempotent.

    Processes should also be robust against sudden death, in the case of a failure in the underlying hardware. A recommended approach is use of a robust queueing backend that returns jobs to the queue when clients disconnect or time out. Either way, a twelve-factor app is architected to handle unexpected, non-graceful terminations.

  10. Dev/prod parity:Keep development, staging, and production as similar as possible

  x      | Traditional app           | Twelve-factor app     |
--------------------|------------------|-----------------------|
Time between deploys | Weeks   | Hours   |
Code authors vs code deployers  | Different people | Same people  |
Dev vs production environments  | Divergent | As similar as possible  |

The twelve-factor developer resists the urge to use different backing services between development and production, even when adapters theoretically abstract away any differences in backing services.
  1. Logs:Treat logs as event streams

    A twelve-factor app never concerns itself with routing or storage of its output stream(maybe dont fit for java app).

  2. Admin processes:Run admin/management tasks as one-off processes

    One-off admin processes should be run in an identical environment as the regular long-running processes of the app. They run against a release, using the same codebase and config as any process run against that release. Admin code must ship with application code to avoid synchronization issues.

2015年05月Reading Notes

发表于 2015-05-01 | 分类于 java

使用spring loaded提高开发效率

spring-loaded和jrebel类似,它能发现您修改的类并重新加载。在开发测试时,您只需要使用它启动后,就可以一直写代码了。jrebel是收费的软件,spring-loaded免费,而且他很了解您代码中用到的spring特性,能很智能的帮忙重新加载类,并把bean注册到spring容器中。

使用很简单,参考下面的步骤:

  1. 下载springloaded
  2. 配置IDEA

    • 打开Run/Debug Configuations,在Defaults中选择Application
    • 在右边的Configuration tab中配置VM options

      -javaagent:/Users/bohr/software/springloaded/springloaded-1.2.3.RELEASE.jar -noverify
      

      有了此默认配置,一劳永逸。上面的路径地址修改为您保存springloaded的路径

  3. 执行任何java类
  4. 修改后,编译此java类就能看到修改后的效果了。

2015年04月Reading Notes

发表于 2015-04-01 | 分类于 java

Enterprise Nashorn

https://community.oracle.com/docs/DOC-910779

本文讲述了Nashorn的一些使用场景:

  1. 由外部提供计算逻辑,服务端执行计算逻辑返回结果
  2. 用java来定义接口,js来写逻辑,方便动态更新计算逻辑。(比如短信中的路由策略,需要经常更新)
  3. 写shell脚本(访问数据库,访问网络,监控…)

Nashorn的性能比前任强多了,但是和java比较还是有差距,主要的性能开销在js->java上。在关注性能的场景,可以在业务使用时,采用方法2,并缓存proxy对象。

private static String isPrime = " function test(num) {\n" + "if (num % 2 == 0)" + "return false;" + "for (var i = 3; i * i <= num; i += 2)"
                                    + "if (num % i == 0)" + "return false;" + "return true;" + "}";
private Supplier<Predicate<Long>> supplier = Suppliers.memoize(() -> getFilter());

private Predicate getFilter() {
    Invocable invocable = (Invocable) this.engine;
    try {
        this.engine.eval(isPrime);
        return invocable.getInterface(Predicate.class);
    } catch (Exception ex) {
        throw Throwables.propagate(ex);
    }
}
@Test
public void testJavaScriptWithMemoize() throws Exception {
    supplier.get().test(172673l);
}

对比了下nashorn和groovy的性能:

NashornPerfTest.testJavaScript: [measured 50000 out of 51000 rounds, threads: 4 (all cores)]
 round: 0.00 [+- 0.00], round.block: 0.00 [+- 0.00], round.gc: 0.00 [+- 0.00], GC.calls: 96, GC.time: 0.25, time.total: 34.20, time.warmup: 3.44, time.bench: 30.76
NashornPerfTest.testJava: [measured 50000 out of 51000 rounds, threads: 4 (all cores)]
 round: 0.00 [+- 0.00], round.block: 0.00 [+- 0.00], round.gc: 0.00 [+- 0.00], GC.calls: 0, GC.time: 0.00, time.total: 0.28, time.warmup: 0.01, time.bench: 0.27
NashornPerfTest.testJavaScriptWithMemoize: [measured 50000 out of 51000 rounds, threads: 4 (all cores)]
 round: 0.00 [+- 0.00], round.block: 0.00 [+- 0.00], round.gc: 0.00 [+- 0.00], GC.calls: 0, GC.time: 0.00, time.total: 0.35, time.warmup: 0.06, time.bench: 0.29
GroovyTest.testGroovyWithMemoize: [measured 50000 out of 51000 rounds, threads: 4 (all cores)]
 round: 0.00 [+- 0.00], round.block: 0.00 [+- 0.00], round.gc: 0.00 [+- 0.00], GC.calls: 5, GC.time: 0.07, time.total: 3.86, time.warmup: 1.75, time.bench: 2.11

spring 多线程中两个事务执行顺序控制

有些场景需要控制两个事务的执行顺序,比如事务A执行完后,需要事务B中执行费时操作。因为是费时操作,一般会把事务B放到独立的线程中执行。然而事务A和事务B在不同的线程中,事务B还会依赖事务A提交的数据。如果在事务A中新启动线程执行事务B,有可能事务A还没有提交,就开始执行事务B了。这种场景需要保证事务A提交后,才能在新线程中执行事务B。

可以使用TransactionSynchronizationManager来解决这个问题:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
       void afterCommit(){
            //submit transaction B to threadpool
       }
});

在事务A中加上此钩子,在afterCommit方法中向线程池提交事务A任务。具体处理代码AbstractPlatformTransactionManager#triggerAfterCommit,ThreadLocal清理AbstractPlatformTransactionManager#cleanupAfterCompletion.

Web应用的缓存设计模式

http://robbinfan.com/blog/38/orm-cache-sumup

robbin大哥讲解了对ORM缓存的理解.我司也有不少项目用了ORM,大多数人没有使用缓存的意识,有这样意识的同学提到过用ehcache,这在单节点的情况下,工作得很好,但是到了线上多机部署,数据不一致的问题就会出现,至少也要选用分布式缓存系统来实现哈。还有一点,因为有了缓存,数据订正这种事情就要谨慎了。

java反射的性能

先看看下面的数据:

Benchmark                                  Mode  Cnt     Score    Error  Units
 testInvokeMethod_Direct                avgt   20     0.587 ±  0.036  ns/op
 testInvokeMethod_Reflectasm            avgt   20    39.940 ±  1.957  ns/op
 testInvokeMethod_Reflectasm_withCache  avgt   20     9.784 ±  0.745  ns/op
 testInvokeMethod_reflect               avgt   20  1513.409 ± 85.396  ns/op
 testInvokeMethod_reflect_withCache     avgt   20    29.444 ±  1.863  ns/op

上面的数据测试了直接调用java方法、通过反射调用java、通过ReflectASM调用java,withCache意思是把中间对象缓存起来。
反射确实很慢,但是只要把反射对象缓存起来,性能提升很大,Reflectasm_withCache比reflect_withCache快了3倍多。

Reflectasm的原理是生成java源代码来实现反射的调用,下面就是生成的源代码。

package reflectasm;
import java.util.Map;
import com.esotericsoftware.reflectasm.MethodAccess;
public class PojoMethodAccess extends MethodAccess {
    public Object invoke(Object paramObject, int paramInt, Object[] paramArrayOfObject) {
        Pojo localPojo = (Pojo) paramObject;
        switch (paramInt) {
            case 0:
                return Boolean.valueOf(localPojo.equals((Object) paramArrayOfObject[0]));
            case 1:
                return localPojo.toString();
            case 2:
                return Integer.valueOf(localPojo.hashCode());
            case 3:
                return localPojo.getName();
            case 4:
                localPojo.setName((String) paramArrayOfObject[0]);
                return null;
        }
        throw new IllegalArgumentException("Method not found: " + paramInt);
    }
}

补充:

–

ROUND 1:
Benchmark                           Mode  Cnt   Score   Error  Units
 MHOpto.mh_invoke                    avgt   15  11.332 ± 0.577  ns/op
 MHOpto.mh_invokeExact               avgt   15  10.605 ± 0.667  ns/op
 MHOpto.mh_invokeExact_static_fianl  avgt   15   3.797 ± 0.201  ns/op
 MHOpto.plain                        avgt   15   4.093 ± 0.156  ns/op
 MHOpto.reflect                      avgt   15  11.599 ± 0.646  ns/op
 MHOpto.unreflect_invoke             avgt   15  11.147 ± 0.743  ns/op
 MHOpto.unreflect_invokeExact        avgt   15  11.392 ± 0.518  ns/op

 ROUND 2:
 Benchmark                           Mode  Cnt   Score   Error  Units
MHOpto.mh_invoke                    avgt   15  11.799 ± 0.847  ns/op
MHOpto.mh_invokeExact               avgt   15  11.830 ± 0.637  ns/op
MHOpto.mh_invokeExact_static_fianl  avgt   15   4.415 ± 0.191  ns/op
MHOpto.plain                        avgt   15   4.084 ± 0.300  ns/op
MHOpto.reflect                      avgt   15  12.191 ± 0.637  ns/op
MHOpto.unreflect_invoke             avgt   15  11.535 ± 0.816  ns/op
MHOpto.unreflect_invokeExact        avgt   15  11.828 ± 0.666  ns/op

MethodHandle太牛叉了。

spring mvc的异步servlet实现

spring异步web处理流程,我们先以Controller方法返回Callable对象为例

  1. http线程处理请求到Controller方法,返回Callable结果
  2. spring选用CallableMethodReturnValueHandler来处理Callable结果,提交Callable到线程池,当前http线程返回,参考WebAsyncManager.startCallableProcessing()
  3. 线程池中线程执行Callable任务,并且dispatch请求到容器,参考WebAsyncManager.setConcurrentResultAndDispatch()
  4. 容器选取http线程继续处理请求

通过分析源代码,下面几点需要关注下:

  1. dispatch请求后,又会执行filterchain,我们需要保证filter只执行一次,filter最好继承OncePerRequestFilter。

  2. spring内置了几种异步结果处理器,CallableMethodReturnValueHandler、AsyncTaskMethodReturnValueHandler、DeferredResultMethodReturnValueHandler分别支持方法返回Callable,WebAsyncTask,DeferredResult。

  3. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#taskExecutor此默认线程池为SimpleAsyncTaskExecutor,此taskExecutor每次都会都会新建线程来处理任务,生产环境建议单独配置线程池。

2015年03月Reading Notes

发表于 2015-03-02 | 分类于 java

The Asset Pipeline

http://guides.rubyonrails.org/asset_pipeline.html

Asset Pipeline对网站的静态资源进行预处理(合并、简化、压缩、预处理coffeescirpt sass等)。对于静态资源的处理,这里面提到的Fingerprinting来优化http 缓存可以借鉴下。

Fingerprinting技术是在文件名中加上文件内容的标识,当文件内容改变时,文件名也改变。比如文件global.css加入md5的指纹后,文件名为global-908e25f4bf641868d8683022a5b62f54.css.

以前我们经常用query string中来标识版本,比如main.js?1.4/main.js?v=1.4.这种方式在某些CDN中有问题(有些CDN只识别文件名,新的版本文件会替换原版本的文件,在部署这个时间窗口会导致页面混乱)。

在使用浏览器缓存时,一般涉及到http header包括下面两种方案:

  1. Expires和Cache-Control: max-age (没有过期之前,完全不发送请求)
  2. Last-Modifed和ETag (内容协商,需要发一个请求,如果内容没有变化,响应304)

当方案2和Fingerprinting结合起来时,就比较完美了。对于现在很多开放CDN来讲,基本上都会用方案1,这是开源库名字里面的版本号就起着Fingerprinting的作用。也有用方案2的,估计是出于统计分析的目的。

对于静态资源的缓存,理想的组合是:

  1. 配置很长的本地缓存时间(善用Expires和Cache-Control: max-age),比如1年
  2. 通过Fingerprinting控制缓存(静态资源文件改变,对应的html的资源引用url也改变)

2015年02月Reading Notes

发表于 2015-02-16 | 分类于 java

Java Lambdas and Low Latency

http://vanillajava.blogspot.hk/2015/01/java-lambdas-and-low-latency.html

Lambdas创建了新对象,在低延迟应用中会给gc带来一点点压力。Escape Analysis(分析对象的使用范围,来做性能优化,比如锁消除,消除对象分配…)能减少这种压力。可以通过jvm参考-XX:BCEATraceLevel=3查看逃逸分析情况,进一步设置-XX:MaxBCEAEstimateSize来调整Maximum bytecode size of a method to be analyzed by BC EA.

Catch common Java mistakes as compile-time errors

http://errorprone.info/

静态代码分析工具又添一员,在编译时检查常见的java代码错误。在jdk8下貌似run不起来。

Google Java编程风格指南

发表于 2015-02-02 | 分类于 java

作者:Hawstein

出处:http://hawstein.com/posts/google-java-style.html

转载说明:N个月前看到Google Java Style,大致扫了下,自认为命名都比较规范了。今天在整理依赖时,看到junit最新版本的release note中提到junit的代码按照Google Java Style fix了一遍。无意中感觉可能我也做得不够好,仔细看看文档,发现有个地方写的非常明确,值得参考。转载这篇中文翻译文章方便大家查看。

目录

  1. 前言
  2. 源文件基础
  3. 源文件结构
  4. 格式
  5. 命名约定
  6. 编程实践
  7. Javadoc
  8. 后记

前言

这份文档是Google Java编程风格规范的完整定义。当且仅当一个Java源文件符合此文档中的规则,
我们才认为它符合Google的Java编程风格。

与其它的编程风格指南一样,这里所讨论的不仅仅是编码格式美不美观的问题,
同时也讨论一些约定及编码标准。然而,这份文档主要侧重于我们所普遍遵循的规则,
对于那些不是明确强制要求的,我们尽量避免提供意见。

1.1 术语说明

在本文档中,除非另有说明:

  1. 术语class可表示一个普通类,枚举类,接口或是annotation类型(@interface)
  2. 术语comment只用来指代实现的注释(implementation comments),我们不使用“documentation comments”一词,而是用Javadoc。

其他的术语说明会偶尔在后面的文档出现。

1.2 指南说明

本文档中的示例代码并不作为规范。也就是说,虽然示例代码是遵循Google编程风格,但并不意味着这是展现这些代码的唯一方式。
示例中的格式选择不应该被强制定为规则。

源文件基础

2.1 文件名

源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。

2.2 文件编码:UTF-8

源文件编码格式为UTF-8。

2.3 特殊字符

2.3.1 空白字符

除了行结束符序列,ASCII水平空格字符(0x20,即空格)是源文件中唯一允许出现的空白字符,这意味着:

  1. 所有其它字符串中的空白字符都要进行转义。
  2. 制表符不用于缩进。

2.3.2 特殊转义序列

对于具有特殊转义序列的任何字符(\b, \t, \n, \f, \r, \”, \’及\),我们使用它的转义序列,而不是相应的八进制(比如\012)或Unicode(比如\u000a)转义。

2.3.3 非ASCII字符

对于剩余的非ASCII字符,是使用实际的Unicode字符(比如∞),还是使用等价的Unicode转义符(比如\u221e),取决于哪个能让代码更易于阅读和理解。

Tip: 在使用Unicode转义符或是一些实际的Unicode字符时,建议做些注释给出解释,这有助于别人阅读和理解。

例如:

String unitAbbrev = "μs";                                 | 赞,即使没有注释也非常清晰
String unitAbbrev = "\u03bcs"; // "μs"                    | 允许,但没有理由要这样做
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s"    | 允许,但这样做显得笨拙还容易出错
String unitAbbrev = "\u03bcs";                            | 很糟,读者根本看不出这是什么
return '\ufeff' + content; // byte order mark             | Good,对于非打印字符,使用转义,并在必要时写上注释

Tip: 永远不要由于害怕某些程序可能无法正确处理非ASCII字符而让你的代码可读性变差。当程序无法正确处理非ASCII字符时,它自然无法正确运行,
你就会去fix这些问题的了。(言下之意就是大胆去用非ASCII字符,如果真的有需要的话)

源文件结构

一个源文件包含(按顺序地):

  1. 许可证或版权信息(如有需要)
  2. package语句
  3. import语句
  4. 一个顶级类(只有一个)

以上每个部分之间用一个空行隔开。

3.1 许可证或版权信息

如果一个文件包含许可证或版权信息,那么它应当被放在文件最前面。

3.2 package语句

package语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里)

3.3 import语句

3.3.1 import不要使用通配符

即,不要出现类似这样的import语句:import java.util.*;

3.3.2 不要换行

import语句不换行,列限制(4.4节)并不适用于import语句。(每个import语句独立成行)

3.3.3 顺序和间距

import语句可分为以下几组,按照这个顺序,每组由一个空行分隔:

  1. 所有的静态导入独立成组
  2. com.google imports(仅当这个源文件是在com.google包下)
  3. 第三方的包。每个顶级包为一组,字典序。例如:android, com, junit, org, sun
  4. java imports
  5. javax imports

组内不空行,按字典序排列。

3.4 类声明

3.4.1 只有一个顶级类声明

每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。

例外:package-info.java,该文件中可没有package-info类。

3.4.2 类成员顺序

类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。
最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如,
新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的。

3.4.2.1 重载:永不分离

当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。

格式

术语说明:块状结构(block-like construct)指的是一个类,方法或构造函数的主体。需要注意的是,数组初始化中的初始值可被选择性地视为块状结构(4.8.3.1节)。

4.1 大括号

4.1.1 使用大括号(即使是可选的)

大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。

4.1.2 非空块:K & R 风格

对于非空块和块状结构,大括号遵循Kernighan和Ritchie风格
(Egyptian brackets):

  • 左大括号前不换行
  • 左大括号后换行
  • 右大括号前换行
  • 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是else或逗号,则不换行。

示例:

return new MyClass() {
  @Override public void method() {
    if (condition()) {
      try {
        something();
      } catch (ProblemException e) {
        recover();
      }
    }
  }
};

4.8.1节给出了enum类的一些例外。

4.1.3 空块:可以用简洁版本

一个空的块状结构里什么也不包含,大括号可以简洁地写成{},不需要换行。例外:如果它是一个多块语句的一部分(if/else 或 try/catch/finally)
,即使大括号内没内容,右大括号也要换行。

示例:

void doNothing() {}

4.2 块缩进:2个空格

每当开始一个新的块,缩进增加2个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。(见4.1.2节中的代码示例)

4.3 一行一个语句

每个语句后要换行。

4.4 列限制:80或100

一个项目可以选择一行80个字符或100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。

例外:

  1. 不可能满足列限制的行(例如,Javadoc中的一个长URL,或是一个长的JSNI方法参考)。
  2. package和import语句(见3.2节和3.3节)。
  3. 注释中那些可能被剪切并粘贴到shell中的命令行。

4.5 自动换行

术语说明:一般情况下,一行长代码为了避免超出列限制(80或100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。

我们并没有全面,确定性的准则来决定在每一种情况下如何自动换行。很多时候,对于同一段代码会有好几种有效的自动换行方式。

Tip: 提取方法或局部变量可以在不换行的情况下解决代码过长的问题(是合理缩短命名长度吧)

4.5.1 从哪里断开

自动换行的基本准则是:更倾向于在更高的语法级别处断开。

  1. 如果在非赋值运算符处断开,那么在该符号前断开(比如+,它将位于下一行)。注意:这一点与Google其它语言的编程风格不同(如C++和JavaScript)。
    这条规则也适用于以下“类运算符”符号:点分隔符(.),类型界限中的&(<T extends Foo & Bar>),catch块中的管道符号(catch (FooException | BarException e)
  2. 如果在赋值运算符处断开,通常的做法是在该符号后断开(比如=,它与前面的内容留在同一行)。这条规则也适用于foreach语句中的分号。
  3. 方法名或构造函数名与左括号留在同一行。
  4. 逗号(,)与其前面的内容留在同一行。

4.5.2 自动换行时缩进至少+4个空格

自动换行时,第一行后的每一行至少比第一行多缩进4个空格(注意:制表符不用于缩进。见2.3.1节)。

当存在连续自动换行时,缩进可能会多缩进不只4个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。

第4.6.3水平对齐一节中指出,不鼓励使用可变数目的空格来对齐前面行的符号。

4.6 空白

4.6.1 垂直空白

以下情况需要使用一个空行:

  1. 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。
    • 例外:两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。
  2. 在函数体内,语句的逻辑分组间使用空行。
  3. 类内的第一个成员前或最后一个成员后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。
  4. 要满足本文档中其他节的空行要求(比如3.3节:import语句)

多个连续的空行是允许的,但没有必要这样做(我们也不鼓励这样做)。

4.6.2 水平空白

除了语言需求和其它规则,并且除了文字,注释和Javadoc用到单个空格,单个ASCII空格也出现在以下几个地方:

  1. 分隔任何保留字与紧随其后的左括号(()(如if, for catch等)。
  2. 分隔任何保留字与其前面的右大括号(})(如else, catch)。
  3. 在任何左大括号前({),两个例外:
    • @SomeAnnotation({a, b})(不使用空格)。
    • String[][] x = foo;(大括号间没有空格,见下面的Note)。
  4. 在任何二元或三元运算符的两侧。这也适用于以下“类运算符”符号:
    • 类型界限中的&(<T extends Foo & Bar>)。
    • catch块中的管道符号(catch (FooException | BarException e)。
    • foreach语句中的分号。
  5. 在, : ;及右括号())后
  6. 如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。
  7. 类型和变量之间:List list。
  8. 数组初始化中,大括号内的空格是可选的,即new int[] {5, 6}和new int[] { 5, 6 }都是可以的。

Note:这个规则并不要求或禁止一行的开关或结尾需要额外的空格,只对内部空格做要求。

4.6.3 水平对齐:不做要求

术语说明:水平对齐指的是通过增加可变数量的空格来使某一行的字符与上一行的相应字符对齐。

这是允许的(而且在不少地方可以看到这样的代码),但Google编程风格对此不做要求。即使对于已经使用水平对齐的代码,我们也不需要去保持这种风格。

以下示例先展示未对齐的代码,然后是对齐的代码:

private int x; // this is fine
private Color color; // this too

private int x; // permitted, but future edits
private Color color; // may leave it unaligned

Tip:对齐可增加代码可读性,但它为日后的维护带来问题。考虑未来某个时候,我们需要修改一堆对齐的代码中的一行。
这可能导致原本很漂亮的对齐代码变得错位。很可能它会提示你调整周围代码的空白来使这一堆代码重新水平对齐(比如程序员想保持这种水平对齐的风格),
这就会让你做许多的无用功,增加了reviewer的工作并且可能导致更多的合并冲突。

4.7 用小括号来限定组:推荐

除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。
我们没有理由假设读者能记住整个Java运算符优先级表。

4.8 具体结构

4.8.1 枚举类

枚举常量间用逗号隔开,换行可选。

没有方法和文档的枚举类可写成数组初始化的格式:

private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }

由于枚举类也是一个类,因此所有适用于其它类的格式规则也适用于枚举类。

4.8.2 变量声明

4.8.2.1 每次只声明一个变量

不要使用组合声明,比如int a, b;。

4.8.2.2 需要时才声明,并尽快进行初始化

不要在一个代码块的开头把局部变量一次性都声明了(这是c语言的做法),而是在第一次需要使用它时才声明。
局部变量在声明时最好就进行初始化,或者声明后尽快进行初始化。

4.8.3 数组

4.8.3.1 数组初始化:可写成块状结构

数组初始化可以写成块状结构,比如,下面的写法都是OK的:

new int[] {
  0, 1, 2, 3
}

new int[] {
  0,
  1,
  2,
  3
}

new int[] {
  0, 1,
  2, 3
}

new int[]
    {0, 1, 2, 3}
4.8.3.2 非C风格的数组声明

中括号是类型的一部分:String[] args, 而非String args[]。

4.8.4 switch语句

术语说明:switch块的大括号内是一个或多个语句组。每个语句组包含一个或多个switch标签(case FOO:或default:),后面跟着一条或多条语句。

4.8.4.1 缩进

与其它块状结构一致,switch块中的内容缩进为2个空格。

每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。

4.8.4.2 Fall-through:注释

在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组,
任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。示例:

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}
4.8.4.3 default的情况要写出来

每个switch语句都包含一个default语句组,即使它什么代码也不包含。

4.8.5 注解(Annotations)

注解紧跟在文档块后面,应用于类、方法和构造函数,一个注解独占一行。这些换行不属于自动换行(第4.5节,自动换行),因此缩进级别不变。例如:

@Override
@Nullable
public String getNameIfPresent() { ... }

例外:单个的注解可以和签名的第一行出现在同一行。例如:

@Override public int hashCode() { ... }

应用于字段的注解紧随文档块出现,应用于字段的多个注解允许与字段出现在同一行。例如:

@Partial @Mock DataLoader loader;

参数和局部变量注解没有特定规则。

4.8.6 注释

4.8.6.1 块注释风格

块注释与其周围的代码在同一缩进级别。它们可以是/* ... */风格,也可以是// ...风格。对于多行的/* ... */注释,后续行必须从*开始,
并且与前一行的*对齐。以下示例注释都是OK的。

/*
 * This is          // And so           /* Or you can
 * okay.            // is this.          * even do this. */
 */

注释不要封闭在由星号或其它字符绘制的框架里。

Tip:在写多行注释时,如果你希望在必要时能重新换行(即注释像段落风格一样),那么使用/* ... */。

4.8.7 Modifiers

类和成员的modifiers如果存在,则按Java语言规范中推荐的顺序出现。

public protected private abstract static final transient volatile synchronized native strictfp

命名约定

5.1 对所有标识符都通用的规则

标识符只能使用ASCII字母和数字,因此每个有效的标识符名称都能匹配正则表达式\w+。

在Google其它编程语言风格中使用的特殊前缀或后缀,如name_, mName, s_name和kName,在Java编程风格中都不再使用。

5.2 标识符类型的规则

5.2.1 包名

包名全部小写,连续的单词只是简单地连接起来,不使用下划线。

5.2.2 类名

类名都以UpperCamelCase风格编写。

类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。

测试类的命名以它要测试的类的名称开始,以Test结束。例如,HashTest或HashIntegrationTest。

5.2.3 方法名

方法名都以lowerCamelCase风格编写。

方法名通常是动词或动词短语。

下划线可能出现在JUnit测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test<MethodUnderTest>_<state>,例如testPop_emptyStack。
并不存在唯一正确的方式来命名测试方法。

5.2.4 常量名

常量名命名模式为CONSTANT_CASE,全部字母大写,用下划线分隔单词。那,到底什么算是一个常量?

每个常量都是一个静态final字段,但不是所有静态final字段都是常量。在决定一个字段是否是一个常量时,
考虑它是否真的感觉像是一个常量。例如,如果任何一个该实例的观测状态是可变的,则它几乎肯定不会是一个常量。
只是永远不打算改变对象一般是不够的,它要真的一直不变才能将它示为常量。

// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Joiner COMMA_JOINER = Joiner.on(',');  // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }

// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};

这些名字通常是名词或名词短语。

5.2.5 非常量字段名

非常量字段名以lowerCamelCase风格编写。

这些名字通常是名词或名词短语。

5.2.6 参数名

参数名以lowerCamelCase风格编写。

参数应该避免用单个字符命名。

5.2.7 局部变量名

局部变量名以lowerCamelCase风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。

虽然缩写更宽松,但还是要避免用单字符进行命名,除了临时变量和循环变量。

即使局部变量是final和不可改变的,也不应该把它示为常量,自然也不能用常量的规则去命名它。

5.2.8 类型变量名

类型变量可用以下两种风格之一进行命名:

  • 单个的大写字母,后面可以跟一个数字(如:E, T, X, T2)。
  • 以类命名方式(5.2.2节),后面加个大写的T(如:RequestT, FooBarT)。

5.3 驼峰式命名法(CamelCase)

驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。
有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。

名字从散文形式(prose form)开始:

  1. 把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。
  2. 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。
    • 推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。
      需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。
  3. 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:
    • 每个单词的第一个字母都大写,来得到大驼峰式命名。
    • 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。
  4. 最后将所有的单词连接起来得到一个标识符。

示例:

Prose form                Correct               Incorrect
------------------------------------------------------------------
"XML HTTP request"        XmlHttpRequest        XMLHTTPRequest
"new customer ID"         newCustomerId         newCustomerID
"inner stopwatch"         innerStopwatch        innerStopWatch
"supports IPv6 on iOS?"   supportsIpv6OnIos     supportsIPv6OnIOS
"YouTube importer"        YouTubeImporter
                          YoutubeImporter*

加星号处表示可以,但不推荐。

Note:在英语中,某些带有连字符的单词形式不唯一。例如:”nonempty”和”non-empty”都是正确的,因此方法名checkNonempty和checkNonEmpty也都是正确的。

编程实践

6.1 @Override:能用则用

只要是合法的,就把@Override注解给用上。

6.2 捕获的异常:不能忽视

除了下面的例子,对捕获的异常不做响应是极少正确的。(典型的响应方式是打印日志,或者如果它被认为是不可能的,则把它当作一个AssertionError重新抛出。)

如果它确实是不需要在catch块中做任何响应,需要做注释加以说明(如下面的例子)。

try {
  int i = Integer.parseInt(response);
  return handleNumericResponse(i);
} catch (NumberFormatException ok) {
  // it's not numeric; that's fine, just continue
}
return handleTextResponse(response);

例外:在测试中,如果一个捕获的异常被命名为expected,则它可以被不加注释地忽略。下面是一种非常常见的情形,用以确保所测试的方法会抛出一个期望中的异常,
因此在这里就没有必要加注释。

try {
  emptyStack.pop();
  fail();
} catch (NoSuchElementException expected) {
}

6.3 静态成员:使用类进行调用

使用类名调用静态的类成员,而不是具体某个对象或表达式。

Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad

6.4 Finalizers: 禁用

极少会去重载Object.finalize。

Tip:不要使用finalize。如果你非要使用它,请先仔细阅读和理解Effective Java
第7条款:“Avoid Finalizers”,然后不要使用它。

Javadoc

7.1 格式

7.1.1 一般形式

Javadoc块的基本格式如下所示:

/**
 * Multiple lines of Javadoc text are written here,
 * wrapped normally...
 */
public int method(String p1) { ... }

或者是以下单行形式:

/** An especially short bit of Javadoc. */

基本格式总是OK的。当整个Javadoc块能容纳于一行时(且没有Javadoc标记@XXX),可以使用单行形式。

7.1.2 段落

空行(即,只包含最左侧星号的行)会出现在段落之间和Javadoc标记(@XXX)之前(如果有的话)。
除了第一个段落,每个段落第一个单词前都有标签<p>,并且它和第一个单词间没有空格。

7.1.3 Javadoc标记

标准的Javadoc标记按以下顺序出现:@param, @return, @throws, @deprecated, 前面这4种标记如果出现,描述都不能为空。
当描述无法在一行中容纳,连续行需要至少再缩进4个空格。

7.2 摘要片段

每个类或成员的Javadoc以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。

这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以A {@code Foo} is a...或This method returns...开头,
它也不会是一个完整的祈使句,如Save the record...。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。

Tip:一个常见的错误是把简单的Javadoc写成/** @return the customer ID */,这是不正确的。它应该写成/** Returns the customer ID. */。

7.3 哪里需要使用Javadoc

至少在每个public类及它的每个public和protected成员处使用Javadoc,以下是一些例外:

7.3.1 例外:不言自明的方法

对于简单明显的方法如getFoo,Javadoc是可选的(即,是可以不写的)。这种情况下除了写“Returns the foo”,确实也没有什么值得写了。

单元测试类中的测试方法可能是不言自明的最常见例子了,我们通常可以从这些方法的描述性命名中知道它是干什么的,因此不需要额外的文档说明。

Tip:如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名getCanonicalName,
就不应该忽视文档说明,因为读者很可能不知道词语canonical name指的是什么。

7.3.2 例外:重载

如果一个方法重载了超类中的方法,那么Javadoc并非必需的。

7.3.3 可选的Javadoc

对于包外不可见的类和方法,如有需要,也是要使用Javadoc的。如果一个注释是用来定义一个类,方法,字段的整体目的或行为,
那么这个注释应该写成Javadoc,这样更统一更友好。

后记

本文档翻译自Google Java Style,
译者@Hawstein。

补充

checkstyle https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml

2015年01月Reading Notes

发表于 2015-01-11 | 分类于 java

构建类型安全的SQL查询

Advanced Spring Data JPA - Specifications and Querydsl

Querying JPA document

querydsl jpa example

querysql很早之前了解过,当时没有看到他的价值,最近在写某业务系统的分页查询过程中,看到基于Specifications写的复杂查询语句,有点乱,感觉有点点不爽。

jpa 提供了Metamodel,但是Specifications难用,生成的语法糖也难用。

下面列出几种在spring-data-jpa中使用查询的例子:

1.使用querydsl:

public List<SchedulerRule> findByOther(String other) {
   BooleanBuilder builder = new BooleanBuilder();
   builder.or(schedulerRule.memo.containsIgnoreCase(other));
   builder.or(schedulerRule.properties.containsIgnoreCase(other));
   builder.or(schedulerRule.dGroup.containsIgnoreCase(other));
   return new JPAQuery(em).from(schedulerRule).where(builder).orderBy(schedulerRule.id.asc()).list(schedulerRule);
 }

2.使用原生sql:

public List<SchedulerRule> findByOther(String other) {
    return (List<SchedulerRule>) em
        .createNativeQuery(
            "select * from  scheduler_rule where  memo LIKE :other OR properties LIKE :other OR dGroup LIKE :other order by id",
            SchedulerRule.class).setParameter("other", "%" + other + "%").getResultList();
}

3.使用@Query:

@Query("from SchedulerRule as rule where mod(rule.id, :clusterSize)= :mod and rule.status = 'NORMAL'")
List<SchedulerRule> findByClient(@Param("clusterSize") int clusterSize, @Param("mod") int mod);

4.使用接口命名生成查询语句:

Page<SchedulerRule> findByCreater(String creator, Pageable pageable);

在使用querydsl时,通过配置annotation processor可以很方便的完成代码生成工作:

<!--querydsl-->
       <plugin>
           <groupId>com.mysema.maven</groupId>
           <artifactId>apt-maven-plugin</artifactId>
           <version>1.1.0</version>
           <configuration>
               <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
           </configuration>
           <dependencies>
               <dependency>
                   <groupId>com.mysema.querydsl</groupId>
                   <artifactId>querydsl-apt</artifactId>
                   <version>3.6.0</version>
               </dependency>
           </dependencies>
           <executions>
               <execution>
                   <phase>generate-sources</phase>
                   <goals>
                       <goal>process</goal>
                   </goals>
                   <configuration>
                       <outputDirectory>src/gen/java</outputDirectory>
                   </configuration>
               </execution>
           </executions>
       </plugin>

ActiveJPA——针对JPA的活动记录模式

http://www.infoq.com/cn/articles/ActiveJPA

活动记录模式在使用上来说,还是很happy的。但是造成的问题是:1.entity和DO耦合在一起了,如果业务复杂,还是老老实实的DDD吧。2.复杂的查询可能还是需要借助DAO。如果自己来实现,考虑上关系映射,最后就是活脱脱的一个hibernat出来了。这个框架借助JPA的能力,简单的实现了活动记录模式。

作者通过java instrument api+javassit来生成便于使用的静态方法(不需要提供类型信息)。

用betamax mock掉外部http/https依赖

http://freeside.co/betamax/

betamax在你的应用和外部应用之间架起了proxy.他会录制第一次请求,在本地文件系统中生成Tape,后续的请求就不会调用目标服务了。我们可以把tape存放在VCS中,也可以编辑此文件,满足特殊需求。

Building a Robust and Secure Web Application With Velocity

http://wiki.apache.org/velocity/BuildingSecureWebApplications

这篇文章很老了,但是很值得参考下。

Best Practices In Building A Secure, Robust Velocity Web Application

  1. Review all context references for unwanted methods.

    不要在Context中放入能改变程序状态的引用。

  2. Encode HTML special characters to avoid cross-scripting vulnerabilities.

    可以通过EscapeHtmlReference对符合特定模式的引用进行过滤。

  3. Use an up-to-date and properly configured app server.

    里面提到通过Java Security Manager来限制应用的行为。这也是一种不错的方式,只是灵活性不好。可以采用findbugs来检查静态代码,再控制好上传的文件/对系统的直接调用就ok了。

  1. Configure Velocity for production use.

    创建EventCartridge和Event Handlers来捕获异常,并记录进日志。这个工作在com.yjf.common.util.Velocitys里面是做了的。但是spring mvc集成 velocity可以做下。提前发现异常(上次CRSF过滤器配置出错导致的页面乱了)。

jello–Front End Integrated Solution for J2EE Velocity

https://github.com/fex-team/jello

http://106.186.23.103:8080/

使用velocity的同学可以关注下:jello针对服务端为 JAVA + Velocity 的前端集成解决方案。为优化前端开发效率而生,提供前后端开发分离、自动性能优化、模块化开发机制等功能。

模板技巧部分文档适合学习velocity的同学看看。

模板引擎的选择

关于thymeleaf的性能:http://forum.thymeleaf.org/Performance-issue-td3722763.html
模式freemarker性能最强,thymeleaf性能差距太大

比较JVM上的模板引擎: http://www.slideshare.net/jreijn/comparing-templateenginesjvm

thymeleaf的优点主要在和前端结合起来很不错,前端切完图,然后加上动态数据的部分就ok了。页面不需要服务端也能渲染出来。

2014年12月Reading Notes

发表于 2014-12-14 | 分类于 java

CPU Flame Graphs

http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html

java里可以很方便的通过jvisualvm来采样性能数据,然后分析每个线程栈中比较费时的cpu操作.java以外的程序,通过cpu火焰图来分析性能问题,很直观(比较起来,jvisualvm的cpu sample report没有cpu火焰图直观).

生成的svg报告中,y轴可以理解为调用栈层次,越大调用层次越深.x轴中的长度是调用占用时间比.

CPU Flame Graphs生成过程需要三步:

  1. 采样性能数据(perf, DTrace, SystemTap, and ktap)
  2. 转换性能数据
  3. 利用性能数据生成svg报告

协程

http://niusmallnan.github.io/_build/html/_templates/openstack/coroutine_usage.html
http://www.dongliu.net/post/5906310176440320

协程是纯软件实现的多任务调度,在软件层面实现任务的保持和恢复.传统的用多线程的方式来实现任务调度,在高并发场景下,CPU创建开销和CPU上下文切换的开销太大.使用协程,任务调度有程序来调度,不涉及到cpu线程切换和cpu大量创建线程,性能会快不少.

在使用协程时,所有的I/O都需要使用异步I/O,不然性能会大打折扣.

在协程中,不同的执行单元之间通信可以采用共享内存或者消息机制.由于共享内存又会引入共享资源的同步,推荐采用消息机制.

基于线程与基于事件的并发编程之争

http://www.jdon.com/46921

基于线程的并发变成带来了很多问题,很难写出高性能的程序。协程和Actor模型也许可以考虑用来降低CS的开销。

2014年11月Reading Notes

发表于 2014-11-04 | 分类于 java

Linux Troubleshooting, Part I: High Load

http://www.linuxjournal.com/magazine/hack-and-linux-troubleshooting-part-i-high-load

10月25(This is my big day)日凌晨线上某系统出现load很高的问题,我当时排查问题的思路和这篇文章类似.简单总结下:

load,系统负载,可以理解为现在等待cpu处理的任务数.如何衡量high呢?这个和cpu核心数有关系,8核cpu,load为8,这个也不能称之为high.

造成high load的原因一般有:

  • CPU-bound load

    某些任务太耗cpu了,导致high load.这个通过看top就能看出来.如果top命令中的xx%wa很高,说明这些任务都在等待去了.

  • load caused by out of memory issues

    由于内存不足,linux在使用swap了,导致high load.这个通过free很容易看出来.

  • I/O-bound load

    iostat/iotop(需安装)都很容易发现原因所在.

我偏向于使用vmstat,用了它,什么都可以看到了.很悲剧的是,这台线上服务器一切正常.服务器重启后,负载一直很低.服务器重启后,一切都正常了.

The Best Plugins for Sublime Text

http://ipestov.com/the-best-plugins-for-sublime-text/

可以把sublime武装成ide的插件.

ibatis和mybatis共存

如果先加载ibatis的jar,会遇到java.lang.reflect.MalformedParameterizedTypeException异常.需要让ibatis后加载.可以修改ibatis依赖的artifactId,比如改为zibatis

MySQL 加锁处理分析

http://hedengcheng.com/?p=771

这篇文章从mysql数据库原理角度来分析锁,很透彻,很深刻.

比如对于一条简单的sql语句:

delete from test where id = ?;

如果id是主键/非空的唯一索引,不会出现gap锁.如果是非索引列,直接锁表(这个太恶心了,因为server和存储引擎分离导致的);如果是非唯一的索引,事务隔离级别是read commit,锁定选取的数据.如果是repeatable read,为了防止幻读,gap lock出现了)

##面向程序员的数据库访问性能优化法则
http://blog.csdn.net/yzsind/article/details/6059209

这篇文章很系统的介绍了程序员应该掌握的数据库优化知识.
首先根据硬件相关知识介绍各种硬件的访问延时/带宽指标:

通过对上面的指标分析,自然得出优化的层次:

  1. 减少数据访问(减少磁盘访问)

    • 正确创建/使用索引
    • 只通过索引访问数据(索引节点中存有数据,通过索引就能拿到数据最高效)
    • 根据查询计划优化索引
    • 一个表上的索引不能太多,会增加修改操作IO.
  2. 返回更少数据(减少网络传输或磁盘访问)

    • 分页(通过rowid分页的方式减少IO,前提是where/order子句都要在索引上)
    • 只返回需要的字段(别使用select *,带来各种问题,对于我们来说,最头痛的是sql版本兼容性问题.)
  1. 减少交互次数(减少网络传输)

    • batch DML(批量操作走jdbc批量接口)
    • In List(多操作合并,依稀记得mysql驱动里也有这样的做法)
    • 设置Fetch Size(默认fetch size可能不是最优的,需要根据实际情况调整,比如分页查询,一次查询100条,在不OOM的前提下增大此值,减少网络io)
    • 使用存储过程(这个得看场景)
    • 使用ResultSet游标处理记录(这一点往往被忽略掉,使用mybatis的RowHandler来解决问题)
  2. 减少服务器CPU开销(减少CPU及内存开销)

    • 使用绑定变量(mysql仅仅支持硬解析,参考)
    • 合理使用排序
  3. 利用更多资源(增加资源)

1234…7
qiubo

qiubo

Keep running!

66 日志
2 分类
76 标签
Links
  • qiuduo
  • daidai
  • bingwei
© 2013 — 2019 qiubo
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4