ENGINEER BLOG

ENGINEER BLOG

SpringのBeanをコマンドラインから起動する

こんにちは。インテグレーションサービス本部の横山です。

Webアプリケーション以外の環境でSpringのBeanを起動したいとき、本格的なバッチ処理であればSpringBatchを使用するのが正解です。
が、そこまで大袈裟にしたくない場合に、mainメソッドから起動する方法を試してみました。

起動するBeanは以下のようなクラスです。サンプルなので、意味のある処理はしていません。コンソールにメッセージを出力するだけです。

【インターフェース】

package sample.springbeanrunner;

/*******************************************************************************
 * 起動するビーンクラスのインターフェース。
 ******************************************************************************/
public interface TargetBean {

  /*****************************************************************************
   * 実行するメソッド。
   ****************************************************************************/
  public void execute();

}

【実装クラス】

package sample.springbeanrunner;

/*******************************************************************************
 * 起動するビーンクラス。
 ******************************************************************************/
public class TargetBeanImpl implements TargetBean {

  /*****************************************************************************
   * 実行するメソッド。
   * @see sample.springbeanrunner.TargetBean#execute()
   ****************************************************************************/
  @Override
  public void execute() {
    System.out.println("★★★ " + this.getClass().getName() + " が起動されました。");

  }

}

このクラスを、mainメソッドから起動します。
コマンドライン引数に、ビーン設定ファイルのパス(オプション)、起動するビーンID(必須)を指定します。
パースには、Apache Commons CLIを使用しました。

【SpringBean起動クラス】

package sample.springbeanrunner;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.springframework.context.support.FileSystemXmlApplicationContext;

/*******************************************************************************
 * SpringBean起動クラス。
 ******************************************************************************/
public class SpringBeanRunner {

  /*****************************************************************************
   * メイン処理。
   ****************************************************************************/
  public static void main(String[] args) {
    // コマンドライン引数をパースする。
    CommandLine cmd = parseCommandLine(args);
    // ビーン設定ファイル名を取得する。
    // ※ コマンドライン引数から指定されないとき、省略時のビーン設定ファイル名を使用する。
    String configLocation =
      (cmd.hasOption("config") ? cmd.getOptionValue("config") : "app-config.xml");
    // SpringFrameworkのビーンファクトリを準備する。
    new FileSystemXmlApplicationContext(configLocation);
    // 起動するBeanを取得する。存在しないとき、例外を投げる。
    TargetBean target = ResourceFactory.getResource(cmd.getOptionValue("target"));
    if (target == null) {
      throw new RuntimeException("ビーン '" + cmd.getOptionValue("target") + "' が定義されていません。");
    }
    // 取得したBeanを実行する。
    target.execute();
  }

  /*****************************************************************************
   * コマンドライン引数をパースする。
   ****************************************************************************/
  private static CommandLine parseCommandLine(String[] args) {
    CommandLine ret = null;
    // オプションのリストを生成する。
    Options options = new Options();
    // リストにオプションを追加する。
    options.addOption(Option.builder("config").required(false).hasArg().argName("config")
                            .desc("ビーン設定ファイルのパス").build());
    options.addOption(Option.builder("target").required().hasArg().argName("target")
                            .desc("起動するBeanID").build());
    // コマンドラインをパースする。
    CommandLineParser parser = new DefaultParser();
    try {
      ret = parser.parse(options, args);
    }
    catch (ParseException e) {
      throw new RuntimeException(e);
    }
    return ret;
  }

}

コマンドラインから指定されたビーンを取得するために、ビーン生成クラスを作ります。
インターフェース「org.springframework.context.ApplicationContextAware」を実装すると、Springが「org.springframework.context.ApplicationContext」を渡してくれます。
このApplicationContextのgetBeanメソッドを呼び出して、指定されたIDのビーンを取得します。

ビーン生成クラスはシングルトンとして、staticメソッドでアクセスできるようにしました。staticメソッドからアクセスするために、自分自身のインスタンスをstatic変数に格納しています。インスタンス化はSpringが行なうので、この格納は@postConstructを指定したメソッドで実行します。

【ビーン生成クラス】

package sample.springbeanrunner;

import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/*******************************************************************************
 * ビーン生成クラス。
 ******************************************************************************/
public class ResourceFactory implements ApplicationContextAware {
  /** 唯一のインスタンス */
  private static ResourceFactory instance;
  /** Springのアプリケーション設定。BeanFactoryとして必要。 */
  private ApplicationContext applicationContext;

  /*****************************************************************************
   * リソースIDで識別される資源を取得する。
   ****************************************************************************/
  public static <T> T getResource(String beanId) {
    T ret = instance.doGetResource(beanId);
    return ret;
  }

  /*****************************************************************************
   * コンストラクタ実行後の後処理を行う。
   ****************************************************************************/
  @PostConstruct
  public void postConstruct() {
    ResourceFactory.instance = this;
  }

  /*****************************************************************************
   * Springのアプリケーション設定を設定する。
   * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
   ****************************************************************************/
  @Override
  public void setApplicationContext(ApplicationContext arg0) {
    // Springのアプリケーション設定を設定する。
    this.applicationContext = arg0;
  }

  /*****************************************************************************
   * リソースIDで識別されるビーンを取得する。
   ****************************************************************************/
  @SuppressWarnings("unchecked")
  private <T> T doGetResource(String beanId) {
    T ret = null;
    try {
      // 設定ファイルからクラスを検索する。
      ret = (T)this.applicationContext.getBean(beanId);
    }
    catch (BeansException e) {
      throw new RuntimeException(e);
    }
    return ret;
  }

}

最後にビーン設定ファイル。

【ビーン設定ファイル】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
  <!-- コンポーネントを検索するクラスパス -->
  <context:component-scan base-package="sample.springbeanrunner" />
  <!-- ビーン生成クラス -->
  <bean id="resourceFactory" scope="singleton"
    class="sample.springbeanrunner.ResourceFactory">
  </bean>
  <!-- 起動するビーンクラス -->
  <bean id="targetBean" scope="prototype"
    class="sample.springbeanrunner.TargetBeanImpl">
  </bean>
</beans>

こうして起点となるビーンさえ起動すれば、あとは通常のSpringと変わりません。