由于自动装配字段,BeanNotOfRequiredTypeException异常

80

我还不熟悉Spring MVC,在构建我的测试项目时,从Tomcat日志中得到了以下信息:

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'divisionController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'adminService' must be of type [employee.service.impl.AdminServiceImpl], but was actually of type [$Proxy52]
    at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:307)
    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)
    ...
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'adminService' must be of type [employee.service.impl.AdminServiceImpl], but was actually of type [$Proxy52]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:360)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)

我有两个角色服务,一个是用户服务,一个是管理员服务,并带有相应的接口:

package employee.service;

import employee.model.EmployeeDiv;
import employee.model.EmployeeInfo;
import employee.model.UserInfo;

import java.util.List;

/**
 *
 * @author serge
 */

public interface AdminService extends UserService {

     //  !!!! for register & udate of Employee use String type birthday !!!!

    /*
     * Employee
     */
    public EmployeeInfo registerEmployee(EmployeeInfo employeeInfo);

    public EmployeeInfo updateEmployee(EmployeeInfo employeeInfo);

    public EmployeeInfo findEmployeeByID(Integer id);   

    /*
     * Division
     */
    public EmployeeDiv registerDivision(EmployeeDiv division);

    public EmployeeDiv updateDivision(EmployeeDiv division);

    public List<EmployeeDiv> findAllDivisions();

    public List<EmployeeDiv> findDivisionsByName(EmployeeDiv division);

    public EmployeeDiv findDivisionsById(Integer id);

    /*
     * Login
     */
    public UserInfo registerUser(UserInfo user);

    public UserInfo updateUser(UserInfo user);

    public List<UserInfo> findAllUsesrs();

    public List<UserInfo> findUsesrByLogin(UserInfo user);

    public UserInfo findUsesrById(Integer id);
}

这是 AdminServiceImpl:

package employee.service.impl;

import employee.DAO.EmployeeDivDAO;
import employee.DAO.EmployeeInfoDAO;
import employee.DAO.UserDAO;
import employee.model.EmployeeDiv;
import employee.model.EmployeeInfo;
import employee.model.UserInfo;
import employee.service.AdminService;
import employee.validation.ParsingDate;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author serge
 *
 * Admin level
 *
 * Service for processing employee, divisions, userslogin
 */

//@Repository("adminService")
@Service("adminService")
@Transactional
public class AdminServiceImpl extends UserServiceImpl implements AdminService {

    protected static Logger adminLogger = Logger.getLogger("service");
    private EmployeeDivDAO emplDivDAO;
    private UserDAO userDAO;
    private EmployeeInfoDAO emplInfoDAO;

    @Autowired
    @Override
    public void setEmployeeDao(EmployeeInfoDAO emplInfoDAO) {
        this.emplInfoDAO = emplInfoDAO;
    }

    @Autowired
    public void setEmployeeDao(EmployeeDivDAO emplDivDAO) {
        this.emplDivDAO = emplDivDAO;
    }

