使用java注解能够实现一些共通的功能,假设有几种格式的csv文件,编码,分隔符,头部行数之类的定义各不相同,但咱们想统一的处理他们,那就须要一个共通的方法。java
也许有人说,不用注解,只用个共通工具类不就好了吗?可是注解让代码更优雅,并且当你增长其余一些需求,好比其余csv格式的时候,只须要加几个注解就能轻松的扩张你的功能。数组
那么看代码吧。bash
定义一个csv格式的注解,包含文件的分隔符,编码等等信息。若是业务需求增多,能够继续添加功能,好比换行符之类。框架
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FileFormat {
// 分隔符
char delimiter() default ',';
// 引用符
char encloseChar() default Character.MIN_VALUE;
// 编码
String fileEncoding() default "UTF-8";
// 头部行数
int headerLineCount() default 0;
// 输出文件是否覆盖
boolean overWriteFlg() default false;
}
复制代码
这里为了扩展性先定义了一个空的接口。若是须要扩展就实现该接口。工具
public interface CSVFormat {
}
复制代码
FreeTextCSVFormat实现了CSVFormat接口,并使用了FileFormat的注解,分隔符,编码等都使用默认值,并无进行特别的设置。oop
@Data
@FileFormat()
public class FreeTextCSVFormat implements CSVFormat {
private String nexcoNumber;
private String msgNumber;
private String cmdMode;
private String text;
}
复制代码
根据注解的设置,读取一行数据。无论是什么编码,或者换行符,都是用通用的readDataLine()方法。ui
@Data
public class CSVFileLineIterator {
// 格式类
private Class<? extends CSVFormat> format;
// 対象文件
private File file;
// 分隔符
private char delimiter;
// 文字编码
private String encoding;
// 头部行数
private int headerLineCount;
// 总共读取行数
private int count;
// reader
private BufferedReader reader;
// msg
private MessageSource msg;
/**
* 构造器。把注解的数据代入成员变量。
*/
public CSVFileLineIterator(File file, Class<? extends CSVFormat> format) throws IllegalArgumentException, FileException {
if (file == null && format == null) {
throw new IllegalArgumentException(msg.getMessage("nullArgument", null, null));
}
if (!file.exists()) {
throw new FileException(msg.getMessage("fileNoFound", null, null));
}
this.file = file;
this.format = format;
FileFormat fileformat = format.getAnnotation(FileFormat.class);
if (fileformat == null) {
throw new FileException(msg.getMessage("noFormatAnnotation", null, null));
}
this.delimiter = fileformat.delimiter();
this.encoding = fileformat.fileEncoding();
this.headerLineCount = fileformat.headerLineCount();
if (this.delimiter == Character.MIN_VALUE) {
throw new FileException(msg.getMessage("illegalDelimiter", new String[] {file.getName()}, null));
}
}
/**
* 用注解指定的编码打开reader
*/
public void open() throws UnsupportedEncodingException, FileNotFoundException {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), this.encoding));
}
/**
* 跳过注解的头部行数,并读取一行,并按照注解的分隔符分割成字符串
*/
public String[] readDataLine() throws IOException {
String line = null;
while ((line = reader.readLine()) != null) {
count++;
if (count <= this.headerLineCount) {
continue;
}
break;
}
return line == null ? null : line.split(String.valueOf(this.delimiter));
}
/**
* 关闭reader
*/
public void close() throws IOException {
this.reader.close();
this.count = 0;
}
}
复制代码
刚才只是读取一行,返回字符串数组。可是咱们有时候想把数据封装到类里,好比上述的FreeTextCSVFormat类。那么能够再定义一个文件内容的注解。this
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FileColumn {
// 列index
int columnIdex() default 0;
// 是否是循环列
boolean isLoop() default false;
}
复制代码
FreeTextCSVFormat,添加FileColumn注解。编码
@Data
@FileFormat()
public class FreeTextCSVFormat implements CSVFormat {
@FileColumn(columnIdex = 0)
private String nexcoNumber;
@FileColumn(columnIdex = 1, isLoop = true)
private String msgNumber;
@FileColumn(columnIdex = 2, isLoop = true)
private String cmdMode;
@FileColumn(columnIdex = 3, isLoop = true)
private String text;
}
复制代码
最后,可使用反射获取columnIdex,并把读取的内容封装进去。具体实现就不贴出来了。spa
使用注解可以提高扩展性,好比添加一种新的csv样式,并不须要修改读取文件的方法,只须要添加使用注解的类就能够了。这样作可以更优雅,还能帮你了解java反射,毕竟平时用框架的注解不少,本身写的机会却不多。