java - interfaz - swing containers netbeans
¿Puedes encontrar todas las clases en un paquete utilizando la reflexión? (23)
Primavera
Este ejemplo es para Spring 4, pero también puede encontrar el escáner classpath en versiones anteriores.
// 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 ...
}
Guayaba de google
Nota: En la versión 14, la API todavía está marcada como @Beta , así que tenga cuidado con el código de producción.
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
}
}
¿Es posible encontrar todas las clases o interfaces en un paquete dado? (Mirando rápidamente, por ejemplo, el Package
, parecería que no.)
Así es como lo hago. Analizo todas las subcarpetas (subpaquetes) y no intento cargar clases anónimas:
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader, recursively, avoiding anonymous classes
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
// This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
ArrayList<File> directories = new ArrayList<File>();
String packageToPath = pckgname.replace(''.'', ''/'');
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can''t get class loader.");
}
// Ask for all resources for the packageToPath
Enumeration<URL> resources = cld.getResources(packageToPath);
while (resources.hasMoreElements()) {
directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
}
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
} catch (UnsupportedEncodingException encex) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
} catch (IOException ioex) {
throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
}
ArrayList<Class> classes = new ArrayList<Class>();
// For every directoryFile identified capture all the .class files
while (!directories.isEmpty()){
File directoryFile = directories.remove(0);
if (directoryFile.exists()) {
// Get the list of the files contained in the package
File[] files = directoryFile.listFiles();
for (File file : files) {
// we are only interested in .class files
if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
// removes the .class extension
int index = directoryFile.getPath().indexOf(packageToPath);
String packagePrefix = directoryFile.getPath().substring(index).replace(''/'', ''.'');;
try {
String className = packagePrefix + ''.'' + file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
} catch (NoClassDefFoundError e)
{
// do nothing. this class hasn''t been found by the loader, and we don''t care.
}
} else if (file.isDirectory()){ // If we got to a subdirectory
directories.add(new File(file.getPath()));
}
}
} else {
throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
}
}
return classes;
}
Debe buscar todas las entradas del cargador de clases en la ruta de clases:
String pkg = "org/apache/commons/lang";
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
System.out.println(url.getFile());
File jar = new File(url.getFile());
// ....
}
Si la entrada es el directorio, simplemente busque en el subdirectorio derecho:
if (jar.isDirectory()) {
File subdir = new File(jar, pkg);
if (!subdir.exists())
continue;
File[] files = subdir.listFiles();
for (File file : files) {
if (!file.isFile())
continue;
if (file.getName().endsWith(".class"))
System.out.println("Found class: "
+ file.getName().substring(0,
file.getName().length() - 6));
}
}
Si la entrada es el archivo y es un archivo jar, inspeccione las entradas ZIP del mismo:
else {
// try to open as ZIP
try {
ZipFile zip = new ZipFile(jar);
for (Enumeration<? extends ZipEntry> entries = zip
.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (!name.startsWith(pkg))
continue;
name = name.substring(pkg.length() + 1);
if (name.indexOf(''/'') < 0 && name.endsWith(".class"))
System.out.println("Found class: "
+ name.substring(0, name.length() - 6));
}
} catch (ZipException e) {
System.out.println("Not a ZIP: " + e.getMessage());
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
Ahora, una vez que tenga todos los nombres de clase dentro del paquete, puede intentar cargarlos con reflexión y analizar si son clases o interfaces, etc.
Debido a la naturaleza dinámica de los cargadores de clases, esto no es posible. Los cargadores de clases no están obligados a decirle a la máquina virtual qué clases puede proporcionar, en lugar de eso, son solo solicitudes de clases entregadas, y deben devolver una clase o lanzar una excepción.
Sin embargo, si escribe sus propios cargadores de clases, o examina los classpaths y sus jars, es posible encontrar esta información. Esto será a través de las operaciones del sistema de archivos, y no la reflexión. Incluso podría haber bibliotecas que pueden ayudarte a hacer esto.
Si hay clases que se generan o se entregan de forma remota, no podrá descubrir esas clases.
El método normal es, en cambio, registrar en algún lugar las clases a las que necesita acceder en un archivo o hacer referencia a ellas en una clase diferente. O simplemente usar la convención cuando se trata de nombrar.
Anexo: La biblioteca de Reflections le permitirá buscar clases en el classpath actual. Se puede utilizar para obtener todas las clases en un paquete:
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
En general, los cargadores de clases no permiten el escaneo a través de todas las clases en el classpath. Pero, por lo general, el único cargador de clases usado es UrlClassLoader, desde el cual podemos recuperar la lista de directorios y archivos jar (ver getURLs ) y abrirlos uno por uno para enumerar las clases disponibles. Este enfoque, llamado exploración de ruta de clase, se implementa en Scannotation y Reflections .
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Otro enfoque es utilizar la API de procesamiento de anotaciones de Java Pluggable para escribir un procesador de anotaciones que recopilará todas las clases anotadas en el momento de la compilación y creará el archivo de índice para uso en tiempo de ejecución. Este mecanismo se implementa en la biblioteca ClassIndex :
// package-info.java
@IndexSubclasses
package my.package;
// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Tenga en cuenta que no se necesita una configuración adicional, ya que el escaneo está completamente automatizado gracias al compilador de Java que descubre automáticamente cualquier procesador que se encuentre en la ruta de clase.
Escribí FastClasspathScanner para resolver este problema. Maneja muchos tipos diferentes de tareas de escaneo de classpath, tiene una API simple, trabaja con muchos ClassLoaders diferentes y entornos de classpath, ha sido cuidadosamente paralelizado y está altamente optimizado para alta velocidad y bajo consumo de memoria. Incluso puede generar una visualización GraphViz del gráfico de clase, que muestra cómo las clases están conectadas entre sí.
Para su pregunta original de encontrar todas las clases o interfaces en un paquete dado, puede hacer:
List<String> classNames = new FastClasspathScanner("com.mypackage").scan()
.getNamesOfAllClasses();
Hay muchas variantes posibles para esto: consulte la documentación (vinculada anteriormente) para obtener información completa.
Google Guava 14 incluye una nueva clase ClassPath
con tres métodos para buscar clases de nivel superior:
-
getTopLevelClasses()
-
getTopLevelClasses(String packageName)
-
getTopLevelClassesRecursive(String packageName)
Ver los javadocs ClassPath
para más información.
Hola. Siempre he tenido algunos problemas con las soluciones anteriores (y en otros sitios).
Yo, como desarrollador, estoy programando un complemento para una API. La API permite el uso de bibliotecas externas o herramientas de terceros. La configuración también consiste en una mezcla de código en archivos jar o zip y archivos de clase ubicados directamente en algunos directorios. Así que mi código tenía que ser capaz de trabajar en cada configuración. Después de mucha investigación, he encontrado un método que funcionará en al menos el 95% de todas las configuraciones posibles.
El siguiente código es básicamente el método de exceso que siempre funcionará.
El código:
Este código escanea un paquete dado para todas las clases que se incluyen en él. Solo funcionará para todas las clases en el ClassLoader
actual.
/**
* 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;
}
Estos tres métodos le brindan la capacidad de encontrar todas las clases en un paquete determinado.
Lo usas así:
getClassesForPackage("package.your.classes.are.in");
La explicación:
El método primero obtiene el ClassLoader
actual. A continuación, recupera todos los recursos que contienen dicho paquete y las iteraciones de estas URL
s. A continuación, crea un URLConnection
y determina qué tipo de URl tenemos. Puede ser un directorio ( FileURLConnection
) o un directorio dentro de un archivo jar o zip ( JarURLConnection
). Dependiendo de qué tipo de conexión tengamos dos métodos diferentes se llamará.
Primero veamos que pasa si es un FileURLConnection
.
Primero verifica si el archivo pasado existe y es un directorio. Si ese es el caso, comprueba si es un archivo de clase. Si es así, se creará un objeto de Class
y se colocará en ArrayList
. Si no es un archivo de clase pero es un directorio, simplemente lo iteramos y hacemos lo mismo. Todos los demás casos / archivos serán ignorados.
Si la URLConnection
es una URLConnection
se JarURLConnection
al otro método auxiliar privado. Este método itera sobre todas las entradas en el archivo zip / jar. Si una entrada es un archivo de clase y está dentro del paquete, se creará y almacenará un objeto de Class
en el ArrayList
.
Después de que se hayan analizado todos los recursos (el método principal) devuelve el ArrayList
contiene todas las clases en el paquete dado, que el ClassLoader
actual conoce.
Si el proceso falla en cualquier punto, se emitirá una excepción ClassNotFoundException con información detallada sobre la causa exacta.
Junté un proyecto github simple que resuelve este problema:
https://github.com/ddopson/java-class-enumerator
Debería funcionar para AMBAS rutas de clase basadas en archivos Y para archivos jar.
Si ejecuta ''make'' después de revisar el proyecto, se imprimirá esto:
Cleaning...
rm -rf build/
Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ .
jar cf build/ClassEnumerator.jar -C build/classes/ pro
Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: ''test'' becomes Resource: ''file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test''
ClassDiscovery: Reading Directory ''/Users/Dopson/work/other/java-class-enumeration/build/classes/test''
ClassDiscovery: FileName ''ClassIShouldFindOne.class'' => class ''test.ClassIShouldFindOne''
ClassDiscovery: FileName ''ClassIShouldFindTwo.class'' => class ''test.ClassIShouldFindTwo''
ClassDiscovery: FileName ''subpkg'' => class ''null''
ClassDiscovery: Reading Directory ''/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg''
ClassDiscovery: FileName ''ClassIShouldFindThree.class'' => class ''test.subpkg.ClassIShouldFindThree''
ClassDiscovery: FileName ''TestClassEnumeration.class'' => class ''test.TestClassEnumeration''
Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar test.TestClassEnumeration
ClassDiscovery: Package: ''test'' becomes Resource: ''jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test''
ClassDiscovery: Reading JAR file: ''/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar''
ClassDiscovery: JarEntry ''META-INF/'' => class ''null''
ClassDiscovery: JarEntry ''META-INF/MANIFEST.MF'' => class ''null''
ClassDiscovery: JarEntry ''pro/'' => class ''null''
ClassDiscovery: JarEntry ''pro/ddopson/'' => class ''null''
ClassDiscovery: JarEntry ''pro/ddopson/ClassEnumerator.class'' => class ''null''
ClassDiscovery: JarEntry ''test/'' => class ''null''
ClassDiscovery: JarEntry ''test/ClassIShouldFindOne.class'' => class ''test.ClassIShouldFindOne''
ClassDiscovery: JarEntry ''test/ClassIShouldFindTwo.class'' => class ''test.ClassIShouldFindTwo''
ClassDiscovery: JarEntry ''test/subpkg/'' => class ''null''
ClassDiscovery: JarEntry ''test/subpkg/ClassIShouldFindThree.class'' => class ''test.subpkg.ClassIShouldFindThree''
ClassDiscovery: JarEntry ''test/TestClassEnumeration.class'' => class ''test.TestClassEnumeration''
Tests Passed.
Véase también mi otra respuesta.
Podría usar este método 1 que usa el 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 Este método se tomó originalmente de http://snippets.dzone.com/posts/show/4831 , que fue archivado por el Archivo de Internet, como está vinculado a ahora. El fragmento también está disponible en https://dzone.com/articles/get-all-classes-within-package .
Probablemente debería echar un vistazo a la biblioteca de código abierto de Reflections . Con él podrás lograr fácilmente lo que quieras.
Primero, configure el índice de reflexiones (es un poco complicado ya que la búsqueda de todas las clases está deshabilitada de manera predeterminada):
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"))));
Luego puedes consultar todos los objetos en un paquete dado:
Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
Sí, usando pocas API, puede hacerlo. He aquí cómo me gusta hacerlo. Me enfrenté a este problema que estaba usando hibernate core y tuve que encontrar clases que estuvieran anotadas con cierta anotación.
Conviértalos en una anotación personalizada con la que marcará las clases que desea recoger.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {
}
Luego marca tu clase con eso como
@EntityToBeScanned
public MyClass{
}
Haga esta clase de utilidad que tiene el siguiente método.
public class ClassScanner {
public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
Reflections reflections = new Reflections(".*");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
return annotated;
}
}
Llame al método allFoundClassesAnnotatedWithEntityToBeScanned () para obtener un conjunto de clases.
Necesitarás libs que se dan a continuación.
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
Sin utilizar bibliotecas extra:
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;
}
}
Vale la pena mencionar
Si desea tener una lista de todas las clases en algún paquete, puede usar Reflection
la siguiente manera:
List<Class> myTypes = new ArrayList<>();
Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
myTypes.add(Class.forName(s));
}
Esto creará una lista de clases que luego podrás usar como desees.
Acabo de escribir una clase util, incluye métodos de prueba, puedes tener un cheque ~
IteratePackageUtil.java:
package eric.j2se.reflect;
import java.util.Set;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
/**
* an util to iterate class in a package,
*
* @author eric
* @date Dec 10, 2013 12:36:46 AM
*/
public class IteratePackageUtil {
/**
* <p>
* Get set of all class in a specified package recursively. this only support lib
* </p>
* <p>
* class of sub package will be included, inner class will be included,
* </p>
* <p>
* could load class that use the same classloader of current class, can''t load system packages,
* </p>
*
* @param pkg
* path of a package
* @return
*/
public static Set<Class<? extends Object>> getClazzSet(String pkg) {
// prepare reflection, include direct subclass of Object.class
Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
.filterInputsBy(new FilterBuilder().includePackage(pkg)));
return reflections.getSubTypesOf(Object.class);
}
public static void test() {
String pkg = "org.apache.tomcat.util";
Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
for (Class<? extends Object> clazz : clazzSet) {
System.out.println(clazz.getName());
}
}
public static void main(String[] args) {
test();
}
}
He estado tratando de usar la biblioteca de Reflections, pero tuve algunos problemas al usarla, y había demasiados archivos que debería incluir simplemente para obtener las clases en un paquete.
Publicaré una solución que encontré en esta pregunta duplicada: ¿Cómo obtener los nombres de todas las clases en un paquete?
La respuesta fue escrita por sp00m ; He añadido algunas correcciones para que funcione:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
public final class ClassFinder {
private final static char DOT = ''.'';
private final static char SLASH = ''/'';
private final static String CLASS_SUFFIX = ".class";
private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path ''%s''. Are you sure the given ''%s'' package exists?";
public final static List<Class<?>> find(final String scannedPackage) {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final String scannedPath = scannedPackage.replace(DOT, SLASH);
final Enumeration<URL> resources;
try {
resources = classLoader.getResources(scannedPath);
} catch (IOException e) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
}
final List<Class<?>> classes = new LinkedList<Class<?>>();
while (resources.hasMoreElements()) {
final File file = new File(resources.nextElement().getFile());
classes.addAll(find(file, scannedPackage));
}
return classes;
}
private final static List<Class<?>> find(final File file, final String scannedPackage) {
final List<Class<?>> classes = new LinkedList<Class<?>>();
if (file.isDirectory()) {
for (File nestedFile : file.listFiles()) {
classes.addAll(find(nestedFile, scannedPackage));
}
//File names with the $1, $2 holds the anonymous inner classes, we are not interested on them.
} else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {
final int beginIndex = 0;
final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
final String className = file.getName().substring(beginIndex, endIndex);
try {
final String resource = scannedPackage + DOT + className;
classes.add(Class.forName(resource));
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
Para usarlo, simplemente llame al método de búsqueda como se menciona en sp00n en este ejemplo: si es necesario, he agregado la creación de instancias de las clases.
List<Class<?>> classes = ClassFinder.find("com.package");
ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
Constructor constructor = aClass.getConstructor();
//Create an object of the class type
constructor.newInstance();
//...
}
No es posible, ya que es posible que no se carguen todas las clases del paquete, mientras que usted siempre sabe el paquete de una clase.
Si simplemente busca cargar un grupo de clases relacionadas, Spring puede ayudarlo.
Spring puede instanciar una lista o un mapa de todas las clases que implementan una interfaz determinada en una línea de código. La lista o mapa contendrá instancias de todas las clases que implementan esa interfaz.
Dicho esto, como una alternativa para cargar la lista de clases fuera del sistema de archivos, en lugar de eso, simplemente implemente la misma interfaz en todas las clases que quiera cargar, independientemente del paquete y use Spring para proporcionarle instancias de todas ellas. De esa manera, puede cargar (y crear instancias) todas las clases que desee, independientemente del paquete en el que estén.
Por otro lado, si lo que desea es tenerlos todos en un paquete, simplemente haga que todas las clases en ese paquete implementen una interfaz determinada.
Siempre que no esté utilizando ningún cargador dinámico de clases, puede buscar la ruta de clase y para cada entrada buscar en el directorio o archivo JAR.
share no me funcionó para pruebas parametrizadas @RunWith(Parameterized.class)
al usar Maven. Las pruebas fueron nombradas correctamente y también donde se encontraron pero no se ejecutaron:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec
Un problema similar ha sido reportado here .
En mi caso @Parameters
es crear instancias de cada clase en un paquete. Las pruebas funcionaron bien cuando se ejecutan localmente en el IDE. Sin embargo, al ejecutar Maven no se encontraron clases con la solución de Aleksander Blomskøld.
Lo hice funcionar con el siguiente recorte que se inspiró en el comentario de David Pärsson sobre la respuesta de Aleksander Blomskøld:
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don''t exclude Object.class */), new ResourcesScanner())
.addUrls(ClasspathHelper.forJavaClassPath())
.filterInputsBy(new FilterBuilder()
.include(FilterBuilder.prefix(basePackage))));
Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);
Basado en share , y en un intento de no confiar en bibliotecas de terceros, implementaría el enfoque del sistema de archivos al inspeccionar la ubicación física del primer paquete con:
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();
new java.io.File(
klass.getResource(
"/" + curPackage.replace( "." , "/")
).getFile()
).listFiles(
new java.io.FileFilter() {
public boolean accept(java.io.File file) {
final String classExtension = ".class";
if ( file.isFile()
&& file.getName().endsWith(classExtension)
// avoid inner classes
&& ! file.getName().contains("$") )
{
try {
String className = file.getName();
className = className.substring(0, className.length() - classExtension.length());
foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
} catch (ClassNotFoundException e) {
e.printStackTrace(System.out);
}
}
return false;
}
}
);
foundClasses = foundClassesDyn.toArray(foundClasses);
Casi todas las respuestas usan Reflections
o leen archivos de clase del sistema de archivos. Si intenta leer las clases del sistema de archivos, puede obtener errores al empaquetar su aplicación como JAR u otro. También es posible que no desee utilizar una biblioteca separada para ese propósito.
Aquí hay otro enfoque que es Java puro y no depende del sistema de archivos.
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class PackageUtil {
public static Collection<Class> getClasses(final String pack) throws Exception {
final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
.map(javaFileObject -> {
try {
final String[] split = javaFileObject.getName()
.replace(".class", "")
.replace(")", "")
.split(Pattern.quote(File.separator));
final String fullClassName = pack + "." + split[split.length - 1];
return Class.forName(fullClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toCollection(ArrayList::new));
}
}
Java 8 no es un deber . Puede utilizar para bucles en lugar de corrientes. Y puedes probarlo así
public static void main(String[] args) throws Exception {
final String pack = "java.nio.file"; // Or any other package
PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}
Es muy posible, pero sin bibliotecas adicionales como Reflections
es difícil ...
Es difícil porque no tienes un instrumento completo para obtener el nombre de la clase.
Y, tomo el código de mi ClassFinder
clase:
package play.util;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Created by LINKOR on 26.05.2017 in 15:12.
* Date: 2017.05.26
*/
public class FileClassFinder {
private JarFile file;
private boolean trouble;
public FileClassFinder(String filePath) {
try {
file = new JarFile(filePath);
} catch (IOException e) {
trouble = true;
}
}
public List<String> findClasses(String pkg) {
ArrayList<String> classes = new ArrayList<>();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry cls = entries.nextElement();
if (!cls.isDirectory()) {
String fileName = cls.getName();
String className = fileName.replaceAll("/", ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf(''.''));
if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
}
}
return classes;
}
}