Delines及其关联技术

项目介绍

ns-cn/delines:基于正则匹配和反射实现解析各种文本(单行或多行)文件转Java实体

  • 特性1、多种基础数据类型支持
  • 特性2、可自定义类型,支持自定义decoder,支持@EntityCreator方式生成实例,可使用Spring Bean方式
  • 特性3、支持单行文本与多种映射实体匹配执行
  • 特性4、支持模型应用范围指定(指定行行或则指定正则形式)
  • 特性5、支持单行和文本的流和字符串文本的映射解析
  • 特性6、支持匹配事件拦截(匹配成功回调执行)
  • 特性7、原生级嵌套支持(since V1.0.2)
  • 特性8、支持编译过程的校验(正则格式校验,必填项校验,@EntityCreator校验(since V1.0.2))

�关键词:$反射$、$正则$、$设计模式$、$编译过程$

功能演示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class TestDelines {

    public static void main(String[] args) {
        String[] lines = new String[]{"P01 小明 14 M 19990909 1999年9月9日", "P02 小霞 15 F 19990919 1999年09月19日"};
        Person person = null;
        for (String line : lines) {
            person = Delines.with(line, Person.class);
            System.out.println(person);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TestDelinesDocument {
    public static void main(String[] args) {
        String text = "成绩单\n" +
                "P01 小明 14 M 19990903\n" +
                "语文 73\n数学 92\n" +
                "P02 小霞 15 F 19980706\n" +
                "语文 64\n数学 94\n" +
                "P03 小文 15 M 19981212\n" +
                "语文 90\n数学 73\n";
        DelinesDocument document = DelinesDocument.of(text);
        Consumer<DelinesDocument> print = (doc) -> {
            List<Person> people = document.getFoundEntities(Person.class);
            if (people != null) {
                Person person = people.get(people.size() - 1);
                List<Score> scores = document.getFoundEntities(Score.class);
                System.out.printf("%s%s\n", person.toString(), scores);
                document.getBusEntity(Score.class).cleanEntities();
            }
        };
        document.registerDelinesEntity(Person.class, ((bus, entity) -> {
                    print.accept(document);
                    return true;
                }))
                .registerDelinesEntity(Score.class)
                .consume();
        print.accept(document);
    }
}

设计模式

介绍应用到delines中的创建型设计模式

单例模式

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

饿汉模式

在类加载的时候就对实例进行初始化,没有线程安全问题;获取实例的静态方法没有使用同步,调用效率高;但是没有使用懒加载,如果该实例从始至终都没被使用过,则会造成内存浪费。

1
2
3
4
5
6
7
public class Singleton {
    private static Singleton instance = new Singleton();
    private Snigleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉模式

在第一次使用的时候才进行初始化,达到了懒加载的效果;由于获取实例的静态方法用synchronized修饰,所以也没有线程安全的问题;但是,这种写法每次获取实例都要进行同步(加锁),因此效率较低。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Singleton {
    private static Singleton instance;
    private Snigleton() {}
    public static synchronized Singleton getInstance() {
        if (instance==null){
            instance = new Singleton();
        }
        return instance;
    }
}

双重检测机制(DCL)

在第一次使用的时候才进行初始化,达到了懒加载的效果;在进行初始化的时候会进行同步(加锁),因此没有线程安全问题;并且只有第一次进行初始化才进行同步,因此不会有效率方面的问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Singleton {
    private static volatile Singleton instance;
    private Snigleton() {}
    public static Singleton getInstance() {
        if (instance==null){
            synchronized (Singleton.class){
                if (instance==null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类(延迟初始化占位类)

JVM将推迟SingletonHolder的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Singleton,因此不需要额外的同步。当任何一个线程第一次调用getInstance时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。

1
2
3
4
5
6
7
8
9
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Snigleton() {}
    public static synchronized Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚举

很简洁的一种实现方式,提供了序列化机制,保证线程安全,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。

1
2
3
public enum Singleton {
    INSTANCE;
}

总结

方式 优点 缺点
饿汉模式 线程安全、效率高 非懒加载
懒汉模式 线程安全、懒加载 效率低
双重检测 线程安全、懒加载、效率高
静态内部类 线程安全、懒加载、效率高
枚举 线程安全、效率高 非懒加载

工厂模式

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class DefaultDecoderFactory implements IDecoderFactory {

    private static final Map<Class<?>, IDelinesDecoder> decoders = new ConcurrentHashMap<>();

    public DefaultDecoderFactory() {
    }

    /**
     * 获取目标类型的解析器实例
     *
     * @param clazz 解析器的类型
     * @return 解析器实例
     */
    @Override
    public IDelinesDecoder get(Class<? extends IDelinesDecoder> clazz) {
        IDelinesDecoder decoder = decoders.get(clazz);
        if (decoder != null) {
            return decoder;
        }
        try {
            decoder = IEntityFactory.build(clazz);
            decoders.put(clazz, decoder);
            return decoder;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

一种通过反射创建实例的思想

常用生成实体的方法:通过构造器new、通过反射获取构造器newInstance。特殊的,如果使用Spring,可以使用BeanFactory获取指定类型的实体。 delines也提供了基于spring方式获取实体的功能,delines-spring

通过给定注解用于标识静态方法生成实例,从而实现对指定实体进行定制

1
2
3
4
5
6
7
8
/**
 * 实体生成器
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityCreator {
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public interface IEntityFactory<T> {
    static <T> T build(Class<T> clazz) {
        if (clazz == null) {
            return null;
        }
        Method[] methods = clazz.getDeclaredMethods();
        Method creator = null;
        for (Method method : methods) {
            EntityCreator entityCreator = method.getAnnotation(EntityCreator.class);
            if (entityCreator != null) {
                if (creator != null) {
                    throw new RuntimeException("@EntityCreator can be only used once in each class");
                }
                if (!Modifier.isStatic(method.getModifiers())
                        || !Modifier.isPublic(method.getModifiers())
                        || method.getParameterCount() != 0
                        || method.getReturnType() != clazz) {
                    throw new RuntimeException("@EntityCreator can be used on only zero-args" +
                            " public static method with returning class itself");
                }
                creator = method;
            }
        }
        if (creator != null) {
            try {
                T t = (T) creator.invoke(null, new Object[]{});
                if (t == null) {
                    throw new RuntimeException("annotated @EntityCreator but returning null");
                }
                return t;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        Constructor<?>[] constructors = clazz.getConstructors();
        try {
            return clazz.newInstance();
        } catch (Exception exception) {
            for (Constructor<?> constructor : constructors) {
                if (constructor.getParameterCount() == 0) {
                    constructor.setAccessible(true);
                    try {
                        Object o = constructor.newInstance(new Object[]{});
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        throw new RuntimeException("now constructor or @EntityCreator zero-args public static method declared");
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public final class DefaultDecoderFactory implements IDecoderFactory {

    private final Map<Class<?>, IDelinesDecoder> decoders = new ConcurrentHashMap<>();

    public static DefaultDecoderFactory customized(){
        decoders.put(SimpleDecoder.class, SimpleDecoder.getInstance());
        return new DefaultDecoderFactory();
    }

    public DefaultDecoderFactory() {
    }

    /**
     * 获取目标类型的解析器实例
     *
     * @param clazz 解析器的类型
     * @return 解析器实例
     */
    @Override
    public IDelinesDecoder get(Class<? extends IDelinesDecoder> clazz) {
        IDelinesDecoder decoder = decoders.get(clazz);
        if (decoder != null) {
            return decoder;
        }
        try {
            decoder = IEntityFactory.build(clazz);
            decoders.put(clazz, decoder);
            return decoder;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

注解处理器

注解处理器(Annotation Processor)是javac的一个工具,不管是运行时注解还是编译时注解,都会通过处理器在编译时进行扫描和处理注解。 常用的场景:生成代码(lombok)、编译过程检查(例如@Override检查)

自定义注解处理器

自定义注解处理器详细步骤:声明处理器、注册处理器、编译过程运行1、 声明处理器:继承自AbstractProcessor

  • @SupportedAnnotationTypes({“com.tangyujun.delines.annotation.DelinesField”}):声明注解处理器处理的目标注解类型
  • @SupportedSourceVersion(SourceVersion.RELEASE_8):指定使用的Java版本,也可以通过AbstractProcessor#getSupportedSourceVersion()指定

自定义注解处理器需要作为当前待编译项目以外的class文件(例如maven作为依赖引入),即编译当前项目以外已经编译好注解处理器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@SupportedAnnotationTypes({"com.tangyujun.delines.annotation.DelinesField"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class DelinesFieldPatternProcessor extends AbstractProcessor {

    public DelinesFieldPatternProcessor() {
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        Messager messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.WARNING, "delines field pattern checking");
        Set<? extends Element> elements = env.getElementsAnnotatedWith(DelinesField.class);
        messager.printMessage(Diagnostic.Kind.NOTE, "total found: " + elements.size());
        boolean success = true;
        try {
            for (Element element : elements) {
                if (element.getKind().equals(ElementKind.FIELD)) {
                    VariableElement variableElement = (VariableElement) element;
                    DelinesField field = variableElement.getAnnotation(DelinesField.class);
                    if (field != null) {
                        if (CharSequenceUtil.isEmpty(field.regExp())) {
                            messager.printMessage(Diagnostic.Kind.ERROR, "@DelinesField without regExp assigned!"
                                    , element);
                        } else {
                            try {
                                if (CharSequenceUtil.isNotBlank(field.regExp())) {
                                    Pattern.compile(field.regExp());
                                }
                            } catch (Exception e) {
                                messager.printMessage(Diagnostic.Kind.ERROR,
                                        "@DelinesField with wrong pattern: " + field.regExp(),
                                        element);
                                success = false;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
        }
        return success;
    }
}

2、注册处理器:可选手动注册、自动注册(使用Google的AutoService)

1
2
3
4
// 在resources/META-INF/services目录下新建javax.annotation.processing.Processor文件
// 在javax.annotation.processing.Processor文件中逐行注册处理器完整类名
com.tangyujun.delines.processor.DelinesFieldPatternProcessor
com.tangyujun.delines.processor.DelinesEntityPatternProcessor
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// 1、引入Google的AutoService
<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service</artifactId>
    <version>1.0.1</version>
</dependency>
<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service-annotations</artifactId>
    <version>1.0.1</version>
    <scope>compile</scope>
</dependency>
// 2、在自定义注解处理器上手动增加@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.tangyujun.delines.annotation.DelinesField"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class DelinesFieldPatternProcessor extends AbstractProcessor {
    // ... ...
}

3、编译编程自动运行

delines编译过程自定义注解处理器运行结果示例

注解处理器应用场景

注解生成代码 @Getter、@Setter等Lombok的应用
注解验证代码结构 @Override、@Deprecated等结构的验证
注解内容验证 delines中@DelinesField、@DelinesEntity中正则格式验证

delines现有注解处理器实现情况

  • 所有有正则的位置的校验
  • 应用范围的声明校验(正则类型和数字类型校验)
  • 自定义类型的解析器实现情况校验
  • 嵌套类型的模型检查,嵌套内无需要映射的字段则编译异常

开放讨论

讨论1:字符串转各种类型的方式

  • delines方式:通过类型判断并用类型的parse方法
  • 其他(待讨论) 讨论2:delines功能建议
  • 原生级内联模型嵌套支持(现有可通过自定义类型解析器实现)
  • 原生级解析结果校验
  • 逐行文本的匹配前处理和匹配后处理 讨论3:delines在FIMS中的应用场景
  • 日志或报文的解析
  • 拓展阅读:Excel的解析工具类:alibaba/easyexcel
  • 其他可能场景
0%