@Autowired - ไม่พบถั่วที่มีคุณสมบัติสำหรับการพึ่งพา


90

ฉันเริ่มโครงการโดยสร้างเอนทิตีบริการและการทดสอบ JUnit สำหรับบริการโดยใช้ Spring และ Hibernate ทั้งหมดนี้ใช้งานได้ดี จากนั้นฉันได้เพิ่ม spring-mvc เพื่อสร้างเว็บแอปพลิเคชันนี้โดยใช้แบบฝึกหัดทีละขั้นตอน แต่เมื่อฉันพยายามสร้าง Controller ด้วยคำอธิบายประกอบ @Autowired ฉันได้รับข้อผิดพลาดจาก Glassfish ในระหว่างการปรับใช้ ฉันเดาว่าด้วยเหตุผลบางอย่าง Spring ไม่เห็นบริการของฉัน แต่หลังจากพยายามหลายครั้งฉันก็ยังไม่สามารถจัดการได้

ทดสอบบริการด้วย

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/beans.xml"})

และ

@Autowired
MailManager mailManager;

ทำงานได้อย่างถูกต้อง

ตัวควบคุมที่ไม่มี @Autowired ด้วยฉันสามารถเปิดโครงการของฉันในเว็บเบราว์เซอร์ได้โดยไม่มีปัญหา

/src/main/resources/beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
        http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd">

    <context:property-placeholder location="jdbc.properties" />

    <context:component-scan base-package="pl.com.radzikowski.webmail">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!--<context:component-scan base-package="pl.com.radzikowski.webmail.service" />-->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- Persistance Unit Manager for persistance options managing -->
    <bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="defaultDataSource" ref="dataSource"/>
    </bean>

    <!-- Entity Manager Factory for creating/updating DB schema based on persistence files and entity classes -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitManager" ref="persistenceUnitManager"/>
        <property name="persistenceUnitName" value="WebMailPU"/>
    </bean>

    <!-- Hibernate Session Factory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--<property name="schemaUpdate" value="true" />-->
        <property name="packagesToScan" value="pl.com.radzikowski.webmail.domain" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            </props>
        </property>
    </bean>

    <!-- Hibernate Transaction Manager -->
    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <!-- Activates annotation based transaction management -->
    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

/webapp/WEB-INF/web.xml

<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="WebApp_ID" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>Spring Web MVC Application</display-name>
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

/webapp/WEB-INF/mvc-dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="pl.com.radzikowski.webmail" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <mvc:annotation-driven/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>

pl.com.radzikowski.webmail.service.AbstractManager

package pl.com.radzikowski.webmail.service;

import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Master Manager class providing basic fields for services.
 * @author Maciej Radzikowski <maciej@radzikowski.com.pl>
 */
public class AbstractManager {

    @Autowired
    protected SessionFactory sessionFactory;

    protected final Logger logger = Logger.getLogger(this.getClass());

}

pl.com.radzikowski.webmail.service.MailManager

package pl.com.radzikowski.webmail.service;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
public class MailManager extends AbstractManager {
    // some methods...
}

pl.com.radzikowski.webmail.HomeController

package pl.com.radzikowski.webmail.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import pl.com.radzikowski.webmail.service.MailManager;

@Controller
@RequestMapping("/")
public class HomeController {

    @Autowired
    public MailManager mailManager;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String homepage(ModelMap model) {
        return "homepage";
    }

}

ข้อผิดพลาด:

SEVERE:   Exception while loading the app
SEVERE:   Undeployment failed for context /WebMail
SEVERE:   Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'homeController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public pl.com.radzikowski.webmail.service.MailManager pl.com.radzikowski.webmail.controller.HomeController.mailManager; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.com.radzikowski.webmail.service.MailManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

ขออภัยสำหรับรหัสจำนวนมาก แต่ฉันไม่รู้ว่าอะไรทำให้เกิดข้อผิดพลาดได้อีกต่อไป

เพิ่มแล้ว

ฉันได้สร้างอินเทอร์เฟซ:

