概述
序言
最近新同事在写完代码时,使用Spring框架实现的接口一直报Failed to load ApplicationContext错误。找我帮忙看看,后面就发现他写代码时犯了一些常见的基本错误。
个人建议:使用之前多了解一点基本原理,即使发现了问题也可以更快的定位出来。
1、照葫芦画瓢时注入组件没有使用@Autowired或@Resource注解。
2、接口实现及接口组件重名。
依赖注入和控制反转
如果对Spring框架有了解的话,应该会知道DI(Dependency Inject)依赖注入和IOC(Inversion of Control)控制反转。
- 控制反转:在面向对象传统编程方式中,获取对象的方式通常是用new关键字主动创建一个对象。而Spring容器(IOC容器)初始化、装配及管理的对象叫做bean,对象的生成则不需要new了,而是通过Spring的配置来自动装配生成单例(scope=singleton)或原型(scope=prototype)对象。
- 依赖注入:IOC容器在运行期间动态地将某种依赖关系注入到对象之中,如对象A 注入(赋值)给对象B的成员变量。
Spring框架的主要功能是通过其核心容器来实现的。Spring框架提供的两种核心容器分别是BeanFactory和ApplicationContext。
常见的注入组件的方式是通过spring.xml配置文件进行注入组件bean,如下,更多参数参考文末链接1。
<?xml version="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方法的依赖注入
- 基于有参构造函数的依赖注入
- 基于工厂方法的依赖注入
- 基于注解的依赖注入
基于set方法的依赖注入
通过set方法注入,在<bean ...></bean>加上下面的配置,前提是BeanName类中有field-name成员变量的setName方法。
<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<Long> insert(ReqDto reqDto) {
// doing something
}
}
Spring的包路径扫描
在spring.xml文件中加上下面配置,可以在com.xxx.packagePath找到所有加上了@RestController、@Controller、@Service、 @Repository、 @Component等注解的类,将这些类解析成一个个的BeanDefinition
<context:component-scan base-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]
参考链接:
1、基本原理 - 容器和bean - [ Spring中文手册 ] - 在线原生手册 - php中文网
最后
以上就是贤惠皮带为你收集整理的Spring组件注入注意事项之一序言依赖注入和控制反转问题定位的全部内容,希望文章能够帮你解决Spring组件注入注意事项之一序言依赖注入和控制反转问题定位所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复