Что такое динамические прокси-классы и почему я должен их использовать?


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

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

Какие-нибудь рекомендуемые чтения?

5 62

5 ответов:

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

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

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

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

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

Я только что придумал интересное применение для динамического прокси.

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

Поэтому я написал LoadSheddingProxy , который принимает два делегата - один является удаленным impl для "нормальной" службы (после поиска 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;
}
}

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

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

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

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

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