    @Autowired
    public void setUserDao(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public AdminServiceImpl() {

        initTestEmployee();
    }

    /**
     * Initialize EmployeeInfo test
     */
    @Transactional
    @Secured("ROLE_ADMIN")
    private EmployeeInfo initTestEmployee() {

        adminLogger.debug("saving testEmployee");

        EmployeeInfo employeeInfo = new EmployeeInfo();
        ParsingDate date = new ParsingDate();
        employeeInfo.setFirstName("Petr");
        employeeInfo.setLastName("Ivanenko");
        employeeInfo.setEmpDiv("second");
        employeeInfo.setBirthdate(date.parseDate("1981-10-03"));
        employeeInfo.setSalary(3500);
        employeeInfo.setActive(true);

        return employeeInfo;
    }

    /**
     * registrating new Employee Information
     *
     * @return EmployeeInfo object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeInfo registerEmployee(EmployeeInfo employeeInfo) {

        adminLogger.debug("registrating new Employee");

        try {

            emplInfoDAO.save(employeeInfo);

        } catch (NullPointerException e) {
        }
        return employeeInfo;
    }

    /**
     * updating Employee Information
     *
     * @return EmployeeInfo object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeInfo updateEmployee(EmployeeInfo employeeInfo) {

        adminLogger.debug("updating Employee with id: " + employeeInfo.getId());
        try {

            emplInfoDAO.update(employeeInfo);

        } catch (NullPointerException e) {
        }
        return employeeInfo;
    }

    /**
     * Retrieving Employee Information by id 
     *
     * @return EmployeeInfo object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeInfo findEmployeeByID(Integer id) {

        adminLogger.debug("Retrieving Employee with id= " + id);
        EmployeeInfo employeeInfo = new EmployeeInfo();
        employeeInfo.setId(id);
        emplInfoDAO.find(employeeInfo);
        return employeeInfo;
    }

    /**
     * registrating new Employee Division
     *
     * @return EmployeeDiv object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeDiv registerDivision(EmployeeDiv division) {

        adminLogger.debug("registrating new Division");
        try {

            emplDivDAO.save(division);

        } catch (NullPointerException e) {
        }
        return division;
    }

    /**
     * updating Employee Division
     *
     * @return EmployeeDiv object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeDiv updateDivision(EmployeeDiv division) {

        adminLogger.debug("updating Division with id: " + division.getId());
        try {

            emplDivDAO.update(division);

        } catch (NullPointerException e) {
        }
        return division;
    }

    /**
     * Retrieving all Employee Divisions
     *
     * @return List of EmployeeDiv objects
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public List<EmployeeDiv> findAllDivisions() {

        adminLogger.debug("Retrieving all divisions");
        return emplDivDAO.findAll();
    }

    /**
     * Retrieving all Employee Divisions by name
     *
     * @return List of EmployeeDiv objects
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public List<EmployeeDiv> findDivisionsByName(EmployeeDiv division) {
        String empDiv = "empDiv";

        adminLogger.debug("Retrieving Divisions by name: " + division.getEmpDiv());
        return emplDivDAO.findAllByParam(empDiv, division.getEmpDiv());
    }

     /**
     * Retrieving Employee Divisions by id 
     *
     * @return EmployeeDiv object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public EmployeeDiv findDivisionsById(Integer id) {

        adminLogger.debug("Retrieving Division with id= " + id);
        EmployeeDiv employeeDiv = new EmployeeDiv();
        employeeDiv.setId(id);
        emplInfoDAO.find(employeeDiv);
        return employeeDiv;
    }

    /**
     * registrating new User Information
     *
     * @return UserInfo object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public UserInfo registerUser(UserInfo user) {

        adminLogger.debug("registrating new User");
        try {

            userDAO.save(user);

        } catch (NullPointerException e) {
        }
        return user;
    }

    /**
     * updating new User Information
     *
     * @return UserInfo object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public UserInfo updateUser(UserInfo user) {

        adminLogger.debug("updating User with id: " + user.getId());
        try {

            userDAO.update(user);

        } catch (NullPointerException e) {
        }
        return user;
    }

    /**
     * retriviting all Users 
     *
     * @return List of UserInfo objects
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public List<UserInfo> findAllUsesrs() {

        adminLogger.debug("Retrieving all Users");
        return userDAO.findAll();
    }

    /**
     * retriving all Users by login
     *
     * @return List of UserInfo objects
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public List<UserInfo> findUsesrByLogin(UserInfo user) {
        String login = "login";

        adminLogger.debug("Retrieving User with login: " + login);
        return userDAO.findAllByParam(login, user.getLogin());
    }

    /**
     * Retrieving Employee Divisions by id 
     *
     * @return EmployeeDiv object
     */
    @Override
    @Transactional
    @Secured("ROLE_ADMIN")
    public UserInfo findUsesrById(Integer id) {

        adminLogger.debug("Retrieving Division with id= " + id);
        UserInfo userInfo = new UserInfo();
        userInfo.setId(id);
        emplInfoDAO.find(userInfo);
        return userInfo;
    }
}

这是applicationContext.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.1.xsd
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <!-- Activates annotations -->

