Java、Rust 技术交流群: 783303214

一、背景

最近有小朋友问我,怎么样在.properties文件中配置数组参数,我给他举了上篇文章中的注解@ConfigurationProperties("xxx"),但是遗憾的是他们的项目并没有接入spring,而是用netty写的什么sdk吧,我猜,所以上述注解无法使用,加上自己很久没有玩反射了,就将就着写了一个demo,以供初学者借鉴,话不多说,不懂的看注释,写的还是比较详细。

二、代码实现

首先,我们定义一个注解类,并规定好其作用域等信息

package com.github;import java.lang.annotation.*;/*** @Author: BBSee* @E-mail: rolltion.zhang@foxmail.com* @CreateDate: 16:16 2019/7/22 0022* @Description:*/
@Target({ ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BBSee {String value() default "";/*** The prefix of your properties fields*/String prefix() default "";/*** These two boolean fields have not been analysed at present* if you do have this requirement,add it to the {@link com.github.PropertiesLoader<>}* @return*/boolean ignoreInvalidFields() default false;boolean ignoreUnknownFields() default true;
}

我们规定该注解只能使用于类上,即我们的pojo类上,prefix为配置文件中的字段前缀,接下来,我们定义注解解释器并对配置文件进行获取,由于时间不是很多,所以只写了核心的功能,即注解加载数组信息,根据pojo类的字段名称加载配置信息等,会有些bug,但是都无关痛痒,具体的看代码注释:

package com.github;import com.github.conf.DataSourceNumberProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Pattern;/*** @Author: BBSee* @E-mail: rolltion.zhang@foxmail.com* @CreateDate: 16:48 2019/7/22 0030* @Description:*/
@Slf4j
public class PropertiesLoader<E>  {private static final String DEFAULT_PROPERTIES_PATH="/application.properties";private  Properties properties;private static final String LEFT_BRACKET="[";private static final String RIGHT_BRACKET="]";/*** this demo uses  regular expression to match fields and array bracket,* it is highly recommend to use expression parsing engine,such as: ANTLR*/public  static final Pattern BRACKET_PATTERN=Pattern.compile("^[a-zA-Z][a-zA-Z\\.0-9]+\\[+[0-9]+\\]$");public E getProperty(Class<?> clazz) throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {/*** when the pojo is annotated**/if(clazz.isAnnotationPresent(BBSee.class)){return   getAnnotatedProperty(clazz);}else {/**when the pojo is not annotated,return an empty pojo */log.warn("pojo class without  annotation {@link com.github.BBSee.class} has " +"not been supported yet,no data injected into class: "+clazz.getName());return (E)clazz.newInstance();}}/*** @param clazz* @return* @throws IllegalAccessException* @throws InstantiationException*/private E getAnnotatedProperty(Class<?> clazz) throws IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {String annoPrefix=clazz.getDeclaredAnnotation(BBSee.class).prefix();/*** when a class has been annotated with the @BBSee annotation and used* the default prefix:empty string value,* this method tries to find out the .properties fields,* matching with a lower case of the pojo class name*/String prefix=annoPrefix.isEmpty()?clazz.getName().toLowerCase():annoPrefix;Field[] fields=clazz.getDeclaredFields();Assert.hasText(prefix, "prefix name must not be null !");Assert.notNull(fields, "pojo fields must not be null!");/*** If you do not have Assert,using objects static methods instead*///Objects.requireNonNull(prefix,"prefix name must not be null !");Object pojo=clazz.newInstance();List<String> keys=this.getPropertyListByPrefix(prefix,null);/*** no .properties fields matched,* do nothing but return a empty pojo instance*/if(keys==null||keys.isEmpty()){return (E)pojo;}for(Field field:fields){/*** only support class which is an*  instance of {@link java.util.List<T>}*/if(java.util.List.class.isAssignableFrom(field.getType())) {String fieldName = field.getName();Type genericType = field.getGenericType();List data=new LinkedList();List<String> sortedKeys=this.getPropertyListByPrefix(prefix,fieldName);if (Optional.ofNullable(sortedKeys).isPresent()){sortedKeys.forEach(key->{data.add(properties.get(key));});}PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);Method writeMethod = pd.getWriteMethod();writeMethod.invoke(pojo,data);}/**do nothing with other data constructs  **/}return (E)pojo;}/*** find out the matching fields names by prefix* @param prefix* @return*/private List<String> getPropertyListByPrefix(String prefix,String fieldName){List<String> keys=new LinkedList();Iterator iterator=properties.keySet().iterator();while(iterator.hasNext()){String keyStr=iterator.next().toString();/*** Confirm that there are at least one matching value*/if(fieldName==null){keys.add(keyStr);break;}/*** It seems that,* this comparison algorithm has some security and mapping bugs,* but I don`t have enough time to fix it,* thus,fixing it by yourself*///FIXMEif(keyStr.startsWith(prefix)&&keyStr.substring(0,prefix.length()).equalsIgnoreCase(prefix)){if(BRACKET_PATTERN.matcher(keyStr).matches()&&keyStr.substring(keyStr.indexOf(LEFT_BRACKET)-fieldName.length(),keyStr.indexOf(LEFT_BRACKET)).equals(fieldName)){keys.add(keyStr);}}}/*** SORT the keys by index before return*/if (Optional.ofNullable(keys).isPresent()){Collections.sort(keys, (o1, o2) -> {int index1=getIndex(o1);int index2=getIndex(o2);if(index1>index2){return 1;}else if (index1<index2){return -1;}else {return 0;}});}return keys;}/**** @param keyStr* @exception RuntimeException  index number* @return*/public Integer getIndex(String keyStr){String indexStr=keyStr.substring(keyStr.indexOf(LEFT_BRACKET)+1,keyStr.indexOf(RIGHT_BRACKET));try {Integer index=Integer.parseInt(indexStr);if(index<0){throw new IndexOutOfBoundsException("Parse exception occurred while parsing .properties fields:"+keyStr);}return index;}catch (NumberFormatException e){throw new RuntimeException("Parse exception occurred while parsing .properties fields:"+keyStr);}}public void load(String...path ) throws IOException {properties = new Properties();if(path.length>0){for(String uri:path){try {load(uri);}catch (IOException e){log.error(e.getMessage());continue;}}}else{load(DEFAULT_PROPERTIES_PATH);}}private void load(String path) throws IOException {try(InputStream file=PropertiesLoader.class.getResourceAsStream(path)) {properties.load(file);} catch (IOException e) {throw new IOException("error occurred while loading property file:"+path,e.getCause());}}public Properties getProperties(){return this.properties;}/**Test using of this class*/public static void main(String[] args){PropertiesLoader<DataSourceNumberProperties> propertiesLoader=new PropertiesLoader();try {propertiesLoader.load();DataSourceNumberProperties properties= propertiesLoader.getProperty(DataSourceNumberProperties.class);int a=1;/*** you can handle these following exceptions in properties loader instead of throwing it* do it yourself*/} catch (IOException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IntrospectionException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}
}

代码可以优化,比如穿插一些设计模式,增加一些静态方法等等,比如我们定义一个pojo类:

@Data
@BBSee(prefix="data")
public class  DataSourceNumberProperties {private List<Integer> sources;}

配置文件中数组:

data.sources[0]=1
data.sources[2]=2
data.sources[3]=3

loader根据字段解析,排序,对应相应的数据类型,代码中正则不支持中文,可以加上。

以上。

Java反射+自定义注解实现配置文件数组加载(实现@ConfigurationProperties(xxx))相关推荐

  1. Java反射自定义注解底层设计原理

    文章目录 一.反射 1. 反射概念 2. 反射机制的优缺点 3. 反射的用途 4. 反射技术的使用 5. 反射常用的Api 6. 反射执行构造函数 7. 反射执行给属性赋值 8. 反射执行调用方法 二 ...

  2. Java 反射将配置文件数据加载到对象属性中

    Java 反射将配置文件数据加载到对象属性中 Java 反射 可以根据类名找到相应的类,也可以将配置文件中的值加载到对应属性中. 需要用到的包:spring-core-3.1.2.Release.ja ...

  3. Java中自定义注解的使用

    Java中自定义注解的使用 一般来说,市面上有一些的框架,企业都不会直接拿过来就用,通过会做二次开发或封装,为了更加适配自己的开发规范和业务.那么在封装或适配的过程中,自定义注解就起着比较重要的作用. ...

  4. 第7章 Java反射与注解

    第7章 Java反射与注解 注解Annotation Annotation的作用 Annotation的格式 Annotation使用位置 内置注解 元注解 自定义注解 设置后自动生成的注解 2.反射 ...

  5. java之自定义注解的完整使用

    小坏java自定义注解的完整使用 一.何为java注解之道 1.java 注解的理解之道 2.java 注解的使用示例之道 3.Java 如何自定义注解之道 4.java 元注解之道 5.java 如 ...

  6. java反射和注解开发(备java基础,javaee框架原理)-任亮-专题视频课程

    java反射和注解开发(备java基础,javaee框架原理)-5358人已学习 课程介绍         Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明. ...

  7. java反射 数组类,乐字节Java反射之三:方法、数组、类加载器和类的生命周期

    继续讲述Java反射之三:方法.数组.类加载器 一.方法 获取所有方法(包括父类或接口),使用Method即可. public static void test() throwsException { ...

  8. Java AOP自定义注解

    一.背景 在之前文章:Java注解详解中,主要介绍了注解的含义.作用.以及常用的各类注解.今天主要介绍在Springboot中如何实现一个自定义注解,通过自定义注解去实现一些定制化的需求. 二.了解元 ...

  9. Java实现自定义注解

    前言 (1)Java实现自定义注解其实很简单,跟类定义差不多,只是属性的定义可能跟我们平时定义的属性略有不同,这里会给大家详解,先来看代码: @Target(ElementType.FIELD) @R ...

最新文章

  1. 【django】HttpResponse对象
  2. ubuntu16.04装MatConvNet
  3. linux--gdb调试
  4. 确定活动的热点垃圾收集器
  5. 循环队列及C语言实现三
  6. 人工智能学习书单推荐
  7. PHP控制网页过期时间的代码!
  8. 如何在股票软件画波浪?波浪原理?初级应用画线
  9. 如何去掉空格 oracle,oracle数据库中如何去除空格
  10. 舞台音效控制软件_舞台音乐控制软件下载
  11. FFT算法的C语言实现
  12. 英语发音规则---ea字母组合发音规律
  13. 全网M1、MAC傻瓜式免费安装xmind
  14. win10:取消电脑代理的方法
  15. 使用c语言编程首先要新建,【C语言编程入门系列】—— 第三章,编写第一个C语言程序!...
  16. [UnexpectedValueException] Your github oauth token for github.com contains invalid characters
  17. 搭建指标体系的底层逻辑
  18. 自动化测试中的验证码问题
  19. 老站长教你如何启用网站统计工具
  20. div让两个按钮并排显示在一行中

热门文章

  1. 分布式缓存redis+本地缓存Caffeine:多级缓存架构在行情系统中的应用
  2. 卷积神经网络实战二 电机轴承故障分类
  3. airbnb 爱彼迎开源 Epoxy 优化使用 RecyclerView
  4. IBM公司面试题(既无聊又有趣)
  5. 2021-2027全球与中国三相电能表市场现状及未来发展趋势
  6. matlab 邻近度 离群点_MATLAB到底有多厉害
  7. c++如何计算增值税(例如12%的增值税)
  8. 小学计算机教案动画欣赏,小学信息技术《文字动画》教案
  9. 银监局计算机考试,2018黑龙江银行考试:银监会计算机笔试备考
  10. python:列表、可迭代对象排序