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中的所有类、注入依赖实例,但是却没有运用过这个知识点,这个报错就需要运用这个知识点来解决。
思维进入死胡同时,要进行发散联想思维,具体说来就是横向联系相关知识点和以前的类似问题,同时把报错所在的步骤放在整体过程中审视。