@Component
public interface IMailManager {

เพิ่มอุปกรณ์:

@Component
@Transactional
public class MailManager extends AbstractManager implements IMailManager {

และเปลี่ยนอัตโนมัติ:

@Autowired
public IMailManager mailManager;

แต่ก็ยังแสดงข้อผิดพลาด (เช่นเมื่อฉันลองใช้ @Qualifier)

.. ไม่สามารถ autowire field: public pl.com.radzikowski.webmail.service.IMailManager pl.com.radzikowski.webmail.controller.HomeController.mailManager ...

ฉันได้ลองใช้ @Component และ @Transactional ด้วยกัน

ฉันไม่ควรใส่ bean.xml ใน web.xml อย่างใด?


คุณกำลังโหลด beans.xml อย่างไร
Michael Wiles

ตอนนี้จะเหลือเพียงอันเดียวMailManagerInterfaceและIMailManager? :)
Patison

ขออภัยฉันวางโค้ดไม่ถูกต้อง ในโปรแกรมของฉันมีIMailManagerทุกที่
Radzikowski

อาตรง .. เพิ่ม<import resource="classpath:/beans.xml"/>ถึงmvc-dispatcher-servlet.xml
Patison

ยังไงก็ตามคุณลอง @emd ตัดสินใจหรือยัง?
Patison

คำตอบ:


81

คุณควร autowire อินเตอร์เฟซแทนระดับAbstractManager MailManagerหากคุณมีการใช้งานที่แตกต่างกันAbstractManagerคุณสามารถเขียน@Component("mailService")แล้ว@Autowired @Qualifier("mailService")รวมกันเพื่อกำหนดค่าคลาสเฉพาะ

เนื่องจาก Spring สร้างและใช้วัตถุพร็อกซีตามอินเทอร์เฟซ


2
ขอบคุณ แต่ยังใช้งานไม่ได้ (ข้อผิดพลาดเดียวกัน) ฉันเพิ่มการเปลี่ยนแปลงในโพสต์ บางทีฉันอาจไม่รวม / ค้นหาบางอย่างในโครงการของฉันอย่างถูกต้อง?
Radzikowski

ฉันสงสัยว่ามีวิธีทำบางอย่าง@Autowired AbstractManager[] abstractManagersที่มีการใช้งานทั้งหมดหรือไม่
WoLfPwNeR

@WoLfPwNeR มันควรจะทำงานตรงตามที่คุณเขียน ดูลิงค์
Patison

ทำไมไม่MailManager?
Alex78191

ฉันมีสอง@Serviceคลาสที่ใช้อินเทอร์เฟซเดียวกัน ทั้งสองอยู่@Autowiredในชั้นเรียนอื่น เริ่มแรกมันทำให้เกิดปัญหาเดียวกันกับโพสต์เดิมเมื่อฉันใช้ประเภทคลาสเฉพาะ ตามคำตอบนี้ฉันเปลี่ยนไปใช้ประเภทอินเทอร์เฟซที่แตกต่างกัน@Qualifier("myServiceA")และ@Qualifier("myServiceB")และปัญหาได้รับการแก้ไขแล้ว ขอบคุณ!
xialin

27

ฉันมีสิ่งนี้เกิดขึ้นเนื่องจากการทดสอบของฉันไม่ได้อยู่ในแพ็คเกจเดียวกับส่วนประกอบของฉัน (ฉันได้เปลี่ยนชื่อแพ็กเกจคอมโพเนนต์ของฉันแล้ว แต่ไม่ใช่แพ็กเกจทดสอบของฉัน) และฉันกำลังใช้@ComponentScanใน@Configurationคลาสทดสอบของฉันดังนั้นการทดสอบของฉันจึงไม่พบส่วนประกอบที่พวกมันอาศัยอยู่

ดังนั้นโปรดตรวจสอบอีกครั้งว่าคุณได้รับข้อผิดพลาดนี้หรือไม่


2
สิ่งนี้ได้ผลสำหรับฉัน การทดสอบของฉันล้มเหลวเมื่อฉัน refactored โค้ดเพื่อย้ายไปยังชื่อแพ็กเกจใหม่เนื่องจากฉันใช้ชื่อแพ็กเกจเก่าใน @ComponentScan
Vijay Vepakomma

1
วิธีใช้@Configurationคลาสจากsrc/mainในsrc/test?
Alex78191

10

สิ่งนี้คือทั้งบริบทของแอปพลิเคชันและบริบทของเว็บแอปพลิเคชันถูกลงทะเบียนใน WebApplicationContext ระหว่างการเริ่มต้นเซิร์ฟเวอร์ เมื่อคุณเรียกใช้การทดสอบคุณต้องบอกอย่างชัดเจนว่าจะโหลดบริบทใด

ลองสิ่งนี้:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/beans.xml", "/mvc-dispatcher-servlet.xml"})

1
จะทำอย่างไรถ้าฉันใช้ Spring Boot?
Alex78191

ฉันลืมเกี่ยวกับการกำหนดค่าการสแกนส่วนประกอบสำหรับการอ้างอิง maven และต่อสู้กับสิ่งนี้ปีละครั้ง
15

หากคุณกำลังพูดถึงแอปพลิเคชันเองและไม่ใช่การทดสอบการรวมสำหรับ Spring boot คุณสามารถทำได้ดังนี้: @SpringBootApplication(scanBasePackages = { "com.package.one", "com.package.two" })
b15

5

ใช้เวลาส่วนใหญ่กับสิ่งนี้! ความผิดฉันเอง! ต่อมาพบว่าชั้นเรียนที่ฉันประกาศคำอธิบายประกอบServiceหรือComponentเป็นประเภทนามธรรม ได้เปิดใช้งานบันทึกการดีบักบน Springframework แต่ไม่ได้รับคำใบ้ โปรดตรวจสอบว่าชั้นเรียนเป็นประเภทนามธรรมหรือไม่ หากใช้กฎพื้นฐานแล้วไม่สามารถสร้างอินสแตนซ์คลาสนามธรรมได้


1
คุณช่วยชีวิตฉันไว้นี่มันยากที่จะตรวจจับ!
Japheth Ongeri - inkalimeva

4

ฉันประสบปัญหาเดียวกันในขณะที่เดินสายคลาสโดยอัตโนมัติจากไฟล์ jar ของฉัน ฉันแก้ไขปัญหาโดยใช้คำอธิบายประกอบ @Lazy:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

