1 注解@Mapper(componentModel = "spring")-自动生成DTO-DO的模型映射
场景: 插件可以自动生成实现类,实现DTO-DO各种模型之间的字段映射(不仅仅限制于DTO-DO)
一,用法说明:
//通常在业务实现类中创建一个用于转换的内部接口
@Mapper(componentModel = "spring")
public interface RoleConvert {
RoleDO convert(RoleDTO roleDTO);
}
使用:
//依赖注入
@Autowired
private RoleConvert roleConvert;
//转化
RoleDO roleDO = roleConvert.convert(roleDTO);
二,高级用法,指定映射字段(支持表达式):
//通常在业务实现类中创建一个用于转换的内部接口
/**
ProductDO中 config字段为json字符串
ProductDTO中 config字段为map类型
*/
@Mapper(componentModel = "spring")
public interface ProductConverter {
@Mappings({
@Mapping(source = "id",target = "productId"),
@Mapping(source = "title",target = "productName"),
@Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "config", expression = "java( com.alibaba.fastjson.JSON.parseObject(pdo.getConfig(),new com.alibaba.fastjson.TypeReference<java.util.Map<java.lang.String,java.lang.String>>(){}) )"),
@Mapping(target = "email", ignore = true) //这个字段忽略不转换
})
ProductDTO convert(ProductDO pdo);
@Mappings({
@Mapping(source = "productId",target = "id"),
@Mapping(source = "productName",target = "title"),
@Mapping(target = "config", expression = "java( com.alibaba.fastjson.JSON.toJSONString(pdto.getConfig()) )")
})
ProductDO convert(ProductDTO pdto);
}
- 还可以支持多个对象转换成一个对象,多对一的写法
- 不单单可以支持spring组件,还可以支持其他组件,componentModel = "spring"
- 此工具更多用法学习链接:blog.csdn.net/qq_34614236…
2 使用Guava优雅实现本地缓存
2.1 本地缓存通常用来解决一些数据量相对较小但是频繁访问的数据
2.2 Guava Cache与ConcurrentHashMap很相似,但也不完全一样。最基本的区别是:
- ConcurrentHashMap会一直保存所有添加的元素,直到显式地移除。
- 相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。
- 在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
2.3 通常来说,Guava Cache适用于:
- 你愿意消耗一些内存空间来提升速度。
- 你预料到某些键会被查询一次以上。
- 缓存中存放的数据总量不会超出内存容量。
- Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached或者Redis这类工具
- 如果你的场景符合上述的每一条,Guava Cache就适合你。
学习链接:gourderwa.blog.csdn.net/article/det…
2.4 代码举例实现:
/**
dao层对应的数据操作实现类:加缓存 查询条件实现等
service业务层 可直接引用
*/
@Component
public class VariableRepositoryImpl implements VariableRepository, InitializingBean {
@Resource
private ArtooSceneVariableDAO variableDAO;
@Resource
private RefreshLocalCacheBuilder cacheBuilder;
private LoadingCache<Long, ArtooSceneVariableDO> idCache;
private LoadingCache<Long, List<ArtooSceneVariableDO>> sceneIdCache;
//删除
@Override
public int delete(Long id) {
// 缓存清除 idCache.invalidateAll(ids) -代表清除多个缓存
idCache.invalidate(id);
//物理删除
return variableDAO.deleteByPrimaryKey(id);
}
//修改
@Override
public Variable update(Variable variable) {
ArtooSceneVariableDO variableDO = convert.convert(variable);
variableDO.setGmtModified(new Date());
variableDAO.updateByPrimaryKeySelective(variableDO);
// 缓存清除
idCache.invalidate(variableDO.getId());
return query(variableDO.getId());
}
//查询一个
@Override
public Variable query(Long id) {
ArtooSceneVariableDO variableDO = idCache.getUnchecked(id);//get不抛出异常
return convert.convert(variableDO);
}
//查询多个
@Override
public List<Variable> queryByIds(List<Long> ids) {
try {
return idCache.getAll(ids) //getAll获取多个 会抛出异常
.values()
.stream()
.map(convert::convert)
.collect(Collectors.toList());
} catch (ExecutionException e) {
LOGGER.warn("found variable fail by ids {}", ids, e);
}
return Collections.emptyList();
}
@Override
public List<Variable> queryBySceneId(Long sceneId) {
try {
return sceneIdCache.get(sceneId) //get获取一个 会抛出异常
.stream()
.map(convert::convert)
.collect(Collectors.toList());
} catch (ExecutionException e) {
LOGGER.warn("found variable fail by sceneId {}", sceneId, e);
}
return Collections.emptyList();
}
//这里实现InitializingBean接口方法 在bean初始化后构建缓存
@Override
public void afterPropertiesSet() throws Exception {
//调用封装的guava缓存方法cacheBuilder.build(**) 实现根据ID主键查询的缓存
this.idCache = cacheBuilder.build(5*60, 25, 500, new CacheLoader<Long, ArtooSceneVariableDO>() {
@Override
public ArtooSceneVariableDO load(Long key) throws Exception {
return variableDAO.selectByPrimaryKey(key);
}
});
//调用封装的guava缓存方法cacheBuilder.build(**) 实现根据外键查询的缓存
this.sceneIdCache = cacheBuilder.build(5*60, 25, 500, new CacheLoader<Long, List<ArtooSceneVariableDO>>() {
@Override
public List<ArtooSceneVariableDO> load(Long key) throws Exception {
ArtooSceneVariableParam variableParam = new ArtooSceneVariableParam();
variableParam.createCriteria().andSceneIdEqualTo(key);
return variableDAO.selectByParam(variableParam);
}
});
}
}
/**
封装的通用的guava缓存实现类
*/
@Component
public class RefreshLocalCacheBuilder {
@Resource(name = "cacheRefreshDbExecutor")
private ExecutorService executor;
/**
* 支持异步刷新的本地缓存实现,会返回一个 LoadingCache 实例
* @param expireTime 数据过期时间 ,单位为秒
* @param refreshTime 数据刷新时间,单位为秒, 需要注意的是 刷新为异步,且只会刷新存在的数据,因此 要小于 expireTime
* @param initialCapacity 初始的容量
* @param loader load
* @return
*/
public <K, V> LoadingCache<K, V> build(long expireTime, long refreshTime, int initialCapacity, CacheLoader<K, V> loader) {
return CacheBuilder.newBuilder()
.expireAfterWrite(expireTime, TimeUnit.SECONDS)
.refreshAfterWrite(refreshTime, TimeUnit.SECONDS)
.initialCapacity(initialCapacity)
.build(CacheLoader.asyncReloading(loader, executor));
}
}
---------------------------------------------------------------------------
/**
springBoot启动配置类
*/
@Configuration
public class SpringBeanConfig {
@Bean
public OkHttpClient build() {
return new OkHttpClient.Builder()
.readTimeout(2000, TimeUnit.MILLISECONDS)
.build();
}
@Bean(name = "cacheRefreshDbExecutor")
public ExecutorService dbRead(){
ThreadFactory threadFactory= new ThreadFactoryBuilder()
.setNameFormat("cache-db-read-thread-pool-%d")
.build();
//用于统一的缓存刷新,因此根据单机tddl 的线程池配置,由于是用于缓存,因此可以丢弃老的任务
//假设db查询在5ms 以内,1s 可吞吐 2000 个 2秒超时, 可设置4000个队列,增加一些冗余4000*1.3
return new ThreadPoolExecutor(
8,10,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(5200),threadFactory,new ThreadPoolExecutor.DiscardOldestPolicy());
}
}
3 利用jdk的Consumer实现通用的接口回调
java.util.function中 Function, Supplier, Consumer, Predicate和其他函数式接口广泛用在支持lambda表达式的API中。这些接口有一个抽象方法,会被lambda表达式的定义所覆盖。
Consumer的作用顾名思义,是给定义一个参数,对其进行(消费)处理,处理的方式可以是任意操作.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
3.1 其核心方法如下:
-
void accept(T t);
对给定的参数T执行定义的操作 -
default Consumer andThen(Consumer<? super T> after)
对给定的参数T执行定义的操作执行再继续执行after定义的操作
3.2 接口回调代码举例:
public class GollHandle{
@Resource
private UserService userService;
@Resource
private LogService logService;
/**
业务处理逻辑
*/
public void handle() {
//上下文参数
HandleContext handleContext = new HandleContext();
Consumer<Product> productConsumer = product -> {
//函数式编程实现接口回调的业务方法accept(T),参数product
logService.recordProductLog(product, handleContext);
};
//购买商品
userService.buyProduct(product, productConsumer);
}
}
/**
用户业务类
*/
public class UserService {
@Resource
private UserDao userDao;
/**
购买商品
*/
public void buyProduct(Product product, Consumer<Product> consumer){
userDao.buy(product);
Optional.ofNullable(consumer).ifPresent(c -> c.accept(product));
}
}
/**
日志业务类
*/
public class LogService {
/**
记录购买商品日志
*/
public void recordProductLog(Product product, HandleContext context){
//记录日志 context上下文参数
}
}