Soyut sınıfların proxy'lerini oluşturmak için java.lang.reflect.Proxy'ye alternatifler (arayüzler yerine)


90

Belgelere göre :

[ java.lang.reflect.] Proxy, dinamik proxy sınıfları ve örnekleri oluşturmak için statik yöntemler sağlar ve ayrıca bu yöntemler tarafından oluşturulan tüm dinamik proxy sınıflarının üst sınıfıdır.

newProxyMethodYöntem (dinamik vekiller üretilmesinden sorumlu) aşağıdaki imzası vardır:

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

Ne yazık ki, dinamik bir proxy üreten bu önler bir uzanan belirli soyut sınıf (yerine uygulanması belirli arabirimleri . Bu mantıklıdır, java.lang.reflect.Proxy"tüm dinamik vekillerin üst sınıfı" olduğu düşünülürse , başka bir sınıfın süper sınıf olmasını engeller.

Bu nedenle, belirli bir soyut sınıftan devralan ve tüm çağrıları soyuta yönlendiren java.lang.reflect.Proxydinamik proxy'ler oluşturabilen herhangi bir alternatif var mı? yöntemlere çağrı işleyicisine mı?

Örneğin, soyut bir sınıfım olduğunu varsayalım Dog:

public abstract class Dog {

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

    public abstract void fetch();

}

Aşağıdakileri yapmama izin veren bir sınıf var mı?

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

Yanıtlar:


123

Javassist (bkz. ProxyFactory) Veya CGLIB kullanılarak yapılabilir .

Adam'ın Javassist kullanan örneği:

Ben (Adam Paynter) Javassist'i kullanarak bu kodu yazdım:

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();

Bu çıktıyı üreten:

Hav!
Yöntem işleyicisi aracılığıyla genel soyut void mock.Dog.fetch () işleme

10
+1: Tam olarak ihtiyacım olan şey! Cevabınızı örnek kodumla düzenleyeceğim.
Adam Paynter

proxyFactory.setHandler()kullanımdan kaldırıldı. Lütfen kullanın proxy.setHandler.
AlikElzin-kilaka

@axtavt "Dog" nesnesi yukarıdaki kodda bir uygulama mı yoksa bir arayüz mü?
stackoverflow

-7

Böyle bir durumda yapabileceğiniz şey, çağrıları soyut sınıfınızın mevcut yöntemlerine yönlendirecek bir proxy işleyicisine sahip olmaktır.

Elbette kodlamanız gerekecek, ancak oldukça basit. Proxy'nizi oluşturmak için ona bir InvocationHandler. Ardından, yalnızca invoke(..)çağrı işleyicinizin yöntemindeki yöntem türünü kontrol etmeniz gerekir. Ancak dikkatli olun: yöntem türünü işleyicinizle ilişkili temel nesneye göre kontrol etmeniz gerekir, soyut sınıfınızın bildirilen türüne göre değil.

Ben örnek olarak köpek sınıfını verseydin, çağırma eylemcinin çağırmak yöntemi olabilir şuna benzer (denilen varolan ilişkili köpek alt sınıf ile .. iyi ... 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 ?
    }
}

Ancak beni meraklandıran bir şey var: Bir dognesneden bahsetmiştim . Ancak, Dog sınıfı soyut olduğundan örnekler oluşturamazsınız, bu nedenle mevcut alt sınıflarınız vardır. Ayrıca, Proxy kaynak kodunun titiz bir incelemesinin ortaya çıkardığı gibi, (Proxy.java:362 adresinde) bir arabirimi temsil etmeyen bir Sınıf nesnesi için bir Proxy oluşturmanın mümkün olmadığını keşfedebilirsiniz).

Yani, gerçekliğin dışında, yapmak istediğiniz şey tamamen mümkün.


1
Cevabınızı anlamaya çalışırken lütfen yanımda olun ... Benim özel durumumda, proxy sınıfının (çalışma zamanında oluşturulan) alt sınıfı olmasını istiyorum Dog(örneğin, Poodleuygulayan bir sınıfı açıkça yazmıyorum fetch()). Bu nedenle, dogyöntemleri çağırmak için bir değişken yok ... Üzgünüm, kafa karıştırıyorsam, bunu biraz daha düşünmem gerekecek.
Adam Paynter

1
@Adam - bazı bayt kodu manipülasyonu olmadan çalışma zamanında dinamik olarak alt sınıflar oluşturamazsınız (CGLib'in böyle bir şey yaptığını düşünüyorum). Kısa cevap, dinamik proxy'lerin arayüzleri desteklemesidir, ancak soyut sınıfları desteklememektedir, çünkü ikisi çok farklı kavramlardır. Soyut sınıfları aklı başında bir şekilde dinamik olarak vekalet etmenin bir yolunu düşünmek neredeyse imkansız.
Andrzej Doyle

1
@Andrzej: İstediğim şeyin bayt kodu manipülasyonu gerektirdiğini anlıyorum (aslında, ASM kullanarak sorunuma bir çözüm yazdım). Ayrıca Java'nın dinamik proxy'lerinin yalnızca arayüzleri desteklediğini anlıyorum. Belki de sorum tam olarak net değildi - ihtiyacım olanı yapan başka bir sınıf (yani, başka bir şey java.lang.reflect.Proxy) olup olmadığını soruyorum .
Adam Paynter

2
Uzun şeyleri kısaltmak için ... hayır (en azından standart Java sınıflarında). Bayt kodu manipülasyonunu kullanarak gökyüzü sınırdır!
Riduidel

9
Olumsuz oy verdim çünkü bu aslında sorunun cevabı değil. OP, bir arayüz yerine bir sınıf için proxy yapmak istediğini ve bunun java.lang.reflect.Proxy ile mümkün olmadığının farkında olduğunu belirtti. Sadece bu gerçeği tekrar edersiniz ve başka bir çözüm sunmazsınız.
jcsahnwaldt Monica'yı eski haline getir
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.