约定优于配置设计范式及Spring Boot源码剖析
一、Spring boot应用回顾
1. 约定优于配置
约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计规范。本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)。好处:大大减少了配置项
2. spring boot 于spring 关系
spring boot基于spring 4.0设计,支持省去applicationContext.xml;不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决
3.SpringBoot主要特性
-
SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中;
-
使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率。
-
自动配置:SpringBoot的自动配置特性利用了Spring对条件化配置的支持,合理地推测应用所需的bean并自动化配置他们;
-
使部署变得简单,SpringBoot内置了三种Servlet容器,Tomcat,Jetty,undertow.我们只需要一个Java的运行环境就可以跑SpringBoot的项目了,SpringBoot的项目可以打成一个jar包。
4.全局配置文件
-
文件名:
application.properties或者application.yml(可以使用其他,需要配置)
-
文件位置
-
注意事项:
- 如果同一个目录下,有application.yml也有application.properties,2.4之前版本默认先读取
application.properties。,2.4及之后默认先读取yml。
- 如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取
到的。
- 创建SpringBoot项目时,一般的配置文件放置在“项目的resources目录下”
-
使用@ConfigurationProperties批量注入配置文件中属性
-
实体类上添加@Component;@ConfigurationProperties注解
//Person @Component //生成当前类的实例对象存到IOC容器中 @ConfigurationProperties(prefix = "person") //实现批量注入(set方法) public class Person { private int id; //id private String name; //名称 private List hobby; //爱好 private String[] family; //家庭成员 private Map map; private Pet pet; //宠物 } //pet public class Pet { // 类型 private String type; // 名称 private String name; }
-
配置文件application.properties中配置要注入的信息
-
或者在文件application.yml中配置
-
-
spring boot测试用例
@RunWith(SpringRunner.class) @SpringBootTest class MySpringbootApplicationTests { @Autowired private Person person; @Test void contextLoads() { System.out.println(person); } }
5.属性注入
- @Value单个属性注入
@Configuration//配置类,自动注入到IOC容器
public class JdbcConfiguration {
@Value("${jdbc.url}")
String url;
@Value("${jdbc.driverClassName}")
String driverClassName;
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
}
- @ConfigurationProperties放到类上批量注入
@Component
@ConfigurationProperties(prefix = "jdbc")
public class JdbcConfiguration {
String url;
String driverClassName;
String username;
String password;
}
- @ConfigurationProperties放到方法上注入(一般用于第三方对象赋值)
@Configuration
public class MyService {
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent(){
return new AnotherComponent();
}
}
- 在配置文件中,属性是松散绑定的
first-name,firstName,first_name ,FIRSTNAME 都会被识别为一个字符串
6. 日志框架
-
抽象层:SLF4J
-
实现层:logback
-
统一日志框架使用步骤
-
排除系统中的其他日志框架。
-
使用中间包替换要替换的日志框架。
-
导入我们选择的 SLF4J 实现。
-
-
使用
Logger logger = LoggerFactory.getLogger(this.getClass()); logger.info("info日志....");
-
配置
#日志配置 # 指定具体包的日志级别 logging.level.com.lagou=trace # 指定控制台日志输出格式 logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n # 指定将日志信息输出到指定文件 logging.file.name=my.log #logging.file.path=/var/log # 指定文件日志输出格式 logging.pattern.file=%d{yyyy-MM-dd} ======= [%thread]====== %-5level %logger{50} - %msg%n
- 源码:
git clone -b master1 https://gitee.com/idse666666/my-springboot.git
二、源码解析
1. 依赖管理
1-1.为什么导入dependency时不需要指定版本?
spring-boot-starter-parent 通过继承 spring-boot-dependencies 从而实现了SpringBoot的版本依
赖管理,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了,这也
就是在 Spring Boot 项目中部分依赖不需要写版本号的原因
1-2.spring-boot-starter-parent父依赖启动器的主要作用是进行版本统一管理,那么项目运行依赖的JAR包是从何而来的?
spring-boot-starter-web依赖启动器的主要作用是打包了Web开发场景所需的底层所有依赖(基于依赖传递,当前项目也存在对应的依赖jar包)
2.自动配置
2-1.Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?
-
@SpringBootApplication:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan组合而成
-
@SpringBootConfiguration:SpringBoot 的配置类,标注在某个类上,表示这是一个 SpringBoot的配置类。
-
@EnableAutoConfiguration:Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的
Bean ,并加载到 IOC 容器。
- @Import:遍历各个组件META-INF目录中所有jar包下的spring.factories文件。收集需要配置的类,然后过滤或排除后装载到ioc容器中
-
@ComponentScan:主要是从定义的扫描路径中,找出标识了需要装配的类自动装配到spring 的bean容器中。
三、自定义starter
- SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。
- 将独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,再由SpringBoot为我们完成自动装配
- 动态数据源,登录模块,基于AOP技术实现日志切面
1.SimpleBean
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "SimpleBean{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2.MyAutoConfiguration
@Configuration
public class MyAutoConfiguration {
static {
System.out.println("MyAutoConfiguration init.... ");
}
@Bean
public SimpleBean simpleBean(){
return new SimpleBean();
}
}
3.在META-INF下建立spring.factories文件以便扫描到我们要注册的starter
4.优化
- 使用@ConditionalOnBean注解,只有ioc容器中注入了这个类才生效
- 自定义出EnableRegisterServer注解,在其上方使用@Import注解使ioc容器中创建configmarker类实例
5.测试
-
我们可以在另一项目中使用pom.xml引入
<dependency> <groupId>com.lagou</groupId> <artifactId>zdy-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
配置yml
simplebean: id: 1 name: zdy
-
测试注入
6.源码
git clone -b master2 https://gitee.com/idse666666/my-springboot.git
四、动态数据源配置
Spring内置了一个AbstractRoutingDataSource,它可以把多个数据源配置成一个Map,然后,根据不同的key返回不同的数据源。因为AbstractRoutingDataSource也是一个DataSource接口,因此,应用程序可以先设置好key, 访问数据库的代码就可以AbstractRoutingDataSource拿到对应的一个真实的数据源,从而访问指定的数据库
1.实现过程图解:
2.配置两个数据源并注入到RoutingDataSource中:
3.RoutingDataSource中创建子类并重写determineCurrentLookupKey方法
4. 提供set和get方法获取存放在ThreadLocal方式获取当前线程key
5.切面技术调用RoutingDataSourceUtil中的ThreadLocal的setkey
6.源码
git clone -b master3 https://gitee.com/idse666666/my-springboot.git
五、缓存
1.重要概念和注解
Spring Cache 只负责维护抽象层,具体的实现由自己的技术选型来决定。将缓存处理和缓存技术
解除耦合。
每次调用需要缓存功能的方法时,Spring会检查指定参数的指定的目标方法是否已经被调用过,如
果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次
调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点:
① 确定那些方法需要被缓存
② 缓存策略
2.@Cacheable
将方法运行的结果进行缓存,以后再获取相同的数据时,直接从缓存中获取,不再调用方法
-
@Cacheable注解的属性
-
可用的SpEL表达式见下表:
- 例:@Cacheable(cacheNames = {“emp”},key = “#id”, condition = “#id > 0”, unless = “#result == null”)缓存的名字为emp,key为id参数,当id>0,且返回结果不为NULL的数据
3.源码
git clone -b master4 https://gitee.com/idse666666/my-springboot.git
缓存,以后再获取相同的数据时,直接从缓存中获取,不再调用方法
-
@Cacheable注解的属性
[外链图片转存中…(img-ztzFDcCh-1629621444536)]
-
可用的SpEL表达式见下表:
[外链图片转存中…(img-pAFTKA9y-1629621444537)]
- 例:@Cacheable(cacheNames = {“emp”},key = “#id”, condition = “#id > 0”, unless = “#result == null”)缓存的名字为emp,key为id参数,当id>0,且返回结果不为NULL的数据
3.源码
git clone -b master4 https://gitee.com/idse666666/my-springboot.git