I\'m working on a semi-large application using Spring 3 and am running into performance problems when throwing hundreds of users at it at once. I\'m using several request scope
As it turns out, Spring actually does cache the request scoped beans, in the request attributes. If you're curious, take a look at AbstractRequestAttributesScope, which RequestScope extends:
public Object get(String name, ObjectFactory objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
}
return scopedObject;
}
So while AbstractBeanFactory.getBean() does get called on every bean method call because of the aop proxy, it only causes Spring to add to that synchronized set if the bean wasn't already found in the request attributes.
Avoiding the proxying of every method call on my request scoped beans would still reduce complexity but with this caching in place, the performance impact would be minimal. I think the slow performance is something I'm going to have to live with if I want a ton of request scoped beans and still serve a ton of requests at a time.
One option is to replace injecting a scoped proxy with a lookup-method:
public abstract class ExecutingClass {
protected abstract SomeInterface makeMyBean();
public void execute() {
SomeInterface myBean = makeMyBean();
String property = myBean.getProperty1();
String otherProperty = myBean.getProperty2();
}
}
That will ensure Spring is asked for the bean only once per request, eliminate any overhead due to the scoped proxy, and shorten stack traces. It is less flexible (in that you can not arbitrarily share references to the request scoped bean and have the scoped proxy use the right bean) but you might not need the flexibility.
Interesting question.
It turns out that Spring's scoped proxy doesn't cache resolved objects, so that every access to the scoped proxy causes getBean()
to be called.
As a workaround, you can create a poor man's caching scoped proxy, something like this (untested, target bean should be request-scoped, but without <aop:scoped-proxy />
):
public class MyScopedProxy implements SomeInterface, BeanFactoryAware {
private BeanFactory factory;
private Scope scope;
private String targetBeanName;
private ThreadLocal<SomeInterface> cache = new ThreadLocal<SomeInterface>();
private SomeInterface resolve() {
SomeInterface v = cache.get();
if (v == null) {
v = (SomeInterface) factory.getBean(targetBeanName);
cache.set(v);
scope.registerDestructionCallback(targetBeanName, new Runnable() {
public void run() {
cache.remove();
}
});
}
return v;
}
public void setBeanFactory(BeanFactory factory) {
this.factory = factory;
this.scope = ((ConfigurableBeanFactory) factory).getRegisteredScope("request");
}
public String getProperty() {
return resolve().getProperty();
}
...
}
Regarding the proxying mechanisms: unlike other AOP proxies, scoped proxies are CGLIB by default, you can override it by setting <aop:scoped-proxy proxy-target-class = "false" />
, but it wouldn't help in this case.