分类目录归档:Java相关

消息收发中的中文乱码

背景:

A发消息给B系统,A、B应用的编码均为UTF-8,但是B收到的消息出现中文乱码。
A发送消息的方式:

new Message(TOPIC, TAG, null, (messageContent.toString()).getBytes())
这里消息接受二进制byte数据,我们把消息string转换为byte的时候,默认使用了getBytes(),未指定编码方式,getBytes()方法的注释如下:Encodes this {@code String} into a sequence of bytes using the platform’s default charset, storing the result into a new byte array.也就是会使用平台默认编码,查看StringCoding.encode方法中的Charset.defaultCharset().name()方法:
继续阅读
Advertisements

Google Guava

1. Optional:

Optional设计目的:
Optional 用于区分 null-object 和 空引用,用来强制提醒开发者,注意对Null的判断。迫使你积极思考引用缺失的情况。
设计Optional类的目的并不是完全取代null, 它的目的是设计更易理解的API。

与JDK总Optional的差异:
JDK 1.8中提供了类似功能的工具类Optional,但是其未实现序列化接口,所以不能在一些需要网络数据传输的场景中使用,比如DTO的属性。

Optional使用场景:

  • 用于可能返回null对象的方法返回值;

2. PreConditions:

前置条件检查,比如对方法参数做非空判断,之前自己回写一些checkNull的流式写法。

Preconditions.checkNotNull(b, " i = %s j = %s k = %s", i, j, k);

3. Objects and MoreObjects:

java.util.Objects 中提供了一些常用的方法,比如equal、hash、compare、requireNonNull等,MoreObjects 是作为对Objects方法的一个补充存在,提供了firstNonNull、toStringHelper等功能;

MoreObjects.firstNonNull()方法常用于对象如果为空时的默认初始化

Date time = null;
time = MoreObjects.firstNonNull(time, new Date());
System.out.println(time);

MoreObjects.toStringHelper经常用于对象的toString方法的实现:

public class ObjectsTest {

public String name;

@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("name", name).toString();
}
}

IDEA中同样提供了自动根据Guava MoreObjects生成toString的工具:

MapStruct 代替BeanUtil 和ModelMapper

一、Object mapping 的技术分类:

  1. 运行期 反射调用set/get 或者是直接对成员变量赋值 。 该方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。这类的代表:Dozer,ModelMaper
  2. 编译期 动态生成set/get代码的class文件 ,在运行时直接调用该class文件。该方式实际上扔会存在set/get代码,只是不需要自己写了。 这类的代表:MapStruct,Selma,Orika ### 主要框架性能对比: 每秒钟执行的object mapping越多越好。 

明显可以看出通过在运行期进行反射的方式执行,性能远不如编译器生成class的方式;

MapStruct 与 Selma的对比:https://java.libhunt.com/project/mapstruct/vs/selma

MapStruct 与 ModelMapper的对比:https://java.libhunt.com/project/mapstruct/vs/modelmapper

综合比较性能、问题排查、文档、成熟度、扩展性等因素来考虑,MapStruct 是一个不错的选择;

继续阅读

Object Mapping in Java

我们在Java代码编写中经常会遇到DO 、DTO之间的对象隐射转换,我们在设计DO、DTO的时候一般会尽量让对象名称、对象属性保持一致,利于属性拷贝,但是现实场景中可能存在一些对象名称不一致、对象类型不一致的情况,不同的拷贝方案,性能与使用场景也可能存在不一样,那么在众多的对象拷贝框架中如何选择合适的使用呢?

常用对象属性拷贝方法:

  1. commons-beanutils 框架中的 BeanUtils
  2. commons-beanutils 框架中的 PropertyUtils
  3. ModelMapper
  4. Cglib中提供的BeanCopier, BulkBean,BeanMap,FastClass/FastMethod
  5. Orika
  6. Dozer
  7. HardCopy,手工硬编码
有人做了一张性能对比图:
object-mapping-per-hightchart-one

继续阅读

Mybatis.generator 自动生成domain、dao、mapper

  我们都知道使用mybatis比较繁琐的是手写domain、dao 和 sqlmap 文件,自己编写不仅容易耗时而且增加了出错的风险,那有没有一种自动化生成sqlmap的工具呢?接下来我们就介绍两款不错的工具。

