Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用大量AOP请求范围bean时的性能问题_Java_Spring_Aop_Cglib - Fatal编程技术网

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

Java 使用大量AOP请求范围bean时的性能问题,java,spring,aop,cglib,Java,Spring,Aop,Cglib,我正在使用Spring3开发一个半大型的应用程序,在一次抛出数百个用户时遇到了性能问题。我使用Spring的AOP代理使用了几个请求范围的bean,我可以看到,每次我调用这些bean中的任何一个方法时,都会调用CGLIB拦截器,然后调用AbstractBeanFactory.getBean(),后者对一组已同步的Spring bean调用add()。由于此add()是同步的,因此当有数千个对服务器的调用等待添加到同一列表时,它会有效地锁定服务器 有没有一种方法可以通过使用请求范围的bean来解决

我正在使用Spring3开发一个半大型的应用程序,在一次抛出数百个用户时遇到了性能问题。我使用Spring的AOP代理使用了几个请求范围的bean,我可以看到,每次我调用这些bean中的任何一个方法时,都会调用CGLIB拦截器,然后调用AbstractBeanFactory.getBean(),后者对一组已同步的Spring bean调用add()。由于此add()是同步的,因此当有数千个对服务器的调用等待添加到同一列表时,它会有效地锁定服务器

有没有一种方法可以通过使用请求范围的bean来解决这个问题?我在Spring文档中读到,如果bean实现了任何接口,则不使用CGLIB(http://static.springsource.org/spring/docs/2.0.0/reference/aop.html#d0e9015)但是我的请求范围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.
    }
}

公共接口{
公共字符串getProperty1();
公共void setProperty1(字符串属性);
公共字符串getProperty2();
公共void setProperty2(字符串属性);
}
公共类SomeClass实现了SomeInterface{
私有字符串属性1;
私有字符串属性2;
公共字符串getProperty1(){return property1;}
public void setProperty1(字符串属性){property1=property;}
公共字符串getProperty2(){return property2;}
public void setProperty2(字符串属性){property2=property;}
}
公共类执行类{
私有接口myBean;
public void execute(){
String property=myBean.getProperty1();//此处调用CGLIB拦截器,将myBean注册为bean
String otherProperty=myBean.getProperty2();//这里也调用了CGLIB拦截器!似乎这是不必要的。它正在杀死我的应用程序。
}
}

我的想法如下:

  • 我是否可以在不代理对Bean进行的每个方法调用的情况下创建一个范围为springbean请求?而且没有将每种方法都标记为“最终”
或者

  • 我是否可以重写Spring的bean工厂来实现一个bean缓存,该缓存将在调用AbstractBeanFactory.getBean()之前检查bean是否被缓存?如果是这样,我在哪里配置Spring来使用我的定制bean工厂
    • 有趣的问题

      事实证明,Spring的作用域代理不会缓存已解析的对象,因此对作用域代理的每次访问都会导致调用
      getBean()

      作为一种解决方法,您可以创建一个穷人的缓存作用域代理,类似这样(未测试,目标bean应该是请求作用域,但没有
      ):

      公共类MyScopedProxy实现了一些接口,BeanFactoryAware{
      私营豆厂;
      私人范围;
      私有字符串targetBeanName;
      私有ThreadLocal缓存=新ThreadLocal();
      私有接口解析(){
      SomeInterface v=cache.get();
      如果(v==null){
      v=(SomeInterface)factory.getBean(targetBeanName);
      cache.set(v);
      registerDestructionCallback(targetBeanName,new Runnable()){
      公开募捐{
      cache.remove();
      }
      });
      }
      返回v;
      }
      公共工厂(豆工厂){
      这个工厂=工厂;
      this.scope=((ConfigurableBeanFactory)工厂).getRegisteredScope(“请求”);
      }
      公共字符串getProperty(){
      返回resolve().getProperty();
      }
      ...
      }
      

      关于代理机制:与其他AOP代理不同,作用域代理在默认情况下是CGLIB,您可以通过设置来覆盖它,但在这种情况下没有帮助。

      一个选项是将注入作用域代理替换为:


      这将确保每个请求只向Spring请求一次bean,消除由于作用域代理而产生的任何开销,并缩短堆栈跟踪。它不太灵活(因为您不能随意共享对请求范围bean的引用,也不能让范围代理使用正确的bean),但您可能不需要这种灵活性。

      事实证明,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;
      }
      
      因此,虽然AbstractBeanFactory.getBean()确实会因为aop代理而在每次bean方法调用中被调用,但它只会导致Spring在请求属性中尚未找到bean时添加到该同步集


      避免在我的请求范围bean上代理每个方法调用仍然会降低复杂性,但是使用这种缓存,性能影响将是最小的。我认为如果我想要大量的请求范围的bean,并且仍然一次提供大量的请求,那么性能低下是我必须忍受的。1次调用可以,但2次调用会杀死你的应用程序?如果它在第一次引用bean时调用它,那就可以了。如果每次我调用bean上的任何方法时它都调用代理类,这会杀死我的应用程序。如果我在多个接口的多个位置出现此问题,我需要为每个接口创建一个新的代理?是否有可能替代AbstractBeanFactory.getBean()并在那里实现一个bean缓存?如果是这样的话,我想不出在哪里
      public abstract class ExecutingClass {
          protected abstract SomeInterface makeMyBean();
      
          public void execute() {
              SomeInterface myBean = makeMyBean();
              String property = myBean.getProperty1(); 
              String otherProperty = myBean.getProperty2(); 
          }
      }
      
      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;
      }