Spring启动性能问题

时间:2011-05-10 09:04:36

标签: java spring spring-mvc classpath bootstrapping

我正在尝试将Spring集成到一个包含数千个类的大型应用程序中,并且由于组件扫描,我在启动容器方面遇到了巨大的延迟。

我已经将“base-package”中指定的目录数量缩小到最小,以减少扫描不相关目录所浪费的时间,但是初始化的类路径扫描部分仍然需要大约1-2个分钟。

那么,有没有办法优化扫描过程?我曾想过将候选类路径存储在一个文件中,然后让容器从文件中获取它们,而不是每次启动都扫描类路径,但我真的不知道从哪里开始,或者甚至可能

非常感谢任何建议。提前致谢。

编辑:从自动生成的xml文件加载bean定义,将Spring自举时间减少到9~10秒,这证实了Spring用于组件类路径扫描的反射api是主要的启动延迟的来源 至于生成xml文件,这里是代码,因为它可能对有相同问题的人有帮助。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;


public class ConfigurationWriter {

    public ArrayList<String> beanDefinitions = new ArrayList<String>();

    public ConfigurationWriter() {

        // the context loaded with old fashioned way (classpath scanning)
        ApplicationContext context = SpringContainerServiceImpl.getInstance().getContext();
        String[] tab = context.getBeanDefinitionNames();
        for (int i = 0; i < tab.length - 6; i++) {
            Class clazz = context.getType(tab[i]);
            String scope = context.isPrototype(tab[i]) ? "prototype" : "singleton";
            String s = "<bean id=\"" + tab[i] + "\" class=\"" + clazz.getName() + "\" scope=\"" + scope + "\"/>";
            beanDefinitions.add(s);
        }
        // Collections.addAll(beanDefinitions, tab);

    }

    @SuppressWarnings("restriction")
    public void generateConfiguration() throws FileNotFoundException {
        File xmlConfig = new File("D:\\dev\\svn\\...\\...\\src\\test\\resources\\springBoost.xml");
        PrintWriter printer = new PrintWriter(xmlConfig);

        generateHeader(printer);

        generateCorpse(printer);

        generateTail(printer);

        printer.checkError();

    }

    @SuppressWarnings("restriction")
    private void generateCorpse(PrintWriter printer) {

        for (String beanPath : beanDefinitions) {
            printer.println(beanPath);
        }

    }

    @SuppressWarnings("restriction")
    private void generateHeader(PrintWriter printer) {
        printer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        printer.println("<beans xmlns=\"http://www.springframework.org/schema/beans\"");
        printer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
        printer.println("xmlns:context=\"http://www.springframework.org/schema/context\"");
        printer.println("xsi:schemaLocation=\"");
        printer.println("http://www.springframework.org/schema/mvc");
        printer.println("http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd");
        printer.println("http://www.springframework.org/schema/beans");
        printer.println("http://www.springframework.org/schema/beans/spring-beans-3.0.xsd");
        printer.println("http://www.springframework.org/schema/context");
        printer.println("http://www.springframework.org/schema/context/spring-context-3.0.xsd\"");
        printer.println("default-lazy-init=\"true\">");
    }

    @SuppressWarnings("restriction")
    private void generateTail(PrintWriter printer) {
        // printer.println("<bean class=\"com.xxx.frmwrk.spring.processors.xxxBeanFactoryPostProcessor\"/>");
        printer.println("<bean class=\"com.xxx.frmwrk.spring.processors.xxxPostProcessor\"/>");
        printer.println("</beans>");
    }

}

6 个答案:

答案 0 :(得分:9)

  

问题:目录中有多少(%)的类是Spring Beans?

     

答案:我不太确定(这是一个非常大的项目),但从我看到的情况来看,我相信它已经在90%到100%左右,因为xml和属性文件被隔离在不同的位置)

如果问题实际上是组件扫描而不是bean初始化过程本身(我非常怀疑),那么我能想象的唯一解决方案是使用Spring XML配置而不是组件扫描。 - (可以自动创建XML文件)。

但是如果你有很多课程并且90% - 其中100%是豆类,那么,扫描文件的减少将最大改善10%-0%。

您应该尝试其他方法来加速初始化,可能使用延迟加载或任何延迟加载相关技术,或(并且这不是一个笑话)使用更快的硬件(如果它不是一个独立的应用程序)。


生成Spring XML的一种简单方法是编写一个简单的spring应用程序,它像原始应用程序一样使用类路径扫描。在所有Bean初始化之后,它在Spring上下文中遍历Bean,检查bean是否属于重要包,并在文件中写入此Bean的XML Config。

答案 1 :(得分:4)

你可以对那里的性能做的不多,我想你并不关心生产环境中的启动,而是你的测试的启动时间*。 两个提示:

  • 检查您的test-appcontext仅使用应用中所需的最低组件
  • 而不是有一个组件扫描指令列表,使用一个,逗号分隔值如下:base-package =“com.package.one,com.package.two ...”

答案 2 :(得分:4)

自动发现带注释的类当前需要扫描指定包中的所有类,并且可能需要很长时间,这是当前类加载机制的已知问题。

Java 9将在这里帮助Jigsaw。

来自Mark Reinold的Java平台模块系统要求,http://openjdk.java.net/projects/jigsaw/spec/reqs/

高效的注释检测 - 必须能够识别模块工件中的所有类文件,其中存在特定注释而不实际读取所有类文件。在运行时,必须能够识别加载模块中存在特定注释的所有类,而无需枚举模块中的所有类,只要注释在运行时保留即可。为了提高效率,可能需要指定只需要以这种方式检测某些注释。 一种可能的方法是使用模块中存在的注释的索引来扩充模块的定义,以及每个注释适用的元素的指示。为了限制索引的大小,只包括注释,这些注释本身用新的元注释注释,比如说@Indexed。

答案 3 :(得分:2)

除了减少要扫描的目录之外,我唯一想到的就是使用lazy bean initialization。 如果你有很多豆子,这可能会有所帮助

答案 4 :(得分:2)

您可以使用Spring Java-based container configuration代替组件扫描。

与基于XML的配置相比,基于Java的容器配置是类型安全的。

但首先,您应该检查您的组件扫描路径是否足够具体,以便它们不包含第三方库类。

答案 5 :(得分:2)

我知道这是一个老问题,正如您将看到当时的情况有所不同,但希望它可以像我一样帮助其他人研究这个问题。

根据对不同问题的回答,@ComponentScan注释现在支持lazyInit标记,这有助于减少启动时间。

https://stackoverflow.com/a/29832836/4266381

注意:您的编辑听起来像是单独切换到XML是神奇的。然而,仔细观察代码,你得到了default-lazy-init="true"。我想知道这是不是真的原因。