web-dev-qa-db-fra.com

Application console Spring configurée à l'aide d'annotations

Je souhaite créer une application de console Spring (exécutée à partir de la ligne de commande avec maven, par exemple: mvn exec: Java -Dexec.mainClass = "package.MainClass").

Est-ce que cette application je veux avoir une sorte de services et couches de dao. Je sais comment le faire pour une application Web, mais je n'ai trouvé aucune information sur la procédure à suivre dans le cas d'une application console (par exemple peut-être avec Swing).

J'essaie de créer quelque chose comme:

public interface SampleService {
 public String getHelloWorld();
}


@Service
public class SampleServiceImpl implements SampleService {
 public String getHelloWorld() {
  return "HelloWorld from Service!";
 }
}

public class Main {
 @Autowired
 SampleService sampleService;
 public static void main(String [] args) {
  Main main = new Main();
  main.sampleService.getHelloWorld();
 }
}

Est-ce possible? Puis-je trouver quelque part un exemple de la façon de le faire?

32
fatman

Jetez un coup d’œil à la référence de ressort, 3.2.2 Instanciation d’un conteneur .

Pour utiliser Spring dans une application console, vous devez créer une instance de ApplicationContext et en obtenir des beans gérés par Spring.

La création d'un contexte à l'aide de la configuration XML est décrite dans la Référence. Pour une approche complètement basée sur les annotations, vous pouvez faire quelque chose comme ceci:

@Component // Main is a Spring-managed bean too, since it have @Autowired property
public class Main {
    @Autowired SampleService sampleService;
    public static void main(String [] args) {
        ApplicationContext ctx = 
            new AnnotationConfigApplicationContext("package"); // Use annotated beans from the specified package

        Main main = ctx.getBean(Main.class);
        main.sampleService.getHelloWorld();
    }
}
33
axtavt

La référence de printemps suggère d'utiliser ClassPathXmlApplicationContext dans la méthode main pour créer le contexte de l'application, puis d'appeler la méthode getBean pour obtenir une référence initiale à un bean à partir du contexte de l'application. Après avoir écrit ce même code plusieurs fois, vous finissez par refactoriser le passe-partout dans cette classe d’utilitaires:

/**
 * Bootstraps Spring-managed beans into an application. How to use:
 * <ul>
 * <li>Create application context XML configuration files and put them where
 * they can be loaded as class path resources. The configuration must include
 * the {@code <context:annotation-config/>} element to enable annotation-based
 * configuration, or the {@code <context:component-scan base-package="..."/>}
 * element to also detect bean definitions from annotated classes.
 * <li>Create a "main" class that will receive references to Spring-managed
 * beans. Add the {@code @Autowired} annotation to any properties you want to be
 * injected with beans from the application context.
 * <li>In your application {@code main} method, create an
 * {@link ApplicationContextLoader} instance, and call the {@link #load} method
 * with the "main" object and the configuration file locations as parameters.
 * </ul>
 */
public class ApplicationContextLoader {

    protected ConfigurableApplicationContext applicationContext;

    public ConfigurableApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * Loads application context. Override this method to change how the
     * application context is loaded.
     * 
     * @param configLocations
     *            configuration file locations
     */
    protected void loadApplicationContext(String... configLocations) {
        applicationContext = new ClassPathXmlApplicationContext(
                configLocations);
        applicationContext.registerShutdownHook();
    }

    /**
     * Injects dependencies into the object. Override this method if you need
     * full control over how dependencies are injected.
     * 
     * @param main
     *            object to inject dependencies into
     */
    protected void injectDependencies(Object main) {
        getApplicationContext().getBeanFactory().autowireBeanProperties(
                main, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
    }

    /**
     * Loads application context, then injects dependencies into the object.
     * 
     * @param main
     *            object to inject dependencies into
     * @param configLocations
     *            configuration file locations
     */
    public void load(Object main, String... configLocations) {
        loadApplicationContext(configLocations);
        injectDependencies(main);
    }
}

Appelez la méthode load dans la méthode principale de votre application. Notez que la classe Main n'est pas un bean créé par Spring et que vous pouvez néanmoins injecter l'une de ses propriétés avec un bean du contexte de l'application.

public class Main {
    @Autowired
    private SampleService sampleService;

    public static void main(String[] args) {
        Main main = new Main();
        new ApplicationContextLoader().load(main, "applicationContext.xml");
        main.sampleService.getHelloWorld();
    }
}
20
Chin Huang

Je devais comprendre cela pour un projet récemment. Je construisais une interface de ligne de commande pour un utilitaire qui serait exécuté à partir d'un travail planifié et réutilisait une partie du code de l'application Web pour le projet. Un problème est survenu lors de l'amorçage de toutes les dépendances @Autowired et je n'en avais pas besoin en fait. J'ai donc amorcé les dépendances spécifiques de la classe principale à l'aide de la méthode AnnotationConfigApplicationContext register (Java.lang.Class ...) comme suit:

@Component
public class SpringAppCLI
{

