Springホームページからダウンロードする。
という方法が正当なのだと思うが、メールアドレスとか入力するのが面倒なので、以下のサイトからダウンロードするのが手っ取り早い。
後者の方法だと最新版はダウンロードできないようだけど。
「-with-dependencies」が付いているファイルは、aspectjだのmockだのlibだの、srcやらtestやらも入っている。
「-with-docs」でもソース(spring-sources.jar)は入っている。
ダウンロードしたら、適当なディレクトリーに解凍する。
Springのコア(spring.jar)はspring-framework-2.5.5/dist/spring.jarにある。
Eclipseでソースの添付を行う場合、spring.jarのソースにdist/spring-sources.jarを指定する。
EclipseのWeb開発プラグイン(WTP)でウェブアプリを作って、そこでlog4jの出力を行うサンプル。(Tomcat5.5で実験)
Springでは、web.xmlにlog4jの設定を書いておき、log4j.propertiesを読み込ませて初期化することが出来る。
WEB-INF/libの下にspring.jarとlog4jのjarファイル(log4j-1.2.14.jarとか)を置いておく。
WEB-INF/web.xmlにlog4jの設定を記述する。
(spring-framework-2.5.5/sample/jpetstoreとかにサンプルがあるので、コピペすればよい)
<!--
- Key of the system property that should specify the root directory of this
- web app. Applied by WebAppRootListener or Log4jConfigListener.
当ウェブアプリのルートディレクトリーを指定するシステムプロパティーのキーとなる。
WebAppRootListenerあるいはLog4jConfigListenerによって設定される。
-->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>spring1_sample.root</param-value>
</context-param>
<!--
- Location of the Log4J config file, for initialization and refresh checks.
- Applied by Log4jConfigListener.
-->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
リスナーにLog4jConfigListenerを指定することで、ウェブアプリ起動時にlog4jの初期化が行われる。
Log4jConfigListener(実体はLog4jWebConfigurer)でweb.xml(ServletContext)からwebAppRootKeyの値が読まれ、ウェブアプリのルートディレクトリーを示すシステムプロパティーのキーとして使用される。
(例えば上記の設定なら、System.getProperty("spring1_sample.root")で、ウェブアプリの実行時のルートディレクトリー(「C:\〜Tomcat〜\webapps\spring1\」)が取得できるようになる)
※したがって、複数のウェブアプリをひとつのAPサーバーで動かす場合は、キー名となるwebAppRootKeyの値は異なるものにしなければならない。(同じ値(キー名)が既に存在しているとIllegalStateExceptionが発生する)
※この辺りの処理は、WebUtils.setWebAppRootSystemProperty()で行われている。
そして、log4jConfigLocationで指定されたlog4j.properties(あるいはlog4j.xmlでも 可能らしい)が読まれる。
log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=${spring1_sample.root}/WEB-INF/spring1.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.propertiesでは、${}でプロパティーの値を取得することが出来る。
(この例では、web.xmlのwebAppRootKeyで指定されたキー名を指定している(この値はウェブアプリの実行時のルートディレクトリーとなる))
複数のウェブアプリでlog4jの出力(設定)を行う場合、web.xmlやlog4j.propertiesを別々に記述する。
その際、前述の通り、web.xmlのwebAppRootKeyの値は異なるものにしておく必要がある。
| spring1/WEB-INF/web.xml | spring2/WEB-INF/web.xml |
<context-param> <param-name>webAppRootKey</param-name> <param-value>spring1.root</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> |
<context-param> <param-name>webAppRootKey</param-name> <param-value>spring2.root</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> |
| spring1/WEB-INF/log4j.properties | spring2/WEB-INF/log4j.properties |
log4j.appender.logfile.File=${spring1.root}/WEB-INF/spring1.log
|
log4j.appender.logfile.File=${spring2.root}/WEB-INF/spring2.log
|
また、log4jの設定自体はクラスローダーによってロードされた領域(てきとーな概念で言えば、ロードされたlog4j.jar毎)に保持される。
つまり、もしlog4j.jarをTomcatのcommon/libのような共通の場所に置いた場合、ウェブアプリが異なっていてもlog4j.jar自体は共有される為、log4jの設定も別々にはならない。
Tomcatの起動ログを見ると、以下のように別々に設定ファイルを読み込めているように見える。
2009/12/23 6:06:42 org.apache.catalina.core.ApplicationContext log 情報: Set web app root system property: 'spring1.root' = [C:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\spring1\] 2009/12/23 6:06:42 org.apache.catalina.core.ApplicationContext log 情報: Initializing log4j from [C:\workspace34\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\spring1\WEB-INF\log4j.properties] 2009/12/23 6:06:42 org.apache.catalina.core.ApplicationContext log 情報: Set web app root system property: 'spring2.root' = [C:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\spring2\] 2009/12/23 6:06:42 org.apache.catalina.core.ApplicationContext log 情報: Initializing log4j from [C:\workspace34\.metadata\.plugins\org.eclipse.wst.server.core\tmp2\wtpwebapps\spring2\WEB-INF\log4j.properties]
しかし、log4j.jarが共有の場合、後から読み込んだ設定が有効になる。
すなわち、どちらのウェブアプリのログも、spring2.logに出力されるようになってしまう。
log4j.jarをcommon/libに置かず、各ウェブアプリのWEB-INF/libに置けば、個別の定義がそれぞれ有効になる。
また、log4j.jarがcommon/libに有っても、WEB-INF/libの下にも置いておけば、別々に扱われるようだ。
(ちなみにshared/libの下にjarファイルを置いても使われないようだった…EclipseのWTPの問題?)
アプリ内で直接newを使ってインスタンスを生成するのではなく、定義ファイルにクラス名を書いておいてそのインスタンスを生成する。
(なお、実行する為には、spring.jarの他にcommons-loggingのjarが必要)
| 通常の実装 | SpringのDIを使った実装 |
|---|---|
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; |
|
public static void main(String[] args) {
Sample1 s = new Sample1Impl();
s.setValue1("zzz");
System.out.println(s.getValue1());
}
|
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Sample1 s = (Sample1) context.getBean("sample1");
System.out.println(s.getValue1());
}
ClassPathXmlApplicationContextクラスで、定義ファイルを読み込む。クラス名の通り、クラスパス内に存在するXMLファイルを読み込む。 つまり、classes直下にあるなら「ac.xml」、「classes/sample/ac.xml」なら「sample/ac.xml」と指定する。 |
classes/applicationContext.xml:<?xml version="1.0" encoding="Windows-31J"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="sample1" class="jp.hishidama.sample.Sample1Impl"> <property name="value1" value="zzz1"/> </bean> </beans> bean要素のid属性でBeanの名前を付ける。Javaプログラム等からは、この名前で参照する。 |
| Sample1インターフェース | Sample1具象クラス |
|---|---|
package jp.hishidama.sample;
public interface Sample1 {
public void setValue1(String s);
public String getValue1();
}
|
package jp.hishidama.sample;
public class Sample1Impl implements Sample1 {
protected String value1;
@Override
public void setValue1(String s) {
this.value1 = s;
}
@Override
public String getValue1() {
return value1;
}
}
|
バッチアプリでインスタンスを生成するのと同様だが、bean定義ファイルの場所はweb.xmlで指定する。
したがって、ApplicationContextの取得方法が異なる。
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils;
public class SampleServlet extends HttpServlet {
@Override
public void init() throws ServletException {
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(super.getServletContext());
Sample1 s = (Sample1) context.getBean("sample1");
System.out.println("Sample1#value1: " + s.getValue1());
}
}
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
contextConfigLocationにbean定義ファイルを指定するが、複数のファイルをスペース区切りで指定することも出来る。
内容はバッチの例と全く同じ。(ファイルの置き場が違うだけ)
bean定義ファイル(applicationContext.xml)にBeanのインスタンス化の方法や初期値を書く。
| 属性 | 内容 | 備考 |
|---|---|---|
| id | そのBeanを表す識別子。他の場所(Javaプログラムとか)からは、この名前を使用して参照する。 | |
| class | 実体を表すクラス。 | |
| scope | インスタンスのスコープ。省略時のデフォルトはsingleton。 singleton …要求が無くても初期処理でインスタンスが1個だけ作られ、要求がある度に同じインスタンスが返される。 prototype…要求がある度に毎回インスタンスが作られる。 |
|
| factory-bean | ファクトリー(そのクラスのインスタンスを生成するクラス)のidを指定する。 つまり、別のbean定義でそのファクトリークラスを指定しなければならない。 |
|
| factory-method | インスタンスを生成するメソッドのメソッド名を指定する。 | |
| init-method | インスタンス生成後に呼ばれる初期化用メソッドのメソッド名を指定する。 | Spring2.5では、この属性を使わなくても、 初期化メソッドに@PostConstructアノテーションを付けることでも呼ばれるようになる。 |
| destroy-method | インスタンスが破棄される時に呼ばれるメソッドのメソッド名を指定する。 これはsingletonのときしか呼ばれないらしい。 (prototypeだとインスタンスのライフサイクル(いつ使われなくなるか)をSpringが管理できないから当然だわなぁ) |
Spring2.5では、この属性を使わなくても、 終了メソッドに@PreDestroyアノテーションを付けることでも呼ばれるようになるらしい。 |
| 例 | applicationContext.xml | 関連するソース | |||
|---|---|---|---|---|---|
| 毎回インスタンスを生成する例 |
<bean id="sample1" class="Sample1Impl" scope="prototype"> <property name="value1" value="zzz1"/> </bean> |
||||
| ファクトリークラスを使用する例 |
<bean id="sample1factory" class="Sample1Factory" /> <bean id="sample1" scope="prototype" factory-bean="sample1factory" factory-method="createSample1"> <property name="value1" value="zzz1"/> </bean>
|
public class Sample1Factory {
public Sample1 createSample1() {
return new Sample1Impl();
}
}
|
|||
| 自分自身のインスタンス取得メソッドを使用する例 |
<bean id="sample2" class="Sample2" scope="singleton" factory-method="getInstance" /> |
public class Sample2 {
private static final Sample2 INSTANCE = new Sample2();
public static Sample2 getInstance() {
return INSTANCE;
}
private Sample2() { //コンストラクターはprivate
}
}
|
|||
| 初期化メソッドを呼び出す例 |
<bean id="sample1" class="Sample1Impl" scope="prototype" init-method="init"> <property name="value1" value="zzz1"/> </bean> |
public void init() { System.out.println(value1); //値はセットされているから使える } |
|||
<bean id="sample1" class="Sample1Impl" scope="prototype"> <property name="value1" value="zzz1"/> </bean> |
@PostConstruct public void init() { System.out.println(value1); //値はセットされているから使える } |
||||
| コンストラクターを呼び出す例 |
<bean id="str1" class="java.lang.String" scope="singleton"> <constructor-arg> <value>str1abc</value> </constructor-arg> </bean> |
複数の引数がある場合、constructor-argを複数書く。 index属性で引数の順序を指定する。(0からの連番) |
|||
| 例 | applicationContext.xml | 関連するソース | |||
| Stringのプロパティーを指定する例 |
<property name="value1" value="zzz1" /> |
protected String value1;
public void setValue1(String s) {
this.value1 = s;
}
|
|||
<property name="value1">
<value>zzz1</value>
</property>
|
|||||
| intのプロパティーを指定する例 |
<property name="value2" value="123" /> |
protected int value2;
public void setValue2(int n) {
this.value2 = n;
}
|
|||
| nullを明示的に指定する例 |
<property name="value1">
<null />
</property>
|
intに対してnullを入れようとすると例外が発生する。 | |||
| 他のBeanを指定する例 |
<property name="value1">
<ref bean="str1" />
</property>
|
<import
resource="context2.xml"/>によって別の定義ファイルを読み込んだ場合、 beanだと別ファイル内で定義されたidを参照できるが、 localだと参照できない。 |
|||
<property name="value1">
<ref local="str1" />
</property>
|
|||||
| Listのプロパティーを指定する例 |
<property name="value3">
<list value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</list>
</property>
|
protected List<String> value3; public void setValue3(List<String> list) { this.value3 = list; }実体はArrayListになるっぽい。 |
|||
| Setのプロパティーを指定する例 |
<property name="value4">
<set value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</set>
</property>
|
protected Set<String> value4; public void setValue4(Set<String> set) { this.value4 = set; }実体はLinkedHashSetになるっぽい。 |
|||
| Mapのプロパティーを指定する例 |
<property name="value5">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="abc" value="123" />
<entry key="def">
<value>456</value>
</entry>
<entry key-ref="str1" value="789" />
<entry key="ghi" value-ref="str1" />
</map>
</property>
|
protected Map<String, String> value5; public void setValue5(Map<String, String> map) { this.value5 = map; }実体はLinkedHashMapになるっぽい。 |
|||
| プロパティーの値をプロパティーファイルから取得する例 |
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>jp/hishidama/sample/sample3.properties</value>
</list>
</property>
</bean>
<property name="value1" value="${prop1}だよん" />
|
ウェブアプリの場合は/WEB-INF/sample3.propertiesの様に指定する。 listなので、複数ファイルを指定可能。 |
|||
sample3.properties:prop1=abc
|
|||||
| 定数を指定する例 |
<bean id="INT_MAX"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField" value="java.lang.Integer.MAX_VALUE"/>
</bean>
<property name="value2" ref="INT_MAX" /> |
||||
XMLファイルにutilのxsdを指定すると、別の定義方法も使えるらしい。
参考: @ITのSpring
2.0時代の開発スタイル(2)
<?xml version="1.0" encoding="Windows-31J"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
〜
</beans>
| 例 | applicationContext.xml | |
|---|---|---|
| List |
<property name="value3">
<util:list list-class="java.util.ArrayList" value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</util:list>
</property>
|
<util:list id="list1"> <value>foo</value> <value>bar</value> </util:list> <property name="value3" ref="list1" /> |
| Set |
<property name="value4">
<util:set set-class="java.util.HashSet" value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</util:set>
</property>
|
<util:set id="set1"> <value>foo</value> <value>bar</value> </util:set> <property name="value4" ref="set1" /> |
| Map |
<property name="value5">
<util:map map-class="java.util.HashMap" key-type="java.lang.String"
value-type="java.lang.String">
<entry key="abc" value="123" />
<entry key="def">
<value>456</value>
</entry>
<entry key-ref="str1" value="789" />
<entry key="ghi" value-ref="str1" />
<entry key="jkl">
<ref local="str1" />
</entry>
</util:map>
</property>
|
<util:map id="map1"> 〜 </util:map> <property name="value5" ref="map1" /> |
| 定数 |
<property name="value2">
<util:constant static-field="java.lang.Integer.MAX_VALUE" />
</property>
|
<util:constant id="INT_MAX" static-field="java.lang.Integer.MAX_VALUE" /> <property name="value2" ref="INT_MAX" /> |
util系の要素は、元の要素を継承して属性がちょっと増えている程度(util:list要素はlist要素を継承し、list-class属性が増えている)なので、今まで通りの使い方をするなら別にutil系の要素を使う必要も無いと思うが。
util系の要素はbeans要素の直下にidを付けて置くことが出来るので、refで参照することが出来る。
Spring2.5では、初期化メソッド・破棄メソッドに関しては、XMLファイル内にinit-method属性やdestroy-method属性でメソッド名を書くのではなく、アノテーションを使うことが出来る。
ただしXMLファイル内にcontext:annotation-config要素を記述しておく必要がある。(記述してないと、アノテーションが付いていても呼ばれない)
参考: InfoQのSpring
2.5の新機能
<?xml version="1.0" encoding="Windows-31J"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
〜
<bean id="sample4" class="jp.hishidama.sample.spring.Sample4Impl" scope="prototype">
<property name="value1" value="zzz4" />
</bean>
〜
<context:annotation-config />
</beans>
| Sample4インターフェース | Sample4具象クラス |
|---|---|
import javax.annotation.PostConstruct;このアノテーションは、JDK1.6に標準で入っている。 |
|
public interface Sample4 {
public void setValue1(String s);
public String getValue1();
}
|
public class Sample4Impl implements Sample4 {
protected String value1;
public void setValue1(String s) {
this.value1 = s;
}
public String getValue1() {
return value1;
}
@PostConstruct
protected void init() {
System.out.println(value1); //値はセットされているから使える
}
}
|
| 初期化メソッドの可視性はpublicでもprivateでもよい。 戻り値はvoid、引数は無し。 |