一. Spring Boot, Spring MVC, Spring 之间的区别
Spring 是一个轻量级的控制反转(IOC) 和面向切面(AOP)的容器框架. Spring 使你能够编写更干净, 更可管理, 并且更易于测试的代码.
Spring MVC 是 Spring 的一个模块, 一个 web 框架. 通过 Dispatcher Servlet, ModelAndView 和 View Resolver, 开发 web 应用变得很容易. 主要针对的是网站应用程序或者服务开发 — URL 路由, Session, 模板引擎, 静态 web 资源等等.
Spring 配置 复杂, 繁琐, 所以推出了 Spring Boot, 约定大于配置, 简化了 Spring 的配置流程.
Spring VS SpringMVC
Spring 是一个一站式的轻量级的 java 开发框架, 核心是控制反转 (IOC) 和面向切面, 针对于开发的WEB层 (SpringMVC, 业务层 (IOC), 持久层(Repository) 等都提供了多种配置解决方案.
SpringMVC是Spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于Spring框架中WEB层开发的一部分.
1.2 SpringMVC VS SpringBoot
SpringMVC属于一个企业 WEB 开发的 MVC 框架 , 涵盖面包括前端视图, 文件配置, 后台接口逻辑的开发等, XML, config 等配置相对比较繁琐复杂.
SpringBoot 框架相对于 SpringMVC 框架来说, 只专注于做开发 Restful API, 不开发前端视图, 配置也简单很多, 约定大于配置.
- Spring 是核心, 提供了基础功能.
- SpringMVC 是基于 Spring 的一个 MVC 框架
- SpringBoot 是为简化 Spring 配置的快速开发的工具
二. Spring Boot Starter 是什么
在我们工作中, 经常会看到各种各样的 Spring Boot Starter, 比如 spring-boot-starter-jdbc
, mybatis-spring-boot-starter
, mybatis-plus-boot-starter
等等. 这些 starter
究竟有什么作用呢?
在了解这些 starter
之前, 我们要知道 SpringMVC 和 Spring Boot 的关系, 他们的关系上文已经说过了, 在这里就不再絮叨了.
正如上文所述, Spring Boot 约定大于配置, 使用注解和配置文件替代了 xml 的繁琐配置, 甚至我们不再需要 外部容器, Spring Boot 内嵌了 Tomcat 容器, 我们只需要执行SpringApplication.run(Application.class, args);
,然后一切就能正常工作.
这些都少不了 Spring Boot 自动装配的功劳. 而关于自动装配, Spring Boot 官方文档是这么说的:
Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added.
Spring Boot 框架会根据当前运行时的 jar 依赖来自动生成并装配相应的 Bean, 文档上举了个例子: 假如 HSQLDB 在你的 classpath下, 并且你没有手动配置任何的数据库连接 Bean, 那么 Spring Boot 会自动装配一个内存的数据库. 另外, 自动装配还支持根据自定义属性和自定义自动装配条件, 功能很强大.
其实 starter
就是一个写好自动装配逻辑的 jar 依赖, 我们只要引用这个 starter
, 那么当程序运行时的依赖库, 配置属性或者其他条件满足 starter
中定义的逻辑是, starter
会自动装配我们的运行环境.
举例说明: 当我们引用 spring-cloud-alibaba-nacos-discovery
这个 starter
时, 并且增加了几个服务注册发现的配置, 那么我们的应用就具备了微服务的能力. 当然, 除此之外, 我们还需要一个注册中心. 这个 starter
并不包含注册中心, 他只是帮助你初始化当前的微服务注册, 发现, 调用, 客户端负载均衡环境, 让你只需要一个 @Autowired
的注解就能使用微服务的能力.
2.1 自动装配原理
它是通过 @Configuration
和 @Conditional
注解来工作的. @Configuration
注解是 Spring 中用来声明 Bean 的;而@Conditional
代表了一类条件注解,表示当且仅当满足了一定条件之后,使用@Configuration
的类才真正创建并装配Bean.
那么我们使用 starter
时只是引用一个 jar 包, Spring Boot 框架是如何知道 jar 包中的自动装配逻辑的呢? 答案是 META-INF/spring.factories
文件. Spring Boot 会检查你的 jar 包中是否存在 META-INF/spring.factories
文件,如果存在则会进一步读取其中的EnableAutoConfiguration
配置项,该配置项包含了jar包中的自动装配类,例如 druid
的 jar:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
如果我们的自动装配存在上下文的依赖, 我们还可以使用 @AutoConfigureAfter
和 @AutoConfigureBefore
来声明装配顺序.
三. 自定义 Spring Boot Starter
Spring Boot 官方文档推荐一个 starter
应当包含如下几个部分:
- autoconfigure模块:实现自动装配的逻辑.
- starter模块:集成autoconfigure模块、依赖库和其他依赖,用户只需要引用这个starter那么就可以使用.
自定义 Spring Boot Starter 就是按照 Spring Boot 的规范来实现上面的两个模块, 如果我们分开这两个模块做成 jar 包比较麻烦, 也可以将它们合成一个大的模块, 也就是最终的 starter 包.
在starer包命名上,Spring Boot官方包一般会以 spring-boot 为前缀,而其他自定义的包则建议不能以 spring-boot 开头,即便使用不同的groupId. 假如我们需要创建一个 ”amqp” 库的 starter,那么 autoconfigure 模块可以命名为 amqp-spring-boot-autoconfigure,而 starter 模块可以命名为 amqp-spring-boot-starter;而如果你选择将这两个模块合成一个模块,那么就采取 amqp-spring-boot-starter 就行.
另外,如果starter使用了一些配置项,那么这些配置项应当放在特定的命名空间(即前缀)下,不能放在 Spring Boot 配置项所在的命名空间下,如 server、management、spring 等等.
四. Spring Boot 的启动流程
用过 Spring Boot 的都知道使用 Spring Boot 和使用 SSM 框架的视觉上明显的差别就是 Spring Boot 有独立的启动类.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.1 SpringBootApplication 注解用到了哪些注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
图片来自网上
在其中比较重要的有三个注解,分别是:
- @SpringBootConfiguration // 继承了Configuration,表示当前是注解类
- @EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
- @ComponentScan(excludeFilters = {} // 扫描路径设置
接下来对这三个注解一一讲解, 增加对 SpringBootApplication 的理解
4.1.1@Configuration
按照原来xml配置文件的形式,在springboot中我们大多用配置类来解决配置问题
- xml 配置文件的形式来配置 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
- java configuration的配置形式配置bean
@Configuration
public class MockConfiguration{
//bean定义
}
注入bean方式的不同:
-
xml配置文件的形式注入bean
<bean id="mockService" class="..MockServiceImpl"> ... </bean>
-
java configuration的配置形式注入bean
@Configuration public class MockConfiguration{ @Bean public MockService mockService(){ return new MockServiceImpl(); } }
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id.
表达bean之间依赖关系的不同:
-
xml配置文件的形式表达依赖关系
<bean id="mockService" class="..MockServiceImpl"> <propery name ="dependencyService" ref="dependencyService" /> </bean> <bean id="dependencyService" class="DependencyServiceImpl"></bean>
-
java configuration配置的形式表达依赖关系(重点)
如果一个bean A的定义依赖其他bean B,则直接调用对应的JavaConfig类中依赖bean B的创建方法就可以了.
@Configuration public class MockConfiguration{ @Bean public MockService mockService(){ return new MockServiceImpl(dependencyService()); } @Bean public DependencyService dependencyService(){ return new DependencyServiceImpl(); } }
4.1.2 @ComponentScan
-
对应xml配置中的元素.
-
ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如 @Component 和 @Repository 等)或者bean定义.
-
将这些 bean 定义加载到 IoC 容器中.
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则 默认 Spring 框架实现会从声明 @ComponentScan所在类的package进行扫描.
4.1.3 @EnableAutoConfiguration
此注解顾名思义是可以自动配置,所以应该是springboot中最为重要的注解.
在spring框架中就提供了各种以@Enable开头的注解,例如: @EnableScheduling, @EnableCaching, @EnableMBeanExport等; @EnableAutoConfiguration的理念和做事方式其实一脉相承简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义.
-
@EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到 IoC 容器[定时任务、时间调度任务]
@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器.
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage // 重点注解 @Import(AutoConfigurationImportSelector.class) // 重点注解 public @interface EnableAutoConfiguration { }
上面的代码中最重要的注解已经标注:
@AutoConfigurationPackage
,@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
通过@Import(AutoConfigurationPackages.Registrar.class)
/** * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing * configuration. */ static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
这是 Registrar 类, 他注册了一个 bean 的定义:
new PackageImport(metadata).getPackageName()
返回了当前主程序类的同级以及子级的包组件, 那就是注册当前主程序类的同级以及子级的包中的符合条件的 bean 的定义
以上图为例, CodeApplication 和 config 包同级, 但是 demo 包是 CodeApplication 的父级, 也就是说,CodeApplication 启动加载的 Bean 中,并不会加载Demo 类,这也就是为什么,我们要把 CodeApplication 放在项目的最高级中.