quarta-feira, 5 de agosto de 2015

Usando reflection em classes não compiladas

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

O tema que abordaremos hoje é relacionado a uma dificuldade que, um dia eu tive e, agora, resolvi compartilhar com vocês, os quais podem estar passando pelo mesmo problema.

Acho interessante que você tenha um conhecimento intermediário em java, amiguinho. Para os apressadinhos de plantão, clique aqui para baixar o projeto.

Vamos lá ..... A situação é a seguinte: Você possui um arquivo java em alguma pasta e precisa usar reflection nessa classe para obter dados e realizar invocações.

Nesse momento você já pensou: "ha ha ... mole ! ". Caaaaaalmmaaaa, existem duas coisas que eu ainda não contei.

  1. Este arquivo não está no seu projeto logo, não está no seu classpath.
  2. Este arquivo não está compilado.
Acho que agora as coisas devem ter ficado um pouco mais complicadas, não !? 

A solução é: temos que compilar esse arquivo java via programação e através disso, gerar e acessar o arquivo .class. A partir do momento que gerarmos um objeto Class dessa nossa classe, a reflection rola fácil.

Vamos ao código.

1:  private void carregarClasseJava() {  
2:              
3:            File javaFile = new File("resources/MinhaClasse.java");  
4:            javaFile.getAbsolutePath();  
5:            File sourceFolder = new File("resources");  
6:            sourceFolder.getAbsolutePath();  
7:              
8:            /*Compila a classe*/  
9:            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();  
10:            compiler.run(null, null, null, javaFile.getPath());  
11:    
12:            /*Define o classloader da nossa classe recém compilada*/  
13:            URLClassLoader classLoader = null;  
14:            try {  
15:                 classLoader = URLClassLoader  
16:                           .newInstance(new URL[] { sourceFolder.toURI().toURL() });  
17:            } catch (MalformedURLException ex) {  
18:                 ex.printStackTrace();  
19:            }  
20:              
21:            /*Crio a representação da classe compilada para posteriormente usar a reflection.*/  
22:            Class clazz = null;  
23:            try {  
24:                 clazz = Class.forName("MinhaClasse", true, classLoader);  
25:            } catch (ClassNotFoundException ex) {  
26:                 ex.printStackTrace();  
27:            }  
28:              
29:            /*Crio a instância da minha classe. Não fiz o casting direto para MinhaClasse porque a mesma não está no pacote src.   
30:             * Caso você queira fazer isso, mova a classe para o pacote src e mude os caminhos dos objetos File.*/  
31:            Object instance = null;  
32:            try {  
33:                 instance = clazz.newInstance();  
34:            } catch (InstantiationException | IllegalAccessException e) {  
35:                 e.printStackTrace();  
36:            }  
37:       }  

Vamos as explicações !!

Da linha 3 até a linha 6, acessamos o arquivo e a pasta aonde ele está. É necessário acessar a pasta do arquivo porque ele se tornará nosso class loader.

Da linha 8 até 10, compilamos o arquivo. Note que depois da linha 10 ser executada, é possível visualizar o arquivo .class na pasta de origem.

Da linha 12 até 19, criamos o nosso class loader. Um class loader é uma classe que carrega outras classes, ou seja, ele carrega o bytecode da sua classe para a memória. Deixarei nas referências um ótimo artigo da DevMedia, explicando os class loaders. Vale a pena dar uma conferida.

Da linha 21 até 27 é onde a mágica acontece. É aqui que passamos para o class loader qual classe carregar. O nome passado deve ser o fully qualified name, ou nome absoluto, da classe. É composto pelo caminho completo de pacotes juntamente com o nome da classe. No nosso exemplo, o fully qualified name da nossa classe é o próprio nome da classe, pois ela não está dentro de um pacote. Porém, se ela estivesse dentro na estrutura de pacotes abaixo:

Figura 1 - Estrutura de pacotes
Seu nome absoluto seria: br.com.meuprojeto.MinhaClasse.

Da linha 29 até 36, já uso a reflection e crio uma instância da nossa classe recém carregada. Pronto !!!

Sei que este post está mais curto do que as postagens habituais mas, achei interessante trazer esse conhecimento para vocês, caros leitores.

Para baixar o projeto, clique aqui.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
How do I programmatically compile and instantiate a Java class? - http://stackoverflow.com/questions/2946338/how-do-i-programmatically-compile-and-instantiate-a-java-class
Entendendo ClassLoaders em Java - http://www.devmedia.com.br/entendendo-classloaders-em-java/29076

Nenhum comentário: