Self Proxy Injection Anti-Pattern

Keywords: #Java #Spring

Because of how Spring’s implementation of AOP works (i.e. proxies), internal calls to a method in a proxied bean cannot be intercepted. This is a limitation of any AOP implementation using dynamic proxies. A way I’ve seen used to bypass this limitation is having an object with a dependency on itself, so that, for instance, internal calls to methods can also be cached when using a cache proxy1. Something like the following:

public class ServiceImpl implements Service, ApplicationContextAware {
	private Service cachedService;
	private ApplicationContext applicationContext;

	private Service getCachedService() {
		if (cachedService == null) {
			cachedService = applicationContext.getBean("Service", Service.class);
		}
		return cachedService;
	}

	public Collection<?> aCachedMethod() {
		/* some expensiven computation of a collection */
	}


	public String getSomething() {
		Collection<?> collection = getCachedService().aCachedMethod();
		return /* some computation with the collection */;
	}

}

Though this might seem ingenious to some, this is essentially a cyclic dependency. Problems with the previous snippet of code are:

  1. It uses the Service Locator anti-pattern, manually getting the proxied Service from the DI container, instead of getting it injected as a dependency.
  2. Whoever maintains the code needs to know and remember to use getCachedService() instead of using cachedService directly, which would bypass the cache and could even result in a NullPointerException.

We could tackle (1), and partially (2) by using property injection:

public class ServiceImpl implements Service {
	private Service cachedService;

	public void setCachedService(Service cachedService) {
		this.cachedService = cachedService;
	}

	public String getSomething() {
		Collection<?> collection = getCachedService().aCachedMethod();
		return /* some computation with the collection */;
	}
}

However, the problem that you need to know about cachedService and remember to use it remains. Also, we cannot make the field final and get it injected with constructor injection.

The solution is the same as for any cyclic dependency: split the class. Those methods that we want to call and be sure are being cached should be in another class. Then all problems go away:

public class ServiceImpl implements Service {
	private final AnotherService anotherService;

	public ServiceImpl(final AnotherService anotherService) {
		this.anotherService = anotherService;
	}

	public String getSomething() {
		Collection<?> collection = anotherService.aCachedMethod();
		return /* some computation with the collection */;
	}
}

Now the only way to call the method needed is through anotherService, so it’s not possible to miss using it and bypass the caching.


  1. Also suggested for instance here. ↩︎