1-4.约定优于配置设计范式及Spring Boot源码剖析.md

约定优于配置设计范式及Spring Boot源码剖析

[toc]


一、Spring boot应用回顾

1. 约定优于配置

约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计规范。本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)。好处:大大减少了配置项

2. spring boot 于spring 关系

spring boot基于spring
4.0设计,支持省去applicationContext.xml;不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决

3.SpringBoot主要特性

  1. SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中;

  2. 使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率。

  3. 自动配置:SpringBoot的自动配置特性利用了Spring对条件化配置的支持,合理地推测应用所需的bean并自动化配置他们;

  4. 使部署变得简单,SpringBoot内置了三种Servlet容器,Tomcat,Jetty,undertow.我们只需要一个Java的运行环境就可以跑SpringBoot的项目了,SpringBoot的项目可以打成一个jar包。

4.全局配置文件

  1. 文件名:

    application.properties或者application.yml(可以使用其他,需要配置)

  2. 文件位置

image-20210812132643579

  1. 注意事项:

    • 如果同一个目录下,有application.yml也有application.properties,2.4之前版本默认先读取

    application.properties。,2.4及之后默认先读取yml。

    • 如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取

    到的。

    • 创建SpringBoot项目时,一般的配置文件放置在“项目的resources目录下”
  2. 使用@ConfigurationProperties批量注入配置文件中属性

    • 实体类上添加@Component;@ConfigurationProperties注解

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      //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中配置要注入的信息

      image-20210812140310015

    • 或者在文件application.yml中配置

      image-20210812142644191

  3. spring boot测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @RunWith(SpringRunner.class)
    @SpringBootTest
    class MySpringbootApplicationTests {

    @Autowired
    private Person person;
    @Test
    void contextLoads() {
    System.out.println(person);
    }
    }

5.属性注入

  1. @Value单个属性注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @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;
    }
  2. @ConfigurationProperties放到类上批量注入

    1
    2
    3
    4
    5
    6
    7
    8
    @Component
    @ConfigurationProperties(prefix = "jdbc")
    public class JdbcConfiguration {
    String url;
    String driverClassName;
    String username;
    String password;
    }
  3. @ConfigurationProperties放到方法上注入(一般用于第三方对象赋值)

    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class MyService {
    @ConfigurationProperties(prefix = "another")
    @Bean
    public AnotherComponent anotherComponent(){
    return new AnotherComponent();
    }
    }
  4. 在配置文件中,属性是松散绑定的

first-name,firstName,first_name ,FIRSTNAME 都会被识别为一个字符串

6. 日志框架

  • 抽象层:SLF4J

  • 实现层:logback

  1. 统一日志框架使用步骤

    • 排除系统中的其他日志框架。

    • 使用中间包替换要替换的日志框架。

    • 导入我们选择的 SLF4J 实现。

  2. 使用

    1
    2
    Logger logger = LoggerFactory.getLogger(this.getClass());
    logger.info("info日志....");
  3. 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #日志配置
    # 指定具体包的日志级别
    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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MyAutoConfiguration {
static {

System.out.println("MyAutoConfiguration init.... ");
}

@Bean
public SimpleBean simpleBean(){
return new SimpleBean();
}

}

3.在META-INF下建立spring.factories文件以便扫描到我们要注册的starter

image-20210816150122887

4.优化

  • 使用@ConditionalOnBean注解,只有ioc容器中注入了这个类才生效

image-20210816150339765

  • 自定义出EnableRegisterServer注解,在其上方使用@Import注解使ioc容器中创建configmarker类实例

image-20210816150602642

5.测试

  • 我们可以在另一项目中使用pom.xml引入

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.lagou</groupId>
    <artifactId>zdy-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    </dependency>
  • 配置yml

    1
    2
    3
    simplebean:
    id: 1
    name: zdy
  • 测试注入

image-20210816150841425

image-20210816151144062

6.源码

git clone -b master2 https://gitee.com/idse666666/my-springboot.git

四、动态数据源配置

Spring内置了一个AbstractRoutingDataSource,它可以把多个数据源配置成一个Map,然后,根据不同的key返回不同的数据源。因为AbstractRoutingDataSource也是一个DataSource接口,因此,应用程序可以先设置好key,
访问数据库的代码就可以AbstractRoutingDataSource拿到对应的一个真实的数据源,从而访问指定的数据库

1.实现过程图解:

image-20210821154316971

2.配置两个数据源并注入到RoutingDataSource中:

image-20210821153323669

3.RoutingDataSource中创建子类并重写determineCurrentLookupKey方法

image-20210821153653187

4. 提供set和get方法获取存放在ThreadLocal方式获取当前线程key

image-20210821153914194

5.切面技术调用RoutingDataSourceUtil中的ThreadLocal的setkey

image-20210821154807822

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


1-4.约定优于配置设计范式及Spring Boot源码剖析.md
http://example.com/36491.html
作者
John Doe
发布于
2022年9月7日
许可协议