Java 用註解實現通用功能-csv檔案的讀取為例
阿新 • • 發佈:2020-06-24
引言
使用java註解可以實現一些共通的功能,假設有幾種格式的csv檔案,編碼,分隔符,頭部行數之類的定義各不相同,但我們想統一的處理他們,那就需要一個共通的方法。
也許有人說,不用註解,只用個共通工具類不就行了嗎?但是註解讓程式碼更優雅,而且當你增加其他一些需求,比如其他csv格式的時候,只需要加幾個註解就能輕鬆的擴張你的功能。
那麼看程式碼吧。
1. 定義註解
定義一個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;
}
複製程式碼
2. 使用註解
FreeTextCSVFormat使用了FileFormat的註解,分隔符,編碼等都使用預設值,並沒有進行特別的設定。
@Data
@FileFormat()
public class FreeTextCSVFormat {
private String nexcoNumber;
private String msgNumber;
private String cmdMode;
private String text;
}
複製程式碼
3. 處理註解,讀取檔案中的一行資料
根據註解的設定,讀取一行資料。不管是什麼編碼,或者換行符,都是用通用的readDataLine()方法。
@Data
public class CSVFileLineIterator<T> implements AutoCloseable {
private Class<T> format;
private File file;
// 分隔符
private char delimiter;
// 編碼
private String encoding;
// 頭部行數
private int headerLineCount;
private int count;
private BufferedReader reader;
public CSVFileLineIterator(File file,Class<T> format) {
FileFormat fileformat = checkParams(file,format);
this.file = file;
this.format = format;
// 從註解中獲取分隔符
this.delimiter = fileformat.delimiter();
// 從註解中獲取編碼
this.encoding = fileformat.fileEncoding();
// 從註解中獲取頭部行數
this.headerLineCount = fileformat.headerLineCount();
}
// 檢查引數
private FileFormat checkParams(File file,Class<T> format) {
Optional.ofNullable(file).orElseThrow(
() -> new FileException("nullArgument" ));
Optional.ofNullable(format).orElseThrow(
() -> new FileException("nullArgument"));
OptionalBoolean.of(!file.exists()).orElseThrow(
() -> new FileException("fileNoFound"));
FileFormat fileformat = format.getAnnotation(FileFormat.class);
Optional.ofNullable(fileformat).orElseThrow(
() -> new FileException("noFormatAnnotation"));
OptionalBoolean.of(fileformat.delimiter() == Character.MIN_VALUE).orElseThrow(
() -> new FileException("illegalDelimiter"));
return fileformat;
}
public void open() {
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),this.encoding));
} catch (UnsupportedEncodingException | FileNotFoundException e) {
throw new FileException("csvOpenFailure",e);
}
}
public String[] readDataLine() {
String line = null;
try {
// 讀取一行
while ((line = reader.readLine()) != null) {
count++;
if (count <= this.headerLineCount) {
// 跳過頭部行數
continue;
}
break;
}
} catch (IOException e) {
throw new FileException("csvReadFailure",e);
}
// 按照指定的分隔符把行內容分隔成字串陣列
return line == null ? null : line.split(String.valueOf(this.delimiter));
}
public void close() {
try {
this.reader.close();
this.count = 0;
} catch (IOException e) {
throw new FileException("csvCloseFailure",e);
}
}
複製程式碼
測試一下
public static void main(String[] args) {
try(CSVFileLineIterator c = new CSVFileLineIterator(new File("C:\\SiLED\\FRU20191209151700.csv"),FreeTextCSVFormat.class)) {
c.open();
String[] s;
while ((s = c.readDataLine()) != null) {
System.out.println("直接readline: " + Arrays.asList(s));
}
}
}
複製程式碼
測試結果
直接readline: [001,301,001,內容1]
直接readline: [001,302,002,內容2,303,003,內容3,304,004,內容4]
複製程式碼
CSV檔案-FRU20191209151700.csv
001,內容1
001,內容4
複製程式碼
4. 功能擴充套件-讀取資料,並封裝到類中
剛才只是讀取一行,返回字串陣列。但是我們有時候想把資料封裝到類裡,比如上述的FreeTextCSVFormat類。那麼可以再定義一個檔案內容的註解。
@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 {
@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,並把讀取的內容封裝進去。具體實現就不貼出來了。
結語
使用註解能夠提升擴充套件性,比如新增一種新的csv樣式,並不需要修改讀取檔案的方法,只需要新增使用註解的類就可以了。這樣做能夠更優雅,還能幫你瞭解java反射,畢竟平時用框架的註解很多,自己寫的機會卻很少。