前两篇文章中,大致分析了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一下,结合官方文档基本能很快的理解。死记硬背还是不靠谱的