`
leeon
  • 浏览: 42361 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

打造0配置文件的SSH框架-10

    博客分类:
  • java
阅读更多
MethodActionConfigurationProvider完整源码
/**
 * 集成xwork的config provier,用来加载brick针对struts2的zero config配置
 * @author leeon
 */
public class MethodActionConfigurationProvider implements ConfigurationProvider {
	
	private static final Log logger = LogFactory.getLog(MethodActionConfigurationProvider.class);
	
	/**
	 * 用来获取xwork config对象,通过该对象来加载config
	 */
	private Configuration configuration = null;
	/**
	 * 从外部传入的参数,用来记录哪些package有zero config的配置类,程序好搜索这些pkg
	 */
	private String methodActionPackage = null;
	/**
	 * 用于记录哪些文件被读取过,根据是否修改来判断是否进行reload操作
	 */
	private Map<String, Long> loadedClassUrls = new HashMap<String, Long>();
	/**
	 * xwork的object factory,用来管理result, iterceptor等配置
	 */
	private ObjectFactory objectFactory = null;
	
	private final static String DEFAULT_METHOD_ACTION_PACKAGE = "brick.web.extend.struts.methodActionPackage";
	
	/**
	 * 构造方法
	 * @param methodActionPackage
	 */
	public MethodActionConfigurationProvider() {
	}
	
	/**
	 * 解构方法,清除loaded url
	 */
	public void destroy() {
		loadedClassUrls.clear();
	}
	
	/**
	 * 继承接口的初始化方法,可以获取到config实例
	 */
	public void init(Configuration configuration) throws ConfigurationException {
		this.configuration = configuration;
		this.loadedClassUrls.clear();
	}
	
	/**
	 * 通过xwork inject获取object factory的方法
	 * @param objectFactory
	 */
	@Inject
    public void setObjectFactory(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
    }
	
	/**
	 * 通过xwork inject获取methodActionPackage的方法
	 * @param context
	 */
	@Inject(value=DEFAULT_METHOD_ACTION_PACKAGE)
    public void setMethodActionPackage(String methodActionPackage) {
        this.methodActionPackage = methodActionPackage.replace('.', '/');
    }

	/**
	 * 继承接口的load pak的方法
	 */
	public void loadPackages() throws ConfigurationException {
		//读取需要寻找Action类的目录
		String[] l = this.methodActionPackage.split(",");

		//搜索所有package类
		List<String> c = new ArrayList<String>();
		for (String p:l) {
			try {
				c.addAll(FileUtil.searchFileFromClassPath(p, ".*\\.class"));
			} catch (Exception e) {
				logger.warn("search file from method action package error:["+p+"]", e);
			}
		}
		
		//读取package信息
		for (String clsName:c) {
			String className = clsName.substring(0, clsName.length() - 6);
			className = className.replace('/', '.');
			
			Class<?> clazz = null;
			try {
				clazz = Class.forName(className);
			} catch (ClassNotFoundException e) {
				logger.warn("class load method action error:["+className+"]", e);
				continue;
			}
			Package p = (Package)clazz.getAnnotation(Package.class);
			if (p == null) continue;
			//读取package
			PackageConfig pc = createPackageConfig(clazz, p);
			//将该类的路径加入Loadedfile
			try {
				loadedClassUrls.put(clsName, BeanUtil.getClassPathFile(clsName).lastModified());
			} catch (Exception e) {
				logger.warn("get class file last modify date from filesystem error :["+clsName+"]", e);
			}
			logger.trace("find action package and loaded:["+pc.getName()+","+pc.getNamespace()+"]");
			
			//读取action信息
			Method[] m = clazz.getMethods();
			for(Method method:m) {
				Action action = (Action)method.getAnnotation(Action.class);
				if (action == null) continue;
				logger.trace("find action method and start parsing:["+action.name()+","+method.getName()+"]");
				
				//读取action的参数
				Map<String, Object> ps = this.createParameters(action.param());
				
				//读取action的exception mapping信息
				List<ExceptionMappingConfig> le = this.createExceptionMappingConfigs(
						(ActionExceptionMappings)method.getAnnotation(ActionExceptionMappings.class));
				
				//读取单个的e m信息
				ExceptionMappingConfig em = this.createExceptionMappingConfig((ActionExceptionMapping)method.getAnnotation(ActionExceptionMapping.class));
				if (em != null) le.add(em);
		
				//读取action的interceptor信息
				List<InterceptorMapping> li = this.createInterceptorMappings(
						(ActionInterceptors)method.getAnnotation(ActionInterceptors.class), pc);
				
				//读取单个的i信息
				li.addAll(this.createInterceptorMapping(
						(ActionInterceptor)method.getAnnotation(ActionInterceptor.class), pc));
		
				//读取action的result信息
				Map<String, ResultConfig> msr = this.createResultConfigs(
						(ActionResults)method.getAnnotation(ActionResults.class), pc);
				
				//读取单个的action r信息
				ResultConfig rc = this.createResultConfig((ActionResult)method.getAnnotation(ActionResult.class), pc);
				if (rc != null) msr.put(rc.getName(), rc);
				
				//构建Action config,并添加到package config中
				ActionConfig ac = new ActionConfig(method.getName(), clazz, ps, msr, li, le);
				pc.addActionConfig(action.name(), ac);
				logger.trace("find action method and end parsing:["+action.name()+","+method.getName()+"]");
			}
			//加载到configuration中
			this.configuration.addPackageConfig(pc.getName(), pc);
		}
	}
	
	/**
	 * 是否允许热加载
	 */
	public boolean needsReload() {
		Set<String> s = loadedClassUrls.keySet();
		for (String clz : s) {
			//获取类的上次修改时间
			long now = 0;
			try {
				now = BeanUtil.getClassPathFile(clz).lastModified();
			} catch (Exception e) {
				logger.warn("get class file last modify date from filesystem error :["+clz+"]", e);
			}
			//根据修改时间和已经记录的修改时间对比,如果发生修改那么reload
			if (loadedClassUrls.get(clz).longValue() != now) {
				logger.trace("find action package class modified and will reload:["+clz+"]");
				return true;
			}
		}
		return false;
	}

	/**
	 * 必须实现但没有作用的方法
	 */
	public void register(ContainerBuilder arg0, LocatableProperties arg1)
			throws ConfigurationException {
	}
	
	/**
	 * 根据参数创建package
	 * @param clazz
	 * @param p
	 * @return
	 */
	private PackageConfig createPackageConfig(Class clazz, Package p) {
		//根据类名创建package
		PackageConfig pc = new PackageConfig(clazz.getName());
		//获取名字空间
		pc.setNamespace(p.namespace());
		//搜索parent,如果没有找到抛出警告
		PackageConfig parent = this.configuration.getPackageConfig(p.parent());
		if (parent != null) pc.addParent(parent);
		else ;
		//返回
		return pc;
	}
	
	
	/**
	 * 装载exception mapping config
	 * 默认使用name和result同名
	 * @param ems
	 * @return
	 */
	private List<ExceptionMappingConfig> createExceptionMappingConfigs(ActionExceptionMappings ems) {
		//构造返回的List
		List<ExceptionMappingConfig> list = new ArrayList<ExceptionMappingConfig>();
		if (ems == null) return list;
		
		//读取aem信息
		ActionExceptionMapping aems[] = ems.value();
		for (ActionExceptionMapping aem : aems) {
			ExceptionMappingConfig emc = new ExceptionMappingConfig(
					aem.result(), aem.exceptionClass().getName(), aem.result(), createParameters(aem.param()));
			if (emc != null) list.add(emc);
		}
		//返回结果list
		return list;
	}
	
	/**
	 * 用于构造单个的action exception mapping
	 * @param em
	 * @return
	 */
	private ExceptionMappingConfig createExceptionMappingConfig(ActionExceptionMapping em) {
		if (em == null) return null;
		ExceptionMappingConfig emc = new ExceptionMappingConfig(
				em.result(), em.exceptionClass().getName(), em.result(), createParameters(em.param()));
		return emc;
	}
	
	
	/**
	 * 装载interceptor 集合
	 * @param is
	 * @param pc
	 * @return
	 */
	private List<InterceptorMapping> createInterceptorMappings(ActionInterceptors is, PackageConfig pc) {
		//构造返回的i集合列表
		List<InterceptorMapping> list = new ArrayList<InterceptorMapping>();
		if (is == null) return list;
		//调用单个的构造方法进行构造
		ActionInterceptor ais[] = is.value();
		for (ActionInterceptor ai : ais) {
			list.addAll(createInterceptorMapping(ai, pc));
		}
		//返回结果
		return list;
	}
	
	/**
	 * 装载单个的interceptor
	 * @param interceptor
	 * @param pc
	 * @return
	 */
	private List<InterceptorMapping> createInterceptorMapping(ActionInterceptor interceptor, PackageConfig pc) {
		List<InterceptorMapping> list = new ArrayList<InterceptorMapping>();
		if (interceptor == null) return list;
		
		//i的参数构造
		Map param = createParameters(interceptor.param());
    	//i的名称
		String name = interceptor.value();
    	//通过i的名字去所有的i中查询 i config
		Object o = pc.getAllInterceptorConfigs().get(name);
    	//查出来的是i config对象
		if (o instanceof InterceptorConfig) {
    		InterceptorConfig config = (InterceptorConfig) o;
            //通过config去加载 真正的 i
    		Interceptor inter = null;
            try {
                inter = objectFactory.buildInterceptor(config, param);
                list.add(new InterceptorMapping(name, inter));
                return list;
            } catch (ConfigurationException ex) {
                logger.warn("Unable to load config class "+config.getClassName()+" at "+
                        ex.getLocation()+" probably due to a missing jar, which might "+
                        "be fine if you never plan to use the "+config.getName()+" interceptor", ex);
                return list;
            }
        //查出来的是i stack config对象,可直接取到i mapping对象
    	} else if (o instanceof InterceptorStackConfig) {
    		InterceptorStackConfig config = (InterceptorStackConfig) o;
    		list.addAll(config.getInterceptors());
    		return list;
    	}
		return list;
	}
	
	
	
	/**
	 * 创建result 集合
	 * @param rs
	 * @param pc
	 * @return
	 */
	private Map<String, ResultConfig> createResultConfigs(ActionResults rs, PackageConfig pc) {
		Map<String, ResultConfig> map = new HashMap<String, ResultConfig>();
		if (rs == null) return map;
		
		ActionResult ars[] = rs.value();
		for (ActionResult ar : ars) {
			ResultConfig rc = createResultConfig(ar, pc);
			if (rc != null) map.put(rc.getName(), rc);
		}
		return map;
	}
	
	 /**
     * Creates a default ResultConfig,
     * using either the resultClass or the default ResultType for configuration package
     * associated this ResultMap class.
     *
     * @param key The result type name
     * @param resultClass The class for the result type
     * @param location Path to the resource represented by this type
     * @return A ResultConfig for key mapped to location
     */
    @SuppressWarnings("unchecked")
	private ResultConfig createResultConfig(ActionResult result, PackageConfig pc) {
    	if (result == null) return null;
    	
    	Map param = createParameters(result.param());
    	Class clazz = result.type();
    	
    	//判断是否使用默认的result type,是的话需要去取默认result type的实现类
    	if (clazz == NullResult.class) {
			String defaultResultType = pc.getFullDefaultResultType();
			ResultTypeConfig resultType = pc.getAllResultTypeConfigs().get(defaultResultType);
			if (resultType.getParams() != null) param.putAll(resultType.getParams());
			try {
				clazz = Class.forName(resultType.getClazz());
			} catch (ClassNotFoundException ex) {
			}
		}

		//设定default param也就是location
    	String defaultParam;
		try {
			defaultParam = (String) clazz.getField("DEFAULT_PARAM").get(null);
		} catch (Exception e) {
			// not sure why this happened, but let's just use a sensible choice
			defaultParam = "location";
		}
		param.put(defaultParam, result.value());
		
		//创建result config返回
		return new ResultConfig(result.name(), clazz.getName(), param);
	}
	
	/**
	 * 构建param anno参数的map的方法
	 * @param ps
	 * @return
	 */
	private Map<String, Object> createParameters(Param[] ps) {
		Map<String, Object> map = new HashMap<String, Object>();
		if (ps == null) return map;
		for (Param p: ps) {
			map.put(p.name(), p.value());
		}
		return map;
	}
	
}




两个用zero config配置的action的例子,一个简单的,一个复杂
看起来还是比较清晰的
@Action(name = "TeacherList")
	@ActionResult(value = "/sample/lesson/TeacherList.jsp")
	public String listTeacher() throws Exception {	
return SUCCESS;
	}


@Action(name = "Login")
	@ActionInterceptors({
		@ActionInterceptor("sendTimeoutRequest"),
		@ActionInterceptor("brickStack")
	})
	@ActionExceptionMappings({ 
		@ActionExceptionMapping(exceptionClass = UserNotExistException.class, result = "UserNotExist"),
		@ActionExceptionMapping(exceptionClass = PasswordNotAvailException.class, result = "PasswordNotAvail")
	})
	@ActionResults({
		@ActionResult(name = "UserNotExist", value = "/sample/lesson/Login.jsp"),
		@ActionResult(name = "PasswordNotAvail", value = "/sample/lesson/Login.jsp"),
		@ActionResult(name = "input", value = "/sample/lesson/Login.jsp"),
		@ActionResult(value = "/sample/lesson/Main.jsp")
	})
	public String login() throws Exception {

		return SUCCESS;
	}
分享到:
评论
14 楼 ta8210 2010-01-10  
还是我的老话。

如果将配置文件中的内容,移动到类中做注解。

在谈零配置其实没有什么意义。项目不需要零配置,项目需要简化配置。
13 楼 ta8210 2010-01-10  
配置文件零化了,但是这不叫零配置。

将配置文件中的内容,移动到类中做注解。这本身就不是零配置。

建议博主在设定几个基本参数之后,一切配置完全由扫描classpath得出。
这样 就可以生成配置来使用。

比方说我有一个 Filter接口,和一个Action接口。  系统扫描所有类将所有实现Filter的实现类都生成过滤器配置,而Action实现类都生成为action配置。

如果某个action单独需要过滤器,这时在通过注解明确指出。如果是通用的则通过PublicFiter策略全局化,在配合 修饰类的注解来解决  例外配置问题。


如果整个项目都是 例外配置,那肯定 都不是例外配置。 需要在做抽象。

至于ioc部分的零配置 spring的自动装配 工作的很好。 可以在做一些优化。
12 楼 leeon 2007-09-30  
引用

加载有@标记的类:现在实现的是在WEB.XML文件中写出包含有@标记的Action所在的包。但是我觉得在WEB.XML中去写不是很方便,因为在项目中并不是每个人都有权利去修改这个文件。所以我觉得在复写XmlConfigurationProvider的时候最好去struts.xml类中去加载包名。根据包名加载都做好了,那还可以添加一个根据类名加载吧呵呵 ,不是很有必要随便说说。


其实就是因为不是每个人都有权利去修改这个web.xml文件,所以才会想出来配置一个目录,然后系统自动去搜索目录下的包括子目录的所有action类。

项目中只要定好,所有的action类必须在某一个目录下,比如sample.web.action这样的目录,具体各个模块再在这个目录的基础上分子目录,web.xml只要定义该目录sample.web.action就可以了。


也像你说的那样,要扩展struts dtd比较麻烦,我要改struts包中的dtd或者改配置文件上的dtd验证url,这样导致struts的xml不标准,对struts的入侵太厉害,毕竟这样就是修改而不是扩展struts的代码了。
11 楼 leeon 2007-09-30  
引用

如果用了@Annotation的话就不能用XML了,所以我建议你不要继承接口而是继承XmlConfigurationProvider类,让同时可以加载xml的配置和@的配置。不同package共用的result Exception还是在struts.xml中配置比较合适。这样的话struts.xml文件还是有存在的必要


这条好像不太对,我们现在就是@annotation和xml一起用,而且正如你想的不同package共用的result Exception, global result, interceptor等等信息都是在struts.xml文件中配置的。

其实struts加载的时候,是先加载XmlConfigurationProvider,再加载自定义的Provider,所以肯定是先加载xml,在加载annotation。

难道你的xml和annotation不能一起用?
10 楼 leeon 2007-09-30  
引用

标记@Action的Target类型是METHOD的,为什么不可以写在TYPE和METHOD呢?甚至一个类中的共用的Result,EXCEPTION,INTERCEPOTR也可以写在类上边啊……因为一个Action类中的CRUD方法返回的结果集和处理错误集很有可能返回的结果是一样的。处理异常也可能一样等等。而对于不同的业务处理方法也可能有特殊的返回结果。这样在这个方法前边只标记他特殊的Result Exception什么什么的


这个....我发的东西的版本比我们项目在用的版本早,后来的版本中,result, exception, interceptor都可以定义在class这个级别上,甚至定义在package-info.java中,这样甚至可以一个package下所有的action class共用一些信息,代码没有贴出来
9 楼 leeon 2007-09-30  
好久没看,你居然写了这么多... 

我条条来回复吧
8 楼 allenBen 2007-09-28  
你写的东西比雷声大雨点小的Apache写的Struts2的Annotion爽多了。呵呵 ! 但是我可以根据我的经验和理解提几点建议吧

    标记@Action的Target类型是METHOD的,为什么不可以写在TYPE和METHOD呢?甚至一个类中的共用的Result,EXCEPTION,INTERCEPOTR也可以写在类上边啊……因为一个Action类中的CRUD方法返回的结果集和处理错误集很有可能返回的结果是一样的。处理异常也可能一样等等。而对于不同的业务处理方法也可能有特殊的返回结果。这样在这个方法前边只标记他特殊的Result Exception什么什么的


    如果用了@Annotation的话就不能用XML了,所以我建议你不要继承接口而是继承XmlConfigurationProvider类,让同时可以加载xml的配置和@的配置。不同package共用的result Exception还是在struts.xml中配置比较合适。这样的话struts.xml文件还是有存在的必要

    加载有@标记的类:现在实现的是在WEB.XML文件中写出包含有@标记的Action所在的包。但是我觉得在WEB.XML中去写不是很方便,因为在项目中并不是每个人都有权利去修改这个文件。所以我觉得在复写XmlConfigurationProvider的时候最好去struts.xml类中去加载包名。根据包名加载都做好了,那还可以添加一个根据类名加载吧呵呵 ,不是很有必要随便说说。

    如果在struts.xml中去写包、类名的话就需要特别的标签了。我不知道DTD怎么扩展。Struts用的是DTD验证。
    <!ELEMENT annotationed  (annotationpkg*,annotationcls*)>
    <!ATTLIST annotationpkg
        name CDATA #REQUIRED
    >
    <!ATTLIST annotationcls
        name CDATA #REQUIRED
    >

说的不对的可以拍砖头,目的只有一个,创建一个真正能提高开发效率的Struts2的Annotation
7 楼 leeon 2007-09-27  
,实不实用可能很难说

不过至少写了这个东西后让我对struts2和xwork熟悉了不少
6 楼 allenBen 2007-09-27  
太感谢你了  你写的这个东西我学习了2天半 终于差不多了
  
5 楼 leeon 2007-09-26  
BeanUtil.java


import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author leeon
 * 对类路径上文件操作的一些工具类
 */
public class BeanUtil {

	private static Log logger = LogFactory.getLog(BeanUtil.class);
	
	
	/**
	 * 根据文件所在类路径返回对应的绝对路径的文件对象。
	 * 该文件的URL是类装载器在类路径上找到的第一个该路径上的文件的URL
	 * @param classPath 文件类路径
	 * @return 文件URL绝对路径对应的File对象
	 * @throws FileNotFoundException
	 * @throws UnsupportedEncodingException
	 * @throws URISyntaxException 
	 */
	public static File getClassPathFile(String classPath) throws FileNotFoundException {
		URL url = getClassPathFileURL(classPath);
		if (url != null && "file".equals(url.getProtocol()))
		try {
			return new File(url.toURI());
		
		//toURI如果有空格,可能会抛出错误,所以需要进行另外的处理
		} catch (URISyntaxException e) {
			logger.warn("url to URI syntax exception, maybe jdk bug");
			logger.warn(e);
			
			String p = url.getFile();
			if (p.charAt(0) == '/' && p.charAt(2) == ':') p = p.substring(1);
			return new File(p);
		}
		else if (url != null)
			throw new FileNotFoundException("this file [" + url + "] in class path:[" + classPath + "] not a filesystem file");
		else
			throw new FileNotFoundException("this file not in class path:["+ classPath + "]");
	}

	/**
	 * 根据文件所在类路径返回该文件对应的绝对URLs。
	 * 该URL数组是类装载器在类路径上找到的所有该路径文件的URL的集合
	 * URL格式可能是file:/D:/foo/bar/classes/com/guanghua/brick/aaa.class
	 * 也有可能是jar:file:/D:/foo/bar/brick.jar!com/guanghua/brick/aaa.class等等
	 * @param classPath 文件类路径
	 * @return 文件所在绝对路径URL的数组
	 * @throws IOException 
	 */
	public static URL[] getClassPathFileURLs(String classPath) throws IOException {
		Enumeration e = Thread.currentThread().getContextClassLoader().getResources(classPath);
		
		//将url放入hashset以去掉重复的url
		HashSet<URL> set = new HashSet<URL>();
		while (e.hasMoreElements()) {
			Object o = e.nextElement();
			set.add((URL)o);
			logger.debug("find file [" + classPath +"] from class path, and url is [" + o + "]");
		}
		
		return (URL[]) set.toArray(new URL[0]);
	}

	/**
	 * 根据文件所在类路径返回该文件对应的绝对URL。
	 * 该URL是类装载器在类路径上找到的第一个该路径上的文件的URL
	 * URL格式可能是file:/D:/foo/bar/classes/com/guanghua/brick/aaa.class
	 * 也有可能是jar:file:/D:/foo/bar/brick.jar!com/guanghua/brick/aaa.class等等
	 * @param classPath 文件类路径
	 * @return 文件所在绝对路径URL
	 * @throws Exception
	 */
	public static URL getClassPathFileURL(String classPath) {
		return Thread.currentThread().getContextClassLoader().getResource(classPath);
	}

	/**
	 * 根据文件所在类路径返回对应文件的输入流。
	 * @param classPath 文件类路径
	 * @return 该文件的输入流
	 */
	public static InputStream getClassPathFileByInputStream(String classPath) {
		return Thread.currentThread().getContextClassLoader().getResourceAsStream(classPath);
	}
}

4 楼 leeon 2007-09-26  
这两个类全是静态方法
FileUtil.java

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author leeon
 * 对类路径上的文件或者文件系统上的文件进行搜索的工具类
 */
public class FileUtil {

	
	private static Log logger = LogFactory.getLog(FileUtil.class);
	
	/**
	 * 在文件系统的某个目录中搜索符合pattern要求的文件,filePath就是搜索的目录的全路径
	 * 比如filePath路径是c:/foo/bar,那么就是搜索c:/foo/bar下的所有符合patter格式的文件
	 * 如果filePath的不是目录或者不存在,那么返回一个空列表。
	 * @param filePath 需要搜索的目录路径
	 * @param pattern 文件名(不包括文件路径)符合的模版,正则表达式的模版
	 * @return 返回搜索到的文件全路径列表
	 */
	public static List<String> searchFileFromFolder(String filePath, String pattern) {
		return searchFileFromFolder(getFileResource(filePath), pattern, null);
	}

	
	/**
	 * 检查文件系统某个目录下所有名字(非路径,只包括文件名)符合正则表达式pattern要求的文件
	 * 返回的结果是一个String的列表,可以有两种结果
	 * 如果prefix为null,列表里的字符串就是符合要求的文件的绝对路径
	 * 如果prefix不为null,列表里的字符串就是类路径格式的字符串,比如在c:/foo/bar下搜索,prefix为foo1/bar1
	 * 搜索到一个文件是c:/foo/bar/test/Test.class,那么返回的list中的字符串就是foo1/bar1/test/Test.class
	 * 如果prefix传的是"",那么返回的字符串就是/test/Test.class
	 * @param folder, 开始搜索的文件夹,如果文件夹为空,或者不存在,或者不是文件夹,那么返回空列表
	 * @param pattern, 搜索用的正则表达式
	 * @param prefix, 返回类路径或者是文件绝对路径的标志,如果是null,返回文件绝对路径
	 * @return 返回list,符合list要求的每一个对象是该文件的绝对路径
	 */
	public static List<String> searchFileFromFolder(File folder, final String pattern, String prefix) {
		List<String> re = new ArrayList<String>();
		
		//给prefix加上/,以便于递归
		if (prefix != null && prefix.length() != 0) prefix = prefix + "/";
		
		//当文件夹存在,并且是文件夹时,才进行搜索
		if (folder.exists() && folder.isDirectory()) {
			
			//获取符合pattern条件的文件列表
			File[] files = folder.listFiles(new FileFilter() {
				public boolean accept(File file) {
					//如果该文件是隐藏的,不继续寻找
					if (file.isHidden())
						return false;
					else {
						//如果该文件是目录那么继续寻找他的儿子
						if (file.isDirectory())
							return true;
						else {
							//如果不是目录,则检验其文件名是否满足要求
							return Pattern.matches(pattern, file.getName());
						}
					}
				}
			});
			
			//遍历文件列表,看是否是目录或者文件,是目录就递归,是文件就根据prefix返回
			for (int i = 0; files != null && i < files.length; i++) {
				if (files[i].isDirectory()) {
					re.addAll(searchFileFromFolder(files[i], pattern, prefix==null?null:prefix+files[i].getName()));
				} else {
					//需要返回文件路径
					if (prefix == null)
						re.add(files[i].getAbsolutePath());
					//需要返回类路径
					else
						re.add(prefix+files[i].getName());
				}
			}			
		}
		return re;
	}
	
	
	/**
	 * 检查zip包中某个目录下的所有文件名(不包括文件路径)符合正则表达式pattern要求的文件
	 * 返回一个文件类路径的列表.
	 * folderPath是搜索的zip包中的类路径文件夹,prefix是一个其实搜索和返回路径的前缀,主要用于搜索war包时
	 * 比如一个war包,foo.war,那么zipFile时foo.war的ZipFile,要搜索pattern为*.hbm.xml,搜索的类路径就是com/guanghua/domain,而搜索的前缀就应该是/WEB-INF/classes
	 * 这时候,真正开始搜索的zip包路径是/WEB-INF/classes/com/guanghua/domain,而返回的文件类路径却是com/guanghua/domain/Test.hbm.xml的形式,返回结果会自动截取前缀
	 * 同样在搜索ear包时,也同理可以这样做。搜索jar包的化,prefix传入""即可
	 * @param zipFile 被搜索的zip文件
	 * @param pattern 文件名必须符合的正则表达式的pattern
	 * @param folderPath 
	 * @param prefix
	 * @return zip文件夹中所有符合条件的文件的类路径列表
	 */
	public static List<String> searchFileFromZip(ZipFile zipFile, String pattern, String folderPath, String prefix) {
		List<String> list = new ArrayList<String>();
		
		//拿到zip中所有的entry
		Enumeration e = zipFile.entries();
		folderPath = folderPath.startsWith("/")?prefix+folderPath:prefix+"/"+folderPath;
		while (e.hasMoreElements()) {			
			ZipEntry zipEntry = (ZipEntry)e.nextElement();			
						
			//获取zip中文件的路径
			String zip = zipEntry.getName();
			zip = zip.startsWith("/")?zip:"/"+zip;
			
			//如果zipEntry的开头是prefix,那么说明这是prefix目录下的一个文件			
			if (!zipEntry.isDirectory() && zip.startsWith(folderPath)) {
				//获取真实的文件名
				String jarFileName = zip.substring(zip.lastIndexOf("/")+1);
				//文件名符合pattern的要求
				if (Pattern.matches(pattern, jarFileName))
					list.add(zip.substring(prefix.length()+1));
			}
		}
		
		return list;
	}
	
	
	
	/**
	 * 在指定的类路径中搜索文件名符合pattern要求的文件
	 * 比如要搜索com/guanghua/brick下所有符合.hbm.xml结尾的文件就可以使用该方法
	 * 返回的结果是符合要求的文件的类路径,比如com/guanghua/brick/domain/foo.hbm.xml
	 * @param classPath 指定的类路径
	 * @param pattern 文件名必须符合的pattern
	 * @return 返回在指定类路径中文件名符合要求的类路径列表
	 * @throws Exception
	 */
	public static List<String> searchFileFromClassPath(String classPath, String pattern) throws IOException {
		//获取指定的类路径的定位URL
		URL[] urls = BeanUtil.getClassPathFileURLs(classPath);
		
		//返回的是list,但是利用set来保证没有重复的url
		List<String> list = new ArrayList<String>();
		Set<String> set = new HashSet<String>();
		
		//没有找到对应的url list,返回空
		if (urls == null) return list;
		for (int i = 0; i < urls.length; i ++) { 
			//将搜索到的结果放入set
			set.addAll(searchFileFromClassPath(urls[i], classPath, pattern));			
		}
		list.addAll(set);
		return list;		
	}
	
	/**
	 * 根据URL确定搜索文件的路径,然后根据classPath确定搜索的类路径,再搜索文件名符合pattern格式的文件
	 * classPath所在的位置可能是jar,可能是war,可能是folder,但必须和url的位置一致。
	 * 在类路径中搜索符合pattern要求的文件
	 * @param url 搜索的jar包或者文件夹所在的url路径,
	 * @param classPath 搜索的类路径
	 * @param pattern 文件名符合的模版
	 * @return 返回在指定类路径中文件名符合要求的类路径列表
	 * @throws IOException
	 */
	public static List<String> searchFileFromClassPath(URL url, String classPath, String pattern) throws IOException {
		
		logger.debug("search file type: ["+ pattern +"]");
		logger.debug("search file in classpath: ["+ classPath +"]");
		logger.debug("the real filepath is : ["+ url +"]");
		
		if (url == null) return new ArrayList<String>();
		
		//处理文件名
		String file = url.getFile();
		int i = file.indexOf("!");
		file = (i != -1)?file.substring(0, i):file;
		
		//获取协议
		String protocol = url.getProtocol();
		//如果类所在的文件路径是jar包
		if ("jar".equals(protocol)) {
			JarURLConnection jc = (JarURLConnection)url.openConnection();
			logger.debug("search jar file from :["+ url +"]");
			return searchFileFromZip(jc.getJarFile(), pattern, classPath, "");
			
		//如果类所在的文件路径是wsjar包
		} else if ("wsjar".equals(protocol)) {
			//wsjar需要去掉file:/才能在ws下识别
			if (file.startsWith("file:/")) file = file.substring(6);
			logger.debug("search wsjar file from :["+ file +"]");
			JarFile jarFile = new JarFile(new File(URLDecoder.decode(file, "UTF-8")));
			return searchFileFromZip(jarFile, pattern, classPath, "");
		
		//如果类所在的文件路径是zip包
		} else if ("zip".equals(protocol)) {				
			//如果类所在的文件路径是war包
			if (file.endsWith("war")) {
				logger.debug("search war file from :["+ file +"]");
				ZipFile zipFile = new ZipFile(new File(URLDecoder.decode(file, "UTF-8")));	
				return searchFileFromZip(zipFile, pattern, classPath, "/WEB-INF/classes");
			
			//如果类所在的文件路径是普通zip包
			} else {
				logger.debug("search zip file from :["+ file +"]");
				ZipFile zipFile = new ZipFile(new File(URLDecoder.decode(file, "UTF-8")));	
				return searchFileFromZip(zipFile, pattern, classPath, "");
			}
							
		//如果是普通的文件协议
		} else if ("file".equals(protocol)) {
			logger.debug("search filesystem folder from :["+ url +"]");
			return searchFileFromFolder(new File(URLDecoder.decode(url.getFile(), "UTF-8")), pattern, classPath);
		
		//其余情况返回空列表
		} else
			return new ArrayList<String>();		
	} 
	
	
	
	/**
	 * 根据传入文件完整路径,获取文件所在的文件夹路径
	 * 原理是截取最后一个"/"之前的字符串作为文件夹名称
	 * @param filePath 文件完整路径
	 * @return 文件夹名称
	 */
	public static String getFloderName(String filePath) {
		return filePath.substring(0, filePath.lastIndexOf("/"));
	}

	/**
	 * 根据传入的文件完整路径,获取文件的名称
	 * 原理是截取最后一个"/"之后的字符串作为文件名
	 * @param filePath 文件完整路径
	 * @return 文件名
	 */
	public static String getFileName(String filePath) {
		return filePath.substring(filePath.lastIndexOf("/") + 1);
	}
	
	/**
	 * 根据文件路径(绝对路径)直接返回文件对象。
	 * 相当于new File(filePath)
	 * @param filePath 文件的绝对路径
	 * @return 文件对象
	 */
	public static File getFileResource(String filePath) {
		return new File(filePath);
	}

}

3 楼 allenBen 2007-09-26  
那么请问能否帖上来 以免我们自己要再写一遍或者修改实现了
2 楼 leeon 2007-09-26  
是我自己写的,呵呵,不好意思
一些小工具类,用来搜索某个类路径下名字符合某个正则表达式的
所有类的方法

嘿嘿,可以无视
1 楼 allenBen 2007-09-26  
请问FileUtil和BeanUtil是什么类?是那个包下边的类啊?不是老大你自己写的吧!
谢谢

相关推荐

Global site tag (gtag.js) - Google Analytics