@Autowired - nie znaleziono kwalifikującego komponentu bean typu dla zależności

89

Rozpocząłem swój projekt od stworzenia encji, usług i testów JUnit dla usług wykorzystujących Spring i Hibernate. Wszystko to działa świetnie. Następnie dodałem spring-mvc, aby utworzyć tę aplikację internetową, korzystając z wielu różnych samouczków krok po kroku, ale kiedy próbuję utworzyć kontroler z adnotacją @Autowired, podczas wdrażania otrzymuję błędy od Glassfish. Myślę, że z jakiegoś powodu Spring nie widzi moich usług, ale po wielu próbach nadal nie mogę sobie z tym poradzić.

Testy usług z

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

i

@Autowired
MailManager mailManager;

działa poprawnie.

Kontrolery bez @Autowired też, mogę bez problemu otworzyć swój projekt w przeglądarce internetowej.

/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 <[email protected]>
 */
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";
    }

}

Błąd:

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)}

Przepraszam za dużo kodu, ale nie wiem już, co może powodować ten błąd.

Dodany

Stworzyłem interfejs:

@Component
public interface IMailManager {

dodane narzędzia:

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

i zmieniony autowired:

@Autowired
public IMailManager mailManager;

Ale nadal generuje błędy (również gdy próbowałem z @Qualifier)

..Could not autowire field: public pl.com.radzikowski.webmail.service.IMailManager pl.com.radzikowski.webmail.controller.HomeController.mailManager ...

Próbowałem też z różnymi kombinacjami @Component i @Transactional.

Czy nie powinienem jakoś umieszczać beans.xml w pliku web.xml?

Radzikowskiego
źródło
Jak ładujesz beans.xml?
Michael Wiles
teraz co powiesz na zostawienie tylko jednego MailManagerInterfacei IMailManager? :)
Patison
Przepraszam, źle wkleiłem kod. W moim programie jest IMailManagerwszędzie.
Radzikowski
ach, dokładnie .. dodaj <import resource="classpath:/beans.xml"/>domvc-dispatcher-servlet.xml
Patison,
Swoją drogą, czy próbowałeś decyzji @emd?
Patison

Odpowiedzi:

81

Powinieneś autowire interfejs AbstractManagerzamiast class MailManager. Jeśli masz różne implikacje AbstractManager, możesz napisać, @Component("mailService")a następnie @Autowired @Qualifier("mailService")połączyć, aby autowire konkretną klasę.

Wynika to z faktu, że Spring tworzy i wykorzystuje obiekty proxy w oparciu o interfejsy.

Patison
źródło
2
Dziękuję, ale nadal nie działa (ten sam błąd). Dodałem zmiany w poście. Może nie umieszczam / nie wyszukuję poprawnie czegoś w moim projekcie?
Radzikowski
Zastanawiam się, czy istnieje sposób na zrobienie czegoś takiego, @Autowired AbstractManager[] abstractManagersktóry zawiera wszystkie jego implementacje.
WoLfPwNeR
@WoLfPwNeR Powinno działać dokładnie tak, jak napisałeś. Zobacz link
Patison,
Dlaczego nie MailManager?
Alex78191
Mam dwie @Serviceklasy implementujące ten sam interfejs. Obie są @Autowiredw innej klasie. Początkowo powodowało to ten sam problem, co oryginalny post, gdy użyłem określonego typu zajęć. Po tej odpowiedzi zmieniłem typ interfejsu na inny @Qualifier("myServiceA")i @Qualifier("myServiceB"), a problem został rozwiązany. Dzięki!
xialin
27

Tak się stało, ponieważ moje testy nie były w tym samym opakowaniu, co moje komponenty. (Zmieniłem nazwę mojego pakietu komponentów, ale nie mojego pakietu testowego). Używałem go @ComponentScanw @Configurationklasie testowej , więc moje testy nie znajdowały komponentów, na których polegały.

Więc sprawdź to dokładnie, jeśli pojawi się ten błąd.

zacumowane
źródło
2
To zadziałało dla mnie. Moje testy zakończyły się niepowodzeniem, gdy zreformowałem kod, aby przejść do nowej nazwy pakietu, ponieważ używałem starej nazwy pakietu w @ComponentScan.
Vijay Vepakomma
1
Jak korzystać @Configurationz klasy src/mainw src/test?
Alex78191
10

Chodzi o to, że zarówno kontekst aplikacji, jak i kontekst aplikacji internetowej są rejestrowane w WebApplicationContext podczas uruchamiania serwera. Po uruchomieniu testu musisz wyraźnie określić, które konteksty mają zostać załadowane.