common-dao

这是一款自己编写的sqlmap生成工具,实现原理是通过Velocity模板模板引擎,预先定义一个模板sqlmap.vm文件,然后通过DaoHelp类把Domain、表名、Dao等注入到Context中,通过反射获取Domain的每个属性,再把解析出来的值注入到vm模板中,生成需要的sqlmap文件。
此框架的优势是:简洁的代码结构、清晰的架构分割,domain、dao、manager三层结构,分成清晰,其次,对表结构设计也有一定的约定,比如id、is_delete、gmt_create、gmt_modified 等,对团队内部编码统一化有一定约束作用。
弊端: 需要编写domain、dao、manager等代码,略繁琐;对数据库层面设置的默认值无法支持;无法更新数据为null等。

mybatis.generator

这个是MyBatis官方推出的一款可以根据表结构自动生成domain、dao、mapper的工具。它通过连接数据库中的表,解析表结构自动解析字段,转换为驼峰形式字段,POJO对象会生成两个,一个为Basic、一个为Criteria,一个Mapper对象,一个sqlmap.xml文件。其次,Generator还提供了一系列插件,比如toString、hashCode、Serializable等,可以对POJO对象进行扩展,用户也可以继承PluginAdapter实现自定义插件,比如分页查询。
使用方法:

①maven依赖:

<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

②配置generator.xml 文件:

继续阅读

计算Java方法执行时间

我们在对系统进行性能调优的时候,很多时候都会统计接口的调用时间或者内部方法的执行时间来查看具体的性能瓶颈点。
  1. 一般比较简单的左右是在方法开始和结束的时候分别添加时间记录方法:
    long startTimeMillis = System.currentTimeMillis()
    …..
    long endTimeMillis = System.currentTimeMillis()
    long spendTime = endTimeMillis - startTimeMillis;
  1. 当然你可以可以通过Apache-Commons工具包中的工具类:
org.apache.commons.lang3.time.StopWatch
StopWatch 底层也是采用的System.currentTimeMillis()进行时间统计,使用StopWatch的优点:
  • 封装好了基本的时间统计方法,你直接在方法开始和结束的时候调用一次就好了,使用简单;
  • 他还提供了时间统计挂起、恢复和复位等功能,这点通过方法一是无法实现的;
使用StopWatch的弊端:
  • 会抛出IllegalStateException或者RuntimeException异常,这点使用者如果不进行封装容易引起业务异常。一般我们不期望一个测试性能的代码影响到了正常的业务逻辑;
  1.   使用Spring AOP方式对指定方法进行耗时统计:

继续阅读

Perm区持续增长的问题排查

线上出现:java.lang.OutOfMemoryError: PermGen space

java.lang.OutOfMemoryError: PermGen space
[2016-05-12 16:55:49.272] [DubboServerHandler-10.252.115.167:20880-thread-180] [ERROR] com.alibaba.dubbo.rpc.filter.ExceptionFilter:90 x:(_cid=a:5bf5c0|t:117|s:1463043299969) -  [DUBBO] Got unchecked and undeclared exception which called by 10.117.22.115. service: com.sfebiz.logistics.api.LogisticsCallbackService, method: bspcallback, exception: java.lang.OutOfMemoryError: PermGen space, dubbo version: 2.5.3-notification-fix, current host: 10.252.115.167
java.lang.OutOfMemoryError: PermGen space

第一时间查看Perm区大小,发现JVM启动参数没有配置,默认情况下是82M左右,原因是运维同学在脚本修改的时候漏了XX参数设置,加上Perm参数,并重启应用。 jvm-start-params-perm-set

但系统监控中的Perm区:呈现缓慢增长

current-logistics-jvm-perm-growing

 

查看历史Perm区的数据信息:

history-logistics-jvm-perm-growing

 

能造成Perm缓慢增长的方式一般有以下几种:
  1. 常量池的膨胀,比如String.intern调用,但是一般会通过GC回收;(JDK7之前常量池位于Perm区,JDK7及8常量池重Perm区迁移到了Heap中)
  2. 动态Java类创建,比如使用Cglib创建类、Gson对象转换、Jaxb对象转换等。

继续阅读