转载

Java 用注解实现通用功能-csv文件的读取为例

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

这里为了扩展性先定义了一个空的接口。如果需要扩展就实现该接口。

public interface CSVFormat {

}
复制代码

FreeTextCSVFormat实现了CSVFormat接口,并使用了FileFormat的注解,分隔符,编码等都使用默认值,并没有进行特别的设置。

@Data
@FileFormat()
public class FreeTextCSVFormat implements CSVFormat {
	private String nexcoNumber;
	private String msgNumber;
	private String cmdMode;
	private String text;
}
复制代码

3. 处理注解,读取文件中的一行数据

根据注解的设置,读取一行数据。不管是什么编码,或者换行符,都是用通用的readDataLine()方法。

@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类。那么可以再定义一个文件内容的注解。

@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,并把读取的内容封装进去。具体实现就不贴出来了。

结语

使用注解能够提升扩展性,比如添加一种新的csv样式,并不需要修改读取文件的方法,只需要添加使用注解的类就可以了。这样做能够更优雅,还能帮你了解java反射,毕竟平时用框架的注解很多,自己写的机会却很少。

原文  https://juejin.im/post/5de8593451882512641659d8
正文到此结束
Loading...