最近新同事在写完代码时,使用Spring框架实现的接口一直报Failed to load ApplicationContext错误。找我帮忙看看,后面就发现他写代码时犯了一些常见的基本错误。
个人建议:使用之前多了解一点基本原理,即使发现了问题也可以更快的定位出来。
1、照葫芦画瓢时注入组件没有使用@Autowired或@Resource注解。
2、接口实现及接口组件重名。
如果对Spring框架有了解的话,应该会知道DI(Dependency Inject)依赖注入和IOC(Inversion of Control)控制反转。
Spring框架的主要功能是通过其核心容器来实现的。Spring框架提供的两种核心容器分别是BeanFactory和ApplicationContext。
常见的注入组件的方式是通过spring.xml配置文件进行注入组件bean,如下,更多参数参考文末链接1。
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- 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.xsd">
- <bean id="bean" class="com.xxx.BeanName">bean>
- beans>
依赖注入主要有下面几种方式,
通过set方法注入,在
<property name = "field_name" value = "field_value">property>
通过BeanName类的有参构造方法,并在配置中添加构造方法的参数和类型。
<constructor-arg value="name" type="java.lang.Demo">constructor-arg>
还需要写一个BeanNameFactory的工厂方法,例如,
- public class BeanNameFactory{
- public static BeanName getDemoBean(String name){
- return new BeanName(name);
- }
- }
然后在配置中添加工厂类及工厂方法的配置参数,
- <bean id="beanNameFactory" class="com.xxx.BeanNameFactory"
- factory-method="getDemoBean">
- <constructor-arg value="test">constructor-arg>
- bean>
然后在使用时通过应用上下文拿到对应的bean,
- //获取spring ioc容器
- ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
-
- //获取bean,org.springframework.context.ApplicationContext接口getBean方法可以通过类名获取对应的bean
- BeanName bean=(BeanName)ctx.getBean("beanNameFactory");
最后一种方式是最为常见的方式,通过注解@Autowired和@Resource注入组件bean,
如下示例所示,注入了一个门面类,
- @Service
- @Slf4j
- public class LogicImpl extends implements Logic {
- @Resource
- private Facade facade;
-
- public Message
insert(ReqDto reqDto) { - // doing something
- }
- }
在spring.xml文件中加上下面配置,可以在com.xxx.packagePath找到所有加上了@RestController、@Controller、@Service、 @Repository、 @Component等注解的类,将这些类解析成一个个的BeanDefinition
package="com.xxx.packagePath"/>
component-scan扫包的标签是通过ComponentScanBeanDefinitionParser解析类进行解析的。
SpringBoot的包路径扫描
SpringBoot在Spring的基础上大部分采用注解的方式进行配置,方式如下,@ComponentScan(value = {"com.xxx.packagePath"})。
ComponentScanBeanDefinitionParser解析类中的parse方法,利用ClassPathBeanDefinitionScanner.doScan方法扫描包路径下的所有组件,并将他们注入到BeanDefinitionHolder中去。
服务启动后,也可以在org.springframework.context.support.AbstractApplicationContext的refresh方法中打断点,查看BeanDefinitionMap(beanFactory对象)已加载的bean。
1、如果作为新人了解了上面的基本知识,那么就不那么容器在依赖注入时漏掉注解,没加注解就相当于在类中定义了一个空对象,再去调用空对象的非静态方法肯定会报错。
2、不同包路径下的相同接口作为组件交给spring管理时注意接口的实现类名不能相同,否则会抛出org.springframework.context.annotation.ConflictingBeanDefinitionException异常。
异常详细信息如下,
Annotation-specified bean name 'xxxImpl' for bean class [com.test.xxx] conflicts with existing, non-compatible bean definition of same name and class[com.test.xxx]
参考链接: