• Spring组件注入注意事项之一


    序言

    最近新同事在写完代码时,使用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。

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    5. <bean id="bean" class="com.xxx.BeanName">bean>
    6. beans>

    依赖注入主要有下面几种方式,

    1. 基于set方法的依赖注入
    2. 基于有参构造函数的依赖注入
    3. 基于工厂方法的依赖注入
    4. 基于注解的依赖注入

    基于set方法的依赖注入

    通过set方法注入,在加上下面的配置,前提是BeanName类中有field-name成员变量的setName方法。

    <property name = "field_name" value = "field_value">property>

    基于有参构造函数的依赖注入

    通过BeanName类的有参构造方法,并在配置中添加构造方法的参数和类型。

    <constructor-arg value="name" type="java.lang.Demo">constructor-arg>

    基于工厂方法的依赖注入

    还需要写一个BeanNameFactory的工厂方法,例如,

    1. public class BeanNameFactory{
    2. public static BeanName getDemoBean(String name){
    3. return new BeanName(name);
    4. }
    5. }

    然后在配置中添加工厂类及工厂方法的配置参数,

    1. <bean id="beanNameFactory" class="com.xxx.BeanNameFactory"
    2. factory-method="getDemoBean">
    3. <constructor-arg value="test">constructor-arg>
    4. bean>

    然后在使用时通过应用上下文拿到对应的bean,

    1. //获取spring ioc容器
    2. ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
    3. //获取bean,org.springframework.context.ApplicationContext接口getBean方法可以通过类名获取对应的bean
    4. BeanName bean=(BeanName)ctx.getBean("beanNameFactory");

    基于注解的依赖注入

    最后一种方式是最为常见的方式,通过注解@Autowired和@Resource注入组件bean,

    如下示例所示,注入了一个门面类,

    1. @Service
    2. @Slf4j
    3. public class LogicImpl extends implements Logic {
    4. @Resource
    5. private Facade facade;
    6. public Message insert(ReqDto reqDto) {
    7. // doing something
    8. }
    9. }

    Spring的包路径扫描

    在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]

    参考链接:

    1、基本原理 - 容器和bean - [ Spring中文手册 ] - 在线原生手册 - php中文网

  • 相关阅读:
    排序算法-堆排序
    python实战故障诊断之CWRU数据集(四):线性回归模型的应用
    高并发下如何防重?
    有谁知道这个3D模型是哪个封装吗,power6的封装实在是找不到
    线上展厅制作成本
    ubuntu18.04 ros 安装 gazebo9
    【ESP32】10.PCF8591数模转换器实验(IIC总线/wire库)
    call,apply,bind指定函数的this指向详解,功能细节,严格和非严格模式下设定this指向
    二叉树的递归问题
    Gurobi 安装
  • 原文地址:https://blog.csdn.net/zkkzpp258/article/details/126653641