    @Autowired
    @Lazy
    private IGalaxyCommand iGalaxyCommand;


3

คุณสามารถลองใส่คำอธิบายประกอบเฉพาะการใช้งานคอนกรีตของคุณด้วยได้@Componentไหม บางทีคำตอบต่อไปนี้อาจช่วยได้ เป็นปัญหาที่คล้ายกัน ฉันมักจะใส่คำอธิบายประกอบ Spring ในคลาสการใช้งาน

https://stackoverflow.com/a/10322456/2619091


2

วิธีที่ถูกต้องคือการเดินสาย AbstractManager อัตโนมัติตามที่ Max แนะนำ แต่วิธีนี้ก็ใช้ได้ดี

@Autowired
@Qualifier(value="mailService")
public MailManager mailManager;

และ

@Component("mailService")
@Transactional
public class MailManager extends AbstractManager {
}

2

ฉันพบสิ่งนี้เมื่อไม่นานมานี้และเมื่อปรากฎว่าฉันได้นำเข้าคำอธิบายประกอบผิดในชั้นเรียนบริการ Netbeans มีตัวเลือกในการซ่อนคำสั่งนำเข้านั่นเป็นสาเหตุที่ฉันไม่เห็นมันในบางครั้ง

ฉันใช้@org.jvnet.hk2.annotations.Serviceแทน@org.springframework.stereotype.Service.


1
  • เหตุผลหนึ่งที่ BeanB อาจไม่มีอยู่ในบริบท
  • อีกสาเหตุหนึ่งของข้อยกเว้นคือการมีอยู่ของถั่วสองเมล็ด
  • หรือคำจำกัดความในบริบท bean ที่ไม่ได้กำหนดถูกร้องขอโดยใช้ชื่อจากบริบท Spring

ดูเพิ่มเติม url นี้:

http://www.baeldung.com/spring-nosuchbeandefinitionexception


1
 <context:component-scan base-package="com.*" />

ปัญหาเดียวกันมาถึงผมแก้ไขได้โดยการรักษาคำอธิบายประกอบเหมือนเดิมและในการมอบหมายงานเซิร์ฟเล็ต :: รักษาสแกนแพคเกจฐานเป็นcom.*.นี้ทำงานให้ฉัน


1

สิ่งนี้อาจช่วยคุณได้:

ฉันมีข้อยกเว้นเหมือนกันในโครงการของฉัน หลังจากค้นหาในขณะที่ฉันพบว่าฉันไม่มีคำอธิบายประกอบ @Service ไปยังคลาสที่ฉันใช้อินเทอร์เฟซที่ฉันต้องการ @Autowired

ในโค้ดของคุณคุณสามารถเพิ่มคำอธิบายประกอบ @Service ในคลาส MailManager

@Transactional
@Service
public class MailManager extends AbstractManager implements IMailManager {

คำอธิบายประกอบมี@Service @Componentการมีทั้งสองอย่างซ้ำซ้อน
AnthonyW

1

แทนที่จะเป็น @Autowire MailManager mailManager คุณสามารถเยาะเย้ยถั่วตามที่ระบุด้านล่าง:

import org.springframework.boot.test.mock.mockito.MockBean;

::
::

@MockBean MailManager mailManager;

นอกจากนี้คุณสามารถกำหนดค่า@MockBean MailManager mailManager;แยกกันใน@SpringBootConfigurationคลาสและเริ่มต้นได้ดังนี้:

@Autowire MailManager mailManager

1

ประสบปัญหาเดียวกันในแอปพลิเคชันสปริงบูตแม้ว่าฉันจะเปิดใช้งานการสแกนเฉพาะแพ็คเกจเช่น

@SpringBootApplication(scanBasePackages={"com.*"})

แต่ปัญหาได้รับการแก้ไขโดยการให้@ComponentScan({"com.*"})ในคลาสแอปพลิเคชันของฉัน


0

ฉันเดาว่าที่นี่

<context:component-scan base-package="pl.com.radzikowski.webmail" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

คำอธิบายประกอบทั้งหมดจะถูกปิดใช้งานก่อนโดย use-default-filters = "false" จากนั้นเปิดใช้งานคำอธิบายประกอบ @Controller เท่านั้น ดังนั้นคำอธิบายประกอบ @Component ของคุณจึงไม่ได้เปิดใช้งาน


0

หากคุณกำลังทดสอบคอนโทรลเลอร์ของคุณ อย่าลืมใช้ @WebAppConfiguration ในชั้นเรียนทดสอบของคุณ


0

ฉันได้รับสิ่งนี้เกิดขึ้นเนื่องจากฉันเพิ่มการพึ่งพาอัตโนมัติให้กับคลาสบริการของฉัน แต่ลืมเพิ่มลงในการจำลองแบบฉีดในการทดสอบหน่วยบริการของฉัน

ข้อยกเว้นการทดสอบหน่วยดูเหมือนจะรายงานปัญหาในคลาสบริการเมื่อปัญหาเกิดขึ้นจริงในการทดสอบหน่วย เมื่อมองย้อนกลับข้อความแสดงข้อผิดพลาดบอกฉันว่าปัญหาคืออะไร


0

วิธีแก้ปัญหาที่ได้ผลสำหรับฉันคือเพิ่มคลาสที่เกี่ยวข้องทั้งหมดลงใน@ContextConfigurationคำอธิบายประกอบสำหรับคลาสทดสอบ

คลาสที่จะทดสอบMyClass.javaมีส่วนประกอบแบบอัตโนมัติสองส่วน: AutowireAและAutowireB. นี่คือการแก้ไขของฉัน

@ContextConfiguration(classes = {MyClass.class, AutowireA.class, AutowireB.class})
public class MyClassTest {
...
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.