另一种JFinal读取配置文件的高效方案

本文是基于JFinal编写的一种读取配置文件的更加高效的方案。如果您没有使用JFInal,没必要阅读此篇文章,以免浪费您的时间。

 

使用过JFinal开发项目的朋友都知道,读取properties配置文件其实非常简单,JFinal已经提供了一个PropKit的工具类,使我们非常方便的读取到配置文件中的某个值。

在代码初始化的时候,首先初始化下Propkit,代码如下:

PropKit.use("jboot.properties");

然后在整个项目中,随便使用PropKit的get方法即可读取jboot.properties文件的任何内容,例如:

String wechatAppId = PropKit.get("wechat_app_id");

非常简洁,读取某个配置文件也就一行代码而已。

 

但是,但是...

 

在我们实际开发中,某个组件的配置,往往是由多个配置信息组成的,比如微信开发的相关配置,需要的信息可能如下:

wechat.debug = false
wechat.appid = xxx
wechat.appsecret = xxx
wechat.token = xxx
wechat.partner = xxx
wechat.paternerKey = xxx
wechat.cert = xxx
wechat.templateId = xxx

那么,如果我们在需要读取这些信息的时候,比如在拦截器里,我们可能需要编写如下的代码:

String wechatAppId = PropKit.get("wechat.id");
String wechatAppSecret= PropKit.get("wechat.appsecret");
String wechatToken = PropKit.get("wechat.token");
String wechatPartner = PropKit.get("wechat.partner");
String wechatPartnerKey = PropKit.get("wechat.partnerKey");
String wechatCert = PropKit.get("wechat.cert");
String wechatTemplateId = PropKit.get("wechat.templateId");

如果再多个地方有这样的需求,那么通常有几个方法:

1、复制这部分代码,到需要的地方。

2、把这部分的代码提取到一个独立的类,里面有getAppId(),getAppSercret()等方法,然后调用。

3、编写一个model类,然后在这个类进行实例化的时候,填充属性。

 

在项目中除了微信配置以外,还有非常多的各种配置,比如数据库配置信息、redis配置信息,缓存配置信息,rpc配置信息等等等等。无论您使用3中方法中的任何一种,都不是高效简洁的。

 

那么,是否可以有更好的办法呢?当然有。

方法如下:

给每中配置编写对于的一个model,然后通过一个配置文件直接读取到这个model。

例如:我们为微信的配置编写了一个WechatConfig类,代码如下:

public class WechatConfig{
   private String wechatAppId;
   private String wechatAppSecret;
   private String wechatToken;
   private String partner;
   private String partnerKey;
   private String cert;
   private String templateId;

   // getter setter 略


}

想获取这个配置信息,只需要通过一个配置文件即可。

WechatConfig config  = MyPropUtils.getConfig(WechatConfig.class);

重点来了,如果编写MyPropUtils这个工具类。

先说思路:

1、读取到properties的所有配置信息。

2、反射读取到WechatConfig的字段。

3、把读取到的properties内容赋值到对应的字段去。

 

看起来也非常简单,直接编写代码。

public static <T> T getConfig(String propFile, Class<T> clazz) {
   

        //不管三七二十一,先初始化这个类。
        Object obj = ClassNewer.newInstance(clazz);


        //读取到Prop信息
        Prop prop = PropKit.use(propFile);
       
        //找到这个类的所有set方法,model类默认有get和set的
        List<Method> setMethods = new ArrayList<>();

        Method[] methods = obj.getClass().getMethods();
        if (ArrayUtils.isNotEmpty(methods)) {
            for (Method m : methods) {
                if (m.getName().startsWith("set") 
                && m.getName().length() > 3 
                && m.getParameterCount() == 1) {
                    setMethods.add(m);
                }
            }
        }

        //遍历所有set方法
        for (Method method : setMethods) {
            try {
                
                // 得到这个set方法对应的key以及value值
                String key = StrKit.firstCharToLowerCase(method.getName().substring(3));
                String   value = prop.get(key);
                

                if (StringUtils.isNotBlank(value)) {
                    //如果这个value值不为空,把value值转化为该set方法需要的数据类型,
                    //可能是boolean,int,datetime等类型
                    Object val = convert(method.getParameterTypes()[0], value);

                    //执行这个方法并进行赋值
                    method.invoke(obj, val);
                }
            } catch (Throwable ex) {
                log.error(ex.toString(), ex);
            }
        }


        //把这个object返回
        return (T) obj;
    }

 

嗯,看起来还不错,但是还是有些问题,这个有些局限性,很多时候在一个配置文件里面可能需要有多个名称一样的配置,比如redis的可能有一个叫password的配置,数据库可能也需要一个叫password的配置,因此,在配置文件里面我们通常会给某个组件添加上前缀,比如redis的相关配置是 redis.password,redis.host这样的,而数据库可能是 mysql.password,mysql.user 等等这样的配置,因此我们需要给这个工具类添加下 可以读取前缀的功能。

