Spring Framework 入門記 Contextその4 リソース

今回は「3.10.3. Using resources within Spring」です.早く3章終わらせたいよっ! AOP(5章)に行きたいよっ!
ここでいうリソースは,URLで示されるものやファイルなどのことで,おおざっぱに言えばjava.io.InputStreamで内容を読み込むことができるようなものです(たぶん).java.lang.ClassLoader#getResource(String)でいうところのリソースと同じようなものですね.
リソースを手に入れるには,ApplicationContext#getResource(String)を使います.このメソッドの引数をlocationと呼びます.ソース上でそうなっているので.それでですね,そのlocationの解釈は,ApplicationContextの実装によって異なるみたいです.ちなみにFileSystemXmlApplicationContext*1の場合は次のようになっているようです.

  1. location"classpath:"で始まっている場合,その後続の文字列は(先頭が'/'で始まっている場合はそれを取り除いて)クラスパス上のリソースとして扱われます.
  2. locationをURLとして解釈できれば(MalformedURLExceptionがスローされなければ),そのURで示されるリソースとして扱われます.
  3. それ以外はファイルシステム上のリソースとして扱われます.ただし,locationの先頭が'/'で始まっている場合は取り除かれます.

という具合です.最後のやつは,WEBアプリケーションで都合がいいようにってことらしいです.
ApplicationContext#getResource(String)の戻り値は,Resourceというインタフェースです.これは,次のメソッドを持っています.

  • getInputStream()
  • getURL()
  • getFile()
  • exists()
  • isOpen()
  • getDescription()

どんなリソースでも,全てのメソッドが有効というわけではありません."file:"でないURLで示されるリソースでは,getFile()は例外を吹っ飛ばしてくれます.
こんな感じでリソースを手に入れて,InputStreamを通して内容を読み込むことができるのですが,これはやっぱりApplicationContextの機能なんですよね.またApplicationContextに依存するのか? なんて思いますよね.その程度のことでApplicationContextAwareimplementsしなきゃいけないのか? なんて思いますよね.
ご安心ください.そんなあなた(誰?)のために,ResourceEditorをご用意させていただきました*2.え? それは何だって? それはですね,PropertyEditorの実装なんですねー.覚えてますか,PropertyEditor.そう,文字列をプロパティの型に変換する,あのプロパティエディタです.BigDecimalなプロパティに値を設定するために使った,あのプロパティエディタです(遠くを見る目).こいつを使うと,Resourceなプロパティにsetterで値を設定できちゃうんですねー.もちろん,コンストラクタにも使えるでしょうとも.
あれ? でも結局,Resourceには依存しちゃうのね.Springとは無関係なBeanとして使えるわけではないのですね.それくらいだったら,素直にjava.io.Filejava.net.URLのプロパティを持つようにした方がいい感じがするなぁ.あ,でもそれだとクラスパスから取ってくることができないのか.うー,なんか今ひとつ.しょうがない,java.io.InputStreamのプロパティで手を打つことにしようか.
なお,ResourceEditorApplicationContextを使用していません.(^^; 実は,getResource(String)は,ResourceLoaderのメソッドで,ApplicationContextはそれをextendsしているんですね.それでResourceEditorが使用するResourceLoaderの実装は,location引数の解釈がFileSystemXmlApplicationContextとは微妙に異なり,ファイルシステムを見てくれません.なんだかなー,今はApplicationContextのお勉強中なのにぃ.
いずれにせよ,あんまり嬉しい代物じゃない気がしてしょうがないんですが,逃げちゃダメですか? そうですかダメですか.無念だ.
やむを得ません,お試ししましょう.
今はApplicationContextの学習中なので,とりあえずは素直にApplicationContextAwareなクラスを作ってみます.

package study;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class ResourceChecker implements ApplicationContextAware {
    private ApplicationContext context;
    private String location;
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }
    public String getLocation() {
        return location;
    }
    public void setLocation(String location) {
        this.location = location;
    }
    public boolean isExists() {
        return context.getResource(location).exists();
    }
}

こいつは,指定されたりソースが存在するかどうかを示すbooleanのread onlyなプロパティを持っています.面倒なので内容を読み込むようなことはしていません.心より恥じる.
次にBean定義のXMLファイル.いつも通り<bean>要素のみの抜粋.

    <bean id="file" class="study.ResourceChecker">
        <property name="location"><value>beans.xml</value></property>
    </bean>
    <bean id="url" class="study.ResourceChecker">
        <property name="location"><value>http://java.sun.com/
    </bean>
    <bean id="classpath" class="study.ResourceChecker">
        <property name="location"><value>classpath:study/Main.class</value></property>
    </bean>
    <bean id="notfound" class="study.ResourceChecker">
        <property name="location"><value>notfound</value></property>
    </bean>

locationを何パターンかご用意させていただきました.
これを入門記の「Contextその1」で作成した実行用のクラスで実行します.

file : study.ResourceChecker@13e58d4
    class=class study.ResourceChecker
    exists=true
    location=beans.xml
url : study.ResourceChecker@39e5b5
    class=class study.ResourceChecker
    exists=true
    location=http://java.sun.com/
classpath : study.ResourceChecker@32efa7
    class=class study.ResourceChecker
    exists=false
    location=classpath:study/Main.class
notfound : study.ResourceChecker@13c7378
    class=class study.ResourceChecker
    exists=false
    location=notfound

こんなもんでいっか.あまり楽しいものではなかったですね.ま,必要になったら思い出すということで.
さぁ,3章も残りはあと少し! 頑張るぞぉ.

*1:だーかーらー,長いんだよー.

*2:もちろん用意したのは私ではありません.