最近遇到的几个问题集合

最近在写项目的时候遇到了几个小问题,记录下来。希望对大家也有所帮助。

如何获取 T 的 class

在写BaseDao 之类的代码的时候,经常会遇到获取泛型T的class的情况?我们发现并没有T.class这种写法,那怎么办呢?想起之前写的Hibernate 里面有相关的代码。通过反射获取T的class

1
2
3
4
5
6
7
8
9
10
11
12
public class AbstractDao<T>{

private Class<T> clz;

public AbstractDao(){
this.clz = (Class<T>)
((ParameterizedType)getClass()
.getGenericSuperclass())
.getActualTypeArguments()[0];
}
...
}

Spring中的 @Value 加载时间

首先 @Value 注解可以方便的获取配置文件*.properties的参数。

在写代码的时候遇到这样一个问题。为了减少重复代码,我们通常需要写一个抽象类把共有的方法抽象到Abstract 类中。AbstractDao<T> 如果遇到需要向这个class的构造方法注入参数。且这个参数是通过抽象方法获取的。且这个数据是使用 Spring的 @Value 注解获取的。这个描述比较绕,我们直接看代码:

1
2
3
4
5
6
7
8
9
public class AbstractDao<T>{
private int tableId;

public AbstractDao(){
this.tableId = setTableId();
}

protected abstract int setTableId();
}
1
2
3
4
5
6
7
8
9
public class UserDao extends AbstractDao<User>{

@Value("${tableid.user}")
private int userTableId;

protected int setTableId() {
return buserTableId;
}
}

代码运行起来以后,我们发现 userTableId,并不能取到相应的值,这个时候@Value失效了。实际上这个问题的根源是因为@Value的加载是发生在对象实例化之后。也就是首先调用对象的构造函数,然后再获取配置文件中的数据。

解决的方案是使用注解 @PostConstruct,意思是构造函数执行完以后再执行注解标记的方法。我们可以吧抽象函数做如下修改:

1
2
3
4
5
6
7
8
9
10
public class AbstractDao<T>{
private int tableId;

@PostConstruct
public init(){
this.tableId = setTableId();
}

protected abstract int setTableId();
}

使用 Java 8 的 lambda 和 stream 来 merge List

在使用微服务架构以后。我们经常会遇到 Merge两个List的场景。比如我们从索引里面获取了一个 List<Long> 包含的是对象的ID的 list。由于前端对象展示的元素需要。用这个ID 的list分别从两个服务批量的查询得到 List<A>List<B>,然后将两个List合二为一成为一个List<C>,返回给前端作为列表页展示。

看代码:
首先我们有一个对象 A:

1
2
3
4
public class A{
private long id;
private String aStr;
}

有另一个对象 B:

1
2
3
4
public class B{
private long id;
private String bStr;
}

页面需要的对象C:

1
2
3
4
5
public class C{
private long id;
private String aStr;
private String bStr;
}

如何有效的把 List<A>List<B> merge 为一个 List呢?

总体思路,因为两个list从不同服务里面获取。有可能两个服务出于健壮性的考虑会抛弃某些查询不到的对象,所以两个list的长度有可能不一致。所以使用一个Map<Long,B> 作为索引。
如果直接写代码会相当繁琐。如果使用 Java 8 的新特性Lamabdastream api 就能快速写出代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

private C getCForAAndB(A a,B b){
C c = new C();
c.setId(a.getId());
c.setAstr(a.getAStr())
c.setBstr(b.getBStr())
return c;
}

private List<C> mergeList(List<A> aList,List<B> bList){
//映射Map
Map<Long,B> bMap = bList.parallelStream()
.collect(Collectors.toMap(B::getId,b -> b));

return aList.parallelStream()
.map(a -> getCForAAndB(a,bMap.get(a.getId)))
.collect(Collectors.toList());
}

这样merge的代码简洁明了。

如果不明白的同学可以参考我之前的 Java 8 教程。