首页 > 代码库 > 文件复制的疑惑

文件复制的疑惑

   最近在写一个gradle插件过程中,为Task定义文件复制方法时,发现文件并没有被复制,本来是一个很简单的操作,却出现了如此不解的现象。因编写gradle插件调试不易,所以花了比较多的时间才发现问题,所以谨此记录。

   下面一个精简版的Task类:

import java.io.File;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.io.FileUtils;
import org.gradle.api.tasks.TaskAction;

public class MyTask {
	private List<Action> actions = new CopyOnWriteArrayList<>();
	
	public void copy(final String source, final String dest) throws Exception {
		this.actions.add(new Action() {
			@Override
			public void execute() throws Exception {
				FileUtils.copyFile(new File(source), new File(dest));
			}
		});
	}
	
	public void copyDependencies() {
		this.actions.add(new Action() {
			@Override
			public void execute() throws Exception {
				File dir = new File("H:\\sourceDir");//测试代码源目录
				for(File file : dir.listFiles()) {
					copy(file.getAbsolutePath(), "H:\\destDir\\"+file.getName());
				}
			}
		});
	}
	
	@TaskAction
	public void execute() throws Exception {
		for(Action action : this.actions) {
			action.execute();
		}
	}
	
	public static interface Action {
		void execute() throws Exception;
	}

}

   如上代码中,为了统一各个方法调用,调用方法时只是在actions列表中添加一个Action,在真正执行Task时迭代actions,依次执行各个Action。在MyTask的构建脚本中会调用copy与copyDependencies方法。如果List使用的是ArrayList而不是CopyOnWriteArrayList的话,执行过程中将会抛出ConcurrentModificationException异常,因为ArrayList在迭代的过程不允许对其进行修改(增加或移除元素)。但是即使使用了ConcurrentModificationException后也并没有达到预期目的,即进行文件复制。细看代码后发现,虽然CopyOnWriteArrayList支持迭代过程对列表进行修改,但是copyDependencies方法中调用copy方法其本意是为了复用copy方法,而这其实只是向actions列表中添加了一个新的Action而已,并不是在执行Action,所以copyDependencies方法中的“文件复制”功能无效。
   明白问题原因为后改进MyTask类为:

import java.io.File;
import java.util.List;
import java.util.ArrayList;

import org.apache.commons.io.FileUtils;
import org.gradle.api.tasks.TaskAction;

public class MyTask {
	private List<Action> actions = new ArrayList<>();
	
	public void copy(final String source, final String dest) throws Exception {
		this.actions.add(new Action() {
			@Override
			public void execute() throws Exception {
				FileUtils.copyFile(new File(source), new File(dest));
			}
		});
	}
	
	public void copyDependencies() {
		this.actions.add(new Action() {
			@Override
			public void execute() throws Exception {
				File dir = new File("H:\\sourceDir");
				for(File file : dir.listFiles()) {
					//不再调用copy方法,而是直接进行文件复制,这样就避免了仅向actions列表中添加元素
					//列表类型也可以使用ArrayList了
					FileUtils.copyFile(file, new File("H:\\destDir\\"+file.getName()));
				}
			}
		});
	}
	
	@TaskAction
	public void execute() throws Exception {
		for(Action action : this.actions) {
			action.execute();
		}
	}
	
	public static interface Action {
		void execute() throws Exception;
	}

}


文件复制的疑惑