주어진 패키지에서 모든 클래스 또는 인터페이스를 찾을 수 있습니까? (예를 들어 빠르게 살펴보면 Package
아니오처럼 보일 것입니다.)
답변
클래스 로더의 동적 특성으로 인해 불가능합니다. 클래스 로더는 VM에 제공 할 수있는 클래스를 알려줄 필요가 없으며 대신 클래스에 대한 요청을 처리 한 것이므로 클래스를 반환하거나 예외를 발생시켜야합니다.
그러나 클래스 로더를 직접 작성하거나 클래스 경로와 항아리를 검사하면이 정보를 찾을 수 있습니다. 이것은 반영이 아닌 파일 시스템 작업을 통해 이루어집니다. 이를 수행하는 데 도움이되는 라이브러리가있을 수도 있습니다.
생성되거나 원격으로 제공되는 클래스가 있으면 해당 클래스를 발견 할 수 없습니다.
일반적인 방법은 액세스 할 클래스를 파일에 등록하거나 다른 클래스에서 참조하는 것입니다. 또는 명명과 관련하여 규칙을 사용하십시오.
부록 : 리플렉션 라이브러리 를 사용하면 현재 클래스 경로에서 클래스를 찾을 수 있습니다. 패키지의 모든 클래스를 가져 오는 데 사용할 수 있습니다.
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
답변
아마도 오픈 소스 리플렉션 라이브러리를 살펴보아야 할 것입니다 . 그것으로 당신은 당신이 원하는 것을 쉽게 달성 할 수 있습니다.
먼저 리플렉션 인덱스를 설정하십시오 (기본적으로 모든 클래스 검색이 비활성화되어 있기 때문에 약간 지저분합니다).
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));
그런 다음 주어진 패키지의 모든 객체를 쿼리 할 수 있습니다.
Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
답변
Google Guava 14에는 ClassPath
최상위 클래스를 스캔하는 세 가지 방법이 있는 새로운 클래스 가 포함되어 있습니다.
getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
자세한 정보 는 ClassPath
javadocs 를 참조하십시오.
답변
를 사용하는 이 방법 1 을 사용할 수 있습니다 ClassLoader
.
/**
* Scans all classes accessible from the context class loader which belong to the given package and subpackages.
*
* @param packageName The base package
* @return The classes
* @throws ClassNotFoundException
* @throws IOException
*/
private static Class[] getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes.toArray(new Class[classes.size()]);
}
/**
* Recursive method used to find all classes in a given directory and subdirs.
*
* @param directory The base directory
* @param packageName The package name for classes found inside the base directory
* @return The classes
* @throws ClassNotFoundException
*/
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
__________
1 이 방법은 원래부터 찍은 http://snippets.dzone.com/posts/show/4831 된, 보관 지금 연결로 인터넷 아카이브에 의해. 스 니펫은 https://dzone.com/articles/get-all-classes-within-package 에서 사용할 수 있습니다 .
답변
봄
이 예제는 Spring 4에 대한 것이지만 이전 버전에서도 클래스 경로 스캐너를 찾을 수 있습니다.
// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");
// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
// ... do your magic with the class ...
}
구글 구아바
참고 : 버전 14에서 API는 여전히 @Beta 로 표시 되므로 프로덕션 코드에주의하십시오.
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
if (info.getName().startsWith("my.package.")) {
final Class<?> clazz = info.load();
// do something with your clazz
}
}
답변
안녕하세요. 위의 솔루션과 다른 사이트에서 항상 문제가있었습니다.
개발자로서 API 용 애드온을 프로그래밍하고 있습니다. API는 외부 라이브러리 또는 타사 도구를 사용하지 못하게합니다. 설정은 jar 또는 zip 파일의 코드와 일부 디렉토리에 직접 위치한 클래스 파일로 구성됩니다. 그래서 내 코드는 모든 설정에서 arround를 사용할 수 있어야했습니다. 많은 연구 끝에 나는 가능한 모든 설정의 적어도 95 %에서 작동하는 방법을 생각해 냈습니다.
다음 코드는 기본적으로 항상 작동하는 과도한 방법입니다.
코드:
이 코드는 포함 된 모든 클래스에 대해 주어진 패키지를 스캔합니다. 현재의 모든 클래스에서만 작동합니다 ClassLoader
.
/**
* Private helper method
*
* @param directory
* The directory to start with
* @param pckgname
* The package name to search for. Will be needed for getting the
* Class object.
* @param classes
* if a file isn't loaded but still is in the directory
* @throws ClassNotFoundException
*/
private static void checkDirectory(File directory, String pckgname,
ArrayList<Class<?>> classes) throws ClassNotFoundException {
File tmpDirectory;
if (directory.exists() && directory.isDirectory()) {
final String[] files = directory.list();
for (final String file : files) {
if (file.endsWith(".class")) {
try {
classes.add(Class.forName(pckgname + '.'
+ file.substring(0, file.length() - 6)));
} catch (final NoClassDefFoundError e) {
// do nothing. this class hasn't been found by the
// loader, and we don't care.
}
} else if ((tmpDirectory = new File(directory, file))
.isDirectory()) {
checkDirectory(tmpDirectory, pckgname + "." + file, classes);
}
}
}
}
/**
* Private helper method.
*
* @param connection
* the connection to the jar
* @param pckgname
* the package name to search for
* @param classes
* the current ArrayList of all classes. This method will simply
* add new classes.
* @throws ClassNotFoundException
* if a file isn't loaded but still is in the jar file
* @throws IOException
* if it can't correctly read from the jar file.
*/
private static void checkJarFile(JarURLConnection connection,
String pckgname, ArrayList<Class<?>> classes)
throws ClassNotFoundException, IOException {
final JarFile jarFile = connection.getJarFile();
final Enumeration<JarEntry> entries = jarFile.entries();
String name;
for (JarEntry jarEntry = null; entries.hasMoreElements()
&& ((jarEntry = entries.nextElement()) != null);) {
name = jarEntry.getName();
if (name.contains(".class")) {
name = name.substring(0, name.length() - 6).replace('/', '.');
if (name.contains(pckgname)) {
classes.add(Class.forName(name));
}
}
}
}
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
throws ClassNotFoundException {
final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
try {
final ClassLoader cld = Thread.currentThread()
.getContextClassLoader();
if (cld == null)
throw new ClassNotFoundException("Can't get class loader.");
final Enumeration<URL> resources = cld.getResources(pckgname
.replace('.', '/'));
URLConnection connection;
for (URL url = null; resources.hasMoreElements()
&& ((url = resources.nextElement()) != null);) {
try {
connection = url.openConnection();
if (connection instanceof JarURLConnection) {
checkJarFile((JarURLConnection) connection, pckgname,
classes);
} else if (connection instanceof FileURLConnection) {
try {
checkDirectory(
new File(URLDecoder.decode(url.getPath(),
"UTF-8")), pckgname, classes);
} catch (final UnsupportedEncodingException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Unsupported encoding)",
ex);
}
} else
throw new ClassNotFoundException(pckgname + " ("
+ url.getPath()
+ ") does not appear to be a valid package");
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
}
} catch (final NullPointerException ex) {
throw new ClassNotFoundException(
pckgname
+ " does not appear to be a valid package (Null pointer exception)",
ex);
} catch (final IOException ioex) {
throw new ClassNotFoundException(
"IOException was thrown when trying to get all resources for "
+ pckgname, ioex);
}
return classes;
}
이 세 가지 방법은 지정된 패키지에서 모든 클래스를 찾을 수있는 기능을 제공합니다.
당신은 이것을 다음과 같이 사용합니다 :
getClassesForPackage("package.your.classes.are.in");
설명:
이 메소드는 먼저 current를 가져옵니다 ClassLoader
. 그런 다음 해당 패키지가 포함 된 모든 리소스를 가져오고이 패키지를 반복 URL
합니다. 그런 다음 a를 만들고 URLConnection
어떤 유형의 URl이 있는지 결정합니다. 디렉토리 ( FileURLConnection
) 또는 jar 또는 zip 파일 ( JarURLConnection
) 내의 디렉토리 일 수 있습니다 . 연결 유형에 따라 두 가지 다른 메소드가 호출됩니다.
먼저이면 어떻게되는지 봅시다 FileURLConnection
.
먼저 전달 된 File이 존재하고 디렉토리인지 확인합니다. 이 경우 클래스 파일인지 확인합니다. 그렇다면 Class
객체가 생성되어에 삽입됩니다 ArrayList
. 클래스 파일이 아니라 디렉토리 인 경우 간단히 반복하여 동일한 작업을 수행합니다. 다른 모든 사례 / 파일은 무시됩니다.
(가) 경우 URLConnection
A는 JarURLConnection
다른 개인 도우미 메서드가 호출됩니다. 이 방법은 zip / jar 아카이브의 모든 항목을 반복합니다. 하나의 항목이 클래스 파일이고 패키지 내부에 있으면 Class
객체가 생성되어에 저장됩니다 ArrayList
.
모든 리소스를 파싱 한 후 (메인 메서드) ArrayList
현재 지정된 패키지의 모든 클래스를 포함하는 클래스를 반환합니다 ClassLoader
.
프로세스가 어느 시점에서 실패 ClassNotFoundException
하면 정확한 원인에 대한 자세한 정보가 포함 된 a 가 발생합니다.
답변
추가 라이브러리를 사용하지 않고 :
package test;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception{
List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
for(Class c:classes){
System.out.println("Class: "+c);
}
}
public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{
String dottedPackage = pack.replaceAll("[/]", ".");
List<Class> classes = new ArrayList<Class>();
URL upackage = cl.getResource(pack);
DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
String line = null;
while ((line = dis.readLine()) != null) {
if(line.endsWith(".class")) {
classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
}
}
return classes;
}
}