1、概述
Mybatis的IO模块主要封装了ClassLoader以及读取资源文件的相关API。
2、类加载器
注:类加载器相关内容来源《Mybatis技术内幕》一书。
Java虚拟机中的类加载器( ClassLoader )负责加载来自文件系统、网络或其他来源的类文件。Java 虚拟机中的类加载器默认使用的是双亲委派模式。有三种默认使用的类加载器,分别是Bootstrap ClassLoader 、Extension ClassLoader 和System ClassLoader (也
被称为Application ClassLoader ),每种类加载器都己经确定从哪个位置加载类文件。其中,Bootstrap ClassLoader 负责加载JDK 自带的比jar 包中的类文件,它是所有类加载器的父加载器,Bootstrap ClassLoader 没有任何父类加载器。Extension ClassLoader 负责加载Java 的扩展类库,也就是从jre/lib/ ext 目录下或者java.ext.dirs 系统属性指定的目录下加载类。System ClassLoader 负责从classpath 环境变量中加载类文件。
3、IO模块包结构
IO模块包结构如下所示。其中,ClassLoaderWrapper是一个ClassLoader的包装器;VFS表示虚拟文件系统(Virtual File System ),它用来查找指定路径下的资源;JBoss6VFS和DefaultVFS两个类是VFS的实现;Resources是一个提供了一系列访问资源的静态方法的工具类;ResolverUtil可以根据指定的条件查找指定包下的类。ExternalResources类是Resources类的扩展,已经废弃。
4、VFS
VFS表示虚拟文件系统,提供了一系列访问应用下资源的方法。Mybatis中提供了JBoss6VFS 和DefaultVFS两个VFS的实现。其中,在创建VFS实例的时候,采用了单例模式,且是静态内部类的单例模式写法(该设计模式此处不做详细记录,后续文章单独介绍)。
- 字段
其中,IMPLEMENTATIONS 主要用来记录Mybatis提供的两个VFS实现类,分别是JBoss6VFS和DefaultVFS。USER_IMPLEMENTATIONS 用来记录用户自定义的VFS实现类,addimplClass ()方法会将指定的VFS实现对应的Class对象添加到USER_IMPLEMENTATIONS 变量中。
/** The built-in implementations. */
public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
/** The list to which implementations are added by {@link #addImplClass(Class)}. */
public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<Class<? extends VFS>>();
- 静态内部类
静态内部类的单例模式写法中的静态内部类,用来实现延迟加载且保证只创建一个单例对象。在静态内部类中,通过createVFS()方法,实现创建静态变量INSTANCE 的值。注意,根据方法中的代码逻辑,可以判断,优先使用用户定义的VFS实现,当用户定义的VFS不存在或者不可用时(根据isValid()方法判断),才加载Mybatis提供的默认实现。
/** Singleton instance holder. */
private static class VFSHolder {
static final VFS INSTANCE = createVFS();
@SuppressWarnings("unchecked")
static VFS createVFS() {
// Try the user implementations first, then the built-ins
List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>();
impls.addAll(USER_IMPLEMENTATIONS);
impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
// Try each implementation class until a valid one is found
VFS vfs = null;
for (int i = 0; vfs == null || !vfs.isValid(); i++) {
Class<? extends VFS> impl = impls.get(i);
try {
vfs = impl.newInstance();
if (vfs == null || !vfs.isValid()) {
if (log.isDebugEnabled()) {
log.debug("VFS implementation " + impl.getName() +
" is not valid in this environment.");
}
}
} catch (InstantiationException e) {
log.error("Failed to instantiate " + impl, e);
return null;
} catch (IllegalAccessException e) {
log.error("Failed to instantiate " + impl, e);
return null;
}
}
if (log.isDebugEnabled()) {
log.debug("Using VFS adapter " + vfs.getClass().getName());
}
return vfs;
}
}
- 获取单例对象的方法
public static VFS getInstance() {
return VFSHolder.INSTANCE;
}
- 添加用户自定义的VFS实现
public static void addImplClass(Class<? extends VFS> clazz) {
if (clazz != null) {
USER_IMPLEMENTATIONS.add(clazz);
}
}
- 根据classname获取对应的Class
/** Get a class by name. If the class is not found then return null. */
protected static Class<?> getClass(String className) {
try {
return Thread.currentThread().getContextClassLoader().loadClass(className);
// return ReflectUtil.findClass(className);
} catch (ClassNotFoundException e) {
if (log.isDebugEnabled()) {
log.debug("Class not found: " + className);
}
return null;
}
}
- 根据clazz和methodName、parameterTypes获取对应的Method
/**
* Get a method by name and parameter types. If the method is not found then return null.
*
* @param clazz The class to which the method belongs.
* @param methodName The name of the method.
* @param parameterTypes The types of the parameters accepted by the method.
*/
protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
if (clazz == null) {
return null;
}
try {
return clazz.getMethod(methodName, parameterTypes);
} catch (SecurityException e) {
log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ". Cause: " + e);
return null;
} catch (NoSuchMethodException e) {
log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ". Cause: " + e);
return null;
}
}
- 调用方法,并返回指定值
有点儿类似动态代理中的方法调用实现。
@SuppressWarnings("unchecked")
protected static <T> T invoke(Method method, Object object, Object... parameters)
throws IOException, RuntimeException {
try {
return (T) method.invoke(object, parameters);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof IOException) {
throw (IOException) e.getTargetException();
} else {
throw new RuntimeException(e);
}
}
}
- getResources()方法,根据路径查找对应的URL集合
/**
* Get a list of {@link URL}s from the context classloader for all the resources found at the
* specified path.
*
* @param path The resource path.
* @return A list of {@link URL}s, as returned by {@link ClassLoader#getResources(String)}.
* @throws IOException If I/O errors occur
*/
protected static List<URL> getResources(String path) throws IOException {
return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
}
- list(String)方法,负责查找指定的资源名称列表
底层是由重写方法list(URL,String)方法负责实现查找指定的资源名称列表。
public List<String> list(String path) throws IOException {
List<String> names = new ArrayList<String>();
for (URL url : getResources(path)) {
names.addAll(list(url, path));
}
return names;
}
- isValid()方法,验证VFS实现在当前环境下是否有效
该方法是抽象方法,由具体实现类来实现该方法,并验证在当前环境下,是否支持该类型的VFS实现。
public abstract boolean isValid();
- list(URL,String)方法,该方法负责查找指定的资源名称列表
该方法是抽象方法,由具体实现类来实现该方法。
protected abstract List<String> list(URL url, String forPath) throws IOException;
5、DefaultVFS
DefaultVFS是VFS的一个实现。主要实现了抽象类中的isValid()和list(URL,String)方法。
- isValid()方法
默认返回true,表示都所有环境下都可以使用该实现类。
@Override
public boolean isValid() {
return true;
}
- list(URL, String)方法
主要提供了查找指定的资源名称列表的方法。URL参数指查找资源的路径,String指在指定的资源路径下,查找指定的目录。该方法提供了读取jar包中资源的方法。
@Override
public List<String> list(URL url, String path) throws IOException {
InputStream is = null;
try {
List<String> resources = new ArrayList<String>();
// First, try to find the URL of a JAR file containing the requested resource. If a JAR
// file is found, then we'll list child resources by reading the JAR.
URL jarUrl = findJarForResource(url);
if (jarUrl != null) {
is = jarUrl.openStream();
if (log.isDebugEnabled()) {
log.debug("Listing " + url);
}
resources = listResources(new JarInputStream(is), path);
}
else {
List<String> children = new ArrayList<String>();
try {
if (isJar(url)) {
// Some versions of JBoss VFS might give a JAR stream even if the resource
// referenced by the URL isn't actually a JAR
is = url.openStream();
JarInputStream jarInput = new JarInputStream(is);
if (log.isDebugEnabled()) {
log.debug("Listing " + url);
}
for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;) {
if (log.isDebugEnabled()) {
log.debug("Jar entry: " + entry.getName());
}
children.add(entry.getName());
}
jarInput.close();
}
else {
/*
* Some servlet containers allow reading from directory resources like a
* text file, listing the child resources one per line. However, there is no
* way to differentiate between directory and file resources just by reading
* them. To work around that, as each line is read, try to look it up via
* the class loader as a child of the current resource. If any line fails
* then we assume the current resource is not a directory.
*/
is = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<String>();
for (String line; (line = reader.readLine()) != null;) {
if (log.isDebugEnabled()) {
log.debug("Reader entry: " + line);
}
lines.add(line);
if (getResources(path + "/" + line).isEmpty()) {
lines.clear();
break;
}
}
if (!lines.isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("Listing " + url);
}
children.addAll(lines);
}
}
} catch (FileNotFoundException e) {
/*
* For file URLs the openStream() call might fail, depending on the servlet
* container, because directories can't be opened for reading. If that happens,
* then list the directory directly instead.
*/
if ("file".equals(url.getProtocol())) {
File file = new File(url.getFile());
if (log.isDebugEnabled()) {
log.debug("Listing directory " + file.getAbsolutePath());
}
if (file.isDirectory()) {
if (log.isDebugEnabled()) {
log.debug("Listing " + url);
}
children = Arrays.asList(file.list());
}
}
else {
// No idea where the exception came from so rethrow it
throw e;
}
}
// The URL prefix to use when recursively listing child resources
String prefix = url.toExternalForm();
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
// Iterate over immediate children, adding files and recursing into directories
for (String child : children) {
String resourcePath = path + "/" + child;
resources.add(resourcePath);
URL childUrl = new URL(prefix + child);
resources.addAll(list(childUrl, resourcePath));
}
}
return resources;
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
// Ignore
}
}
}
}
/**
* List the names of the entries in the given {@link JarInputStream} that begin with the
* specified {@code path}. Entries will match with or without a leading slash.
*
* @param jar The JAR input stream
* @param path The leading path to match
* @return The names of all the matching entries
* @throws IOException If I/O errors occur
*/
protected List<String> listResources(JarInputStream jar, String path) throws IOException {
// Include the leading and trailing slash when matching names
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path = path + "/";
}
// Iterate over the entries and collect those that begin with the requested path
List<String> resources = new ArrayList<String>();
for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
if (!entry.isDirectory()) {
// Add leading slash if it's missing
String name = entry.getName();
if (!name.startsWith("/")) {
name = "/" + name;
}
// Check file name
if (name.startsWith(path)) {
if (log.isDebugEnabled()) {
log.debug("Found resource: " + name);
}
// Trim leading slash
resources.add(name.substring(1));
}
}
}
return resources;
}
6、ClassLoaderWrapper
ClassLoaderWrapper是一个ClassLoader的包装器。其中包含了多个ClassLoader对象。通过调整多个类加载器的使用顺序, ClassLoaderWrapper 可以确保返回给系统使用的是正确的类加载器。使用ClassLoaderWrapper 就如同使用一个ClassLoader 对象,ClassLoaderWrapper 会按照指定的顺序依次检测其中封装的ClassLoader 对象,并从中选取第一个可用的ClassLoader 完成相关功能。
- 字段
ClassLoaderWrapper有两个字段defaultClassLoader( 默认类加载器)、systemClassLoader(系统类加载器)。其中,systemClassLoader字段由构造函数直接赋值;defaultClassLoader字段由使用该类的对象进行赋值,因为该字段不是私有字段,所以可以通过对象.字段的格式进行赋值,在Resources类中,有关于ClassLoaderWrapper对象的defaultClassLoader字段赋值的方法。
/**
* 默认类加载器
*/
ClassLoader defaultClassLoader;
/**
* 系统类加载器
*/
ClassLoader systemClassLoader;
- 构造函数
主要实现了systemClassLoader字段的赋值,即实现了系统类加载器的赋值。
/**
* 构造函数
*/
ClassLoaderWrapper() {
try {
systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {
// AccessControlException on Google App Engine
}
}
- getClassLoaders() 获取加载器列表
定义了可能用到的所有类加载器。
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
- getResourceAsStream()方法
根据resource获取InputStream的方法,底层是通过InputStream returnValue = classLoader.getResourceAsStream(resource);实现。有三个重载方法:
public InputStream getResourceAsStream(String resource) {
return getResourceAsStream(resource, getClassLoaders(null));
}
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) {
if (null != cl) {
// try to find the resource as passed
InputStream returnValue = cl.getResourceAsStream(resource);
// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
if (null == returnValue) {
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}
- classForName()方法、getResourceAsURL()方法
这两个方法和getResourceAsStream()方法一样,也有三个重载方法,只是底层的实现稍有差别,不再贴出代码了。
7、ResolverUtil
ResolverUtil可以根据指定的条件查找指定包下的类,其中使用的条件由Test接口表示。
ResolverUtil中使用c lassLoader 字段( ClassLoader 类型)记录了当前使用的类加载器,默认情况下,使用的是当前线程上下文绑定的ClassLoader ,我们可以通过setC!assLoader ()方法修改使用类加载器。My Batis 提供了两个常用的Test 接口实现,分别是IsA 和AnnotatedWith。IsA 用于检测类是否继承了指定的类或接口, AnnotatedWith 用于检测类是否添加了指定的注解。
- 字段
matches字段表示查找到的所有符合条件的Class集合;classloader表示查找类时,使用的类加载器,当为null时,默认使用当前线程的加载器,即Thread.currentThread().getContextClassLoader()。
/** The set of matches being accumulated. */
private Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>();
/**
* The ClassLoader to use when looking for classes. If null then the ClassLoader returned
* by Thread.currentThread().getContextClassLoader() will be used.
*/
private ClassLoader classloader;
/**
* Provides access to the classes discovered so far. If no calls have been made to
* any of the {@code find()} methods, this set will be empty.
*
* @return the set of classes that have been discovered.
*/
public Set<Class<? extends T>> getClasses() {
return matches;
}
/**
* Returns the classloader that will be used for scanning for classes. If no explicit
* ClassLoader has been set by the calling, the context class loader will be used.
*
* @return the ClassLoader that will be used to scan for classes
*/
public ClassLoader getClassLoader() {
return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
}
/**
* Sets an explicit ClassLoader that should be used when scanning for classes. If none
* is set then the context classloader will be used.
*
* @param classloader a ClassLoader to use when scanning for classes
*/
public void setClassLoader(ClassLoader classloader) {
this.classloader = classloader;
}
- 内部类 条件判断接口和类
Test是接口,IsA和AnnotatedWith是两个实现类,主要是用于判断查询出来的类是否符合要求,符合要求的类会通过addIfMatching()方法添加到字段matches中。
/**
* A simple interface that specifies how to test classes to determine if they
* are to be included in the results produced by the ResolverUtil.
*/
public interface Test {
/**
* Will be called repeatedly with candidate classes. Must return True if a class
* is to be included in the results, false otherwise.
*/
boolean matches(Class<?> type);
}
/**
* 判断 是否是以parentType为基类的类
* A Test that checks to see if each class is assignable to the provided class. Note
* that this test will match the parent type itself if it is presented for matching.
*/
public static class IsA implements Test {
private Class<?> parent;
/** Constructs an IsA test using the supplied Class as the parent class/interface. */
public IsA(Class<?> parentType) {
this.parent = parentType;
}
/** Returns true if type is assignable to the parent type supplied in the constructor. */
@Override
public boolean matches(Class<?> type) {
return type != null && parent.isAssignableFrom(type);
}
@Override
public String toString() {
return "is assignable to " + parent.getSimpleName();
}
}
/**
* 判断是否包含有annotation注解
* A Test that checks to see if each class is annotated with a specific annotation. If it
* is, then the test returns true, otherwise false.
*/
public static class AnnotatedWith implements Test {
private Class<? extends Annotation> annotation;
/** Constructs an AnnotatedWith test for the specified annotation type. */
public AnnotatedWith(Class<? extends Annotation> annotation) {
this.annotation = annotation;
}
/** Returns true if the type is annotated with the class provided to the constructor. */
@Override
public boolean matches(Class<?> type) {
return type != null && type.isAnnotationPresent(annotation);
}
@Override
public String toString() {
return "annotated with @" + annotation.getSimpleName();
}
}
- 根据指定的条件查找指定包下的类
findAnnotated()方法、findImplementations()方法分别用来在指定路径下加载有指定注解、是指定类子类的方法。他们分别创建了自己的Test实现类,即判断是否符合条件的Test子类,然后底层最终是由find()方法实现,查询和判断;最后符合条件的类通过addIfMatching()方法,赋值到matches集合中。其中,在find()方法中,真正实现加载类的代码是VFS.getInstance().list(path);即是由VFS实现了类加载,然后在find()方法中,通过Test实现类,进行是否符合条件的判断。
public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) {
if (packageNames == null) {
return this;
}
Test test = new AnnotatedWith(annotation);
for (String pkg : packageNames) {
find(test, pkg);
}
return this;
}
public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) {
if (packageNames == null) {
return this;
}
Test test = new IsA(parent);
for (String pkg : packageNames) {
find(test, pkg);
}
return this;
}
public ResolverUtil<T> find(Test test, String packageName) {
String path = getPackagePath(packageName);
try {
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
if (child.endsWith(".class")) {
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
@SuppressWarnings("unchecked")
protected void addIfMatching(Test test, String fqn) {
try {
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
Class<?> type = loader.loadClass(externalName);
if (test.matches(type)) {
matches.add((Class<T>) type);
}
} catch (Throwable t) {
log.warn("Could not examine class '" + fqn + "'" + " due to a " +
t.getClass().getName() + " with message: " + t.getMessage());
}
}
8、Resources
Resources是一个提供了多个静态方法的工具类, 其中封装了一个ClassLoaderWrapper 类型的静态字段, Resources提供的这些静态方法都是通过调用ClassLoaderWrapper 对象的相应方法实现的。
其中,getDefaultClassLoader()和setDefaultClassLoader()方法用于获取或者设置classLoaderWrapper.defaultClassLoader字段的值.在ClassLoaderWrapper类分析的时候,提到过,即在setDefaultClassLoader()方法中,设置了ClassLoaderWrapper类的默认类加载器。
主要提供了以下方法:
- getResourceURL()方法
- getResourceAsStream()方法
- getResourceAsProperties()方法
- getResourceAsReader()方法
- getResourceAsFile()方法
- getUrlAsStream()方法
- getUrlAsReader()方法
- getUrlAsProperties()方法
- classForName()方法
public class Resources {
/**
* 类加载器包装类实例
*/
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
/**
* Charset to use when calling getResourceAsReader.
* null means use the system default.
*/
private static Charset charset;
Resources() {
}
/**
* Returns the default classloader (may be null).
* 获取默认类加载器,通过类加载器的包装类实例获取
* @return The default classloader
*/
public static ClassLoader getDefaultClassLoader() {
return classLoaderWrapper.defaultClassLoader;
}
/**
* Sets the default classloader
* 设置默认类加载器,通过类加载器的包装类实例设置
* @param defaultClassLoader - the new default ClassLoader
*/
public static void setDefaultClassLoader(ClassLoader defaultClassLoader) {
classLoaderWrapper.defaultClassLoader = defaultClassLoader;
}
/**
* Returns the URL of the resource on the classpath
* 根据resource获取URL,底层通过类加载器的包装类实例(classLoaderWrapper)实现:
* URL url = classLoader.getResource(resource);
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static URL getResourceURL(String resource) throws IOException {
// issue #625
return getResourceURL(null, resource);
}
/**
* Returns the URL of the resource on the classpath
* 根据resource获取URL,底层通过类加载器的包装类实例(classLoaderWrapper)实现:
* URL url = classLoader.getResource(resource);
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
if (url == null) {
throw new IOException("Could not find resource " + resource);
}
return url;
}
/**
* Returns a resource on the classpath as a Stream object
* 根据resource获取InputStream,底层通过类加载器的包装类实例(classLoaderWrapper)实现:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}
/**
* Returns a resource on the classpath as a Stream object
* 根据resource获取InputStream,底层通过类加载器的包装类实例(classLoaderWrapper)实现:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
/**
* Returns a resource on the classpath as a Properties object
* 根据resource获取Properties,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取InputStream对象,然后再封装成Properties对象:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* Properties props = new Properties();
* props.load(in);
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Properties getResourceAsProperties(String resource) throws IOException {
Properties props = new Properties();
InputStream in = getResourceAsStream(resource);
props.load(in);
in.close();
return props;
}
/**
* Returns a resource on the classpath as a Properties object
* 根据resource获取Properties,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取InputStream对象,然后再封装成Properties对象:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* Properties props = new Properties();
* props.load(in);
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException {
Properties props = new Properties();
InputStream in = getResourceAsStream(loader, resource);
props.load(in);
in.close();
return props;
}
/**
* Returns a resource on the classpath as a Reader object
* 根据resource获取Properties,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取InputStream对象,然后再封装成Reader对象:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* new InputStreamReader(inputStream);
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Reader getResourceAsReader(String resource) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getResourceAsStream(resource));
} else {
reader = new InputStreamReader(getResourceAsStream(resource), charset);
}
return reader;
}
/**
* Returns a resource on the classpath as a Reader object
* 根据resource获取Properties,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取InputStream对象,然后再封装成Reader对象:
* InputStream returnValue = classLoader.getResourceAsStream(resource);
* new InputStreamReader(inputStream);
* @param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getResourceAsStream(loader, resource));
} else {
reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
}
return reader;
}
/**
* Returns a resource on the classpath as a File object
* 根据resource获取File,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取URL对象,然后再封装成File对象:
* new File(getResourceURL(resource).getFile())
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static File getResourceAsFile(String resource) throws IOException {
return new File(getResourceURL(resource).getFile());
}
/**
* Returns a resource on the classpath as a File object
* 根据resource获取File,底层通过类加载器的包装类实例(classLoaderWrapper)
* 获取URL对象,然后再封装成File对象:
* new File(getResourceURL(resource).getFile())
* @param loader - the classloader used to fetch the resource
* @param resource - the resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException {
return new File(getResourceURL(loader, resource).getFile());
}
/**
* Gets a URL as an input stream
*
* @param urlString - the URL to get
* @return An input stream with the data from the URL
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getUrlAsStream(String urlString) throws IOException {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
return conn.getInputStream();
}
/**
* Gets a URL as a Reader
*
* @param urlString - the URL to get
* @return A Reader with the data from the URL
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Reader getUrlAsReader(String urlString) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getUrlAsStream(urlString));
} else {
reader = new InputStreamReader(getUrlAsStream(urlString), charset);
}
return reader;
}
/**
* Gets a URL as a Properties object
*
* @param urlString - the URL to get
* @return A Properties object with the data from the URL
* @throws java.io.IOException If the resource cannot be found or read
*/
public static Properties getUrlAsProperties(String urlString) throws IOException {
Properties props = new Properties();
InputStream in = getUrlAsStream(urlString);
props.load(in);
in.close();
return props;
}
/**
* Loads a class
*
* @param className - the class to fetch
* @return The loaded class
* @throws ClassNotFoundException If the class cannot be found (duh!)
*/
public static Class<?> classForName(String className) throws ClassNotFoundException {
return classLoaderWrapper.classForName(className);
}
public static Charset getCharset() {
return charset;
}
public static void setCharset(Charset charset) {
Resources.charset = charset;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68912.html