使用大量AOP请求范围bean时的性能问题

Cam*_*ron 9 java aop spring cglib

我正在使用Spring 3处理一个半大型应用程序,并且在同时向其投掷数百个用户时遇到了性能问题.我使用Spring的AOP代理使用了几个请求范围的bean,我可以看到每次调用其中一个bean上的任何方法时,都会调用CGLIB拦截器,然后调用AbstractBeanFactory.getBean(),它调用add()on现有Spring bean的同步集.由于这个add()是同步的,所以当有数千个调用它等待添加到同一个列表时,它会有效地锁定服务器.

有没有办法使用请求范围的bean来解决这个问题?我在Spring文档中读到,如果bean实现了任何接口(http://static.springsource.org/spring/docs/2.0.0/reference/aop.html#d0e9015),那么CGLIB就不会被使用,但是我的请求范围是bean所有实现一个(实际上是同一个),它仍然发生.我肯定需要将bean作为请求作用域,因为它们的某些字段是在应用程序的一个部分中为特定请求计算的,然后我使用SpEL在同一请求期间在应用程序的不同部分获取它们的值.我想如果我把bean原型作为范围,当我第二次使用SpEL来获取它时,我会有一个新鲜的对象.

这是一个代码示例,说明了我的问题.请参阅最后两行,了解描述我遇到问题的地方.

<!-- Spring config -->
<bean name="someBean" class="some.custom.class.SomeClass" scope="request">
    <property name="property1" value="value1"/>
    <property name="property2" value="value2"/>
    <aop:scoped-proxy/>
</bean>

<bean name="executingClass" class="some.other.custom.class.ExecutingClass" scope="singleton">
    <property name="myBean" ref="someBean" />
</bean>


public Interface SomeInterface {
    public String getProperty1();
    public void setProperty1(String property);
    public String getProperty2();
    public void setProperty2(String property);
}

public class SomeClass implements SomeInterface {
    private String property1;
    private String property2;

    public String getProperty1() { return propery1; }
    public void setProperty1(String property) { property1=property;}

    public String getProperty2() { return propery2; }
    public void setProperty2(String property) { property2=property;}
}


public class ExecutingClass {
    private SomeInterface myBean;

    public void execute() {
        String property = myBean.getProperty1(); // CGLIB interceptor is invoked here, registering myBean as a bean
        String otherProperty = myBean.getProperty2(); // CGLIB interceptor is invoked here too!  Seems like this is unnecessary. And it's killing my app.
    }
}
Run Code Online (Sandbox Code Playgroud)

我的想法是以下之一:

  • 我可以在没有代理对bean进行的每个方法调用的情况下创建一个Spring Bean请求作用域吗?并没有将每种方法都标记为"最终"?

要么...

  • 我是否可以覆盖Spring的bean工厂来实现Bean缓存,该缓存将在调用AbstractBeanFactory.getBean()之前检查bean是否被缓存?如果是这样,我在哪里配置Spring以使用我的自定义bean工厂?

Cam*_*ron 4

事实证明,Spring 实际上确实在请求属性中缓存了请求范围的 bean。如果您好奇,请看一下 AbstractRequestAttributesScope,它是 RequestScope 的扩展:

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;
}
Run Code Online (Sandbox Code Playgroud)

因此,虽然由于 aop 代理,AbstractBeanFactory.getBean() 确实在每个 bean 方法调用中被调用,但如果在请求属性中尚未找到该 bean,它只会导致 Spring 添加到该同步集。

避免对我的请求作用域 bean 上的每个方法调用进行代理仍然会降低复杂性,但有了此缓存,对性能的影响将很小。我认为如果我想要大量请求范围的 bean 并且仍然一次服务大量请求,那么我将不得不忍受缓慢的性能。