    <context:annotation-config />

    <!-- Scans for annotated components in base-package-->

    <context:component-scan base-package="employee" />

    <bean class="employee.service.impl.AdminServiceImpl"/>
    <bean class="employee.service.impl.UserServiceImpl"/>
    <!--bean class="employee.DAO.impl.EmployeeInfoDAOImpl"/>
    <bean class="employee.DAO.impl.EmployeeDivDAOImpl"/>
    <bean class="employee.DAO.impl.UserDAOImpl"/-->

    <!-- for Spring Jackson JSON support  -->

    <mvc:annotation-driven/>

    <!-- Shared Hibernate SessionFactory in a Spring application context. -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>

        <!--property name="dataSource">
            <ref bean="dataSource"/>
        </property-->

        <property name="annotatedClasses">
            <list>
                <value>employee.model.UserInfo</value>
                <value>employee.model.EmployeeInfo</value>
                <value>employee.model.EmployeeDiv</value>
            </list>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- for database, imports the properties from database.properties -->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <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>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:database.properties"/>
    </bean>
</beans>
请告诉我这个bean名称有什么问题,我知道这是AOP问题:
Bean named 'adminService' must be of type [employee.service.impl.AdminServiceImpl], but was actually of type [$Proxy52]

我该怎么修复它?

我在控制器中使用AdminServiceImpl:

package employee.controller;

import employee.model.EmployeeDiv;
import employee.service.impl.AdminServiceImpl;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;

/**
 * @author serge
 *
 * Handles and retrieves division related requests
 */
@Controller
@RequestMapping("/division")
public class DivisionController {

    protected static Logger logger = Logger.getLogger("controller");
    @Resource(name = "adminService")
    private AdminServiceImpl adminService;

    /**
     * Handles and retrieves a /WEB-INF/jsp/divisionpage.jsp
     *
     * containing all division
     *
     * @return the name of the JSP page
     */
    @RequestMapping(method = RequestMethod.GET)
    public String getAllPage(Model model) {
        logger.debug("Received request to show all division page");

        // Retrieve all division and attach to model 
        model.addAttribute("division", adminService.findAllDivisions());
        return "divisionpage";
    } ....
9个回答

167

你的代码中一定有类似这样的自动装配AdminServiceImpl的部分:

@Autowired
private AdminServiceImpl adminService;

要么仅仅依赖于接口:

@Autowired
private AdminService adminService;

或启用了CGLIB代理。

类似问题


8
@Serge:你可以自己解决这个常见的问题。只需要查找类型为“AdminServiceImpl”的自动装配字段,我确信你会有一些这样的字段。也就是说,查找你实际使用“AdminServiceImpl”的地方。删除“Impl”以仅依赖于接口。就这样! - Tomasz Nurkiewicz
1
将Impl删除,只依赖于接口。如果我理解正确的话?我需要用AdminService替换所有AdminServiceImpl的使用。 - Valknut
3
@Serge:确实。事实上,如果你仍然依赖于类,为什么要分离类和接口呢? ...Impl 出现的唯一位置是在类定义中,其他地方都没有。 - Tomasz Nurkiewicz
告诉我关于“自动装配”的事。我应该如何正确地自动装配字段?我是应该在接口中这样做还是我已经做得没问题了? - Valknut
3
使用接口而不是实现,并用@Qualifier进行注释以获取正确的类。救了我的命! - Cristian F.
互联网的绝对纯净力量在这个回答中得到了充分的体现。 - Airwavezx

36

使用接口 AdminService 而非实现类。

这个错误由注解 @Transactional 引起,Spring运行时AdminService 创建了一个代理。


或者,如果您将@EnableTransactionManagement添加到配置中,则@Transactional将开始工作。对我来说。 - borino

27

如果您的服务类实现了某些接口,Spring 默认采用 JDK 代理,这就是为什么您会遇到那个错误的原因。您可以通过在接口上使用 @Autowired 或启用 CGLIB 代理 来解决该问题。

我通过在 Spring 应用上下文中使用 proxy-target-class 属性来启用 CGLIB 代理以解决该问题。

<tx:annotation-driven proxy-target-class="true"
        transaction-manager="transactionManager" />

11

确保你已经开启了proxyTargetClass,例如:

@EnableTransactionManagement(proxyTargetClass = true)

9
我在搜索我的问题时发现了很多类似于这个问题的问题。然而,在我的情况下,我已经使用了一个接口。因此,我认为这可能对其他人有所帮助:
如果您有两个具有相同名称的bean,则此异常也可能会出现!
在我的情况下,我在我的applicationContext.xml中有其他的bean配置。问题出现在我合并了两个应用程序之后。第二个应用程序定义了一个@Resource,并且其成员变量名称与上面提到的第一个应用程序的bean名称匹配。当然,第一个应用程序的bean配置不适用于通过@Resource由第二个应用程序包含的bean。

8
我曾经遇到过一些测试端点的@SpringBootTest类的问题。这些端点依赖于一个DB服务,该服务在几个方法上有@Transactional注解。
由于@Transactional的存在,Spring会拦截它并创建代理类,因此Spock(或junit)将难以注入正确的模拟对象。
我的解决方法是将DB服务下移一层,并在中间层周围进行纯Spock测试,这不受影响。

7

我曾经遇到过同样的问题,但我通过以下方法解决了这个问题:

请将你的实现类替换为接口。 例如:

class Abc
{
 @Autowire
 private Boy boy // remove @BoyImpl

.............
...................
}

谢谢。太好了。你的建议完美地解决了我的问题。不确定为什么有人投反对票。 - Rajesh Kumar Raj

5

当执行单元测试时,我也遇到了问题。

我创建了一个模拟服务来实现真实服务,覆盖一些方法以便于测试某些用户案例。

@Component
public static class FooServiceImplMock extends FooServiceImpl {
    @Override
    protected void bar() {
        // do some specific loginc here 
    }
}

但是当执行测试方法时

@Autowired
private Foo1ServiceImplMock foo1ServiceImplMock;

我遇到了以下错误:
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'fooServiceImplTest.FooServiceImplMock' is expected to be of type 'com.foo.business.impl.FooServiceImplTest$FooServiceImplMock' but was actually of type 'com.sun.proxy.$Proxy97'

原因:

FooServiceImpl 中使用了 @Cacheable 注解的方法。

@Cacheable("eventTypes")

我知道EnableCaching的javadoc中提到:

org.springframework.cache.annotation.EnableCaching 
boolean proxyTargetClass()

Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to standard Java interface-based proxies. The default is false.

因此,解决方法是在Spring应用程序上下文中明确指定使用CGLIB。
 <cache:annotation-driven proxy-target-class="true"/>

5

最近我遇到了一个问题。在测试现有类时,出现了“预期为类型……但实际上是类型'com.sun.proxy.$ Proxy115'”的错误。

原来问题出在我使用了Intellij的“将方法上移 [到接口]”功能,将一个@ Transactional方法添加到接口的实现类中,并且Intellij决定在接口方法声明中添加@ Transactional。如果接口中只有一个@ Transactional方法,则会导致问题,从而出现此错误。


1
这对我帮助很大。我没有费心去看IntelliJ的“将方法提取到接口”实际上为我提取了什么。谢谢,伙计。 - mikaelovi
1
在抽象类上使用@Transactional也会导致相同的问题。 - bebbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接