Java Reflection: Recarregando cache de propriedades

Veja neste artigo como usar reflexão em Java para recarregar propriedades em cache.

É muito comum utilizarmos cache de diversos tipos de propriedades para aumentar a performance do nosso sistema, em vez executar buscas constantes no banco de dados, fazemos apenas uma busca armazenando em memória o resultado e fazendo com que a próxima consulta seja feita em memória e não no banco, isso é o que chamamos de Cache. Este trata-se de um dispositivo que faz a intermediação entre um processo que precisa de determinado valor e o real local onde este valor está, assim temos uma camada mais próxima ao processo provendo um acesso mais rápido.

Isso ocorre frequentemente quando você abre um aplicativo no seu sistema operacional, a primeira vez que o aplicativo é aberto há uma demora muito maior do que a segunda vez que este é aberto, pois os processos que são executados fazem mais acesso a memória e menos ao disco rígido que possuo tempo de resposta muito maior.

Neste artigo veremos um exemplo de Cache de propriedades que podem ser recarregadas a qualquer momento usando a técnica de reflexão em Java.

Classe Cache

Nossa classe não possui implementações para comunicação com o banco de dados, pois o objetivo deste artigo não é esse, vejamos nossa classe:

Listagem 1. CacheUtils

package lab; import java.util.List; public class CacheUtils { private List situacoes; private List tipos; public void recarregarSituacoes(){ System.out.println("recarrregando situações..."); } public void recarregarTipos(){ System.out.println("recarrregando tipos..."); } public List getSituacoes() { if (situacoes == null){ recarregarSituacoes(); } return situacoes; } public List getTipos() { if (tipos == null){ recarregarTipos(); } return tipos; } }

Criamos duas propriedades que podem não fazer muito sentido pois estão fora de contexto, mas o importante é perceber que estas tratam-se de listas que irão armazenar valores para uso durante todo o contexto do sistema, podemos imaginar: Situações de Documentos (Aberto, Fechado, Enviado, Perdido, Cancelado, Roubado e etc.) e Tipo de Documentos (Carta, Oficio, Curriculo, Anexo e etc.).

A primeira vez que o método getSituacoes() for chamado não haverá nenhum valor carregando então o método recarregarSituacoes() será chamado e todos os valores serão buscados no banco de dados, sendo assim da próxima vez isso não será mais necessário.

Se você estiver trabalhando com Hibernate poderia usar NamedQueries para fazer busca destes valores no banco de dados, ou mesmo poderá usar o JDBC sem nenhum framework, a escolha não cabe a este artigo.

Agora imagine a seguinte situação: Você possui o sistema em execução em pleno horário comercial e não poderá reinicia-lo para recarregar propriedades que você alterou no banco de dados ou mesmo adicionou, por exemplo a adição de uma nova situação para os docmentos. Como eles estão em cache você não poderá ver essa nova situação até reiniciar o serviço, pode ser o Tomcat, Glassfish, jBoss e etc.

A melhor solução é criar um método capaz de carregar essas propriedades forçando que a busca seja feita novamente no banco de dados, por isso criamos estes dois métodos: recarregarSituacoes()e recarregarTipos().

Mas ainda temos outro problema, como iremos chamar esse método de maneira rápida? E qualquer outro método que precisemos para recarregar valores em cache? Uma ótima solução seria usar Reflection e é isso que veremos no próximo tópico.

Usando Reflection

A Reflexão ou Reflection é uma técnica usada para mudar a estrutura de um programa em tempo de execução, o que normalmente ocorreria em tempo de design. Em nosso caso não a usaremos para mudar, mas sim para consultar. Veja bem, nós precisamos executar métodos de “realod” em tempo de execução, então usaremos a Reflexão para dizer qual classe devemos usar e qual método queremos executar, isso nos dá uma maior flexibilidade permitindo que não apenas métodos de realod sejam executados, mas também quaisquer outros disponíveis.

Nosso formulário deverá ficar igual ao apresentado na Figura 1.

Figura 1. Freflection

O primeiro campo de texto receberá o nome da classe onde os métodos estão, sendo que é necessário colocar o caminho completo incluindo o pacote. Ex: meupacote.ClasseABC..

