Por acaso hoje, de repente quis dar uma olhada no proxy dinâmico do JDK, porque já sabia um pouco sobre ele e só queria testar seu uso. Rapidamente escrevi estas interfaces e classes:
Classe de interface: UserService.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
interface pública UserService {
public int salvar();
atualização pública nula (int id);
}
Classe de implementação: UserServiceImpl.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
A classe pública UserServiceImpl implementa UserService {
@Substituir
public int salvar() {
System.out.println("salvar usuário....");
retornar 1;
}
@Substituir
atualização pública void(int id) {
System.out.println("atualizar um usuário " + id);
}
}
Aí escrevi ansiosamente o InvocationHandler que queria: a função disso é muito simples, é registrar o horário de início e término da execução do método.
TimeInvocationHandler.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
importar java.lang.reflect.InvocationHandler;
importar java.lang.reflect.Method;
A classe pública TimeInvocationHandler implementa InvocationHandler {
@Substituir
invocação de objeto público (proxy de objeto, método de método, objeto [] args)
lança Arremessável {
System.out.println("startTime: " +System.currentTimeMillis());
Objeto obj = método.invoke(proxy, args);
System.out.println("endTime: " +System.currentTimeMillis());
retornar objeto;
}
}
Depois de todos os preparativos, é claro que é hora de começar a escrever os testes!
Teste.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
importar java.lang.reflect.Proxy;
teste de classe pública {
public static void main(String[] args) { 9 TimeInvocationHandler timeHandler = new TimeInvocationHandler();
UserService você = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
u.update(2);
você.save();
}
}
Rodou alegremente, mas não deu nenhuma cara. O resultado foi uma exceção que preencheu a tela:
Copie o código do código da seguinte forma:
hora de início: 1352877835040
hora de início: 1352877835040
hora de início: 1352877835040
Exceção no thread "main" java.lang.reflect.UndeclaredThrowableException
em $Proxy0.update(fonte desconhecida)
em com.yixi.proxy.Test.main(Test.java:11)
Causado por: java.lang.reflect.InvocationTargetException
em sun.reflect.NativeMethodAccessorImpl.invoke0 (Método Nativo)
em sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
em sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
em java.lang.reflect.Method.invoke(Method.java:597)
em com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
... mais 2
A exceção com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) informa claramente o problema na linha 12 de TimeInvocationHandle: isto é
Copie o código do código da seguinte forma:
invocação de objeto público (proxy de objeto, método de método, objeto [] args)
lança Arremessável {
System.out.println("startTime: " +System.currentTimeMillis());
Objeto obj = método.invoke(proxy, args);
System.out.println("endTime: " +System.currentTimeMillis());
retornar objeto;
}
Não há nada de errado com o método! Como o método invoke() parece fornecer todos os parâmetros exigidos pelo método.invoke(Object, Object[]), nós o usaremos naturalmente. Se você realmente pensa assim, então você enganou o JDK. uma armadilha. Vejamos primeiro a maneira correta de escrever. Caso alguns alunos não estejam com vontade de ler o seguinte, pelo menos me dê a solução correta:
Modifique TimeInvocationHandler.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
importar java.lang.reflect.InvocationHandler;
importar java.lang.reflect.Method;
A classe pública TimeInvocationHandler implementa InvocationHandler {
Objeto privado o;
public TimeInvocationHandler(Object o){
isto.o = o;
}
@Substituir
invocação de objeto público (proxy de objeto, método de método, objeto [] args)
lança Arremessável {
System.out.println("startTime: " +System.currentTimeMillis());
Objeto obj = método.invoke(o, args);
System.out.println("endTime: " +System.currentTimeMillis());
retornar objeto;
}
}
Modificar Test.java
Copie o código do código da seguinte forma:
pacote com.yixi.proxy;
importar java.lang.reflect.Proxy;
teste de classe pública {
public static void main(String[] args) {
TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl());
UserService você = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
u.update(2);
você.save();
}
}
Agora aqui está a saída correta:
Copie o código do código da seguinte forma:
hora de início: 1352879531334
atualizar um usuário 2
hora final: 1352879531334
hora de início: 1352879531334
usuário salvo....
hora final: 1352879531335
Se quiser menos código, você pode escrever uma classe anônima diretamente:
pacote com.yixi.proxy;
importar java.lang.reflect.InvocationHandler;
importar java.lang.reflect.Method;
importar java.lang.reflect.Proxy;
teste de classe pública {
public static void main(String[] args) {
final UserServiceImpl usi = new UserServiceImpl();
UserService você = (UserService) Proxy.newProxyInstance(
usi.getClass().getClassLoader(),
usi.getClass().getInterfaces(),
novoInvocationHandler() {
@Substituir
invocação de objeto público (proxy de objeto, método de método, objeto [] args)
lança Arremessável {
System.out.println("startTime: " +System.currentTimeMillis());
Objeto obj = método.invoke(usi, args);
System.out.println("endTime: " +System.currentTimeMillis());
retornar objeto;
}
});
u.update(2);
você.save();
}
}
Como o primeiro parâmetro em method.invoke(target,args); é o objeto de destino, por que o método Invoke de invocationHandler precisa de um parâmetro de proxy Object? Vamos olhar para baixo!
Para o método de invocação mais importante (na minha opinião pessoal), vamos dar uma olhada no que o JDK diz:
Copie o código do código da seguinte forma:
invocar
Invocação de objeto (proxy de objeto,
Método método,
Objeto[] argumentos)
throws Throwable manipula chamadas de método na instância do proxy e retorna o resultado. Este método é chamado no manipulador de invocação quando o método é chamado na instância de proxy à qual está associado.
parâmetro:
proxy - a instância do proxy na qual o método é chamado
método - uma instância de método correspondente ao método de interface chamado na instância do proxy. A classe declarante de um objeto Method será a interface na qual o método é declarado, que pode ser uma superinterface da interface proxy da qual a classe proxy herda o método.
args - uma matriz de objetos contendo os valores dos argumentos passados para a chamada do método na instância do proxy ou nulo se o método da interface não aceitar argumentos. Os parâmetros de tipos básicos são agrupados em instâncias da classe wrapper básica apropriada (como java.lang.Integer ou java.lang.Boolean).
proxy - a instância do proxy na qual o método é chamado? O que esta frase significa? atuando? método é o método proxy? Então meu método para executar o proxy não deveria ser Object obj = method.invoke(proxy, args);? Não me virei naquela hora. Fui ao grupo de discussão e fui ao Google, mas não consegui encontrar nenhuma inspiração, achei melhor olhar o código-fonte e talvez pudesse ver alguma coisa!
Abra o código fonte da classe Proxy e descubra o que é um construtor:
Copie o código do código da seguinte forma:
protectedInvocationHandlerh;
Proxy protegido(InvocationHandler h) {
isto.h = h;
}
Use InvocationHandler como parâmetro do construtor do Proxy.... Então para que ele usa InvocationHandler? Existe alguma conexão com o método Invocation() em InvocationHandler?
Meu primeiro pensamento é que o Proxy chamará internamente a seguinte instrução:
Copie o código do código da seguinte forma:
h.invoke(este,nomedométodo,args);
Porque você precisa chamar o método invocar para executar o método correspondente.
Vamos dar uma olhada nisso primeiro
Aqui você encontrará algo que parece fazer sentido: quando u.update(2), àProxy chamará handler.invoke(proxyClass,update,2) à, o que significa proxyClass.update(2);
Quando u.save(); Proxy chamará handler.invoke(proxyClass,save,null) àisto é, proxyClass.save();
Quando Test.java é alterado para isto:
Copie o código do código da seguinte forma:
teste de classe pública {
public static void main(String[] args) {
final UserServiceImpl usi = new UserServiceImpl();
UserService você = (UserService) Proxy.newProxyInstance(
usi.getClass().getClassLoader(),
usi.getClass().getInterfaces(),
novoInvocationHandler() {
@Substituir
invocação de objeto público (proxy de objeto, método de método, objeto [] args)
lança Arremessável {
retornar nulo;
}
});
u.update(2);
você.save();
}
}
Observe que o método da classe anônima neste momento retorna nulo. Se você executá-lo, encontrará:
Copie o código do código da seguinte forma:
Exceção no thread "principal" java.lang.NullPointerException
em $Proxy0.save(fonte desconhecida)
em com.yixi.proxy.Test.main(Test.java:17)
Há um ponteiro nulo na linha 17, ou seja, o método u.save() aqui possui um elemento nulo. Será que u está vazio? Não deveria ser. Se u for nulo, então u.update(2) reportará uma exceção de ponteiro nulo quando eu comentar a linha 17, a exceção desaparece, indicando que u.update() pode ser executado normalmente. Então, por que isso acontece?
Na verdade, é por isso que o método invocar retorna nulo:
Preste atenção aos dois métodos da classe UserService:
Copie o código do código da seguinte forma:
interface pública UserService {
public int salvar();
atualização pública nula (int id);
}
O método Save() retorna um tipo int e o método update retorna um tipo void de acordo com a estimativa acima, handler.invoke() implementa proxyClass.update(2);, e o método de retorno no método de invocação é correspondente ao retorno; valor do método proxy,
Portanto, quando o método de invocação retorna nulo, o método de atualização do agente recebe um valor de retorno nulo e retorna originalmente nulo, portanto, nenhuma exceção é relatada e o salvamento do agente deve retornar um valor do tipo int. O que retornamos aqui ainda é nulo, e a JVM não pode converter nulo. Ele é convertido para o tipo int, portanto, uma exceção é relatada com clareza e também pode provar relativamente a suposição anterior.
O primeiro parâmetro proxy no método de invocação de InvocationHandler parece ser apenas para permitir que a classe Proxy passe a referência do objeto proxy proxyClass ao chamar o método com a referência de seu próprio objeto InvocationHandler para concluir o negócio que proxyClass precisa concluir .
Talento literário não é bom! A capacidade é limitada! Espero que vocês possam me corrigir...