处理命令行参数与Spring

39

我正在编写一款使用Spring框架的命令行应用程序,该应用程序需要解析命令行参数。如何将这些参数传递给Spring?我应该如何构造main()函数,以便首先解析命令行参数,然后初始化Spring?即使这样,如何将包含解析参数的对象传递给Spring?

8个回答

40

我能想到两种可能性。

1)设置一个静态引用(一个静态变量,在这种情况下通常被认为是不好的,但是因为只有一个命令行调用,所以在这种情况下是可以接受的)。

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

然后,您可以通过以下方式在Spring中引用命令行参数:

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

如果你完全反对使用静态变量,你可以采取以下方法:

2) 通过编程方式将参数添加到应用程序上下文中:

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

 }

将命令行参数解析留给读者作为练习。


4
+1 静态引用。6行代码胜过20行。 - Leonel
我非常喜欢这个解决方案,并找到了这种奇特的方法来解决这个练习: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

7

请查看我的Spring-CLI库 - 在http://github.com/sazzer/spring-cli - 这是一种实现此操作的方式。它提供了一个主类,自动加载spring上下文,并具有使用Commons-CLI自动解析命令行参数并将其注入到您的bean中的功能。


7

从Spring 3.1开始,不需要其他答案中建议的任何自定义代码。请查看CommandLinePropertySource,它提供了一种将CL参数注入到上下文中的自然方法。

如果你是一个幸运的Spring Boot开发者,你可以更进一步简化你的代码,利用SpringApplication给你以下内容:

默认情况下,该类将执行以下步骤来引导您的应用程序:

...

注册CommandLinePropertySource以将命令行参数公开为Spring属性

如果您对Spring Boot属性解析顺序感兴趣,请参阅this page


这应该是正确的做法。对我来说完美地运作。 - johnlinp

5

您还可以将一个对象数组作为第二个参数传递给getBean方法,它将被用作构造函数或工厂的参数。

public static void main(String[] args) {
   Mybean m = (Mybean)context.getBean("mybean", new Object[] {args});
}

3

考虑以下类:

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

}

随着:

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

}

1

这里有一个引导Spring的示例,只需像平常一样获取传递的参数,然后使您在bean上调用的函数(在此案例中为deployer.execute())将它们作为字符串或通过任何您认为合适的格式进行接收。

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

啊,我应该提到我的Spring初始化必须在命令行被解析之后进行,因为它依赖于命令行的值。 - lowellk

0

我不确定你想要实现什么,也许你可以添加一些关于命令和参数的详细信息,以及你期望应用程序产生的结果。

我认为这不是你需要的,但它可能会帮助其他读者:Spring支持使用双破折号从命令行接收属性(例如:java -jar app.jar --my.property="Property Value")。请查看此文档以获取更多信息: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-command-line-args


0
s. Pauk answer的启发,我只需使用Spring的CommandLinePropertySource子类:SimpleCommandLinePropertySource。
// command line example : c:\> java.exe com.stackeroverflower.answer.132231.ProjetClientApplication --spring.profiles.active=local response --mysuperdate=2023-09-21T14:21:21.212

package com.stackeroverflower.answer.132231.ProjetClientApplication;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.SimpleCommandLinePropertySource;


@SpringBootApplication
public class ProjetClientApplication {


    private static final Logger logger = LoggerFactory.getLogger(ProjetClientApplication.class);

    
    public static void main(String[] args) {
        Arrays.stream(args).forEach(logger::info); // display > 
                // > INFO com.stackeroverflower.answer.132231.ProjetClientApplication - --spring.profiles.active=local
                // > INFO com.stackeroverflower.answer.132231.ProjetClientApplication - response
                // > INFO com.stackeroverflower.answer.132231.ProjetClientApplication - --respStatsAtDate=2023-09-21T14:21:21.212
                // > INFO com.stackeroverflower.answer.132231.ProjetClientApplication - -Xms4096M
                // > INFO com.stackeroverflower.answer.132231.ProjetClientApplication - -Xmx4096M
        
        SpringApplication.run(ProjetClientApplication.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
            SimpleCommandLinePropertySource ps = new SimpleCommandLinePropertySource(args);
            logger.info(" mysuperdate value is : {}", ps.getProperty("mysuperdate") ); // display > 
                // INFO  ProjetClientApplication :: lambda$1 >  mysuperdate value is : 2023-09-21T14:21:21.212
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接