Ao digitar o nome da classe qualificada o botão “Carregar Métodos” deverá ser acionado para que os métodos disponíveis nesta classe sejam carregados no combobox “Método”. Por último o botão “Executar” irá buscar o método naquela class e executá-lo. Se você desejar criar o mesmo formulário acima no Netbeans poderá usar a listagem abaixo que trata-se de um código para geração do arquivo “.form” lido pelo Netbeans:

Listagem 2. Freflection.form

<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JFrameFormInfo"> <Properties> <Property name="defaultCloseOperation" type="int" value="3"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="generateCenter" type="boolean" value="false"/> </SyntheticProperties> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <Component id="jButtonExecutar" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jButtonCarregarMetodos" min="-2" max="-2" attributes="0"/> </Group> <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0"> <Component id="jComboBoxMetodos" alignment="0" max="32767" attributes="0"/> <Component id="jTextFieldClasse" alignment="0" pref="219" max="32767" attributes="0"/> </Group> </Group> <EmptySpace pref="159" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> <EmptySpace pref="14" max="32767" attributes="0"/> <Component id="jLabel1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jTextFieldClasse" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jLabel2" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jComboBoxMetodos" min="-2" max="-2" attributes="0"/> <EmptySpace min="-2" pref="22" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> <Component id="jButtonExecutar" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButtonCarregarMetodos" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="javax.swing.JButton" name="jButtonExecutar"> <Properties> <Property name="text" type="java.lang.String" value="Executar"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonExecutarActionPerformed"/> </Events> </Component> <Component class="javax.swing.JTextField" name="jTextFieldClasse"> </Component> <Component class="javax.swing.JLabel" name="jLabel1"> <Properties> <Property name="text" type="java.lang.String" value="Classe"/> </Properties> </Component> <Component class="javax.swing.JLabel" name="jLabel2"> <Properties> <Property name="text" type="java.lang.String" value="Método"/> </Properties> </Component> <Component class="javax.swing.JButton" name="jButtonCarregarMetodos"> <Properties> <Property name="text" type="java.lang.String" value="Carregar Métodos"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonCarregarMetodosActionPerformed"/> </Events> </Component> <Component class="javax.swing.JComboBox" name="jComboBoxMetodos"> </Component> </SubComponents> </Form>

Posteriormente a isso temos nosso formulário Freflection.java, primeiramente iremos mostrar o código completo referente a ele e explicaremos os detalhes de cada lógica:

Listagem 3. Freflection.java

/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package lab; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.swing.JOptionPane; public class FReflection extends javax.swing.JFrame { private Class classLoaded; /** * Creates new form FReflection */ public FReflection() { initComponents(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jButtonExecutar = new javax.swing.JButton(); jTextFieldClasse = new javax.swing.JTextField(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jButtonCarregarMetodos = new javax.swing.JButton(); jComboBoxMetodos = new javax.swing.JComboBox(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jButtonExecutar.setText("Executar"); jButtonExecutar.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonExecutarActionPerformed(evt); } }); jLabel1.setText("Classe"); jLabel2.setText("Método"); jButtonCarregarMetodos.setText("Carregar Métodos"); jButtonCarregarMetodos.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButtonCarregarMetodosActionPerformed(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jButtonExecutar) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jButtonCarregarMetodos)) .addComponent(jLabel1) .addComponent(jLabel2) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(jComboBoxMetodos, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jTextFieldClasse, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 219, Short.MAX_VALUE))) .addContainerGap(159, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap(14, Short.MAX_VALUE) .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTextFieldClasse, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jComboBoxMetodos, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(22, 22, 22) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButtonExecutar) .addComponent(jButtonCarregarMetodos)) .addContainerGap()) ); pack(); }// </editor-fold>//GEN-END:initComponents private void jButtonExecutarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonExecutarActionPerformed try { Object o = classLoaded.newInstance(); Method method = classLoaded.getMethod(jComboBoxMetodos.getSelectedItem().toString(), null); method.invoke(o, null); } catch (NoSuchMethodException e){ JOptionPane.showMessageDialog(this, "Método não encontrado"); } catch (IllegalArgumentException | SecurityException | InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } }//GEN-LAST:event_jButtonExecutarActionPerformed private void jButtonCarregarMetodosActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCarregarMetodosActionPerformed if (loadClass()){ jComboBoxMetodos.removeAll(); for(Method m : classLoaded.getDeclaredMethods()){ jComboBoxMetodos.addItem(m.getName()); } } }//GEN-LAST:event_jButtonCarregarMetodosActionPerformed private boolean loadClass(){ try { classLoaded = Class.forName(jTextFieldClasse.getText().trim()); return true; } catch (ClassNotFoundException e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, "Classe não encontrada"); return false; } } /** * @param args the command line arguments */ public static void main(String args[]) { /* Set the Nimbus look and feel */ //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(FReflection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (InstantiationException ex) { java.util.logging.Logger.getLogger(FReflection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { java.util.logging.Logger.getLogger(FReflection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(FReflection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } //</editor-fold> /* Create and display the form */ java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new FReflection().setVisible(true); } }); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButtonCarregarMetodos; private javax.swing.JButton jButtonExecutar; private javax.swing.JComboBox jComboBoxMetodos; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JTextField jTextFieldClasse; // End of variables declaration//GEN-END:variables }

