39

When I'm writing a Spring command line application which parses command line arguments, how do I pass them to Spring? Would I want to have my main() structured so that it first parses the command line args and then inits Spring? Even so, how would it pass the object holding the parsed args to Spring?

lowellk
  • 2,049
  • 4
  • 21
  • 26

7 Answers7

40

Two possibilities I can think of.

1) Set a static reference. (A static variable, although typically frowned upon, is OK in this case, because there can only be 1 command line invocation).

public class MyApp {
  public static String[] ARGS; 
  public static void main(String[] args) {
    ARGS = args;
      // create context
  }
}

You can then reference the command line arguments in Spring via:

<util:constant static-field="MyApp.ARGS"/>

Alternatively (if you are completely opposed to static variables), you can:

2) Programmatically add the args to the application context:

 public class MyApp2 {
   public static void main(String[] args) {
     DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // Define a bean and register it
     BeanDefinition beanDefinition = BeanDefinitionBuilder.
       rootBeanDefinition(Arrays.class, "asList")
       .addConstructorArgValue(args).getBeanDefinition();
     beanFactory.registerBeanDefinition("args", beanDefinition);
     GenericApplicationContext cmdArgCxt = new GenericApplicationContext(beanFactory);
     // Must call refresh to initialize context 
     cmdArgCxt.refresh();

     // Create application context, passing command line context as parent
     ApplicationContext mainContext = new ClassPathXmlApplicationContext(CONFIG_LOCATIONS, cmdArgCxt);

     // See if it's in the context
     System.out.println("Args: " + mainContext.getBean("args"));
   }

   private static String[] CONFIG_LOCATIONS = new String[] {
     "applicationContext.xml"
   };

 }

Parsing the command line arguments is left as an exercise to the reader.

Justin Wrobel
  • 1,981
  • 2
  • 23
  • 36
flicken
  • 15,443
  • 4
  • 29
  • 29
  • 4
    +1 for the static reference. 6 lines of code are better than 20. – Leonel May 28 '10 at 11:31
  • I liked very lot this solution and found this kinky way to solve the exercise:System.out.println("Args: " + mainContext.getBean("args")); String[] arg1 = (String[]) ((Collection)mainContext.getBean("args")).toArray(new String[0]); String o = arg1[0]; System.out.println(o); – hephestos Jun 30 '12 at 19:13
7

Have a look at my Spring-CLI library - at http://github.com/sazzer/spring-cli - as one way of doing this. It gives you a main class that automatically loads spring contexts and has the ability to use Commons-CLI for parsing command line arguments automatically and injecting them into your beans.

Graham
  • 4,095
  • 4
  • 29
  • 37
7

Starting from Spring 3.1 there is no need in any custom code suggested in other answers. Check CommandLinePropertySource, it provides a natural way to inject CL arguments into your context.

And if you are a lucky Spring Boot developer you could simplify your code one step forward leveraging the fact that SpringApplication gives you the following:

By default class will perform the following steps to bootstrap your application:

...

Register a CommandLinePropertySource to expose command line arguments as Spring properties

And if you are interested in the Spring Boot property resolution order please consult this page.

Community
  • 1
  • 1
S. Pauk
  • 5,208
  • 4
  • 31
  • 41
5

You can also pass an Object array as a second parameter to getBean which will be used as arguments to the constructor or factory.

public static void main(String[] args) {
   Mybean m = (Mybean)context.getBean("mybean", new Object[] {args});
}
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
BeWarned
  • 2,280
  • 1
  • 15
  • 22
3

Consider the following class:

public class ExternalBeanReferneceFactoryBean 
    extends AbstractFactoryBean
    implements BeanNameAware {

    private static Map<String, Object> instances = new HashMap<String, Object>();
    private String beanName;

    /**
     * @param instance the instance to set
     */
    public static void setInstance(String beanName, Object instance) {
        instances.put(beanName, instance);
    }

    @Override
    protected Object createInstance() 
        throws Exception {
        return instances.get(beanName);
    }

    @Override
    public Class<?> getObjectType() {
        return instances.get(beanName).getClass();
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

}

along with:

/**
 * Starts the job server.
 * @param args command line arguments
 */
public static void main(String[] args) {

    // parse the command line
    CommandLineParser parser = new GnuParser();
    CommandLine cmdLine = null;
    try {
        cmdLine = parser.parse(OPTIONS, args);
    } catch(ParseException pe) {
        System.err.println("Error parsing command line: "+pe.getMessage());
        new HelpFormatter().printHelp("command", OPTIONS);
        return;
    }

    // create root beanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // register bean definition for the command line
    ExternalBeanReferneceFactoryBean.setInstance("commandLine", cmdLine);
    beanFactory.registerBeanDefinition("commandLine", BeanDefinitionBuilder
        .rootBeanDefinition(ExternalBeanReferneceFactoryBean.class)
        .getBeanDefinition());

    // create application context
    GenericApplicationContext rootAppContext = new GenericApplicationContext(beanFactory);
    rootAppContext.refresh();

    // create the application context
    ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { 
        "/commandlineapp/applicationContext.xml"
    }, rootAppContext);

    System.out.println(appContext.getBean("commandLine"));

}
Brian Dilley
  • 3,888
  • 2
  • 24
  • 24
1

Here is an example to boot strap spring for a Main method, simply grab the passed params as normal then make the function you call on your bean (in the case deployer.execute()) take them as Strings or via any format you feel suitable.

public static void main(String[] args) throws IOException, ConfigurationException {
    Deployer deployer = bootstrapSpring();

    deployer.execute();
}

private static Deployer bootstrapSpring()
{
    FileSystemXmlApplicationContext appContext = new FileSystemXmlApplicationContext("spring/deployerContext.xml");

    Deployer deployer = (Deployer)appContext.getBean("deployer");
    return deployer;
}
  • Ah, I should have mentioned that my spring initialization must come after the command line has been parsed, as it depends on values from the command line – lowellk Sep 25 '08 at 18:07
0

I'm not sure what you are trying to achieve exactly, maybe you can add some details on what the command and the arguments will look like, and what outcome you expect from your application.

I think this is not what you need, but it might help other readers: Spring supports receiving properties from the command line using double hyphen (e.g. java -jar app.jar --my.property="Property Value" Have a look at this documentation for more information: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-command-line-args

Gerardo Roza
  • 2,746
  • 2
  • 24
  • 33