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;
}
}