소개
스프링 프로그래밍 모델을 기반으로 손쉽게 커맨드라인 애플리케이션(interactive shell)을 만들 수 있도록 도와주는 프로젝트입니다.
홈페이지(GitHub)
https://github.com/spring-projects/spring-shell
샘플 애플리케이션
Spring Shell은 샘플 애플리케이션을 제공합니다. HelloWorld 애플리케이션은 부트스트랩(Bootstrap)/커맨드(Command)/베너(Banner)/프롬프트(Prompt)의 기본 구현 방법을 보여줍니다.
샘플을 통해 Spring Shell을 이해하는게 효과적이기 때문에 이를 통해 Spring Shell 사용 방법을 하나하나 설명토록 하겠습니다.
Clone
먼저 Spring Shell 프로젝트를 Clone합니다.
~$ git clone git@github.com:spring-projects/spring-shell.git
빌드 및 실행
Clone이 완료되면, samples/helloworld
디렉토리로 이동한 후 ./gradlew installApp
명령으로 HelloWorld 샘플을 빌드합니다.
~$ cd spring-shell/samples/helloworld
~/spring-shell/samples/helloworld$ ./gradlew installApp
:compileJava
:processResources
:classes
:jar
:startScripts
:installApp
BUILD SUCCESSFUL
./gradlew -q run
명령 혹은 build/install/helloworld/bin/helloworld
스크립트를 실행하면 HelloWorld 샘플을 실행할 수 있습니다.
build/install/helloworld
디렉토리에는 실행 스크립트(bin/helloworld)와 의존 라이브러리(lib/*.jar)가 모두 들어 있기 때문에 이 디렉토리만 배포하면 누구든 이 애플리케이션을 사용할 수 있게 됩니다.
사실 실행만 원한다면 빌드 없이 ./gradlew -q run
명령만 사용해도 됩니다.
~$ cd spring-shell/samples/helloworld
~/spring-shell/samples/helloworld$ ./gradlew -q run
=======================================
* *
* HelloWorld *
* *
=======================================
Version:1.2.3
Welcome to HelloWorld CLI
hw-shell>
구현 상세
HelloWorld는 아래 5개의 클래스로 구현되어 있었습니다.
- org.springframework.shell.samples.helloworld.Main
- org.springframework.shell.samples.helloworld.commands.HelloWorldCommands
- org.springframework.shell.samples.helloworld.commands.MyBannerProvider
- org.springframework.shell.samples.helloworld.commands.MyHistoryFileNameProvider
- org.springframework.shell.samples.helloworld.commands.MyPromptProvider
Main 클래스는 org.springframework.shell.Bootstrap 클래스의 main 메소드를 호출하여 애플리케이션을 실행합니다.
/**
* Main class that delegates to Spring Shell's Bootstrap class in order to simplify debugging inside an IDE
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Bootstrap.main(args);
}
이 때 "/META-INF/spring/spring-shell-plugin.xml" 파일을 이용해 ApplicationContext를 생성합니다. spring-shell-plugin.xml 파일은 다음과 같이 단촐합니다.
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.springframework.shell.samples.helloworld.commands" />
</beans>
component-scan을 통해 bean을 찾기 때문에 Main 이외의 모든 클래스는 Component annotation을 달고 있습니다.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyPromptProvider extends DefaultPromptProvider {
@Override
public String getPrompt() {
return "hw-shell>";
}
@Override
public String getProviderName() {
return "My prompt provider";
}
}
위의 MyPromptProvider 클래스는 Order annotation 또한 달고 있는데, 이는 혹 클래스 패스에 다른 plugin이 Provider(Banner/Prompt/HistoryFileName) 구현체를 가지고 있더라도 내가 구현한 Provider를 우선하도록(Ordered.HIGHEST_PRECEDENCE
) 합니다. Provider에 대해서는 곧 설명합니다.
Provider는 Spring Shell이 쉘을 커스터마이징 할 수 있도록 제공하는 확장 포인트로 모두 org.springframework.shell.plugin.NamedProvider 인터페이스를 상속한 인터페이스입니다.
HelloWorld가 사용하는 구현 클래스는 모두 Spring Shell이 제공하는 각 Provider의 기본 구현체(DefaultXXXProvider)를 상속하고 있습니다. HelloWorld는 총 3개의 Provider 구현 클래스를 가지고 있습니다.
- MyBannerProvider는 이름이 알려주듯이 배너 정보를 담고 있는데, 이외에 버전정보(
getVersion()
), 환영 메시지(getWelcomeMessage()
) 또한 제공합니다.
- MyPromptProvider는 프롬프트 모양을 정의(
getPrompt()
)합니다.
- MyHistoryFileNameProvider는 쉘의 동작 히스토리를 담을 로그파일 이름(
my.log
)을 정의합니다.
MyHistoryFileNameProvider의 코드는 다음과 같습니다.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyHistoryFileNameProvider extends DefaultHistoryFileNameProvider {
public String getHistoryFileName() {
return "my.log";
}
@Override
public String getProviderName() {
return "My history file name provider";
}
}
MyBannerProvider의 코드는 다음과 같습니다.
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyBannerProvider extends DefaultBannerProvider {
public String getBanner() {
StringBuffer buf = new StringBuffer();
buf.append("=======================================" + OsUtils.LINE_SEPARATOR);
buf.append("* *"+ OsUtils.LINE_SEPARATOR);
buf.append("* HelloWorld *" +OsUtils.LINE_SEPARATOR);
buf.append("* *"+ OsUtils.LINE_SEPARATOR);
buf.append("=======================================" + OsUtils.LINE_SEPARATOR);
buf.append("Version:" + this.getVersion());
return buf.toString();
}
public String getVersion() {
return "1.2.3";
}
public String getWelcomeMessage() {
return "Welcome to HelloWorld CLI";
}
@Override
public String getProviderName() {
return "Hello World Banner";
}
}
HelloWorld를 실행하면 배너와 버전 정보, 환영 메시지가 출력된 후 hw-shell> 프롬프트 옆에서 커서가 깜빡입니다.
앞에서 설명했듯이 이 때 출력되는 모든 정보는 MyBannerProvider가 제공하며, hw-shell> 프롬프트는 MyPromptProvider에서 정의하고 있습니다.
그리고 HelloWorld를 실행한 디렉토리를 탐색해 보면 MyHistoryFileNameProvider에서 정의한 my.log
파일이 생성된 것을 확인할 수 있습니다.
=======================================
* *
* HelloWorld *
* *
=======================================
Version:1.2.3
Welcome to HelloWorld CLI
hw-shell>
지금까지 Spring Shell에 대한 기본적인 내용과 Provider에 대해 알아봤습니다.
가장 중요한 커맨드 클래스에 대해서는 따로 글을 올리겠습니다.