函数式接口自定义缓存

实现的技术参看了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();
    }
}
BeanDescCache

 

自己自定义的类:

@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();
    }
}
MyBeanCache
@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;
    }


}
BeanConfig
@FunctionalInterface
public interface  MyBeanInterface<T> {
    T apply() ;
}
MyBeanInterface

 

使用方法为:

  /**
     * 获取{@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)内部通过反射实现创建对象
    }
getBeanDesc
@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;
    }
}
View LoginCOntroller

 

对比可以发现使用BeanDescCache的几个亮点:

1.声明一个INSTANCE对象,可以避免显示声明对象。

2.使用了WeakConcurrentMap,是线程安全的类,所以可以被多线程共享。

3.使用了Map的putIfAbsent方法,避免多次创建对象,节省资源。

自定义的Bean亮点:

1.声明了泛型对象,虽然需要显式声明,但是可以灵活存入不同的对象类型。场景是多种类型都需要缓存。

 

总结:一种通过函数式接口创建内存缓存的方式。可以在方法调用灵活生成指定的对象,比如在调用的时候用放射和普通方法生成对象。

可以防止多次创建对象的性能问题(反射创建对象的性能消耗比普通的方式消耗上千倍)。

 

posted @ 2023-07-24 14:35  IT知识生产小店铺  阅读(12)  评论(0编辑  收藏  举报