Preciso ler as classes contidas em um pacote Java. Essas classes estão no caminho de classe. Preciso fazer essa tarefa diretamente de um programa Java. Você conhece uma maneira simples de fazer?
List<Class> classes = readClassesFrom("my.package")
Preciso ler as classes contidas em um pacote Java. Essas classes estão no caminho de classe. Preciso fazer essa tarefa diretamente de um programa Java. Você conhece uma maneira simples de fazer?
List<Class> classes = readClassesFrom("my.package")
Respostas:
Se você tiver Spring em seu classpath, o seguinte fará isso.
Encontre todas as classes em um pacote que são anotadas com XmlRootElement:
private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
List<Class> candidates = new ArrayList<Class>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + "**/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isCandidate(metadataReader)) {
candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
}
}
}
return candidates;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}
private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
try {
Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
if (c.getAnnotation(XmlRootElement.class) != null) {
return true;
}
}
catch(Throwable e){
}
return false;
}
Você pode usar o Projeto Reflexões descrito aqui
É bastante completo e fácil de usar.
Breve descrição do site acima:
O Reflections varre seu caminho de classe, indexa os metadados, permite que você consulte em tempo de execução e pode salvar e coletar essas informações para muitos módulos em seu projeto.
Exemplo:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Eu uso este, ele funciona com arquivos ou arquivos jar
public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL packageURL;
ArrayList<String> names = new ArrayList<String>();;
packageName = packageName.replace(".", "/");
packageURL = classLoader.getResource(packageName);
if(packageURL.getProtocol().equals("jar")){
String jarFileName;
JarFile jf ;
Enumeration<JarEntry> jarEntries;
String entryName;
// build jar file name, then loop through zipped entries
jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
System.out.println(">"+jarFileName);
jf = new JarFile(jarFileName);
jarEntries = jf.entries();
while(jarEntries.hasMoreElements()){
entryName = jarEntries.nextElement().getName();
if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
names.add(entryName);
}
}
// loop through files in classpath
}else{
URI uri = new URI(packageURL.toString());
File folder = new File(uri.getPath());
// won't work with path which contains blank (%20)
// File folder = new File(packageURL.getFile());
File[] contenuti = folder.listFiles();
String entryName;
for(File actual: contenuti){
entryName = actual.getName();
entryName = entryName.substring(0, entryName.lastIndexOf('.'));
names.add(entryName);
}
}
return names;
}
Spring implementou uma excelente função de pesquisa de caminho de classe no PathMatchingResourcePatternResolver
. Se você usar o classpath*
prefixo:, poderá encontrar todos os recursos, incluindo classes em uma determinada hierarquia, e até mesmo filtrá-los, se desejar. Então você pode usar os filhos de AbstractTypeHierarchyTraversingFilter
, AnnotationTypeFilter
e AssignableTypeFilter
para filtrar esses recursos quer por anotações nível de classe ou em interfaces que implementar.
Java 1.6.0_24:
public static File[] getPackageContent(String packageName) throws IOException{
ArrayList<File> list = new ArrayList<File>();
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
.getResources(packageName);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
File dir = new File(url.getFile());
for (File f : dir.listFiles()) {
list.add(f);
}
}
return list.toArray(new File[]{});
}
Esta solução foi testada no ambiente EJB .
Scannotation e Reflections usam abordagem de varredura de caminho de classe:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Outra abordagem é usar a API de processamento de anotação Java Pluggable para escrever um processador de anotação que irá coletar todas as classes anotadas em tempo de compilação e construir o arquivo de índice para uso em tempo de execução. Este mecanismo é implementado na biblioteca ClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Essa funcionalidade ainda está ausente da API de reflexão Java, tanto quanto eu sei. Você pode obter um objeto de pacote apenas fazendo isto:
Package packageObj = Package.getPackage("my.package");
Mas, como você provavelmente percebeu, isso não permitirá que você liste as classes desse pacote. A partir de agora, você deve adotar uma abordagem mais orientada para o sistema de arquivos.
Eu encontrei alguns exemplos de implementação neste post
Não estou 100% certo de que esses métodos funcionarão quando suas aulas estiverem enterradas em arquivos JAR, mas espero que um deles faça isso para você.
Eu concordo com @skaffman ... se você tiver outra maneira de fazer isso, eu recomendo fazer isso.
O mecanismo mais robusto para listar todas as classes em um determinado pacote é atualmente ClassGraph , porque ele lida com a maior variedade possível de mecanismos de especificação de caminho de classe , incluindo o novo sistema de módulo JPMS. (Eu sou o autor.)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
eXtcos parece promissor. Imagine que você deseja encontrar todas as classes que:
Com eXtcos, isso é tão simples quanto
ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();
Set<Class> classes = scanner.getClasses(new ClassQuery() {
protected void query() {
select().
from(“common”).
andStore(thoseExtending(Component.class).into(classStore)).
returning(allAnnotatedWith(MyComponent.class));
}
});
Bill Burke escreveu um (bom artigo sobre digitalização de classe] e então escreveu Scannotation .
O Hibernate já está escrito:
O CDI pode resolver isso, mas não sei - não investigou totalmente ainda
.
@Inject Instance< MyClass> x;
...
x.iterator()
Também para anotações:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Acontece que eu o implementei e funciona na maioria dos casos. Como é longo, coloco em um arquivo aqui .
A ideia é encontrar a localização do arquivo de origem da classe que está disponível na maioria dos casos (uma exceção conhecida são os arquivos da classe JVM - até onde testei). Se o código estiver em um diretório, verifique todos os arquivos e apenas os arquivos de classe local. Se o código estiver em um arquivo JAR, verifique todas as entradas.
Este método só pode ser usado quando:
Você tem uma classe que está no mesmo pacote que deseja descobrir. Essa classe é chamada de SeedClass. Por exemplo, se você deseja listar todas as classes em 'java.io', a classe inicial pode ser java.io.File
.
Suas aulas estão em um diretório ou em um arquivo JAR que contém informações do arquivo de origem (não o arquivo de código-fonte, mas apenas o arquivo de origem). Pelo que tentei, funciona quase 100%, exceto a classe JVM (essas classes vêm com a JVM).
Seu programa deve ter permissão para acessar o ProtectionDomain dessas classes. Se o seu programa for carregado localmente, não haverá problema.
Eu testei o programa apenas para o meu uso normal, então ele ainda pode ter problemas.
Eu espero que isso ajude.
Aqui está outra opção, pequena modificação em outra resposta acima / abaixo:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
Na época em que os miniaplicativos eram comuns, era possível ter uma URL no caminho de classe. Quando o carregador de classe exigisse uma classe, ele pesquisaria todos os locais no caminho de classe, incluindo recursos http. Como você pode ter coisas como URLs e diretórios no caminho de classe, não há uma maneira fácil de obter uma lista definitiva das classes.
No entanto, você pode chegar bem perto. Algumas das bibliotecas do Spring estão fazendo isso agora. Você pode obter todos os jar's no classpath e abri-los como arquivos. Você pode então pegar essa lista de arquivos e criar uma estrutura de dados contendo suas classes.
usar especialista em dependência:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
então use este código:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent - a razão pela qual a associação é uma forma tem a ver com o fato de que qualquer classe em qualquer componente de seu CLASSPATH pode se declarar em qualquer pacote (exceto para java / javax). Assim, simplesmente não há mapeamento de TODAS as classes em um determinado "pacote" porque ninguém sabe nem pode saber. Você pode atualizar um arquivo jar amanhã e remover ou adicionar classes. É como tentar obter uma lista de todas as pessoas chamadas John / Jon / Johan em todos os países do mundo - nenhum de nós é onisciente, portanto, nenhum de nós jamais terá a resposta correta.