函数式接口自定义缓存
实现的技术参看了hutool工具依赖包的BeanDescCache的实现方式。
技术为:枚举类+ConcurrentHashMap+泛型
效果为:线程安全的内存缓存,可以防止多次创建对象导致的性能问题。
BeanDescCache对象为:
package cn.hutool.core.bean; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.map.WeakConcurrentMap; /** * Bean属性缓存<br> * 缓存用于防止多次反射造成的性能问题 * * @author Looly */ public enum BeanDescCache { INSTANCE; private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>(); /** * 获得属性名和{@link BeanDesc}Map映射 * * @param beanClass Bean的类 * @param supplier 对象不存在时创建对象的函数 * @return 属性名和{@link BeanDesc}映射 * @since 5.4.2 */ public BeanDesc getBeanDesc(Class<?> beanClass, Func0<BeanDesc> supplier) { return bdCache.computeIfAbsent(beanClass, (key)->supplier.callWithRuntimeException()); } /** * 清空全局的Bean属性缓存 * * @since 5.7.21 */ public void clear() { this.bdCache.clear(); } }
自己自定义的类:
@Getter public class MyBeanCache<T> { private final ConcurrentHashMap<Class<?> ,T> beanCache = new ConcurrentHashMap<>(); public T change(Class<?> clazz, MyBeanInterface<T> myBeanInterface){ /*虽然myBeanInterface的接口apply没有参数 ,但是beanCache的computeIfAbsent的函数式接口apply(T)中有参数,所以需要传参数,否则无法确认参数类型异常*/ /*所以这里的技术是方法引用,只要保证返回值一致即可使用替换*/ return beanCache.computeIfAbsent(clazz, (key)->myBeanInterface.apply()); } public void clear(){ beanCache.clear(); } }
@Configuration public class BeanConfig { @Bean public MyBeanCache baseInfoBeanCache(){ MyBeanCache<BaseInfo> myBean = new MyBeanCache(); return myBean; } @Bean public MyBeanCache emailBeanCache(){ MyBeanCache<BaseInfo> myBean = new MyBeanCache(); return myBean; } }
@FunctionalInterface public interface MyBeanInterface<T> { T apply() ; }
使用方法为:
/** * 获取{@link BeanDesc} Bean描述信息 * * @param clazz Bean类 * @return {@link BeanDesc} * @since 3.1.2 */ public static BeanDesc getBeanDesc(Class<?> clazz) { return BeanDescCache.INSTANCE.getBeanDesc(clazz, () -> new BeanDesc(clazz)); // new BeanDesc(clazz)内部通过反射实现创建对象 }
@RestController public class LoginController { @Resource private MyBeanCache baseInfoBeanCache; @Resource private MyBeanCache emailBeanCache; @GetMapping("/login") public String login(){ return "login"; } /** * 邮箱注册 * @param baseInfo * @return */ @PostMapping("/mailLogin") public String autoEmail(@RequestBody BaseInfo baseInfo){ if (null == baseInfo.getEmailAddress() ){ return "failure"; } baseInfoBeanCache.change(baseInfo.getClass(), () -> createBaseInfo()); emailBeanCache.change(baseInfo.getClass(), () -> createBaseInfoByReflection()); return "success"; } /** * 使用普通方式构建 * @return */ private BaseInfo createBaseInfo(){ BaseInfo baseInfo = new BaseInfo(); baseInfo.setSex("1"); return baseInfo; } /** * 使用映射构建 * @return */ private BaseInfo createBaseInfoByReflection() { BaseInfo baseInfo = null; try{ Class<BaseInfo> baseInfoClass = BaseInfo.class; baseInfo = baseInfoClass.getConstructor().newInstance(); }catch (Exception e){ e.printStackTrace(); } return baseInfo; } }
对比可以发现使用BeanDescCache的几个亮点:
1.声明一个INSTANCE对象,可以避免显示声明对象。
2.使用了WeakConcurrentMap,是线程安全的类,所以可以被多线程共享。
3.使用了Map的putIfAbsent方法,避免多次创建对象,节省资源。
自定义的Bean亮点:
1.声明了泛型对象,虽然需要显式声明,但是可以灵活存入不同的对象类型。场景是多种类型都需要缓存。
总结:一种通过函数式接口创建内存缓存的方式。可以在方法调用灵活生成指定的对象,比如在调用的时候用放射和普通方法生成对象。
可以防止多次创建对象的性能问题(反射创建对象的性能消耗比普通的方式消耗上千倍)。
人前不露怯,
远足不露财,
内外当整洁,
自奉须俭约。