2011-01-24 18 views
31

Voglio creare un'applicazione di console di primavera (eseguendo da riga di comando con maven ad esempio: mvn exec: java -Dexec.mainClass = "package.MainClass").Applicazione console di primavera configurata usando annotazioni

È questa applicazione che voglio avere alcuni tipi di servizi e livelli dao. So come farlo per un'applicazione web ma non ho trovato alcuna informazione su come fare in caso di un'applicazione console (forse Leter con Swing).

sto cercando di creare qualcosa di simile:

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

E 'possibile? Posso trovare da qualche parte un esempio su come farlo?

risposta

31

Dai un'occhiata al riferimento di primavera, 3.2.2 Instantiating a container.

Per utilizzare Spring in un'applicazione console è necessario creare un'istanza di ApplicationContext e ottenere da essa i bean gestiti da Spring.

La creazione di un contesto mediante la configurazione XML è descritta nel Riferimento. Per approccio completamente annotazioni-based, si può fare someting in questo modo:

@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(); 
    } 
} 
+2

Anche se mi piacciono le annotazioni sulla configurazione: ctx = new AnnotationConfigApplicationContext (MyConfig.class); –

+0

Come si possono fornire gli argomenti della riga di comando al contesto dell'applicazione in modo che il contesto a sua volta possa iniettare questi argomenti in un bean? –

20

La primavera di riferimento suggerisce di utilizzare ClassPathXmlApplicationContext nel metodo main per creare il contesto dell'applicazione, quindi chiamare il metodo getBean per ottenere un riferimento iniziale ad un fagiolo dal contesto dell'applicazione. Dopo aver scritto questo stesso codice di un paio di volte, si finisce refactoring del testo costante in questa classe di utilità:

/** 
* 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); 
    } 
} 

chiamate il metodo load nel metodo principale dell'applicazione. Si noti che la classe Main non è un bean creato da Spring e tuttavia è possibile iniettare una delle sue proprietà con un bean dal contesto dell'applicazione.

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(); 
    } 
} 
3

Per quanto riguarda la risposta di Chin Huang sopra ...

Il tuo esempio non potrà mai funzionare, o almeno non sta lavorando per me a livello locale. Questo perché stai iniziando l'inizializzazione dell'oggetto @Autowired ma si specifica AutowireCapableBeanFactory.AUTOWIRE_NO nelle proprietà. Piuttosto, impostalo su AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE o AutowireCapableBeanFactory.AUTOWIRE_BY_NAME.

Inoltre, questo è strano, quindi potrei fare qualcosa di sbagliato. Ma sembra che con AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, devo avere un setProp() con @Autowired affinché funzioni. Così, invece di questo:

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

devo fare questo:

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

Se devo, come nell'esempio originale di Chin, i dati privati ​​con @Autowired, il DI fallisce. Sto usando 3.1.1.RELEASE e penso che alcune delle cose del cablaggio automatico siano cambiate in 3.1.x, quindi potrebbe essere dovuto a quello. Ma sono curioso di sapere perché questo non avrebbe funzionato, dal momento che è coerente con le versioni precedenti di Spring.

2

Si può fare in questo modo:

  • fare l'inizializzazione nel metodo principale
  • È quindi possibile utilizzare il metodo di avvio come il controller 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(); 

    } 

} 
3

Recentemente ho pensato a un progetto. Stavo creando una CLI per un'utilità che sarebbe stata eseguita da un lavoro programmato e riutilizzato parte del codice dell'applicazione Web per il progetto. Ho avuto un problema durante il bootstrap di tutte le dipendenze di @Autowired e in realtà non avevo bisogno di tutte queste, quindi ho eseguito il bootstrap delle dipendenze specifiche nella classe principale usando il metodo AnnotationConfigApplicationContext register (java.lang.Class ...) come segue:

@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(); 
     } 
    } 
} 

ed ecco la classe di configurazione:

@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; 
    } 
} 
+0

Questo è pulito ... –

0

Questa è stata la mia soluzione per eseguire un'uscita. Lo uso in un modulo che funziona come base comune a tutti gli altri specifici: un sito Web e uno API. Quando specificherò gli argomenti giusti sul modulo giusto, verrà eseguito il compito giusto.

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

Come si vede, ho disabilitato anche l'ambiente Web non utilizzato e JMX. Mi concentrerò sulla scansione del classpath dal pacchetto della classe e userò le capacità di autoconfigurazione di Spring Boot. Dopo che l'applicazione ha terminato di fare ciò di cui ha bisogno, si chiude come un'app console.

Problemi correlati