Primeiramente temos o construtor da classe que faz chamada ao método initComponents(), responsável por inicializar os componentes gráficos, estes são gerados automaticamente pelo Netbeans caso você esteja usando esta IDE.

Perceba que a classe possui um atributo chamado “classLoaded”. Esse atributo armazenará a classe carregada para posteriormente mostrar seus métodos no formulário, assim poderemos escolher um dos métodos que deverão ser executados.

O primeiro passo é digitar o nome da classe, ex: “br.com.meuprojeto.MinhaClasse” e depois clicar em “Carregar Métodos”, que chamará a seguinte ação:

private void jButtonCarregarMetodosActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonCarregarMetodosActionPerformed if (loadClass()){ jComboBoxMetodos.removeAll(); for(Method m : classLoaded.getDeclaredMethods()){ jComboBoxMetodos.addItem(m.getName()); } } }//GEN-LAST:event_jButtonCarregarMetodosActionPerformed

Esse método primeiramente faz o carregamento da classe no atributo “classLoaded” que citamos anteriormente (mais a frente explicaremos o método loadClass()). Se tudo der certo então removemos os métodos, se existir algum, do jComboBoxMetodos e iremos adicionando método a método novamente.

O loadClass() usa o método Class.forName() para buscar a classe no classpath através do caminho passado, se ele não retornar uma exceção ClassNotFoundException então quer dizer que a classe foi encontrada.

private boolean loadClass(){ try { classLoaded = Class.forName(jTextFieldClasse.getText().trim()); return true; } catch (ClassNotFoundException e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, "Classe não encontrada"); return false; } }

Neste ponto os métodos já foram carregados no combobox e poderá ser escolhido algum para execução. Ao clicar no botão “Executar” o método selecionado será executado através de reflexão:

private void jButtonExecutarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonExecutarActionPerformed try { Object o = classLoaded.newInstance(); Method method = classLoaded.getMethod(jComboBoxMetodos.getSelectedItem().toString(), null); method.invoke(o, null); } catch (NoSuchMethodException e){ JOptionPane.showMessageDialog(this, "Método não encontrado"); } catch (IllegalArgumentException | SecurityException | InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } }//GEN-LAST:event_jButtonExecutarActionPerformed

Primeiramente é criada uma instância da classe carregada, pois só podemos executar métodos de objetos a não ser que o método seja estático, que não é nosso caso. Criado o objeto agora nós podemos carregar o Method através do getMethod() passando o nome do método escolhido no combobox:

Object o = classLoaded.newInstance(); Method method = classLoaded.getMethod(jComboBoxMetodos.getSelectedItem().toString(), null);

Por último, com o Method e o Object em memória, nós fazemos chamada o invoke() que irá executar aquele determinado método no objeto passado:

method.invoke(o, null);

Tratamos neste artigo da execução de métodos para carregar propriedades que estão em Cache usando Reflexão, mas este foi apenas um caso específico para utilização de Reflexão, você poderá aplicar em muitos outros. Não tratamos de métodos recebendo parâmetros, mas você poderá, partindo destes exemplos, criar a lógica necessária para recebimento de parâmetros.

Artigos relacionados