>思维导图梳理
(基本概念)
(装配方式)
> 什么是 springboot
在学 springboot 之前,你必须有 spring、spring mvc 基础,springboot 的诞生其实就是用来简化新 Spring 应用的初始搭建以及开发过程,该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
它集成了大量常用的第三方库配置 (例如 JDBC, Mongodb, Redis, Mail,rabbitmq 等等),所以在 Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用 (out-of-the-box),大部分的 Spring Boot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
也就是说,以前集成 ssm 框架需要一大堆的 xml 配置文件,效率底下,而使用了 springboot 之后,很多时候我们不需要写任何配置了,有时候直接通过 @EnableXXX 就能开启某个模块的功能。
现在问题来了,你知道 @EnableXXX 是什么原理吗?
> mvc、boot、cloud
这里直接引用网友的总结给大家介绍一下:
Spring 是一个 "引擎";springmvc 是框架,web 项目中实际运行的代码;spring boot 只是一个配置工具,整合工具,辅助工具,是一套快速开发整合包。
Spring Boot :J2EE 一站式解决方案 Spring Cloud :分布式整体解决方案
> 约定大于配置的体现
在于减少软件开发人员所需要做出的决定的数量,从而获得简单的好处,而又不失去其中的灵活性。
1、Spring Boot 默认提供静态资源目录位置需置于 classpath 下,目录名需符合如下规则:/static /public /resources /META-INF/resources 优先级:META/resources > resources > static > public
2、spring boot 默认的配置文件必须是,也只能是 application 或 application-xxx 命名的 yml 文件或者 properties 文件,我推荐尽量使用 yml 文件~ 3、application.yml 中默认属性:a、数据库连接信息必须是以 spring: datasource: 为前缀,如:
spring: datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/demo username: root password: root
b、多环境配置。该属性可以根据运行环境自动读取不同的配置文件。例如将该属性定义为 dev 的话,Spring Boot 会额外从 application-dev.yml 文件中读取该环境的配置。
spring: profiles.active: dev
c、修改端口号、请求路径
server: port: 8080 context-path: /demo
4、starter 启动器,开箱即用的 Starter 依赖让 springboot 可以实现零配置即可自动完成框架的整合。
-
spring-boot-starter-web
-
嵌入 tomcat 和 web 开发需要 servlet 与 jsp 支持
-
spring-boot-starter-data-jpa
-
数据库支持
-
spring-boot-starter-data-redis
-
redis 数据库支持
-
spring-boot-starter-data-solr
-
solr 支持
-
mybatis-spring-boot-starter
-
第三方的 mybatis 集成 starter
接下来我们来分析一下 springboot 注入 bean 有多少种方式。
> 手动装配
在学习 springboot 中,我喜欢把总结 springboot 的一些特性,以及使用 springboot 的一些规律,比如:在 springboot 加载 bean 的过程我分为了
-
手动装配
-
自动装配
两种方式,而手动装配又分为了
-
模式注解装配
-
@Enable 模块装配
-
条件装配
3 种方式,接下来我们来一一探讨每种。
首先来看下手动装配:
1、模式注解装配
其实就是使用 @Component 注解,或者 @Component 注解的拓展,比如 @Controller、@Service、Repository、@Configruation 等,
这也是我们最常用的一种方式,直接通过 spring mvc 的注解把组件 bean 注入到 spring 容器中。
2、@Enable 模块装配
- 基于接口驱动实现
当我们需要开启 springboot 项目的缓存功能时候,我们直接打开 @EnableCaching 注解就可以注入 Caching 模块,这时候我们就可以开心使用 @Cacheable、@CacheEvict 等注解,这是怎么做到的?
其实你打开 @EnableCaching 的源码你就能看到:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}
上面最重要的一句代码就是 @Import({CachingConfigurationSelector.class}),你会发现,其实使用 @EnableCaching,就是为了导入 CachingConfigurationSelector.class 这配置类。
而这个 CachingConfigurationSelector,其实实现了 ImportSelector 接口,ImportSelector 接口是 spring 中导入外部配置的核心接口,只有一个方法 selectImports,其实就是根据 EnableCaching 的元数据属性(proxyTargetClass、mode、order),选择出需要转配的 Configuration。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
总结其实是这样子,@EnableCaching 其实就是根据元数据属性然后选择性条件判断注入需要的配置,比较灵活。
- 基于注解驱动实现
然后我们来看另一种没有元数据属性的 @EnableWebMvc。
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { }
可以很直观看到,其实 @EnableWebMvc 其实就是为了导入 DelegatingWebMvcConfiguration 配置类,某种程度上,可以认为 @EnableWebMvc 其实和 @Import({DelegatingWebMvcConfiguration.class}) 是对等的,只是起了一个有意义的名字而已。
所以我们总结一下 @EnableXXX 模块注入,基于接口驱动实现是实现 ImportSelector 接口,通过注解参数选择需要导入的配置,而基于注解驱动实现其实就是 @Import 的派生注解,直接导入某个配置类。
思维导图总结如下:
3、条件装配
所谓条件装配,其实是 Bean 装配的前置条件,我们先来看一下例子:
-
@ConditionalOnBean
-
仅仅在当前上下文中存在某个对象时,才会实例化一个 Bean
-
@ConditionalOnExpression
-
当表达式为 true 的时候,才会实例化一个 Bean
-
@ConditionalOnMissingClass
-
某个 class 类路径上不存在的时候,才会实例化一个 Bean
-
@ConditionalOnNotWebApplication
-
不是 web 应用
这就是条件装配,当这些条件注解放在某个 bean 上面的时候,只有满足了条件才能注入 bean,这也是为什么 springboot 能这么智能,知道哪些模块需要开启,哪些不需要,比如当你导入 Freemaker 的 jar 包之后,就自动帮你加载 Freemaker 的的相关配置,其实你看下代码:
@Configuration
@ConditionalOnClass({freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class})
@EnableConfigurationProperties({FreeMarkerProperties.class})
@Import({FreeMarkerServletWebConfiguration.class, FreeMarkerReactiveWebConfiguration.class, FreeMarkerNonWebConfiguration.class})
public class FreeMarkerAutoConfiguration {
...
}
这些 springboot 的自动配置类上面一般是不是都有 @ConditionalOnClass 注解,这里是说当发现项目有 freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class 这两个 Class 存在时候,我就加载这个 FreeMarkerAutoConfiguration,什么时候才会存在这两个 Class?当我们导入 jar 包时候:
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-freemarker</artifactid>
</dependency>
所以,当我们没有导入相关 jar 包时候,我们不用担心 springboot 会自动开启某些功能,而是会智能判断哪些需要开启,哪些需要跳过。
我们打开 @ConditionalOnClass 的源码,发现其实是 @Conditional 拓展出来的注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
Class<!--?-->[] value() default {};
String[] name() default {};
}
实现逻辑如下:OnClassCondition.class 实现 Condition 接口,并实现 matches() 方法,如果 matches 方法返回 true,那么带有 @Conditional 注解的 bean 就会装载,false 就不会装载。
思维导图总结如下:
> 自动装配
ok,刚才我们已经说了很多关于手动装配部分的东西,现在我们来看下自动装配,其实很多时候自动装配就是手动装配的综合运用,只不过在转配 bean 或配置类时候,我们不在需要使用 @EnableXXX 来导入功能,而是通过自动注入方式。
这时候自动注入的条件判断(@Conditional)就显得非常重要了。
我们再用刚才说的 Freemaker 作为例子,springboot 集成 freemaker 非常简单,只需要导入 starter 的 jar 包就会自动实现注入,这个自动集成就是 FreeMarkerAutoConfiguration 这里配置的。
这里有个问题,你知道为什么 springboot 会自动去判断和加载 FreeMarkerAutoConfiguration 这个配置类吗?我没有写类似的 @EnableFreemaker,那项目怎么识别的。
其实如果你看过 springboot 的源码,你就会发现:
- org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<string> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
上面的意思是去扫描项目下所有的 META-INF/spring.factories 文件,然后把 EnableAutoConfiguration.class 作为 key 找出对应的值,这个值是个 List。那么我们来看下其中一个 spring.factories 长什么样子的。
- spring-boot-autoconfigure/2.1.2.RELEASE/spring-boot-autoconfigure-2.1.2.RELEASE.jar!/META-INF/spring.factories
可以看到 EnableAutoConfiguration 作为 key 有很多个值,比如 RabbitMq 的自动配置类等,而你认证点看,就能找到 FreeMarkerAutoConfiguration 这配置类了。
所以情况是这个的,当 springboot 项目启动时候,项目会去加载所有的 spring.factories 文件,然后在 EnableAutoConfiguration 后面的所有配置类其实都是可以实现自动装配的配置,至于需不需要装配,就需要条件装配来判定是否满足特定的条件了。
有了这点基础之后,我们就可以自己去写自动装配了。
第一步、编写需要自动装载的配置类。
说明:@Configuration 表示是个配置类 @ConditionalOnSystemProperty 表示需要满足当前系统是 win10 系统
@Configuration
@ConditionalOnSystemProperty(value = "Windows 10")
public class SayHelloWorldAutoConfiguration {
@Bean
SayHelloWorld autoSayHelloWorld() {
System.out.println("here to !!auto!!loading bean autoSayHelloWorld!");
return new SayHelloWorld();
}
}
第二步、在 resources 目录下新建 META-INF 文件夹,编写 spring.factories。
Auto Configure 自动装配自定义的配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.other.configuration.SayHelloWorldAutoConfiguration
启动 springboot 之后就会自动加载这个配置类,于是,我们就注入了 SayHelloWorld 这个业务 bean,项目中就可以直接注入使用啦~
有人说,这和直接写个 @Configruation 有啥区别,区别在于 @Configruation 的配置必须写在 Spring 能扫描到的目录下,而自动装配不需要。
思维导图总结如下:
> 结束语
好了,今天的内容先到这里了哈,后面我们会去用详细分析和代码实现 @EnableXXX,还有自动装配,手写 starter 等
我是吕一明,感谢关注我的公众号:java 思维导图。
关注公众号,回复“思维导图”获取导图源文件