Spróbuj tego:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/beans.xml", "/mvc-dispatcher-servlet.xml"})
emd
źródło
1
Jak to zrobić, jeśli używam Spring Boot?
Alex78191
Zapominam o konfigurowaniu skanowania komponentów pod kątem zależności Mavena i walczę z tym jak raz w roku.
b15
Jeśli mówisz o samej aplikacji, a nie o testach integracyjnych, dla Spring boot możesz to zrobić tak: @SpringBootApplication(scanBasePackages = { "com.package.one", "com.package.two" })
b15
5

Spędziłem z tym dużo czasu! Mój błąd! Później stwierdziłem, że klasa, na której zadeklarowałem adnotację Servicelub Componentbyła typu abstrakcyjnego. Włączono dzienniki debugowania na Springframework, ale nie otrzymano żadnej wskazówki. Sprawdź, czy klasa jest typu abstrakcyjnego. Jeśli tak, zastosowana podstawowa reguła nie może utworzyć instancji klasy abstrakcyjnej.

James Jithin
źródło
1
Uratowałeś mi dzień, panie, tak trudno to wykryć!
Japheth Ongeri - inkalimeva
3

Miałem ten sam problem podczas automatycznego łączenia klasy z jednego z moich plików jar. Naprawiłem problem, używając adnotacji @Lazy:

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

    @Autowired
    @Lazy
    private IGalaxyCommand iGalaxyCommand;

Ved Singh
źródło
2

Poprawnym sposobem powinno być automatyczne podłączenie AbstractManagera, jak zasugerował Max, ale to też powinno działać dobrze.

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

i

@Component("mailService")
@Transactional
public class MailManager extends AbstractManager {
}
Abhishek Anand
źródło
2

Niedawno wpadłem na to i jak się okazało, zaimportowałem niewłaściwą adnotację w mojej klasie usług. Netbeans ma możliwość ukrycia instrukcji importu, dlatego przez jakiś czas jej nie widziałem.

Użyłem @org.jvnet.hk2.annotations.Servicezamiast @org.springframework.stereotype.Service.

kazmer
źródło
1
  • Jednym z powodów, dla których BeanB może nie istnieć w kontekście
  • Inną przyczyną wyjątku jest istnienie dwóch fasoli
  • Lub definicje w fasoli kontekstowej, która nie jest zdefiniowana, są żądane za pomocą nazwy z kontekstu Spring

zobacz więcej ten adres URL:

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

zohreh
źródło
1
 <context:component-scan base-package="com.*" />

Pojawił się ten sam problem, rozwiązałem go, utrzymując adnotacje w stanie nienaruszonym i w serwlecie dyspozytorskim : utrzymując skanowanie pakietu podstawowego, ponieważ com.*.działało to dla mnie.

Ramanpreet Singh
źródło
1

To może ci pomóc:

Mam ten sam wyjątek w moim projekcie. Po przeszukaniu stwierdziłem, że brakuje mi adnotacji @Service do klasy, w której implementuję interfejs, który chcę @Autowired.

W swoim kodzie możesz dodać adnotację @Service do klasy MailManager.

@Transactional
@Service
public class MailManager extends AbstractManager implements IMailManager {
Akshay Pethani
źródło
@ServiceAdnotacja zawiera @Component. Posiadanie obu jest zbędne.
AnthonyW
1

Zamiast @Autowire MailManager mailManager, możesz kpić z fasoli, jak podano poniżej:

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

::
::

@MockBean MailManager mailManager;

Możesz także skonfigurować @MockBean MailManager mailManager;osobno w @SpringBootConfigurationklasie i zainicjować jak poniżej:

@Autowire MailManager mailManager
Barani r
źródło
1

Napotkałem ten sam problem w mojej aplikacji do rozruchu wiosennego, mimo że miałem włączone skanowanie specyficzne dla mojego pakietu, np

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

Ale problem został rozwiązany przez dostarczenie @ComponentScan({"com.*"})w mojej klasie Application.

Rajish Nair
źródło
0

Domyślam się, że tutaj

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

wszystkie adnotacje są najpierw wyłączane przez use-default-filters = "false", a następnie włączane są tylko adnotacje @Controller. Dlatego Twoja adnotacja @Component nie jest włączona.

LoBo
źródło
0

Jeśli testujesz swój kontroler. Nie zapomnij użyć @WebAppConfiguration w swojej klasie testowej.

Joao Luiz Cadore
źródło
0

Stało się tak, ponieważ dodałem autowired zależność do mojej klasy usług, ale zapomniałem dodać ją do wstrzykniętych makiet w moim teście jednostki usług.

Wydaje się, że wyjątek testu jednostkowego zgłosił problem w klasie usługi, gdy problem faktycznie występował podczas testu jednostkowego. Z perspektywy czasu komunikat o błędzie powiedział mi dokładnie, na czym polega problem.

Curtis Yallop
źródło