Spring が依存性注入を処理する方法をよりよく理解しようとしていますが、(少なくとも私にとっては) 非常に奇妙な状況に直面するようになりました。
私は、次のような視覚的に記述できる階層化されたアプリケーションを持っています: web - ビジネス - ドメインモデル - dal ... 別々のモジュールのすべてのレイヤー。
クラスに必要なものを注入するために注釈を使用しています。さらに複雑にするために、ビジネス層でキュウリを使用しています。つまり、テスト時にテスト Bean を明示的に注入し、テストしていないときに実際の Bean を注入する必要があります。
すべてが正常にコンパイルされ、実行および注入 ( @Inject
) もすべて正常に行われます...次の行を Web レイヤーの context.xml に追加するまで:
<context:component-scan base-package="com.foo.bar.baz.bleep.bll" />
... これが私の問題の始まりです。その行が追加されるとコンパイルは正常に完了しますが、Tomcat で実行すると次のエラーが発生します。
Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sampleAppServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.bar.baz.bleep.dal.SampleAppDao com.foo.bar.baz.bleep.bll.SampleAppServiceImpl.sampleAppDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.foo.bar.baz.bleep.dal.SampleAppDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4797)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5291)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
突然、コンテナーから dao が失われました (?)。
ビジネス層のビジネス サービスを Web 層で利用したいのですが、bll のスキャンを Web 層に追加しようとすると、すぐにビジネス層が壊れてしまいます。何を与える?
私web-context.xml
のWebレイヤー
<context:component-scan base-package="com.foo.bar.baz.bleep.bll" />
<context:component-scan base-package="com.foo.bar.baz.bleep.domainmodel" />
<context:component-scan base-package="com.foo.bar.baz.bleep.web" />
私cucumber.xml
のビジネス層
<context:component-scan base-package="com.foo.bar.baz.bleep.bll" />
<context:component-scan base-package="com.foo.bar.baz.bleep.domainmodel" />
<import resource="classpath:business-context.xml" />
私の二人business-context.xml
テストbusiness-context.xml
<bean id="sampleAppDao" class="com.foo.bar.baz.bleep.dal.inMemorySampleAppDaoImpl"></bean>
本物business-context.xml
<bean id="sampleAppDao" class="com.foo.bar.baz.bleep.dal.sampleAppDaoImpl"></bean>
私の実装はすべて、適切なコンポーネント アノテーション (つまり、Web サーブレットの @Controller、ビジネス サービスの @Service、dao の @Repository) で注釈が付けられています。すべてのモジュールには、pom.xml 内の他のモジュールへの必要な依存関係があります。
2 つの質問があります。
私は何を間違っていますか?明らかに=)
この自動配線ビジネスはすべて間違っていますか?
context:component-scan base-package="com.foo" />
多くのチュートリアルで見たように、すべて ( ) ではなく、各モジュールが特に必要とするパッケージのみをスキャンすることを考えていました。注意すべき点は、以前に他のプロジェクトですべてをスキャンしようとしましたが、同じ問題に遭遇したことです。そのため、各モジュールに必要なものだけをスキャンし始めました。
この仕事をする方法について私にアドバイスしてください!
更新 1
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Display Name Here</display-name>
<!-- Spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-context.xml</param-value>
</context-param>
<!-- Vaadin servlet -->
<servlet>
<servlet-name>Coconut Scooter</servlet-name>
<servlet-class>ru.xpoft.vaadin.SpringVaadinServlet</servlet-class>
<init-param>
<description>Vaadin UI to display</description>
<param-name>beanName</param-name>
<param-value>mainUI</param-value>
</init-param>
<init-param>
<description>Application widgetset</description>
<param-name>widgetset</param-name>
<param-value>com.foo.bar.baz.bleep.common.widgetset.AppWidgetSet</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Coconut Scooter</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Coconut Scooter</servlet-name>
<url-pattern>/VAADIN/*</url-pattern>
</servlet-mapping>
<context-param>
<description>Vaadin production mode</description>
<param-name>productionMode</param-name>
<param-value>false</param-value>
</context-param>
</web-app>
web-context.xml
<context:component-scan base-package="com.foo.bar.baz.bleep"
use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
きゅうり.xml
<context:component-scan base-package="com.foo.bar.baz.bleep">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<import resource="classpath:business-context.xml" />
更新 2
M. Deinum の回答のコメントのように、Spring MVC もディスパッチャーサーブレットも使用しない場合、Bean を単一の ApplicationContext にロードするために、すべてのコンテキスト ファイルを ContextLoaderListener に安全にロードできます。これは、すべてのパッケージをスキャンする component-scan を使用するだけで実行できます。以下の更新されたコンテキスト ファイルを参照してください。
web-context.xml
<context:component-scan base-package="com.foo.bar.baz.bleep" />
きゅうり.xml
<context:component-scan base-package="com.foo.bar.baz.bleep" />
<import resource="classpath:business-context.xml" />