使用大量 AOP 请求范围 bean 时的性能问题 [英] Performance problems when using lots of AOP request scoped beans

查看:35
本文介绍了使用大量 AOP 请求范围 bean 时的性能问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 scoped beans using Spring's AOP proxy and I can see that every time I call any method on one of these beans, the CGLIB interceptor is invoked, which then calls AbstractBeanFactory.getBean(), which calls add() on a Synchronized Set of existing Spring beans. Since this add() is synchronized, it effectively locks up the server when there are thousands of calls to it all waiting to add to the same list.

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

Is there a way to get around this using request scoped beans? I read in the Spring documentation that CGLIB isn't used if the bean implements any interface (http://static.springsource.org/spring/docs/2.0.0/reference/aop.html#d0e9015) but my request scoped beans all implement one (the same one in fact) and it's still happening. And I definitely need the beans to be request scoped because some of their fields are calculated in one part of the app for a particular request and then I use SpEL to get their value in a different part of the app during the same request. I think if I made the beans prototype scoped, I'd have a fresh object when I used SpEL to get them the second time.

这是说明我的问题的代码示例.请参阅最后两行的评论,以了解我究竟在哪里遇到了问题.

Here is a code sample that illustrates my problem. See the last two lines for comments describing where exactly I'm having issues.

<!-- 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.
    }
}

<小时>

我的想法是以下之一:


My ideas are one of the following:

  • 是否可以在不代理对 bean 进行的每个方法调用的情况下发出范围内的 Spring Bean 请求?并且没有将每个方法都标记为final"?

或...

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

推荐答案

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

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;
}

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

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.

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

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.

这篇关于使用大量 AOP 请求范围 bean 时的性能问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