2、SpringBoot 与 Spring 的千丝万缕
想要深入了解 SpringBoot,必须知道 Spring。我们可以从其命名 SpringBoot 中的关键字“boot”上,可以管中窥豹。“boot” 英文意思:启动。也就是说 SpringBoot 这个微服务框架设计的初衷,就是为了快速启动一个 Spring 应用。
自始至终,SpringBoot 框架都是为了能够帮助使用 Spring 框架的开发者高效快速的构建一个个基于 Spring以及 Spring 生态体系的应用解决方案。
所以要深刻理解 SpringBoot 框架,我们需要深刻理解 Spring 框架。
0、Spring 框架的起源
这一段我不想浪费笔墨过多赘述。例如:黑暗的 EJB 时代、基于各种容器和J2EE规范的统治时代等等。
这里着重介绍一个人:Rod Johnson。就是他和他的那本书给Java的开发迎来了曙光。
感兴趣的朋友,可以自己 Google 查询。
1、对 Spring 中IoC(控制反转)和DI(依赖注入) 的理解
Java日常的开发中,一个对象在需要其他的合作对象时,程序猿都要将合作对象创建出来(比如 new 对象),这个合作对象是由自己主动创建出来的。创建合作对象的主动权在自己手上,需要时候就主动创建,这样耦合性很高。(耦合性:A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起)。
但是在使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题,A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。
基于以上观点,得出控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
IoC其实有两种实现方式:
①DI(Dependency Injection):当前软件实体被动接受其依赖的其他组件被 IoC 容器注入。
②DL(Dependency Lookup):当前软件实体主动去某个服务注册地查找其依赖的那些服务。
通常提到的 Spring Ioc ,实际上是指 Spring框架提供的Ioc容器实现,而使用 Spring IoC容器的一个典型代码片段如下:
public class Application {
public static void main(String[] args) {
ApplicationContext context=new FileSystemXmlApplicationContext("....");
MockService mockService = context.getBean(MockService.class);
mockService.doSomething();
}
}
任何一个使用 Spring 构建的独立 Java 应用,通常都会存在一行类似于“context.getBean(MockService.class);”的代码,实际上这段代码做的就是 DL 的工作,而构建的任何一种 IoC容器背后(BeanFactory 或者 ApplicationContext)发生的事情,则更多指的是 DI 的过程。
2、Spring 中IoC实现原理
Spring 的 IoC 容器中发生的可以总结为两个阶段:
① 收集和注册
此阶段可以认为是构建和收集 bean 定义的阶段,在这个阶段可以通过 XML 或者 Java 代码的方式定义一些 bean ,然后通过手动组装或者让容器基于某些机制自动扫描,将这些 bean 定义收集到 IoC 容器中,例如:
通过 XML 配置收集并注册单一的 bean
<bean id="mockService" class="com.rookie.MockServiceImpl">
...
</bean>
如果嫌逐个收集 bean 定义比较麻烦,想批量收集并注册到 IoC 容器中,可以通过 XML Schema 形式的配置进行批量扫描并采集和注册:
<context:component-scan base-package="com.rookie"/>
② 分析和组装
当收集和注册阶段完成后,可以暂且认为 IoC容器中充斥着一个个独立的 bean。实际上,它们之间是有依赖关系的,所以IoC 容器在第二个阶段就是分析这些已经存在于 IoC 容器中的 bean,根据它们之间的依赖关系先后组装它们。
如果 IoC 容器发现某个 bean 依赖另外一个 bean,它就会将这个bean注入给依赖它的bean,直到所有 bean 的依赖都注入完成,所有 bean 都“整装待发”,整个 IoC 容器的工作即算完成。
3、了解 JavaConfig
Java 5的推出,让我们可以基于纯注解进行依赖注入。
①表达式层面
基于 XML 的配置方式如下:
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- bean 的定义-->
</beans>
基于 JavaConfig 的配置方式如下:
@Configuration
public class MockConfiguration{
// bean 的定义
}
②注册 bean 定义层面
基于 XML 的配置形式是如下:
<bean id="mockService" class="com.rookie.MockServiceImpl">
...
</bean>
基于 JavaConfig 的配置方式如下:
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl();
}
}
任何一个标注了 @Bean 的方法,其返回值将作为一个 bean 定义注册到 Spring 的 IoC 容器,方法名将默认成为该 bean 定义的 id。
③表达依赖注入关系层面
如果 bean 与 bean 之间有依赖关系,在 XML 形式中一般是如下形式:
<bean id="dependencyService" class="com.rookie.DependencyServiceImpl"/>
<bean id="mockService" class="com.rookie.MockServiceImpl">
<property name="dependencyService" ref="dependencyService"/>
</bean>
JavaConfig中则是这样表达的:
@Configuration
public class MockConfiguration{
@Bean
public MockService mockService(){
return new MockServiceImpl(dependencyService());
}
@Bean
public DependencyService dependencyService(){
return new DependencyServiceImpl();
}
}
也就是说如果一个bean的定义依赖其他bean,则直接调用对应的 Javaconfig 类中依赖 bean 的创建方法即可
4、高人气的 Annotation
① @ComponentScan
@ComponentScan对应XML配置形式为<context:component-scan base-package="xxxxx"/>,配合比如 @Component、@Repository 等,即可将这些元信息注解的 bean 定义批量采集到 Spring 的 IoC 容器中。
base-package=“xxxxx”,是指自动扫描的范围。
@ComponentScan 亦是 SpringBoot 中一个很重要的组件。
② @PropertySource 和 @PropertySources
@PropertySource 用于从某些地方加载 *.property 文件内容,并将其中的属性加载到 IoC 容器中,以便于填充一些 bean 定义属性的占位符
@Configuration
@PropertySource("classpath:2.property")
@PropertySource("classpath:1.property")
@PropertySource(".....")
public class LoadConfiguration {
......
}
如果想声明多个 PropertySource ,则可以使用 @PropertySources
@Configuration
@PropertySources({
@PropertySource("classpath:2.property"),
@PropertySource("classpath:1.property")
....
})
public class LoadConfiguration {
......
}
③ @Import 和 @ImportResource
XML 形式配置中,可以通过
的形式将多个分开的容器配置合到一个配置中,如果使用 JavaConfig ,可以如下所示:
@Configuration
@Import(MockConfiguration.class)
public class LoadLoadConfiguration {
.........
}
@Import 只负责引入 JavaConfig 形式定义的 IoC 容器配置,如果有一些遗留的配置或者遗留系统需要以 XML 形式来配置,可以使用@ImportResource将其一起合并到当前 JavaConfig 配置的容器中。
@Configuration
@Import(MockConfiguration.class)
@ImportResource(".......")
public class LoadLoadConfiguration {
.........
}
5、总结
此章内容回顾了 Spring 的框架一些基本内容,为下次 SpringBoot 的深入打基础。
参考文章如下:
浅谈对Spring IOC以及DI的理解:https://blog.csdn.net/luoyepiaoxue2014/article/details/72426666
如果只用过 SpringBoot 而没有使用过 Spring 搭建过环境的朋友,可以参考我以前的文章,可以对比下,以前的配置多么繁琐。
SSM基本配置:https://blog.csdn.net/CC1014524900/article/details/81713572