Что такое динамические прокси-классы и почему я должен их использовать?
Каков пример использования динамического прокси-сервера?
Как они связаны с генерацией и отражением байт-кода?
Какие-нибудь рекомендуемые чтения?
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 библиотеки информация.