Criação de Objetos Dinâmicos com Proxy em Java
Em Java, Proxy é um mecanismo que permite criar objetos que se comportam como instâncias de interfaces, mas cujas chamadas de métodos podem ser interceptadas e processadas de forma personalizada.
A funcionalidade é fornecida pela classe java.lang.reflect.Proxy
.
Proxies não são comuns no desenvolvimento do dia a dia. Eles são usados, em geral, em cenários de infraestrutura, como sistemas de plugins, roteamento de chamadas para servidores remotos, ligação entre eventos e ações ou depuração de chamadas de métodos.
Um proxy criado em tempo de execução implementa um ou mais interfaces, redirecionando as chamadas de seus métodos para um handler.
Proxy e InvocationHandler
Um proxy de interface em Java não define o comportamento dos métodos em tempo de execução.
Em vez disso, a lógica é delegada a um objeto que implementa a interface InvocationHandler
.
Essa interface possui um único método:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
Parâmetros do método:
proxy
: o próprio objeto proxy em que o método foi chamado.method
: o objetoMethod
que representa o método da interface chamado.args
: array de argumentos passados para o método ounull
se não houver argumentos. Argumentos de tipos primitivos são convertidos para seus wrappers (por exemplo,int
emInteger
,boolean
emBoolean
).
O valor retornado pelo método invoke()
será o valor devolvido ao chamador do método do proxy.
Sempre que um método do proxy é chamado, o método invoke()
do handler é executado, recebendo o Method
correspondente e os argumentos da chamada.
Exemplo: Criando um Handler para Depuração
O exemplo a seguir mostra um handler que registra no console as chamadas dos métodos e depois as repassa para o objeto real:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class DebugHandler implements InvocationHandler {
private Object target;
DebugHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
// Exibe o nome do objeto alvo
System.out.print(target);
// Exibe o nome do método
System.out.print("." + m.getName() + "(");
// Exibe os argumentos
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (i != args.length - 1) System.out.print(", ");
System.out.print(args[i]);
}
}
System.out.println(")");
// Executa o método no objeto alvo
return m.invoke(target, args);
}
}
Criando um Proxy
A criação de um proxy é feita com o método estático Proxy.newProxyInstance()
:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Parâmetros:
loader
: o carregador de classes a ser usado. Geralmente utiliza-se o carregador do sistema, obtido comClassLoader.getSystemClassLoader()
.interfaces
: array de interfaces que o proxy implementará.h
: instância deInvocationHandler
que processará as chamadas aos métodos.
Exemplo Completo
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class Program {
public static void main(String[] args) {
Object start = new Point(10, 15);
var handler = new DebugHandler(start);
Movable proxy = (Movable) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[] { Movable.class },
handler
);
proxy.move(25);
proxy.move(15);
proxy.move(10);
}
}
class Point implements Movable {
private int x;
private int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
public void move(int distance) {
System.out.printf("Movendo o ponto (%d, %d) em %d unidades%n%n", x, y, distance);
x += distance;
y += distance;
}
}
interface Movable {
void move(int distance);
}
class DebugHandler implements InvocationHandler {
private Object target;
public DebugHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
System.out.print(target);
System.out.print("." + m.getName() + "(");
if (args != null) {
for (int i = 0; i < args.length; i++) {
if (i != args.length - 1) System.out.print(", ");
System.out.print(args[i]);
}
}
System.out.println(")");
return m.invoke(target, args);
}
}
Explicação do Exemplo
- A interface
Movable
define um único métodomove()
. - A classe
Point
implementa essa interface e atualiza suas coordenadas no métodomove()
. - O handler
DebugHandler
intercepta as chamadas e exibe informações sobre os métodos chamados. - O proxy é criado com
Proxy.newProxyInstance()
, implementando a interfaceMovable
. - As chamadas a
proxy.move()
são interceptadas pelo handler, que as repassa ao objeto real (Point
).
Saída do Programa
(10, 15).move(25) Movendo o ponto (10, 15) em 25 unidades (35, 40).move(15) Movendo o ponto (35, 40) em 15 unidades (50, 55).move(10) Movendo o ponto (50, 55) em 10 unidades
Resumo
- A classe
Proxy
permite criar objetos dinâmicos que implementam interfaces em tempo de execução. - A lógica dos métodos é delegada a um objeto que implementa
InvocationHandler
. - O método
invoke()
intercepta as chamadas e pode adicionar comportamentos antes ou depois de repassar a execução ao objeto real. - Essa técnica é útil para logging, depuração, integração remota e criação de plugins.