Spring的Ioc的理解

  • Post author:
  • Post category:其他


前两篇文章中,大致分析了spring是如何注册、创建、实例化bean的。

搞清楚这些之后,其实对于Ioc的理解就特别清楚了。


Ioc(控制反转)也叫DI(依赖注入),个人觉得依赖注入比较好理解

,因为从前两篇文章可以知道,spring完成的工作就是实例化Bean,在实例化该Bean的过程中,其依赖的另一个Bean才被设置进来或者创建。所以依赖注入比较好理解一些。

官方文档解释如下:

官方文档


所以Spring完成的工作基本是两大部分

1、创建bean对象:

官方创建bean对象的文档地址

创建一个对象时,

可以使用构造器、静态工厂、实例工厂这三种方式

官方文档部分

2、实例化对象:

官方文档依赖注入的部分

实例化对象的时候对于对象里面的属性如何设置呢?

要么通过set方法设置进去、要么就是构造器

依赖注入

搞清楚这些,spring的基本运作方式就很清晰了,至于其他的,只不过就是spring对不同bean进行的管理、和使用方面的的不同了

小思考:


现在平时工作中,基本很少用到xml配置了,而且业务中如果有很多bean,写xml文件无疑是很痛苦的。那spring是如何把我们需要的bean,在容器启动之后注册进去的呢?

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注册扫描,不需要显式的写bean标签了-->
    <context:component-scan base-package="com.learn.spring.spring01.service"/>
    <!--<bean id="schoolService" class="com.learn.spring.spring01.service.SchoolServiceImpl">-->

    <!--</bean>-->
</beans>
@Component
public class SchoolServiceImpl {

    public String getTimeStr(){
      return   LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:MM:ss"));
    }

    public String getInStr(String str){
        return str;
    }
}

开启注解扫描之后,同时在bean上加入Component、Service等注解,就不需要显式的写bean标签了。


spring具体怎么做的呢

在解析xml文件、注册bean时parseBeanDefinitions方法判断

当前标签不是xml的bean标签,而是context标签,则会调用parseCustomElement方法->handler.parse

->pare方法如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        String basePackage = element.getAttribute("base-package");
        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ",; \t\n");
        ClassPathBeanDefinitionScanner scanner = this.configureScanner(parserContext, element);
// 关键的两行
        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
        this.registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
        return null;
    }


doScan方法会扫描xml文件配置的包路径下的类,如果是有注解,则会进入最后一个判断,进行beanDefinition的注册,调用的方法和过程和第一篇说的一毛一样

if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
// 注册bean
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }

下面的registerComponents是做一些额外的工作,主要工作还是在doScan中完成的。

进一步思考:现在的springboot又是如何扫描的呢?

@Component注解已经默认放在了@SpringbootApplication注解里面了,至于springboot是怎么实现的,可以自行debug查看,原理都是相通的


我听到的我会忘记,我看到的我会记住,我做过才会明白。

所以有的时候对某个概念不熟悉、不理解,自己动动手debug一下,结合官方文档基本能很快的理解。死记硬背还是不靠谱的



版权声明:本文为dghkgjlh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。