使用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反射,畢竟平時用框架的註解不少,本身寫的機會卻不多。