    /**
     * Service to be autowired!
     */
    @Autowired
    private SampleService sampleService;

    /**
     *
     */
    public static void main(String[] args) throws Exception {

        final AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // setup configuration
        applicationContext.register(SampleServiceConfig.class);
        applicationContext.register(SampleServiceRepository.class);
        applicationContext.register(JpaConfig.class);
        applicationContext.register(CommandLineConfig.class);
        applicationContext.register(SampleService.class);
        applicationContext.register(SpringAppCLI.class);
        // add CLI property source
        applicationContext.getEnvironment().getPropertySources()
                .addLast(new SimpleCommandLinePropertySource(args));

        // setup all the dependencies (refresh) and make them run (start)
        applicationContext.refresh();
        applicationContext.start();

        try {
            SpringAppCLI springAppCLI = applicationContext.getBean(SpringAppCLI.class);
            springAppCLI.doWhatever();
        } catch (Exception e) {
            //some handling

        } finally {
            applicationContext.close();
        }
    }
}

et voici la classe de configuration: 

@Configuration
@ComponentScan(basePackageClasses = SolrLoadCLI.class, includeFilters = @Filter(Controller.class), useDefaultFilters = false)
class CommandLineConfig implements ApplicationContextAware {

    /**
     * 
     */
    private ApplicationContext applicationContext;

    /**
     * 
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 
     * @return
     */
    @Bean
    public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Resource[] resourceArray = new Resource[2];
        resourceArray[0] = new ClassPathResource("/SampleService.properties");
        resourceArray[1] = new ClassPathResource("/Database.properties");
        ppc.setLocations(resourceArray);
        return ppc;
    }
}
3
gffny

En ce qui concerne la réponse de Chin Huang ci-dessus ...

Votre exemple ne fonctionnera pas réellement, ou du moins ne fonctionnera pas pour moi localement. En effet, vous initialisez l'objet SampleService à l'aide de @Autowired, mais vous spécifiez AutowireCapableBeanFactory.AUTOWIRE_NO dans les propriétés. Au lieu de cela, définissez-le sur AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE ou AutowireCapableBeanFactory.AUTOWIRE_BY_NAME.

Aussi, c'est étrange, alors je peux faire quelque chose de mal. Mais il semble qu'avec AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, je dois avoir une setProp() avec @Autowired pour que cela fonctionne. Donc au lieu de cela:

public class Main {
    @Autowired
    private SampleService sampleService;

    public static void main(String[] args) {
        Main main = new Main();
        ApplicationContextLoader loader = new ApplicationContextLoader();
        loader.load(main, "applicationContext.xml");
        main.sampleService.getHelloWorld();
    } 
}

Je dois faire ceci:

public class Main {
    private SampleService sampleService;

    public static void main(String[] args) {
        Main main = new Main();
        ApplicationContextLoader loader = new ApplicationContextLoader();
        loader.load(main, "applicationContext.xml");
        main.sampleService.getHelloWorld();
    } 

    @Autowired
    public void setSampleService(SampleService sampleService) {
        this.sampleService = sampleService;
    }
}

Comme dans l'exemple original de Chin, j'ai des données privées avec @Autowired, la DI échoue. J'utilise 3.1.1.LELEASE et je pense que certains éléments de l'auto-câblage ont été modifiés en 3.1.x, c'est peut-être pour cette raison. Mais je suis curieux de savoir pourquoi cela ne fonctionnerait pas, puisque cela correspond aux versions précédentes de Spring.

3
Spanky Quigman

Vous pouvez le faire de cette façon: 

  • Faire l'initialisation dans votre méthode principale
  • Vous pouvez ensuite utiliser la méthode de démarrage comme contrôleur Sudo
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import com.org.service.YourService;

@Component
public class YourApp{

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "ApplicationContext.xml");

        YourApp p = context.getBean(App.class);
        p.start(args);
    }

    @Autowired
    YourService yourService;
    private void start(String[] args) {

        yourService.yourMethod();

    }

}
2
royjavelosa

C'était ma solution pour exécuter une sortie. Je l'utilise dans un module qui fonctionne comme un terrain commun à tous les autres: un site Web et un API. Lorsque je spécifie les bons arguments sur le bon module, la bonne tâche sera exécutée.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
@EnableAutoConfiguration
public class CLIApp {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx =
                new SpringApplicationBuilder(CLIApp.class)
                        .web(false)
                        .properties("spring.jmx.enabled=false")
                        .run(args);

        final int exitCode = SpringApplication.exit(ctx);

        System.out.println("************************************");
        System.out.println("* Console App sucessfully executed *");
        System.out.println("************************************");

        System.exit(exitCode);
    }
}

Comme vous le voyez, j’ai également désactivé l’environnement Web non utilisé et JMX. Je vais me concentrer sur l'analyse du classpath du package de la classe et utiliser les compétences de configuration automatique de Spring Boot . Une fois l'application terminée, elle se ferme comme une application de console.

0
EliuX