Spring Framework4のClassPathXmlApplicationContextで存在するはずのサービス定義ファイルが読み込めない場合の解決策


スポンサーリンク

Spring FrameworkでDependency Injectionを使いたい。

でも、Spring Framworkで絶対パスを指定しているはずなのに、
というか、File.exists()で存在確認しているはずなのに、ファイルが読み込めない。

なんてことでハマったので、解決策を記しておく。

Springは2014年現在最新のSpring4.1.2。
Mavenの依存関係は以下。

pom.xml

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.2.RELEASE</version>
    <scope>runtime</scope>
</dependency>

ダメな場合のJavaはこちら。
f:id:sho322:20141127140913j:plain

package twitter.action;

import java.io.File;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import twitter.analyze.PublicTimeLineSearch;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

public class Main {
	public static void main(String[] args) throws TwitterException {
		Twitter twitter = new TwitterFactory().getInstance();

		//本当は外部から渡す
		File serviceFile = new File("services.xml");
		if (!serviceFile.exists()) {
			System.err.println("サービス定義ファイルが存在しません:" + serviceFile.getPath());
			System.exit(1);
		} else {
			System.out.println("サービス定義を読み込みます:" + serviceFile.getAbsolutePath());
		}
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { serviceFile.getAbsolutePath()});
		//ApplicationContext context = new FileSystemXmlApplicationContext( serviceFile.getAbsolutePath());

		PublicTimeLineSearch pSearch = context.getBean("publicTimeLineAnalyzor",PublicTimeLineSearch.class);

		List<Status> list = pSearch.getQuerySearchResult(twitter);

	}
}


これを実行すると、こんなエラーが出る。

サービス定義を読み込みます:C:\eclipse\pleiades\eclipse\workspace_mine\twitter-analyzor\services.xml
11 27, 2014 1:55:38 午後 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@742cb491: startup date [Thu Nov 27 13:55:38 JST 2014]; root of context hierarchy
11 27, 2014 1:55:38 午後 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [C:/eclipse/pleiades/eclipse/workspace_mine/twitter-analyzor/services.xml]
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [C:/eclipse/pleiades/eclipse/workspace_mine/twitter-analyzor/services.xml]; nested exception is java.io.FileNotFoundException: class path resource [C:/eclipse/pleiades/eclipse/workspace_mine/twitter-analyzor/services.xml] cannot be opened because it does not exist
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:344)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:252)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
	at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
	at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
	at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
	at twitter.action.Main.main(Main.java:27)
Caused by: java.io.FileNotFoundException: class path resource [C:/eclipse/pleiades/eclipse/workspace_mine/twitter-analyzor/services.xml] cannot be opened because it does not exist
	at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330)
	... 13 more

そういうときのもっとも簡単な解決策は、FileSystemXmlApplicationContextを使ってサービス定義を読み込むことだ。
以下のようにすると、サービス定義を読み込むことができる。

package twitter.action;

import java.io.File;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import twitter.analyze.PublicTimeLineSearch;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

public class Main {
	public static void main(String[] args) throws TwitterException {
		Twitter twitter = new TwitterFactory().getInstance();

		//本当は外部から渡す
		File serviceFile = new File("services.xml");
		if (!serviceFile.exists()) {
			System.err.println("サービス定義ファイルが存在しません:" + serviceFile.getPath());
			System.exit(1);
		} else {
			System.out.println("サービス定義を読み込みます:" + serviceFile.getAbsolutePath());
		}
		//ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { serviceFile.getAbsolutePath()});
		ApplicationContext context = new FileSystemXmlApplicationContext( serviceFile.getAbsolutePath());

		PublicTimeLineSearch pSearch = context.getBean("publicTimeLineAnalyzor",PublicTimeLineSearch.class);

		List<Status> list = pSearch.getQuerySearchResult(twitter);

	}
}

参考)
http://stackoverflow.com/questions/21479331/can-not-open-beans-xml-config-file-because-does-not-exist

ClassPathXmlApplicationContextを使って読み込みたい場合は、書き方に工夫がいる。

new ClassPathXmlApplicationContext("file:src/main/resources/beans.xml");

のように、file:を接頭辞につけなければいけない。

以下の例だとうまいこと読み込むことができた。

package twitter.action;

import java.io.File;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import twitter.analyze.PublicTimeLineSearch;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;

public class Main {
	public static void main(String[] args) throws TwitterException {
		Twitter twitter = new TwitterFactory().getInstance();

		//本当は外部から渡す
		File serviceFile = new File("services.xml");
		if (!serviceFile.exists()) {
			System.err.println("サービス定義ファイルが存在しません:" + serviceFile.getPath());
			System.exit(1);
		} else {
			System.out.println("サービス定義を読み込みます:" + serviceFile.getAbsolutePath());
		}
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"file:" + serviceFile.getAbsolutePath()});
		//ApplicationContext context = new FileSystemXmlApplicationContext( serviceFile.getAbsolutePath());

		PublicTimeLineSearch pSearch = context.getBean("publicTimeLineAnalyzor",PublicTimeLineSearch.class);

		List<Status> list = pSearch.getQuerySearchResult(twitter);


	}
}

参考)
http://stackoverflow.com/questions/12893760/spring-cannot-find-bean-xml-configuration-file-when-it-does-exist

Spring Framework 4プログラミング入門

Spring Framework 4プログラミング入門