@Autowired - не найдено подходящего bean-компонента типа для зависимости

89

Я начал свой проект с создания сущностей, сервисов и тестов JUnit для сервисов, использующих Spring и Hibernate. Все это прекрасно работает. Затем я добавил spring -mvc, чтобы сделать это веб-приложение с использованием множества различных пошаговых руководств, но когда я пытаюсь создать контроллер с аннотацией @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:

@Autowired
public IMailManager mailManager;

Но он все еще выдает ошибки (также когда я пробовал с @Qualifier)

.. Не удалось выполнить автоматическое подключение поля: public pl.com.radzikowski.webmail.service.IMailManager pl.com.radzikowski.webmail.controller.HomeController.mailManager ...

Я тоже пробовал разные комбинации @Component и @Transactional.

Разве я не должен как-то включить beans.xml в web.xml?

Радзиковский
источник
Как вы загружаете beans.xml?
Майкл Уайлс,
как насчет того, чтобы оставить только одно из 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")комбинировать его для определенного класса autowire.

Это связано с тем, что Spring создает и использует прокси-объекты на основе интерфейсов.

Патисон
источник
2
Спасибо, но все равно не работает (та же ошибка). Я добавил изменения в пост. Может я что-то неправильно включаю / не ищу в своем проекте?
Radzikowski
Мне интересно, есть ли способ сделать что-то подобное @Autowired AbstractManager[] abstractManagers, содержащее все его реализации.
WoLfPwNeR 08
@WoLfPwNeR Должно работать именно так, как вы написали. См. Ссылку
Patison
А почему бы и нет MailManager?
Alex78191
У меня есть два @Serviceкласса, реализующие один и тот же интерфейс. Оба находятся @Autowiredв другом классе. Изначально это вызывало ту же проблему, что и исходное сообщение, когда я использовал определенный тип класса. После этого ответа я перешел на использование типа интерфейса с разными @Qualifier("myServiceA")и @Qualifier("myServiceB"), и проблема была решена. Благодарность!
xialin
27

У меня это произошло, потому что мои тесты не были в том же пакете, что и мои компоненты. (Я переименовал свой пакет компонентов, но не свой тестовый пакет.) И я использовал его @ComponentScanв своем тестовом @Configurationклассе, поэтому мои тесты не находили компоненты, на которые они полагались.

Итак, дважды проверьте это, если вы получите эту ошибку.

пришвартованные
источник
2
Это сработало для меня. Мои тесты не прошли, когда я реорганизовал код для перехода к новому имени пакета, потому что я использовал старое имя пакета в @ComponentScan.
Виджай Вепакомма 01
1
Как использовать @Configurationкласс из src/mainв src/test?
Alex78191
10

Дело в том, что и контекст приложения, и контекст веб-приложения регистрируются в WebApplicationContext во время запуска сервера. При запуске теста вы должны явно указать, какие контексты загружать.

Попробуй это:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/beans.xml", "/mvc-dispatcher-servlet.xml"})
EMD
источник
1
Как это сделать, если я использую Spring Boot?
Alex78191
Я забываю о настройке сканирования компонентов для зависимостей maven и борюсь с этим, как раз в год.
b15
Если вы говорите о самом приложении, а не о тестах интеграции, для загрузки Spring вы можете сделать это так: @SpringBootApplication(scanBasePackages = { "com.package.one", "com.package.two" })
b15,
5

Я потратил на это много времени! Виноват! Позже обнаружили , что класс , на котором я объявил аннотацию Serviceили Componentбыл типа реферата. Включил журналы отладки на Springframework, но никаких подсказок получено не было. Пожалуйста, проверьте, имеет ли класс абстрактный тип. Если тогда применяется основное правило, не может создать экземпляр абстрактного класса.

Джеймс Джитин
источник
1
Вы спасли мне день, сэр, это так сложно обнаружить!
Иафет Онгери - инкалимева
3

Можете ли вы попробовать аннотировать только вашу конкретную реализацию @Component? Возможно, следующий ответ может помочь. Это вроде похожая проблема. Я обычно помещаю аннотации Spring в классы реализации.

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

Ямилмедина
источник
3

Я столкнулся с той же проблемой при автоматическом подключении класса из одного из моих файлов jar. Я исправил проблему, используя аннотацию @Lazy:

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

    @Autowired
    @Lazy
    private IGalaxyCommand iGalaxyCommand;

Вед Сингх
источник
2

Правильный способ - автоматическое подключение AbstractManager, как предлагал Макс, но это тоже должно работать нормально.

@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-компонентов.
  • Или определения в контексте bean-компонента, который не определен, запрашиваются по имени из контекста Spring

увидеть больше этот URL:

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

зохре
источник
1
 <context:component-scan base-package="com.*" />

возникла та же проблема, я решил ее, сохранив аннотации нетронутыми и в диспетчере servlet :: сохраняя сканирование базового пакета, поскольку com.*.это сработало для меня.

Раманприт Сингх
источник
1

Это может помочь вам:

У меня такое же исключение в моем проекте. После поиска, когда я обнаружил, что мне не хватает аннотации @Service для класса, в котором я реализую интерфейс, который я хочу использовать @Autowired.

В своем коде вы можете добавить аннотацию @Service к классу MailManager.

@Transactional
@Service
public class MailManager extends AbstractManager implements IMailManager {
Акшай Петхани
источник
@ServiceАннотаций содержит @Component. И то, и другое - излишне.
AnthonyW
1

Вместо @Autowire MailManager mailManager вы можете издеваться над bean-компонентом, как показано ниже:

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

::
::

@MockBean MailManager mailManager;

Кроме того, вы можете настроить @MockBean MailManager mailManager;отдельно в @SpringBootConfigurationклассе и инициализировать, как показано ниже:

@Autowire MailManager mailManager
Барани р
источник
1

Столкнулся с той же проблемой в моем приложении весенней загрузки, хотя у меня было включено сканирование моего пакета, например

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

Но проблема была решена путем предоставления @ComponentScan({"com.*"})в моем классе Application.

Раджиш Наир
источник
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 не включена.

LoBo
источник
0

Если вы тестируете свой контроллер. Не забудьте использовать @WebAppConfiguration в своем тестовом классе.

Жоао Луис Кадоре
источник
0

У меня это произошло, потому что я добавил автоматическую зависимость к своему классу обслуживания, но забыл добавить ее к внедренным имитам в моем модульном тесте службы.

Исключение модульного теста, по-видимому, сообщало о проблеме в классе обслуживания, когда проблема действительно была в модульном тесте. Оглядываясь назад, я могу сказать, что в сообщении об ошибке было точно сказано, в чем проблема.

Кертис Яллоп
источник