Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
Spring官方提供了Spring Initializr:https://start.spring.io/,帮助快速的搭建Spring Boot工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.ygang</groupId>
<artifactId>springboot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 一般情况下使用Springboot官方提供的启动器父依赖统一管理springboot各组件版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- web场景启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- springboot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package top.ygang.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApplicationStart {
public static void main(String[] args) {
SpringApplication.run(ApplicationStart.class,args);
}
}
注意:启动类一般要放在工程的顶级目录中(相对于其他类)
package top.ygang.springbootdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/helloworld")
public String helloworld(){
return "Hello Spring Boot!";
}
}
spring-boot-starter-web
的默认端口为:8080
Springboot默认的配置文件名为:application.yml
或application.properties
#端口号
server.port=8080
#项目名,可以不写,默认localhost:8080就可以访问项目
server.servlet.context-path=/app
#配置静态资源访问路径,resources文件夹下
spring.resources.static-locations=classpath:/templates/
#前缀后缀
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
#数据库连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# spring.datasource.driverClassName=org.postgresql.Driver
# 初始化时建立物理连接的个数
spring.datasource.druid.initial-size=5
# 最小连接池数量
spring.datasource.druid.min-idle=5
# 最大连接池数量
spring.datasource.druid.max-active=20
# 获取连接时最大等待时间,单位毫秒
spring.datasource.druid.max-wait=60000
# 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.druid.test-while-idle=true
# 既作为检测的间隔时间又作为testWhileIdel执行的依据
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
spring.datasource.druid.min-evictable-idle-time-millis=30000
spring.datasource.url=jdbc:mysql://localhost:3306/mana?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
#redis配置
spring.redis.host = localhost
spring.redis.password = eduask
spring.redis.port = 6379
#分页拦截器,为true先设定数据库中查询的范围,再根据条件查询
pagehelper.reasonable=false
#mapper.xml扫描,如果mapper.xml和mapper.java在一个包里,那么注解MapperScan就可以了
mybatis.mapper-locations=classpath*:top.ygang.mapper/*.xml
全名**:YAML Ain't Markup Language**
以数据为中心,比json、xml等更适合做配置文件
key:[空格]value
:表示一对键值对(空格必须有)k: v
:字面直接来写;""
:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
"zhangsan \n lisi"
zhangsan 换行 lisi
''
:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
'zhangsan \n lisi'
zhangsan \n lisi
在下一行来写对象的属性和值的关系;注意缩进
friends:
lastName: zhangsan
age: 20
#行内写法:
friends: {lastName: zhangsan,age: 18}
用- value
表示数组中的一个元素
pets:
- cat
- dog
- pig
#端口号
server:
port: 8080
servlet:
context-path: /app
#配置thymeleaf的视图解析器ThymeleafViewResolver
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML5
cache: false
encoding: UTF-8
#redis配置
redis:
host: localhost
password: eduask
port: 6379
#数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/manage?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
#数据库连接池
druid:
# 初始化时建立物理连接的个数
initial-size: 5
# 最小连接池数量
min-idle: 5
# 最大连接池数量
max-active: 20
# 获取连接时最大等待时间,单位毫秒
max-wait: 60000
# 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
test-while-idle: true
# 既作为检测的间隔时间又作为testWhileIdel执行的依据
time-between-eviction-runs-millis: 60000
#销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接
min-evictable-idle-time-millis: 30000
servlet:
#文件上传配置
multipart:
enabled: true
#单个文件的大小
max-file-size: 20MB
#限制总上传文件的大小
max-request-size: 20MB
#分页拦截器
pagehelper:
reasonable: false
#日志
logging:
level:
root: info
top.ygang: debug
org.springframework.security: warn
在resource下新建三个配置文件:application.yml
(通用),application-dev.yml
(开发),application-prod.yml
(生产)
使用方式1,在application.yml
中指定当前使用的配置文件类型
spring:
profiles:
active: dev
使用方式2,在jar包启动的使用,添加命令,指定使用的配置文件类型
java -jar test.jar --spring.profiles.active=dev
spring:
application:
name: test
在java bean代码中读取name属性值,使用@Value
注解
@Value("${spring.application.name}")
private String name;
minio:
endpoint: localhost:9000
accesskey: minioadmin
secretkey: minioadmin
使用@ConfigurationProperties
的配置类用于读取配置文件,在spring容器启动时,就会在yml中读取
@Component
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioConfig{
private String endpoint;
private String accesskey;
private String secretkey;
}
有的时候,我们自己开发一个模块,希望我们的Bean对应的配置文件项会有提示,例如
就可以引入该依赖,搭配注解@ConfigurationProperties
使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
引入后,要求Bean必须添加@Component
注解,并且属性必须有public
的getter
、setter
,例如
@ConfigurationProperties(prefix = "student")
@Component
public class Student {
/**
* 编号
*/
private String id = "001";
/**
* 姓名
*/
private String name = "lucy";
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后就可以build project
,会在target/classes/META-INF
目录下生成spring-configuration-metadata.json
文件,里面有对改配置项的说明
{
"groups": [
{
"name": "student",
"type": "top.ygang.springbootdemo.Student",
"sourceType": "top.ygang.springbootdemo.Student"
}
],
"properties": [
{
"name": "student.id",
"type": "java.lang.String",
"description": "编号",
"sourceType": "top.ygang.springbootdemo.Student"
},
{
"name": "student.name",
"type": "java.lang.String",
"description": "姓名",
"sourceType": "top.ygang.springbootdemo.Student"
}
],
"hints": []
}
在SpringBoot出现之前,如果我们想使用SpringMVC来构建我们的web项目,必须要做的几件事情如下:
DispatcherServlet
,并配置url映射springmcv-servlet.xml
,在其中配置SpringMVC中几个重要的组件,处理映射器(HandlerMapping)、处理适配器(HandlerAdapter)、视图解析器(ViewResolver)applicationcontext.xml
文件中引入 springmvc-servlet.xml
文件以上这几步只是配置好了SpringMVC,如果我们还需要与数据库进行交互,就要在application.xml
中配置数据库连接池DataSource,如果需要数据库事务,还需要配置TransactionManager…
这就是使用Spring框架开发项目带来的一些的问题:
而在SpringBoot出现之后,它为我们提供了一个强大的功能来解决上述的两个痛点,这就是SpringBoot的starters(场景启动器)。
Spring Boot通过将我们常用的功能场景抽取出来,做成的一系列场景启动器,这些启动器帮我们导入了实现各个功能所需要依赖的全部组件,我们只需要在项目中引入这些starters,相关场景的所有依赖就会全部被导入进来,并且我们可以抛弃繁杂的配置,仅需要通过配置文件来进行少量的配置就可以使用相应的功能。
在导入的starter之后,SpringBoot主要帮我们完成了两件事情
SpringApplication.run()
方法主要完成了以下功能:
这个注解作为一个Springboot工程的开始,也是Springboot的核心注解,它有如下两个核心的元注解
@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 {}
这个注解有一个元注解@Configuration
,也就是标志了这个类是一个配置类,并且注入Spring容器进行管理
这个注解是Springboot开启自动配置的核心注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
的作用是将main
包下的所有组件注册进容器@Import({AutoConfigurationImportSelector.class})
的作用是加载自动装配类:AutoConfigurationImportSelector
,自动装配的核心功能也就是通过这个类实现的AutoConfigurationImportSelector
有一个关键的方法如下
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//第1步:判断自动装配开关是否打开
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//第2步:用于获取注解中的exclude和excludeName。
//获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//第3步:获取需要自动装配的所有配置类,读取META-INF/spring.factories
//读取所有预配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//第4步:符合条件加载
//去掉重复的配置类
configurations = removeDuplicates(configurations);
//执行
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//校验
checkExcludedClasses(configurations, exclusions);
//删除
configurations.removeAll(exclusions);
//过滤
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
//创建自动配置的对象
return new AutoConfigurationEntry(configurations, exclusions);
}
第一步判断自动装配开关是否打开:默认spring.boot.enableautoconfiguration = true
,可在application.properties
或application.yml
中设置
spring.factories
中为Spring官方提供的所有自动配置类的候选列表,不光是这个依赖下的META-INF/spring.factories
被读取到,所有Spring Boot Starter下的META-INF/spring.factories
都会被读取到。
这些自动配置类并不是一定会生效,而是要符合一定的条件,而条件就是根据自动配置类上的@ConditionOnXXX
来判断,只有所有注解都满足条件,则这个类才会生效。
Spring Boot 提供的条件注解如下:
@ConditionalOnBean
:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass
:当类路径下有指定类的条件下@ConditionalOnMissingClass
:当类路径下没有指定类的条件下@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnResource
:类路径是否有指定的值@ConditionalOnExpression
:基于 SpEL 表达式作为判断条件@ConditionalOnJava
:基于 Java 版本作为判断条件@ConditionalOnJndi
:在 JNDI 存在的条件下差在指定的位置@ConditionalOnNotWebApplication
:当前项目不是Web项目的条件下@ConditionalOnWebApplication
:当前项目是Web项目的条件下可以在Springboot配置文件中加入debug: true
来开启调试日志,确定有哪些AutoConfiguration生效,日志信息主要包括以下
Application.run()
,完成对Spring容器的初始化,并且开启启动类注解的功能@SpringBootApplication
,最核心的就是自动装配功能spring.boot.enableautoconfiguration = true
,则开始执行自动装配classpath://META-INF/spring.factories
,获取所有自动配置类@ConditionalXXX
过滤出需要加载的自动配置类,并进行加载@EnableConfigurationProperties
、@ComponentScan
,读取配置文件、加载Bean等,完成模块功能官方定义的场景启动器命名格式为:spring-boot-starter-xxx
,例如spring-boot-starter-web
自定义或第三方的场景启动器命名格式为:xxx-spring-boot-starter
,例如mybatis-spring-boot-starter
该自定义模块要实现引入后自动在控制台输出接口调用情况
1、创建maven项目mylistener-spring-boot-starter
2、引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.ygang</groupId>
<artifactId>mylistener-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.2.0.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 包含很多与自动配置相关的注解的定义,必须要引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 编写自定义配置类需要 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 功能需要 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>
3、创建配置类读取配置文件
@Component
@ConfigurationProperties(prefix = "mylistener")
public class MylistenerProperties {
/**
* 输出日期格式
*/
private String dateformat = "yyyy-MM-dd HH:mm:ss";
public String getDateformat() {
return dateformat;
}
public void setDateformat(String dateformat) {
this.dateformat = dateformat;
}
}
4、创建切面类,监听所有GET方法
@Aspect
@Component
public class GetAdvice {
public GetAdvice() {
System.out.println("GetAdvice加载进入IOC容器!!!");
}
@Autowired
private MylistenerProperties mylistenerProperties;
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void pc(){}
@Before("pc()")
public void before(JoinPoint jp){
SimpleDateFormat dateFormat = new SimpleDateFormat(mylistenerProperties.getDateformat());
MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
System.out.println(dateFormat.format(new Date()) + " 方法:" + method.getName() + "被执行!!!");
}
}
5、创建自动装配类,完成自动装配
@Configuration
// 限定自动装配类生效条件:当为Web应用时加载
@ConditionalOnWebApplication
// 加载配置文件,并注入Bean到容器
@EnableConfigurationProperties({
MylistenerProperties.class
})
// 扫描所有切面,注入Spring容器
@ComponentScan("top.ygang.mylistener.advice")
public class MylistenerAutoConfiguration {
}
6、在resources
目录下创建META-INF/spring-factories
文件,使SpringBoot可以找到自动装配类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
top.ygang.mylistener.MylistenerAutoConfiguration
7、打包、安装:mvn clean install
8、在上面的Hello World项目中,引入模块组件
<dependency>
<groupId>top.ygang</groupId>
<artifactId>mylistener-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
9、启动项目,由于已经在配置文件中配置了:debug: true
,所以可以看到所有AutoConfiguration
的加载情况,可以看到模块组件已经被加载
10、调用接口http://localhost:8080/helloworld
,看到控制台输出
此时分为两种情况,一种是xml映射文件没有和mapper接口放在一起,而是单独放在别的目录,例如resouces下,可以通过mybatis配置项进行配置
mybatis:
mapper-locations: classpath*:/mapper/**/*.xml
另一种情况是,SpringBoot项目打jar包后,没有将xml文件打进jar,可以通过pom.xml
配置
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
Spring Boot中使用ENC(Environment-Neutral Configuration)主要是为了将配置信息从应用程序代码中分离出来,以提高安全性和可维护性。ENC的主要优点包括:
pmo.xml依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
配置加密盐值
jasypt:
encryptor:
# password值任意,最好随机字符
password: hhX4FzbwcT
工具类使用
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
public class JasyptTest {
/**
* 加密盐值,使用完成后进行删除,或者不能提交到`生产环境`,比如:
*/
private final static String PASSWORD = "hhX4FzbwcT";
public static void main(String[] args) {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
// 用于设置加密密钥。密钥是用于加密和解密字符串的关键信息。
config.setPassword(PASSWORD);
// 加密算法的名称,jasypt-3.0.5版本后默认的加密方式
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
// 用于设置加密时迭代次数的数量,增加迭代次数可以使攻击者更难进行密码破解。
config.setKeyObtentionIterations("1000");
// 加密器池的大小。池是一组加密器实例,可确保加密操作的并发性。
config.setPoolSize("1");
// 用于设置JCE(Java Cryptography Extension)提供程序的名称。
config.setProviderName("SunJCE");
// 用于设置生成盐的类名称。在此配置中,我们使用了org.jasypt.salt.RandomSaltGenerator,表示使用随机生成的盐。
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
// 用于设置Jasypt使用的初始化向量(IV)生成器的类名。初始化向量是在加密过程中使用的一个固定长度的随机数,用于加密数据块,使每个数据块的加密结果都是唯一的。在此配置中,我们使用了org.jasypt.iv.RandomIvGenerator类,该类是一个随机生成器,用于生成实时随机IV的实例。这样可以确保每次加密的IV都是唯一的,从而增加加密强度。
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
// 指定加密输出类型。在此配置中,我们选择了base64输出类型。
config.setStringOutputType("base64");
encryptor.setConfig(config);
// 明文1
String name_encrypt = "root";
// 明文2
String password_encrypt = "123456";
// 明文加密
String encrypt1 = encryptor.encrypt(name_encrypt);
String encrypt2 = encryptor.encrypt(password_encrypt);
System.out.println("明文加密1:" + encrypt1);
System.out.println("明文加密2:" + encrypt2);
// 密文解密
String decrypt1 = encryptor.decrypt(encrypt1);
String decrypt2 = encryptor.decrypt(encrypt2);
System.out.println("密文解密1:" + decrypt1);
System.out.println("密文解密2:" + decrypt2);
}
}
加密配置使用
sys:
name: ENC(Yt36hceu3xGXEzrz2jCPjvalaXQ5yIHE04SVT6lIkcktrxqtBZrlivkAkA9/9oZ2)
password: ENC(0Ci6irPOko9IG+hBZJAGoguIuE52gF/XiigCV4DwLm6NfkoyvV4Etgc9FzKK3MYl)
测试
@RestController
public class TestController {
@Value("${sys.name}")
private String name;
@Value("${sys.password}")
private String password;
@GetMapping("/test")
public void test() {
System.out.println("name = " + name);
System.out.println("password = " + password);
}
}
原理
1、在jasypt-spring-boot-starter
包的自动配置类JasyptSpringBootAutoConfiguration
中通过@Import
注解引入了EnableEncryptablePropertiesConfiguration
配置类,该类中environment
参数储存了yaml文件元数据;
2、在postProcessBeanFactory
方法中,调用environment.getPropertySources()
方法获取yaml配置项目;
3、通过convertPropertySources
进行数据转换,修改yaml中配置项值,具体实现为调用instantiatePropertySource()
方法,在该方法中,匹配propertySource instanceof MapPropertySource
,转化为EncryptableMapPropertySourceWrapper
对象;
4、EncryptableMapPropertySourceWapper
类中,通过getProperty(name)
方法进行匹配,匹配成功后调用resolver.resolvePropertyValue
进行ENC配置的解密并且替换原有的propertySource
值;