还有另一个缺点是,每次调用这个getConfig方法,都是重新实例化一个新的实例,配置信息在整个项目中都很少发生变化,为了更加高效,我们可以把这个getConfig读取到的model都是单利的,而不是每次都是一个新的实例。

 

因此,我们可以优化如下:

   private static ConcurrentHashMap<String, Object> configs = new ConcurrentHashMap<>();
    public static <T> T getConfig(String prefix, String propFile, Class<T> clazz) {
        
        //检查前缀
       if (StringUtils.isBlank(prefix)) {
            throw new JbootException("prefix must not be null");
        }

        //先尝试从map中获取内容
        Object obj = configs.get(prefix);
        if (obj != null) {
            return (T) obj;
        }

        obj = ClassNewer.newInstance(clazz);


        Prop prop =  PropKit.use(propFile);
            

        List<Method> setMethods = new ArrayList<>();

        Method[] methods = obj.getClass().getMethods();
        if (ArrayUtils.isNotEmpty(methods)) {
            for (Method m : methods) {
                if (m.getName().startsWith("set") 
                    && m.getName().length() > 3 
                    && m.getParameterCount() == 1) {
                    setMethods.add(m);
                }
            }
        }


        for (Method method : setMethods) {
            try {

                String key = StrKit.firstCharToLowerCase(method.getName().substring(3));

                
                //添加上前缀的key
                key = prefix.trim() + "." + key;
                

                String value = prop.get(key);
                

                if (StringUtils.isNotBlank(value)) {
                    Object val = convert(method.getParameterTypes()[0], value);
                    method.invoke(obj, val);
                }
            } catch (Throwable ex) {
                log.error(ex.toString(), ex);
            }
        }

        //设置到map里去
        configs.put(prefix, obj);

        return (T) obj;
    }

 

代码写到这里,已经很不错了,但是还有几个小问题:

1、调用这个方法需要穿3个参数,太麻烦了。

2、通常在我们整个项目中,一般就只有几个配置文件,不会太多,所以没有必要每次都传配置文件的名字。

 

我们继续优化下,处理办法是添加几个重载的方法。

    private static ConcurrentHashMap<String, Object> configs = new ConcurrentHashMap<>();
    private static final String defalutConfigFile = "jboot.properties";
 

    public static <T> T get(Class<T> clazz) {
        PropertieConfig propertieConfig = clazz.getAnnotation(PropertieConfig.class);
        if (propertieConfig == null) {
            throw new JbootException("PropertieConfig annotation must 
                                      not be null in class : " + clazz);
        }

        return getConfig(propertieConfig.prefix(), propertieConfig.file(), clazz);
    }



    public static <T> T getConfig(String prefix, Class<T> clazz) {

      return getConfig(prefix,defalutConfigFile,clazz);

    }



    public static <T> T getConfig(String prefix, String propFile, Class<T> clazz) {
        
        //检查前缀
       if (StringUtils.isBlank(prefix)) {
            throw new JbootException("prefix must not be null");
        }

        //先尝试从map中获取内容
        Object obj = configs.get(prefix);
        if (obj != null) {
            return (T) obj;
        }

        obj = ClassNewer.newInstance(clazz);


        Prop prop =  PropKit.use(propFile);
            

        List<Method> setMethods = new ArrayList<>();

        Method[] methods = obj.getClass().getMethods();
        if (ArrayUtils.isNotEmpty(methods)) {
            for (Method m : methods) {
                if (m.getName().startsWith("set") 
                    && m.getName().length() > 3 
                    && m.getParameterCount() == 1) {
                    setMethods.add(m);
                }
            }
        }


        for (Method method : setMethods) {
            try {

                String key = StrKit.firstCharToLowerCase(method.getName().substring(3));

                
                //添加上前缀的key
                key = prefix.trim() + "." + key;
                

                String value = prop.get(key);
                

                if (StringUtils.isNotBlank(value)) {
                    Object val = convert(method.getParameterTypes()[0], value);
                    method.invoke(obj, val);
                }
            } catch (Throwable ex) {
                log.error(ex.toString(), ex);
            }
        }

        //设置到map里去
        configs.put(prefix, obj);

        return (T) obj;
    }

 

OK,代码编写完了,如何使用呢?

配置内容如下:

jboot.myconfig.name=aaa
jboot.myconfig.passowrd=bbb
jboot.myconfig.age=10

编写model类:

@PropertieConfig(prefix="jboot.myconfig")
public class MyConfigModel{
    private String name;
    private String password;
    private int age;

    //getter setter 略
}

读取 model 内容:

MyConfigModel config = MyPropUtils.getConfig(MyConfigModel.class);

 

怎么样?是不是跟简单,有更多时间陪女朋友了?

 

具体代码在:https://git.oschina.net/fuhai/jboot/blob/master/src/main/java/io/jboot/config/JbootProperties.java ,请执行参考。

 

我的个人微信公众号,经常分享一些技术心得和心里路程,希望也能认识你。

转载于:https://my.oschina.net/yangfuhai/blog/1516622

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值