A Reflection API é um poderoso recurso da JVM que torna possível invocação de métodos de objetos em memória através de forma programática.
Quando bem utilizado, esse recurso pode executar tarefas extremamente arrojadas.
Requisito:
Represar todos os métodos DAOs em uma sessão web, e executá-los todos de uma vez dentro de uma transação ao final da sessão
JVM 5 + Spring + Jetty 1.6
Solução:
Interceptando todas as chamadas dos métodos DAOs, armazeno uma sequência de invocações em uma Collection em um Statefull Session Bean, de forma que fiquem represados em memória.
StatefullBeanInterface:
addCallStack(Object target, String methodName, Object... args);ExampleDAO:
@Transactional(propagation = Propagation.MANDATORY)addUser(User user) { /* real persistence operation */}addUser(StatefullBean s, User user) { /* interceptor operation */ s.addCallStack(this, "addUser", user);}Ao final da sessão, o usuário confirma a execução dos métodos. Agora entra em ação a Reflection.
@Transactional(propagation = Propagation.REQUIRES_NEW)public void execCallStack()throws CallStackException { for (Object o : callStack.keySet()) { List call = callStack.get(o); for (MethodCall method : call) { Object[] args = method.getArgs(); Method[] allMethods = o.getClass().getDeclaredMethods(); for (Method m : allMethods) { if (m.getName().equals(method.getMethodName()) && m.getGenericParameterTypes().length == args.length) { try { m.invoke(o, args); break; } catch (Exception e) { throw new CallStackException(e); } } } } }}Passo a passo:
@Transactional(propagation = Propagation.REQUIRES_NEW)public void execCallStack()throws CallStackException {Solicitamos ao container que crie uma nova transação ao iniciar a execução dos métodos DAO represados.
Os métodos são anotados com propagação de transação mandatória (Propagation.MANDATORY). Dessa forma, se forem invocados foram de uma transação, uma exceção será gerada.
for (Object o : callStack.keySet()) { List call = callStack.get(o); for (MethodCall method : call) { Object[] args = method.getArgs();Estamos percorrendo a Collection com os objetos alvo da invocação, o nome dos métodos e seus respectivos argumentos.
Method[] allMethods = o.getClass().getDeclaredMethods();for (Method m : allMethods) {Aqui começa a reflection. getDeclaredMethods retorna todos os métodos de uma classe. A classe Method encapsula suas declarações, como argumentos, exceções, encapsulamento, entre outros.
if (m.getName().equals(method.getMethodName()) && m.getGenericParameterTypes().length == args.length) {Procuramos o método correspondente através do nome e a quantidade de argumentos, para minimizar eventuais problemas com sobrecarga.
Checagens adicionais podem ser feitas, porém no caso acima não houve necessidade.
m.invoke(o, args);Finalmente o método invoke executa o código. O primeiro argumento é uma instância Object que implemente a Classe alvo, e o segundo argumento é um vetor com os métodos.
A CallStackException é uma exceção Runtime criada especificamente para essa situação, para efetuar o rollback das operações em banco em caso de algum método falhar.
Mais informações:
http://java.sun.com/docs/books/tutorial/reflect/http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html