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

StackOverflow https://stackoverflow.com/questions/933993

Вопрос

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

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

Есть что-нибудь, что рекомендуется почитать?

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

// 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, но не имеет никакого отношения к генерации байт-кода.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top