Implementação de Proxy Dinâmico em Java com Reflexão

Um proxy dinâmico em Java permite interceptar chamadas a métodos de um objeto real, adicionando comportamentos adicionais como log, autorização ou medição de tempo sem modificar a implementação original. Isso é feito com base na API de reflexão e na enterface java.lang.reflect.InvocationHandler.

A seguir, apresentamos uma implementação personalizada de proxy dinâmico que permite configurar ações antes e após a execução dos métodos do objeto alvo, além de mensagens customizadas e controle sobre o retorno do proxy.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

public class CustomDynamicProxy implements InvocationHandler {

   private Object target;
   private String[] preMessages;
   private String[] postMessages;
   private Set<MethodCall> preActions = new HashSet<>();
   private Set<MethodCall> postActions = new HashSet<>();
   private Set<String> methodsReturningProxy = new HashSet<>();

   public CustomDynamicProxy(Object target) {
       this.target = target;
   }

   public Object getProxy() {
       return Proxy.newProxyInstance(
           target.getClass().getClassLoader(),
           target.getClass().getInterfaces(),
           this
       );
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       executePreMessages();
       executePreActions();

       Object result = method.invoke(target, args);

       executePostMessages();
       executePostActions();

       return shouldReturnProxy(method.getName()) ? proxy : result;
   }

   private void executePreMessages() {
       if (preMessages != null) {
           for (String msg : preMessages) {
               if (msg != null && !msg.trim().isEmpty()) {
                   System.out.println(msg);
               }
           }
       }
   }

   private void executePostMessages() {
       if (postMessages != null) {
           for (String msg : postMessages) {
               if (msg != null && !msg.trim().isEmpty()) {
                   System.out.println(msg);
               }
           }
       }
   }

   private void executePreActions() {
       performActions(preActions);
   }

   private void executePostActions() {
       performActions(postActions);
   }

   private void performActions(Set<MethodCall> actions) {
       if (actions == null || actions.isEmpty()) return;

       for (MethodCall call : actions) {
           try {
               call.getMethod().invoke(call.getInstance(), call.getArgs());
           } catch (Exception e) {
               System.err.println("Erro ao executar ação: " + e.getCause().getMessage());
               e.printStackTrace();
           }
       }
   }

   public void setPreMessages(String... messages) {
       this.preMessages = messages;
   }

   public void setPostMessages(String... messages) {
       this.postMessages = messages;
   }

   public void addPreAction(Method method, Object instance, Object... args) {
       preActions.add(new MethodCall(method, instance, args));
   }

   public void addPostAction(Method method, Object instance, Object... args) {
       postActions.add(new MethodCall(method, instance, args));
   }

   public void markMethodToReturnProxy(String methodName) {
       methodsReturningProxy.add(methodName);
   }

   private boolean shouldReturnProxy(String methodName) {
       return methodsReturningProxy.contains(methodName);
   }

   private static class MethodCall {
       private final Method method;
       private final Object instance;
       private final Object[] args;

       MethodCall(Method method, Object instance, Object... args) {
           this.method = method;
           this.instance = instance;
           this.args = args;
       }

       Method getMethod() { return method; }
       Object getInstance() { return instance; }
       Object[] getArgs() { return args; }
   }
}

Para demonstrar o uso, definimos uma interface simples Person e duas implementações:

public interface Person {
   void name();
}
public class XiaoMing implements Person {
   @Override
   public void name() {
       System.out.println("===== Eu sou o XiaoMing =====");
   }
}
public class XiaoHong implements Person {
   @Override
   public void name() {
       System.out.println("===== Eu sou a XiaoHong =====");
   }
}

Classe de teste para validar o comportamento do proxy:

public class ProxyTest {
   public static void main(String[] args) {
       try {
           XiaoMing xiaoMing = new XiaoMing();
           XiaoHong xiaoHong = new XiaoHong();

           CustomDynamicProxy proxyHandler = new CustomDynamicProxy(xiaoMing);
           Person proxy = (Person) proxyHandler.getProxy();

           // Configura ações antes e depois usando o método 'name' de XiaoHong
           proxyHandler.addPreAction(
               xiaoHong.getClass().getMethod("name"), 
               xiaoHong
           );
           proxyHandler.addPostAction(
               xiaoHong.getClass().getMethod("name"), 
               xiaoHong
           );

           // Executa o método através do proxy
           proxy.name();

       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

Saída esperada ao executar ProxyTest:

===== Eu sou a XiaoHong =====
===== Eu sou o XiaoMing =====
===== Eu sou a XiaoHong =====

Este exemplo mostra como o proxy dinâmico pode orquestrar chamadas a métodos reais com lógica adicional ao redor, utilizando reflexão para invocar métodos em tempo de execução. O mecanismo opera exclusivamente nos pontos antes e depois da execução do método — não é possível inserir lógica no interior do método original sem bytecode manipulation.

Tags: java Proxy Dinâmico Reflexão InvocationHandler Programação Orientada a Aspectos

Publicado em 10-15 02:38