Java反射+自定义注解实现配置文件数组加载(实现@ConfigurationProperties(xxx))
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))相关推荐
- Java反射自定义注解底层设计原理
文章目录 一.反射 1. 反射概念 2. 反射机制的优缺点 3. 反射的用途 4. 反射技术的使用 5. 反射常用的Api 6. 反射执行构造函数 7. 反射执行给属性赋值 8. 反射执行调用方法 二 ...
- Java 反射将配置文件数据加载到对象属性中
Java 反射将配置文件数据加载到对象属性中 Java 反射 可以根据类名找到相应的类,也可以将配置文件中的值加载到对应属性中. 需要用到的包:spring-core-3.1.2.Release.ja ...
- Java中自定义注解的使用
Java中自定义注解的使用 一般来说,市面上有一些的框架,企业都不会直接拿过来就用,通过会做二次开发或封装,为了更加适配自己的开发规范和业务.那么在封装或适配的过程中,自定义注解就起着比较重要的作用. ...
- 第7章 Java反射与注解
第7章 Java反射与注解 注解Annotation Annotation的作用 Annotation的格式 Annotation使用位置 内置注解 元注解 自定义注解 设置后自动生成的注解 2.反射 ...
- java之自定义注解的完整使用
小坏java自定义注解的完整使用 一.何为java注解之道 1.java 注解的理解之道 2.java 注解的使用示例之道 3.Java 如何自定义注解之道 4.java 元注解之道 5.java 如 ...
- java反射和注解开发(备java基础,javaee框架原理)-任亮-专题视频课程
java反射和注解开发(备java基础,javaee框架原理)-5358人已学习 课程介绍 Java注解是附加在代码中的一些元信息,用于一些工具在编译.运行时进行解析和使用,起到说明. ...
- java反射 数组类,乐字节Java反射之三:方法、数组、类加载器和类的生命周期
继续讲述Java反射之三:方法.数组.类加载器 一.方法 获取所有方法(包括父类或接口),使用Method即可. public static void test() throwsException { ...
- Java AOP自定义注解
一.背景 在之前文章:Java注解详解中,主要介绍了注解的含义.作用.以及常用的各类注解.今天主要介绍在Springboot中如何实现一个自定义注解,通过自定义注解去实现一些定制化的需求. 二.了解元 ...
- Java实现自定义注解
前言 (1)Java实现自定义注解其实很简单,跟类定义差不多,只是属性的定义可能跟我们平时定义的属性略有不同,这里会给大家详解,先来看代码: @Target(ElementType.FIELD) @R ...
最新文章
- 【django】HttpResponse对象
- ubuntu16.04装MatConvNet
- linux--gdb调试
- 确定活动的热点垃圾收集器
- 循环队列及C语言实现三
- 人工智能学习书单推荐
- PHP控制网页过期时间的代码!
- 如何在股票软件画波浪?波浪原理?初级应用画线
- 如何去掉空格 oracle,oracle数据库中如何去除空格
- 舞台音效控制软件_舞台音乐控制软件下载
- FFT算法的C语言实现
- 英语发音规则---ea字母组合发音规律
- 全网M1、MAC傻瓜式免费安装xmind
- win10:取消电脑代理的方法
- 使用c语言编程首先要新建,【C语言编程入门系列】—— 第三章,编写第一个C语言程序!...
- [UnexpectedValueException] Your github oauth token for github.com contains invalid characters
- 搭建指标体系的底层逻辑
- 自动化测试中的验证码问题
- 老站长教你如何启用网站统计工具
- div让两个按钮并排显示在一行中
热门文章
- 分布式缓存redis+本地缓存Caffeine:多级缓存架构在行情系统中的应用
- 卷积神经网络实战二 电机轴承故障分类
- airbnb 爱彼迎开源 Epoxy 优化使用 RecyclerView
- IBM公司面试题(既无聊又有趣)
- 2021-2027全球与中国三相电能表市场现状及未来发展趋势
- matlab 邻近度 离群点_MATLAB到底有多厉害
- c++如何计算增值税(例如12%的增值税)
- 小学计算机教案动画欣赏,小学信息技术《文字动画》教案
- 银监局计算机考试,2018黑龙江银行考试:银监会计算机笔试备考
- python:列表、可迭代对象排序