Что такое классы динамического прокси и зачем мне их использовать? - PullRequest
67 голосов
/ 01 июня 2009

Какой вариант использования динамического прокси?

Как они связаны с генерацией и отражением байт-кода?

Любое рекомендуемое чтение?

Ответы [ 5 ]

28 голосов
/ 01 июня 2009

A динамический прокси-класс - это класс, который реализует список интерфейсы, указанные во время выполнения, так что вызов метода через один из интерфейсов экземпляра класса будет закодирован и отправляется на другой объект через единый интерфейс. Может быть используется для создания типового прокси-объекта для списка интерфейсов не требуя предварительной генерации прокси-класса. Динамический прокси классы полезны для приложения или библиотеки, которые должны обеспечить безопасная по типу отражательная отправка вызовов на объектах, которые представляют API интерфейсов.

Динамические прокси-классы

23 голосов
/ 26 января 2014

Я настоятельно рекомендую этот ресурс .

Прежде всего, вы должны понять, какой вариант использования прокси-шаблона. Помните, что основной целью прокси является контроль доступа к целевой объект, а не для повышения функциональности целевой объект Контроль доступа включает в себя синхронизацию, аутентификацию, удаленный доступ (RPC), ленивую реализацию (Hibernate, Mybatis), AOP (транзакция).

В отличие от статического прокси, динамический прокси генерирует байт-код, который требует отражения Java во время выполнения. При динамическом подходе вам не нужно создавать прокси-класс, что может привести к большему удобству.

16 голосов
/ 18 ноября 2009

Я только что предложил интересное использование динамического прокси.

У нас возникли некоторые проблемы с некритичной службой, которая связана с другой зависимой службой, и мы хотели найти способы обеспечения отказоустойчивости, когда эта зависимая служба становится недоступной.

Итак, я написал LoadSheddingProxy , который принимает два делегата - один является удаленным имплементом для «нормальной» службы (после поиска JNDI). Другим объектом является «фиктивный» сброс нагрузки. Существует простая логика, связанная с каждым вызовом метода, который улавливает тайм-ауты и перенаправляет их на манекен на определенное время перед повторной попыткой. Вот как я это использую:

// This is part of your ServiceLocator class
public static MyServiceInterface getMyService() throws Exception
{
    MyServiceInterface loadShedder = new MyServiceInterface() {
        public Thingy[] getThingys(Stuff[] whatever) throws Exception {
            return new Thingy[0];
        }
        //... etc - basically a dummy version of your service goes here
    }           
    Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER);
    try {
        MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
                ctx.lookup(MyServiceHome.JNDI_NAME), 
                MyServiceHome.class)).create();
        // Here's where the proxy comes in
        return (MyService) Proxy.newProxyInstance(
            MyServiceHome.class.getClassLoader(),
        new Class[] { MyServiceInterface.class },
        new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000));  // 10 minute retry
    } catch (RemoteException e) {    // If we can't even look up the service we can fail by shedding load too
        logger.warn("Shedding load");
        return loadShedder;
    } finally {
        if (ctx != null) {
        ctx.close();
        }
    }
}

А вот и прокси:

public class LoadSheddingProxy implements InvocationHandler {

static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class);

Object primaryImpl, loadDumpingImpl;
long retry;
String serviceName;
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>();

public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry)
{
    this.serviceName = serviceName;
    this.primaryImpl = primaryImpl;
    this.loadDumpingImpl = loadDumpingImpl;
    this.retry = retry;
}

public Object invoke(Object obj, Method m, Object[] args) throws Throwable
{
    try
    {
        if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) {
            Object ret = m.invoke(primaryImpl, args);
            servicesLastTimedOut.remove(serviceName);
            return ret;
        } 
        return m.invoke(loadDumpingImpl, args);
    }
    catch (InvocationTargetException e)
    {
        Throwable targetException = e.getTargetException();

        // DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it???
        if (targetException instanceof RemoteException) {
            servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis()));
        }
        throw targetException;
    }                    
}

private boolean timeToRetry() {
    long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue();
    return (System.currentTimeMillis() - lastFailedAt) > retry;
}
}
8 голосов
/ 01 июня 2009

Класс java.lang.reflect.Proxy позволяет динамически реализовывать интерфейсы, обрабатывая вызовы методов в InvocationHandler. Он считается частью средства отражения Java, но не имеет ничего общего с генерацией байт-кода.

У Sun есть учебник по использованию класса Proxy. Google тоже помогает.

5 голосов
/ 01 июня 2009

Один из вариантов использования - Hibernate - он предоставляет вам объекты, реализующие интерфейс ваших классов моделей, но в методах получения и установки находится код, связанный с БД. То есть вы используете их, как будто они просто POJO, но на самом деле под прикрытием происходит много всего.

Например, вы просто вызываете метод получения лениво загруженного свойства, но на самом деле свойство (возможно, структура большого объекта) извлекается из базы данных.

Вы должны проверить библиотеку cglib для получения дополнительной информации.

...