web-dev-qa-db-fra.com

Problèmes de performances au démarrage du printemps

J'essaie d'intégrer Spring dans une application assez grande avec des milliers de classes, et je connais d'énormes retards dans le démarrage de mon conteneur en raison de l'analyse des composants.

J'ai déjà réduit le nombre de répertoires spécifiés dans le "base-package", au minimum afin de réduire le temps perdu à analyser les répertoires non pertinents, mais la partie d'analyse du chemin de classe de l'initialisation prend toujours environ 1-2 minutes.

Alors, existe-t-il un moyen d'optimiser le processus de numérisation? J'ai pensé à stocker le chemin des classes candidates dans un fichier et à faire le conteneur puis à les extraire du fichier au lieu de scanner le chemin des classes à chaque démarrage, mais je ne sais pas vraiment par où commencer ni si c'est même possible .

Tout conseil est fort apprécié. Merci d'avance.


Edit1 : Le chargement des définitions de bean à partir d'un fichier xml généré automatiquement, a réduit le temps Spring bootstrap à 9 ~ 10 secondes, ce qui confirme que l'api de réflexion utilisé par Spring pour l'analyse du chemin de classe des composants est la principale source de retards de démarrage.
Quant à la génération du fichier xml voici le code, car il pourrait être utile pour quelqu'un avec les mêmes problèmes.

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

}

Edit 2: Avec Spring 5 comprenant un ensemble important d'optimisations pour accélérer l'initialisation du contexte, il est également livré avec une fonctionnalité intéressante et pratique qui permet de générer un index des composants candidats au moment de la compilation: Spring Context Indexer

37
Mehdi

Question: Combien (en%) des classes dans les répertoires sont des Spring Beans?

Réponse: Je ne suis pas vraiment sûr (c'est un très gros projet), mais d'après ce que j'ai vu, je pense qu'il est autour de 90 à 100%, car les fichiers XML et Propriétés sont isolés dans des emplacements séparés)

Si le problème est vraiment l'analyse des composants et non le processus d'initialisation du bean lui-même (et j'en doute fortement), alors la seule solution que j'imagine est d'utiliser la configuration Spring XML au lieu de l'analyse des composants. - (Vous pouvez créer automatiquement le fichier XML).

Mais si vous avez plusieurs classes et que 90% - 100% d'entre elles sont des Beans, alors, la réduction des fichiers scannés aura une amélioration maximale de 10% -0%.

Vous devriez essayer d'autres façons d'accélérer votre initialisation, en utilisant le chargement différé ou toute autre technique liée au chargement paresseux, ou (et ce n'est pas une blague) utiliser un matériel plus rapide (s'il ne s'agit pas d'une application autonome).


Un moyen simple de générer le XML Spring est d'écrire une application Spring simple qui utilise l'analyse du chemin de classe comme votre application d'origine. Une fois tous les Beans initialisés, il parcourt les Beans dans le contexte Spring, vérifie si le bean appartient au package important et écrit la configuration XML de ce bean dans un fichier.

9
Ralph

La découverte automatique des classes annotées nécessite actuellement d'analyser toutes les classes dans le ou les packages spécifiés et peut prendre un certain temps, un problème connu du mécanisme de chargement de classe actuel.

Java 9 va aider ici avec Jigsaw.

Tiré de Java Platform Module System requirements by Mark Reinold, http://openjdk.Java.net/projects/jigsaw/spec/reqs/ :

Détection efficace des annotations - Il doit être possible d'identifier tous les fichiers de classe dans un artefact de module dans lequel une annotation particulière est présente sans lire réellement tous les fichiers de classe. Au moment de l'exécution, il doit être possible d'identifier toutes les classes d'un module chargé dans lesquelles une annotation particulière est présente sans énumérer toutes les classes du module, tant que l'annotation a été conservée pendant l'exécution. Pour des raisons d'efficacité, il peut être nécessaire de spécifier que seules certaines annotations doivent être détectables de cette manière. Une approche potentielle consiste à augmenter la définition d'un module avec un index des annotations présentes dans le module, ainsi qu'une indication des éléments auxquels s'applique chaque annotation. Pour limiter la taille de l'index, seules les annotations qui sont elles-mêmes annotées avec une nouvelle méta-annotation, disons @Indexed, seraient incluses.

4
Jeroen Borgers

Vous ne pouvez pas faire grand-chose sur les performances, je suppose que vous n'êtes pas préoccupé par le démarrage dans l'environnement de production, mais par le temps de démarrage de vos tests *. Deux astuces:

  • Vérifiez que votre test-appcontext utilise uniquement les composants minimalement requis de votre application
  • au lieu d'avoir une liste de directives d'analyse des composants, utilisez-en une, avec une valeur séparée par des virgules comme ceci: base-package = "com.package.one, com.package.two ..."
4
abalogh

Je sais que c'est une vieille question, et comme vous le verrez, la situation était différente à ce moment-là, mais j'espère que cela pourra aider les autres à faire des recherches sur ce problème comme moi.

Selon cette réponse à une autre question, The @ComponentScan l'annotation prend désormais en charge l'indicateur lazyInit, ce qui devrait aider à réduire le temps de démarrage.

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

Remarque: Votre modification a donné l'impression que le passage au XML était en soi la magie. Pourtant, en regardant de plus près le code, vous aviez default-lazy-init="true". Je me demande si c'était la vraie raison.

3
Xiangming Hu

Vous pouvez utiliser Spring's configuration de conteneur basée sur Java au lieu de l'analyse des composants.

Par rapport à la configuration basée sur XML, la configuration de conteneur basée sur Java est sécurisée par type.

Mais tout d'abord, vous devez vérifier si les chemins de balayage des composants sont suffisamment spécifiques pour ne pas inclure de classes de bibliothèques tierces.

2
rwitzel

La seule chose qui me vient à l'esprit, outre la réduction des répertoires à analyser, est l'utilisation de initialisation du bean paresseux . Que cela puisse aider si vous avez beaucoup de haricots

2
Hons