【问题标题】:How to think when wiring beans豆子接线时如何思考
【发布时间】:2013-08-30 07:36:01
【问题描述】:

我试图更好地掌握 Spring 处理依赖注入的方式,但我遇到了一个(至少对我而言)非常奇怪的情况。

我有一个分层应用程序,可以在视觉上描述如下: web - business - domainmodel - dal ...每一层都在单独的模块中。

我正在使用注解来注入我的类中需要的东西,并让事情变得更复杂我在业务层中使用 cucumber,这意味着我需要在测试时显式注入测试 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

<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-servlet 上的 @Controller、业务服务上的 @Service 和 dao 上的 @Repository) 所有模块都对 pom.xml 中的其他模块具有必要的依赖关系。

我有两个问题:

  1. 我做错了什么?显然 =)

  2. 我把这个自动接线的事情搞错了吗?我在想我只会扫描每个模块特别需要的包,而不是所有东西(context:component-scan base-package="com.foo" /&gt;),就像我在许多教程中看到的那样。有一点需要注意:我之前在其他项目中尝试过扫描所有内容,但遇到了同样的问题,这就是为什么我开始扫描每个模块所需的内容。

请告诉我如何完成这项工作!

更新 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 也不使用 dispatcherservlet 时,您可以安全地将所有上下文文件加载到 ContextLoaderListener 中,以便将您的 bean 加载到单个 ApplicationContext 中。这可以通过简单地使用扫描所有包的组件扫描来完成。请参阅下面更新的上下文文件。

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" />

【问题讨论】:

  • 您是如何在 Controller 中声明 Dao 或在哪里使用它的?
  • @araknoid 我没有在控制器中声明 dal-package (daos),因为控制器不需要知道它。但是,我已经在business-context.xmls中声明了daos,它是cucumber.xml的资源。
  • 作为提示...您应该将组件扫描分组为:&lt;context:component-scan base-package="com.foo.bar.baz.bleep.domainmodel, com.foo.bar.baz.bleep.bll, xxx, xxx" /&gt;,而不是拥有多个 xml 元素。
  • 是的,明白了。感谢您的建议。不幸的是,这不能解决我的问题。
  • 您是否尝试过在context.xml 中也导入您的business-context.xml 资源?

标签: java spring


【解决方案1】:

您的配置错误,您的 web 层应该只扫描与 web 相关的 bean,您也在扫描其他类型的 bean,这些 bean 也在您的其他应用程序上下文中扫描。基本上,您正在创建 yuor 服务的 2 个实例,第二个因无法满足其依赖关系而失败。

所以基本上你的网站应该这样做

<context:component-scan base-package="com.foo.bar.baz.bleep.web" />

不多也不少。另一个可以保持原样。

查看我的相关回答here

【讨论】:

  • 另外,为了更好地控制创建的 bean,可以优先使用 XML 配置。
  • 如果我不扫描 bll 层,我的 web 层如何知道我的服务?在我的 Web 层中 - 我是否不必 @Inject 我的 sampleAppService 才能使用 someRandomService(),(因此扫描 bll-package)?
  • 查看我的相关答案。一般来说,你有一个ContextLoaderListener,它引导一个弹簧ApplicationContext(通常称为根上下文)。此根上下文用作DispatcherServlet 加载的ApplicationContext 的父级。现在,当您的 bean 需要在自己的上下文中找不到的依赖项时,它将查看其父级。
  • 好的,希望我正确理解了您的相关答案。所以我所做的是使用 context:component-scan + filters 组件扫描仅 Web 层中的 Controller 注释和组件扫描除业务层中的 Controller 注释之外的所有内容。这导致了 Tomcat 的成功编译和成功启动,但是当我尝试 @Inject sampleAppService 时,它​​说我没有该类型的匹配 bean。
  • 所有内容都加载在单个 ApplicationContext 中(元数据由多个 xml 文件组成,但它们都加载在单个 ApplicationContext 中)。所有&lt;context:component-scan .. /&gt; 都合并为一个。如果有另一个 ApplicationContext 执行相同的 &lt;context:component-scan .. /&gt; 或者您明确定义了另一个相同类型的 bean(它没有覆盖其他定义),那么您只会得到重复的 bean。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-14
  • 1970-01-01
  • 2013-10-11
相关资源
最近更新 更多