Strona główna > Java > Dynamic proxy

Dynamic proxy

Trafiłem ostatnio na nowy sposób tworzenie dynamicznego proxy. Przezwycięża on braki standardowego (znanego mi dotychczas) rozwiązania. Pozwala na tworzenie proxy na podstawie konkretnych klas, a nie tylko interfejsów.

Do tego celu jest używana biblioteka cglib. Kręciła się ona od dawna w mym otoczeniu (choćby Spring) i z grubsza wiedziałem do czego ona służy (coś z generacją kodu). Jednak jakoś nie miałem okazji zagłębić się w nią. Ostatni trafiłem na nią okrężną drogą, poprzez mockito.
Miałem potrzebę napisania narzędzia do tworzenia proxy. Chciałem stworzyć to za pomocą dotychczas znanego mi mechanizmu (wiedząc o jego ograniczeniach) i tu zaistniało olśniewające skojarzenie faktów😉. Po co mam godzić się na te ograniczenia, skoro wydaje się że jest masa kodu, gdzie nie jest to problemem. Pewnie wcześneij gdy pojawiała się tak myśl to próbowałem grzebać w dużych projektach, typu Spring, Hibernate czy JBoss. Gdzie po pewnym czasie grzebania w źródłach dawałem sobie spokój. Teraz jednak może dzięki uporowi, nabytemu już doświadczeniu, czy właśnie prostocie przykładu dotarłem do wiedzy🙂.
Przebijając się przez kod mockito dotarłem do klasy Enhancer, która to początek swój ma w cglib (wydaje się być żywcem skopiowana z tego projektu ;)).

Tak więc wyłuskałem ją do swych potrzeb i mogłem rozszerzyć swój kod o tworzenie proxy dla klas (bez potrzeby tworzenia dla nich interfejsów).

Przykład jak to wygląda w kodzie poniżej. Dla potrzeb testów wystarczy mieć mockito, aby były wszystkie potrzebne biblioteki. Kod jest zmodyfikowaną wersją przykładu, który pokazuje jak można budować proxy w standardowy sposób, jak i z użyciem cglib.

public class ProxyTest {

	public static void main(String[] args) {
		System.out.println("\n--- orginał ---");
		ArrayList<String> orginal = new ArrayList<String>();
		orginal.add("tekst");
		System.out.println("value: " + orginal);
		System.out.println("size: " + orginal.size());

		System.out.println("\n--- nowy sposób, konkretne klasy ---");
		
		ArrayList<String> kopia = newInstance(orginal);

		System.out.println("value: " + kopia);
		System.out.println("size: " + kopia.size());
		
		System.out.println("\n--- stary sposób, tylko interfejsy ---");
		List<String> kopia2 = (List<String>)DebugProxy.newInstance(orginal);

		System.out.println("value: " + kopia2);
		System.out.println("size: " + kopia2.size());
	}

	public static <T> T newInstance(T obj) {
		InvocationHandler handler = new DebugProxy(obj);
		Enhancer e = new Enhancer();
		e.setSuperclass(obj.getClass());
		e.setCallback(new HandlerAdapter(handler));
		e.setInterfaces(obj.getClass().getInterfaces());
		Object wrapedObject = e.create();
		return (T)wrapedObject;
	}

	private static class HandlerAdapter implements MethodInterceptor {
		private InvocationHandler handler;

		public HandlerAdapter(InvocationHandler handler) {
			this.handler = handler;
		}

		public Object aroundAdvice(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			return handler.invoke(obj, method, args);
		}

		@Override
		public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
			return handler.invoke(arg0, arg1, arg2);
		}
	}

	private static class DebugProxy implements java.lang.reflect.InvocationHandler {

		private Object obj;

		public static Object newInstance(Object obj) {
			return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass()
					.getInterfaces(), new DebugProxy(obj));
		}

		private DebugProxy(Object obj) {
			this.obj = obj;
		}

		public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
			Object result;
			try {
				System.out.println("before method " + m.getName());
				result = m.invoke(obj, args);
			} catch (InvocationTargetException e) {
				throw e.getTargetException();
			} catch (Exception e) {
				throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
			} finally {
				System.out.println("after method " + m.getName());
			}
			return result;
		}
	}

}

Drobna uwaga: są pewne ograniczenia, nie można tego stosować do finalnych klas np. Strigów.

Kategorie:Java Tags:
  1. Brak komentarzy.
  1. No trackbacks yet.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d bloggers like this: