spring依赖注入报错

在上一篇文章“spring的主要内容之依赖注入”中,我总结了通过XML配置文件注入的两种方式,分别是构造方法注入和set方法注入。在使用set方法注入时,遇到了一个报错,找了好长时间才找到问题。这个错误是在启动服务时报出来的。

现在分别贴出代码和报错信息。

java代码:

package com.zaoren.controller;

import java.util.List;
import java.util.Set;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.zaoren.bean.BookFriend;
import com.zaoren.service.BossService;
import com.zaoren.service.UserService;


@RequestMapping(value="user")
public class UserController {
    
    private UserService uService;
    private BossService bService;
    private String bookName;
    private List<String> bookNameList;
    private Set<String> bookNoSet;
    private List<BookFriend> bookFriends;
    
    @RequestMapping(value="trade",method=RequestMethod.GET)
    public void trade() {
        System.out.println("--------->>>buy a book.");
        System.out.println("The name of the book is"+ bookName);
        
        System.out.println("查看待购清单,发现还有如下书籍:");
        for(String bookName : bookNameList) {
            System.out.println(bookName);
        }
        
        System.out.println("查看待购清单,发现还有如下书籍编号:");
        for(String bookNo : bookNoSet) {
            System.out.println(bookNo);
        }
        
        System.out.println("有书友如下:");
        for(BookFriend bookFriend : bookFriends) {
            System.out.println(bookFriend);
        }
        
        uService.searchBook();
        bService.downBook();
    }
    
    
    public void setuService(UserService uService) {
        System.out.println("调用setuService");
        this.uService = uService;
    }

    public void setbService(BossService bService) {
        System.out.println("调用setbService");
        this.bService = bService;
    }

    public void setBookName(String bookName) {
        System.out.println("调用setBookName");
        this.bookName = bookName;
    }

    public void setBookNameList(List<String> bookNameList) {
        System.out.println("调用setBookNameList");
        this.bookNameList = bookNameList;
    }

    public void setBookNoSet(Set<String> bookNoSet) {
        System.out.println("调用setBookNoSet");
        this.bookNoSet = bookNoSet;
    }

    public void setBookFriends(List<BookFriend> bookFriends) {
        System.out.println("调用setBookFriends");
        this.bookFriends = bookFriends;
    }

    
    public UserController(UserService uService,BossService bService,String bookName, List<String> bookNameList, 
            Set<String> bookNoSet, List<BookFriend>bookFriends
                                  ){ 
        System.out.println("六参构造方法调用"); 
        this.uService= uService; 
        this.bService = bService; 
        this.bookName = bookName;
        this.bookNameList = bookNameList; this.bookNoSet = bookNoSet;
        this.bookFriends = bookFriends;
    }
     
}

spring配置文件的相关代码如下:

<bean id="uService" class="com.zaoren.service.UserService"/>
    <bean id="bService" class="com.zaoren.service.BossService"/>
    <bean id="bookFriend" class="com.zaoren.bean.BookFriend"/>
    
    <bean id="uController2" class="com.zaoren.controller.UserController">
        <property name="uService" ref="uService"/>
        <property name="bService" ref="bService"/>
        <property name="bookName" value="ss"/>
        <property name="bookNameList">
            <list>
                <value>茶花女</value>
                <value>三重门</value>
                <value>明朝那些事儿</value>
            </list>
        </property>
        <property name="bookNoSet">
            <set>
                <value>111222</value>
                <value>111223</value>
                <value>111224</value>
                <value>111224</value>
            </set>
        </property>
        <property name="bookFriends">
            <list>
                <ref bean="bookFriend"/>            
                <ref bean="bookFriend"/>        
                <ref bean="bookFriend"/>
            </list>
        </property>
    </bean>

 

启动服务时的报错信息如下:

信息: Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Wed Oct 16 10:42:24 CST 2019]; root of context hierarchy
十月 16, 2019 10:42:26 上午 org.springframework.web.context.support.XmlWebApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'uController2' defined in class path resource [WEB-INF/spring-servlet.xml]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [java.lang.String]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
十月 16, 2019 10:42:26 上午 org.springframework.web.context.ContextLoader initWebApplicationContext
严重: Context initialization failed

分析:

在排查报错原因的前期里,我一直先入为主的认为是由于注入UserController的属性时出现了错误,于是我反复检查xml配置文件和java代码,但是没能发现问题所在。

我做这个实验的因由是在spring读物上看到了依赖注入相关的讲解,讲解上说用标签<property>来注入最终会通过调用set方法来实现。但是我查看日志,却没有发现调用set方法的痕迹,反而发现了调用六参构造方法的痕迹。这让我很困惑,和讲解上说的不一样。

于是我困惑之下,就注释掉了六参构造方法,发现报错就消失了,服务正常启动了。这让我很奇怪,一时间不知道为何会这样。于是我展开联想,发散思维,联想到了构造方法相关的知识,如下:

构造方法分为无参构造方法和有参构造方法。

无参构造方法是隐式的,默认存在,不需要我们自己编写。

如果我们添加一个有参构造方法,则隐式无参构造方法会被覆盖,不再起作用。

 

将这几个知识点、之前报错信息结合、之后的正常启动现象结合,立即就明白了之前报错的原因,解释如下:

根据依赖注入的步骤,我们知道注入属性之前,会先进行实例化,实例化是通过调用构造方法来进行的。之前的报错就是因为调用构造方法出错了。六参构造方法覆盖了隐式的无参构造方法,所以在实例化时,调用的是六参构造方法。在spring配置文件中,我们有实例化UserService和BossService,名称分别为uService和bService,所以调用六参构造方法时,前两个参数没有报错,因为在spring容器中找到了符合类型要求的实例。但是六参构造方法的第三个参数需要一个String类型的实例,不管是三种装配方式的哪一种,我们都没有实例化过一个String类型,所以在spring容器中无法找到一个String类型的实例,因此在此处就会报错。

 

当我注释掉六参构造方法后,隐式的无参构造方法又被激活,并且只存在这一个构造方法,所以在依赖注入之前的实例化时,调用的是这一个构造方法,它不需要任何参数,所以不会报如上的错误。

所以我们应当知道,xml配置文件注入的set方法注入,能且只能调用无参构造方法,所以必须要使无参构造方法生效,要么去掉所有的显式构造方法,要么显式声明无参构造方法。

以上是整个思维过程的总结,思维亮点是遇到死胡同时,进行发散联想思维。下面进行“马后炮”式分析。

 

将报错的原因用中文表述出来,大概意思是无法找到一个String类型的实例去匹配构造方法的第三个参数,从而导致创建uController2出错。

如果我们经验足够,就应该知道“创建uController2出错”中的“创建”二字,其实就是在说调用构造方法时出现错误,而不是指注入属性时出现错误。

我之前只知道依赖注入的三个步骤,分别是搜集beanDefinition、实例化集合beanDefinitions中的所有类、注入依赖实例,但是却没有运用过这个知识点,这个报错就需要运用这个知识点来解决。

思维进入死胡同时,要进行发散联想思维,具体说来就是横向联系相关知识点和以前的类似问题,同时把报错所在的步骤放在整体过程中审视。

posted on 2019-10-16 10:56  星辰划过指尖  阅读(911)  评论(0编辑  收藏  举报