一、Preconditions使用,里面牵扯到一个问题,是直接抛出异常还是对字段做校验呢,回复中的一段关于适用unchecked Exception还是checked Exception写的非常好,如下:
Preconditions的异常类型问题 牵扯到设计准则,后面的章节‘Throwables’也提到了一点。一般准则认为,违反方法契约(包括方法所使用的对象或外部数据状态)时抛出UncheckedException,其定义方法的可用性范畴;而方法因为不可控原因无法履行契约,则抛出CheckedException。 所以Preconditions就跟它的名字一样,是用来定义方法契约的,这样方法才算完整地自描述和可重用了。
对设计理想的应用来说,Runtimeexception应该绝少触发。当调用这些方法时,你可以根据领域对象的业务含义,在调用方预先作检查,比如bean-validator这样的手段。而详细去看bean-validator的定义时,你就会发现它完全撇清了检查和异常的概念。
二、BiMap是JDK中不存在的一种MAP结构,与MAP不同,BiMap不仅仅提供了【键->值】的映射关系,同时提供了【值->键】的映射关系代码如下:
package cn.outofmemory.guava.collection;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
public class BiMapDemo {
public static void main(String[] args) {
BiMap<String,String> weekNameMap = HashBiMap.create();
weekNameMap.put("星期一","Monday");
weekNameMap.put("星期二","Tuesday");
weekNameMap.put("星期三","Wednesday");
weekNameMap.put("星期四","Thursday");
weekNameMap.put("星期五","Friday");
weekNameMap.put("星期六","Saturday");
weekNameMap.put("星期日","Sunday");
System.out.println("星期日的英文名是" + weekNameMap.get("星期日"));
System.out.println("Sunday的中文是" + weekNameMap.inverse().get("Sunday"));
}
}
三、Table是JDK中不存在的另外一种MAP结构,简单的说就是【双键->值 】的映射模式,实际应用代码如下:
@Override
public void changeReportRecordOwner(List<Long> ids, Long accountId, Long memberId) throws BusinessException {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<ReportRecord> list = findReportRecord(ids);
if (CollectionUtils.isEmpty(list)) {
return;
}
Table<Long, String, ReportCategory> table = HashBasedTable.create();
for (ReportRecord record : list) {
Long sAccountId = record.getOrgAccountId();
if (!ObjectUtils.equals(sAccountId, accountId)) {//跨单位
ReportCategory newCategory = getVReportCategory(record.getReportCategoryId(), accountId, table);
if (newCategory != null) {
record.setReportCategoryId(newCategory.getId());
} else {
record.setUseFlag(RecordUseFlag.Disable.key);
record.setReportCategoryId(null);
LOG.error("报表[id=" + record.getId() + "]记录无报表分类, 设置为停用状态");
}
record.setOrgAccountId(accountId);
}
record.setCreateMember(memberId);
record.setUpdateDate(new Date());
}
updateReportRecord(list);
saveReportCategory(Lists.newArrayList(table.values()));
}
private ReportCategory getVReportCategory(Long categoryId, Long accountId, Table<Long, String, ReportCategory> table) throws BusinessException {
ReportCategory category = getReportCategory(categoryId);
if (category == null) {
return null;
}
//优先从当前缓存中找
ReportCategory newCategory = table.get(accountId, category.getName());
if (newCategory != null) {
return newCategory;
}
//从数据库中找
List<ReportCategory> nameList = findReportCategoryByName(category.getName(), accountId);
if (CollectionUtils.isNotEmpty(nameList)) {
return nameList.get(0);
} else {
newCategory = new ReportCategory();
newCategory.setIdIfNew();
newCategory.setName(category.getName());
newCategory.setParentId(ReportCategory.ROOT_ID);
newCategory.setCreateDate(new Date());
newCategory.setModifyDate(new Date());
newCategory.setOrgAccountId(accountId);
newCategory.setSort(0);
newCategory.setNodePath("0");
Long adminId = getAdmin(accountId);
newCategory.setCreateMember(adminId);
newCategory.setModifyMember(adminId);
}
table.put(accountId, newCategory.getName(), newCategory);
return newCategory;
}
四、使用ClassPath+ClassInfo用于加载指定包路径下的类非常有用,请看实际代码:
classes = Sets.newHashSet();
ClassPath classPath = ClassPath.from(CTPRestApplication.class.getClassLoader());
// jersey基础类
String basePackage = ServerDetector.isWebSphere() ? "org.codehaus.jackson.jaxrs" : "com.fasterxml.jackson.jaxrs.json";
for (ClassInfo ci : classPath.getTopLevelClasses(basePackage)) {
classes.add(ci.load());
}
// V5基础类
String seeyonPackage = "com.seeyon.ctp.rest.resources";
for (ClassInfo ci : classPath.getTopLevelClasses(seeyonPackage)) {
Class<?> clazz = ci.load();
if (clazz.isAssignableFrom(BaseResource.class)) {
classes.add(clazz);
}
}
由于ClassPath是在guava14才执行,在JDK1.7上才做支持,对于垃圾的WAS环境而言又只能使用JDK1.6,现实用spring resource加载来做实现,代码如下:
private static Set<Class<?>> loadTopLevelClasses(String basePackage) throws IOException {
Set<Class<?>> classes = Sets.newHashSet();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage)) + "/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
if (ArrayUtils.isNotEmpty(resources)) {
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
String className = metadataReader.getClassMetadata().getClassName();
if (className.indexOf("$") > -1) {//不加载内部类
continue;
}
try {
Class<?> clazz = Class.forName(className);
classes.add(clazz);
} catch (ClassNotFoundException e) {
LOG.error("类加载异常:", e);
}
}
}
}
return classes;
}
五、一致性哈希(网友说在分布式上应用比较多,当时没有接触到,不是很理解)
int bucket = Hashing.consistentHash(id, buckets) // bucket 的范围在 0 ~ buckets 之间
六、使用Tables.transpose竟然可以达到转置效果(惊不惊喜,意不意外)!
Table<String, String, Object> table = HashBasedTable.create();
table.put("a", "javase", 80);
table.put("b", "javaee", 90);
table.put("c", "javame", 100);
table.put("d", "guava", 70);
for (Cell<String, String, Object> cell : table.cellSet()) {
System.out.println(cell.getRowKey() + ":" + cell.getColumnKey() + ":" + cell.getValue());
}
System.out.println("=======转置后========");
Table<String, String, Object> table2 = Tables.transpose(table);
for (Cell<String, String, Object> cell : table2.cellSet()) {
System.out.println(cell.getRowKey() + ":" + cell.getColumnKey() + ":" + cell.getValue());
}