最近在利用工作之余自学点东西,主要想长点见识。学习的方向主要是基础的springboot,以及springboot与各种数据库交互、队列的关联使用;spring基于注解开发;jvm的底层分析与代码优化的方向,高并发高可用场景模拟等。后续可能会接触一点机器学习与自然语言相关知识,不过这个后面再说吧。学习过程中自认为比较重要的东西会记录下来,以供日后参考使用。
还是想说一句,其实一切在springboot的官方说明文档上已经说明的很清楚了,多看看文档会有很大的帮助(中文文档总感觉是机翻)。另外此文章不太适合0基础的小伙伴。
springboot
我认为springboot的核心是自动装配,且在满足自动装配的同时也支持高度定制化,定制化的过程也不复杂。后面就学到哪说到哪吧
- springboot jar版本管理中心
在pom.xml中可以发现,有个父项目是
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
而点进这个父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<properties>
<activemq.version>5.15.13</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.81</appengine-sdk.version>
<artemis.version>2.12.0</artemis.version>
<aspectj.version>1.9.6</aspectj.version>
<assertj.version>3.16.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.10.13</byte-buddy.version>
<caffeine.version>2.8.5</caffeine.version>
<cassandra-driver.version>4.6.1</cassandra-driver.version>
<classmate.version>1.5.1</classmate.version>
<commons-codec.version>1.14</commons-codec.version>
<commons-dbcp2.version>2.7.0</commons-dbcp2.version>
<commons-lang3.version>3.10</commons-lang3.version>
<commons-pool.version>1.6</commons-pool.version>
<commons-pool2.version>2.8.0</commons-pool2.version>
<couchbase-client.version>3.0.6</couchbase-client.version>
<db2-jdbc.version>11.5.4.0</db2-jdbc.version>
<dependency-management-plugin.version>1.0.9.RELEASE</dependency-management-plugin.version>
<derby.version>10.14.2.0</derby.version>
...多余的我就不一一列举了
可以发现真正管理springboot的各种版本依赖的其实是 spring-boot-dependencies,这里是springboot的版本仲裁中心,以后导入依赖默认是不需要写版本的,没有在dependencies里面管理的依赖还是需要声明版本号。这已经可以避免掉很多的版本冲突情况了。
- springboot 场景启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot将大部分常用的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有相关依赖都会导入进来。要用什么功能就导入什么场景的启动器。再加以简单配置,就可以直接使用。
- springboot 配置文件
配置文件名称固定 例如application-*.properties
,application-*.yml
,application-*.yaml
.需要注意的是,配置文件只有放在指定位置才生效,且高优先级同配置会覆盖低优先级,不同配置会互相组合。
说到这里,我们来说个配置文件的注入吧。yml配置文件如下:
#这是配置文件
person:
lastName: hello
age: 18
boss: false
birth: 2017/12/12
maps: {k1: v1,k2: 12}
lists:
- lisi
- zhaoliu
dog:
name: 小狗
age: 12
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
* @Component:只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
也可通过@Value来进行配置文件的内容注入(@value使用方式不做赘述),我们可以来比较两者的特性:
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装(对象包对象) | 支持 | 不支持 |
也可通过@PropertySource
形式加载指定的配置文件使配置文件内容注入
//新建person.properties文件,在里面对实体类属性进行赋值
@PropertySource(value = {"classpath:person.properties"})
@Component
public class Person {
也可以通过使用spring配置文件的形式,使用@ImportResource
使配置文件生效,不在此说明。
@ImportResource(locations = {"classpath:beans.xml"})
还可以配合@Configuration
与@Bean
形式来给容器添加组件
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件,在配置文件中用<bean><bean/>标签添加组件
*/
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
@Bean
public HelloService helloService02(){
System.out.println("配置类@Bean给容器中添加组件了...");
return new HelloService();
}
}
- springboot Profile文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml。默认使用application.properties的配置;
yml支持多文档块方式,使用 — 分割
server:
port: 8081
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev
---
server:
port: 8084
spring:
profiles: prod #指定属于哪个环境
激活指定profile(好像使用最多的是idea打包时选择哪个环境):
1、在配置文件中指定 spring.profiles.active=dev
2、命令行:java -jar xx.jar --spring.profiles.active=dev
3、虚拟机参数:-Dspring.profiles.active=dev
- springboot 配置文件加载位置与顺序
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
–file:./config/
–file:./
–classpath:/config/
–classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot会从这四个位置全部加载主配置文件;互补配置;
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
java -jar xx.jar --spring.config.location=G:/application.properties
java -jar xx.jar --server.port=8087 --server.context-path=/abc
还有其它的方式来更改配置文件内容,详细的可以见官方文档
- springboot 自动配置原理
SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
@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 {
@EnableAutoConfiguration
作用:
在org.springframework.boot:spring-boot-autoconfigure:2.3.2.RELEASE
jar中有个EnableAutoConfiguration
接口,其导入了AutoConfigurationImportSelector
自动配置选择器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
这个类里面有个这个方法:
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
SpringFactoriesLoader.loadFactoryNames()
这个方法是扫描META-INF/spring.factories
路径下所有的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中。随意从spring.factories
中看一个自动装配类(以RedisAutoConfiguration
举例)
@Configuration(proxyBeanMethods = false)
//表示这是一个配置类,与编写的配置文件一样,也可以给容器中添加组件
@ConditionalOnClass(RedisOperations.class)
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;
//判断当前是否有RedisOperations组件,如果有,当前配置类生效
@EnableConfigurationProperties(RedisProperties.class)
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和RedisProperties绑定起来;并把RedisProperties加入到ioc容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
//注册组件到容易,注册完成后,可以自动注入。
@ConditionalOnMissingBean(name = "redisTemplate")
//同为Conditional派生注解,详细规则下方有说明
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
再来看RedisProperties.class
@ConfigurationProperties(prefix = "spring.redis")
//从配置文件中获取指定的值和bean的属性进行绑定,一般情况下,这个类有哪些值,那么在配置文件里就能配置哪些值。额外的值除非手动注册,不然不生效
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:password@example.com:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
/**
* Whether to enable SSL support.
*/
private boolean ssl;
总结来说:
1)、SpringBoot启动会加载大量的自动配置类
2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
再来看看Conditional派生注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效
=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:(自动配置类启用的)
-----------------
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
- @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
Negative matches:(没有启动,没有匹配成功的自动配置类)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
AopAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)
今天就写到这里吧,下次更新springboot的底层日志框架
在此声明下,我不是大神,我是按照自己的理解来写的,想到哪写到哪,目的是为了以后自己能回顾一些东西。大家仅供参考