Java 用註解實現通用功能-csv文件的讀取爲例

引言

使用java註解能夠實現一些共通的功能,假設有幾種格式的csv文件,編碼,分隔符,頭部行數之類的定義各不相同,但咱們想統一的處理他們,那就須要一個共通的方法。java

也許有人說,不用註解,只用個共通工具類不就好了嗎?可是註解讓代碼更優雅,並且當你增長其餘一些需求,好比其餘csv格式的時候,只須要加幾個註解就能輕鬆的擴張你的功能。數組

那麼看代碼吧。bash

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. 使用註解

這裏爲了擴展性先定義了一個空的接口。若是須要擴展就實現該接口。工具

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;
}
複製代碼

3. 處理註解,讀取文件中的一行數據

根據註解的設置,讀取一行數據。無論是什麼編碼,或者換行符,都是用通用的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;
	}
	
}
複製代碼

4. 功能擴展-讀取數據,並封裝到類中

剛纔只是讀取一行,返回字符串數組。可是咱們有時候想把數據封裝到類裏,好比上述的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反射,畢竟平時用框架的註解不少,本身寫的機會卻不多。

相關文章
相關標籤/搜索