Alternativas para java.lang.reflect.Proxy para criar proxies de classes abstratas (em vez de interfaces)

90

De acordo com a documentação :

[ java.lang.reflect.] Proxyfornece métodos estáticos para a criação de classes e instâncias de proxy dinâmico, e também é a superclasse de todas as classes de proxy dinâmico criadas por esses métodos.

O newProxyMethodmétodo (responsável por gerar os proxies dinâmicos) possui a seguinte assinatura:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

Infelizmente, isso impede que se gere um proxy dinâmico que estenda uma classe abstrata específica (em vez de implementar interfaces específicas). Isso faz sentido, considerando que java.lang.reflect.Proxyé "a superclasse de todos os proxies dinâmicos", evitando assim que outra classe seja a superclasse.

Portanto, há alguma alternativa para java.lang.reflect.Proxyque possa gerar proxies dinâmicos que herdam de uma classe abstrata específica, redirecionando todas as chamadas aos métodos abstratos para o manipulador de invocação?

Por exemplo, suponha que eu tenha uma classe abstrata Dog:

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

Existe uma classe que me permite fazer o seguinte?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler
Adam Paynter
fonte

Respostas:

123

Isso pode ser feito usando Javassist (consulte RecursosProxyFactory ) ou CGLIB .

Exemplo de Adam usando Javassist:

Eu (Adam Paynter) escrevi este código usando Javassist:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

O que produz esta saída:

Uau!
Manipulação de void mock.Dog.fetch () abstrato público por meio do manipulador de método
axtavt
fonte
10
+1: Exatamente o que eu preciso! Vou editar sua resposta com meu código de amostra.
Adam Paynter de
proxyFactory.setHandler()está obsoleto. Por favor, use proxy.setHandler.
AlikElzin-kilaka de
@axtavt é o objeto "Dog" uma implementação ou uma interface no código acima?
stackoverflow
-7

O que você pode fazer nesse caso é ter um manipulador de proxy que redirecionará chamadas para métodos existentes de sua classe abstrata.

É claro que você terá que codificá-lo, mas é bastante simples. Para criar seu proxy, você terá que dar a ele um InvocationHandler. Você só terá que verificar o tipo de método no invoke(..)método do seu manipulador de invocação. Mas cuidado: você terá que verificar o tipo de método em relação ao objeto subjacente associado ao seu manipulador, e não em relação ao tipo declarado de sua classe abstrata.

Se eu tomar como exemplo sua classe dog, o método invoke do manipulador de invocação pode ser parecido com este (com uma subclasse dog associada existente chamada ... bem ... dog)

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

No entanto, há algo que me mantém pensando: já falei sobre um dogobjeto. Mas, como a classe Dog é abstrata, você não pode criar instâncias, portanto, existem subclasses. Além disso, como uma inspeção rigorosa do código-fonte do Proxy revela, você pode descobrir (em Proxy.java:362) que não é possível criar um Proxy para um objeto Classe que não represente uma interface).

Portanto, fora da realidade , o que você quer fazer é perfeitamente possível.

Riduidel
fonte
1
Tenha paciência enquanto tento entender sua resposta ... No meu caso específico, quero que a classe proxy (gerada em tempo de execução) seja a subclasse de Dog(por exemplo, não estou escrevendo explicitamente uma Poodleclasse que implementa fetch()). Portanto, não há nenhuma dogvariável para invocar os métodos ... Desculpe se estou confuso, terei que pensar um pouco mais.
Adam Paynter de
1
@Adam - você não pode criar subclasses dinamicamente em tempo de execução sem alguma manipulação de bytecode (acho que CGLib faz algo assim). A resposta curta é que os proxies dinâmicos oferecem suporte a interfaces, mas não a classes abstratas, porque os dois são conceitos muito diferentes. É quase impossível pensar em uma maneira de criar proxy dinamicamente para classes abstratas de maneira sã.
Andrzej Doyle
1
@Andrzej: Eu entendo que o que estou pedindo requer manipulação de bytecode (na verdade, eu já escrevi uma solução para meu problema usando ASM). Eu também entendo que os proxies dinâmicos de Java suportam apenas interfaces. Talvez minha pergunta não tenha sido totalmente clara - estou perguntando se há alguma outra classe (isto é, algo diferente de java.lang.reflect.Proxy) disponível que faz o que eu preciso.
Adam Paynter de
2
Bem, para encurtar as coisas longas ... não (pelo menos nas classes Java padrão). Usando a manipulação de bytecode, o céu é o limite!
Riduidel
9
Eu votei contra porque não é realmente uma resposta para a pergunta. OP declarou que deseja usar como proxy uma classe, não uma interface, e está ciente de que isso não é possível com java.lang.reflect.Proxy. Você simplesmente repete esse fato e não oferece outra solução.
jcsahnwaldt Reintegrar Monica em