SpringBoot是整合Spring技术栈的一站式框架,是简化Spring技术栈的快速开发脚手架,优点是以下内容且使用简单,缺点是易使用,但只要报错就不易解决,且封装太深,内部原理复杂,不容易精通。
- 创建独立Spring应用
- 内嵌web服务器
- 自动starter依赖,简化构建配置
- 自动配置Spring以及第三方功能
- 提供生产级别的监控、健康检查及外部化配置
- 无代码生成、无需编写XML
1. 创建maven工程
1.1 引入依赖
<!--继承SpringBoot官方指定的父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--加入web开发所需要的场景启动器-->
<dependency>
<!--指定groupId、artifactId即可,版本已在父工程中定义-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!--maven构建过程相关配置-->
<build>
<!--构建过程中所需要用到的插件-->
<plugins>
<!--这个插件将springboot应用打包成一个可执行的jar包-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.2 创建主程序
/**
* 主程序类
* @SpringBootApplication:这是一个SpringBoot应用
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
1.3 编写业务
@Controller
public class HelloHandler {
@ResponseBody
@RequestMapping("/get/spring/boot/hello/message")
public String getMessage(){
return "first blood!!!";
}
}
1.4 测试
直接运行主程序类的main方法
1.5 部署(命令行启动)
- 进入所在工程,执行打包命令
- 进入target目录,找到打包好的jar包
- 把jar包复制到想要存放的位置,以方便执行
- 打开cmd命令行工具
- 执行 java -jar xxx.jar 命令
2. SpringBoot配置文件
2.1 概述
SpringBoot配置文件有两种,分别是properties属性文件和yaml 文件。文件保存位置是main/resources 目录下,文件名是 application.properties 或 application.yaml,两个文件可以同时使用,且以properties为主。注意:SpringBoot本身已经有自动配置了,配置文件是对自动配置的调整和修改。
2.2 相关注解@Component、@ConfigurationProperties、@Value
- 使用@Component注解是为了将当前类加入IOC容器
- @ConfigurationProperties表示将配置文件中的对应属性的值注入到当前组件
- prefix属性限定了配置文件中属性的范围,当我们指定了prefix="xxx"时,只有xxx属性中的数据会被注入
- 加入spring-boot-configuration-processor依赖后编写配置文件会根据@ConfigurationProperties注解标记的类生成提示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
@Value注解能够以更简单的方式读取配置文件中的值,但是仅限于简单类型
配置文件
atguigu:
message:
config: "This is a message from config file"
测试
@Value("${atguigu.message.config}")
private String message;
@Test
public void testValueAnnotation(){
System.out.println(message);
}
2.3 日志级别和范围的设置
设置全局范围的日志级别,影响范围太大,不建议使用
logging:
level:
root: debug
局部代码设置日志级别,使用"包名: 级别"的方式具体的指定
logging:
level:
具体的包名: 为具体的包设置日志范围和级别(debug等)
3. SpringBoot相关的注解
3.1 @Configuration注解
使用 @Configuration 注解标记一个类之后,这个类成为配置类,加载这个类中的配置可以取代以前的xml配置文件
/**
*1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
*2、配置类本身也是组件
*/
@Configuration
public class SpringAnnotationConfig{
}
基于注解类而不是xml配置文件创建IOC容器对象的代码如下:
ApplicationContext iocContainer = new AnnotationConfigApplicationContext(SpringAnnotationConfig.class);
3.2 @Bean注解
相当于xml配置文件中的bean标签。用于把一个类的对象加入ioc容器
@Configuration
public class SpringAnnotationConfig{
//外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
//给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
@Bean
public EmployeeService getEmployeeService(){
return new EmployeeService();
}
}
3.3 @Import注解
相当于@Bean注解,使用@Import注解可以更便捷的将一个类加入ioc容器
//@Import({xxx.class,yyy.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Import({xxx.class,yyy.class})
@Configuration
public class SpringAnnotationConfig{
}
3.4 @Conditional注解
条件装配:满足Conditional指定的条件,才进行组件注入
3.5 @ComponentScan注解
指定ioc容器扫描的包,相当于在xml中配置context:component-scan
3.6 @SpringBootApplication注解
表示当前应用是一个SpringBoot应用,包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan注解。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
该注解有以下三个注解
3.6.1 @SpringBootConfiguration注解
@Configuration注解的SpringBoot版
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
3.6.2 @ComponentScan注解,指定扫描哪些包
3.6.3 @EnableAutoConfiguration注解
启用自动化配置功能
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
SpringBoot在这里通过@Import注解中的AutoConfigurationImportSelector.class将所有需要导入的组件以全类名的方式返回。这些组件就会添加到容器中,给容器中导入非常多的自动配置类(XxxAutoConfiguration)。这样就给容器中导入了这个场景需要的所有组件,并配置好这些组件。而这些组件以前是需要我们手动在xml中配置才能加入ioc容器。
3.6.3.1 @AutoConfigurationPackage注解
指定自动化配置的包
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
SpringBoot在这里通过@Import注解中的 AutoConfigurationPackages.Registrar.class 将主启动类所在包和其子包中的所有组件扫描到ioc容器中
4. SpringBoot工作原理
- 读取spring.factories文件
- SpringBoot启动时会读取spring-boot-autoconfigur-2.6.6.jar包下的META-INF/spring.factories文件,读取org.springframework.boot.autoconfigure.EnableAutoConfiguration属性的值加载自动配置类
- 加载XxxProperties类
- 根据自动配置类XxxAutoConfiguration中指定的XxxProerties类设置自动配置的属性值,开发者也可以根据XxxProperties类中指定的属性在yml配置文件中修改自动配置
- 根据@ConditionalXxx注解决定加载哪些组件
- SpringBoot通过@ConditionalXxx注解指定特定组件加入ioc容器时所需要具备的特定条件。这个组件会在满足条件时加入ioc容器。
总结:
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。从xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration ---- 组件 ---- xxxxProperties里面拿值 ---- application.properties
5. SpringBoot整合第三方工具
5.1 整合Mybatis
5.1.1 依赖引入
<!--引入Mybatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--数据库连接池druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入测试的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
5.1.2 建表
CREATE TABLE `table_emp` (
`emp_id` int NOT NULL AUTO_INCREMENT,
`emp_name` varchar(100) DEFAULT NULL,
`emp_age` int DEFAULT NULL,
PRIMARY KEY (`emp_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
5.1.3 实体类
public class Employee {
private Integer empId;
private String empName;
private Integer empAge;
public Employee() {
}
public Employee(Integer empId, String empName, Integer empAge) {
this.empId = empId;
this.empName = empName;
this.empAge = empAge;
}
...
}
5.1.4 Mapper配置文件,可以使用xml配置文件,也可以Mybatis基于注解的方式
Mapper配置文件(建在resource/mybatis/mapper目录下)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springboot.mapper.EmpMapper">
<select id="selectAll" resultType="com.atguigu.springboot.entity.Employee">
select emp_id empId,emp_name empName,emp_age empAge
from table_emp
</select>
</mapper>
Mapper接口
public interface EmpMapper {
List<Employee> selectAll();
}
yaml配置文件
spring:
datasource:
# 不重要,用于标识,相当于bean标签的 id 属性
name: mydb
# 数据源的类型:DruidDataSource
type: com.alibaba.druid.pool.DruidDataSource
# 连接数据库的 url 地址
url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC
# 用户名
username: root
# 密码
password: abc123
# 数据库驱动
driver-class-name: com.mysql.cj.jdbc.Driver
# 做 mybatis 的配置
mybatis:
# 用于指定 XxxMapper.xml 配置文件的位置
mapper-locations: classpath:mybatis/mapper/*Mapper.xml
# 针对具体的某个包,设置日志级别,以便打印日志,就可以看到Mybatis打印的 SQL 语句了
logging:
level:
com.atguigu.springboot.mapper: debug
5.1.5 主启动类
// 主程序类
// @SpringBootApplication:这是一个SpringBoot应用
@SpringBootApplication
// 扫描Mapper接口所在的包,配置了这个扫描我们才能对 Mapper 接口进行装配
// mybatis.mapper-locations在SpringBoot配置文件中使用,作用是扫描 Mapper 接口对应的XML文件
// @MapperScan会扫描 Mapper 接口类,并生成对应的实现类
@MapperScan("com.atguigu.springboot.mapper")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
5.1.6 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisTest {
@Autowired
private EmpMapper empMapper;
private Logger logger = LoggerFactory.getLogger(MybatisTest.class);
@Test
public void testMapper(){
List<Employee> employeeList = empMapper.selectAll();
for ( Employee emp : employeeList ) {
logger.debug(emp.toString());
}
}
}
5.2 整合Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。
5.2.1 依赖引入
redis环境搭建
1、阿里云按量付费redis。经典网络
2、申请redis的公网连接地址
3、修改白名单,允许0.0.0.0/0 访问
<!--引入redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
5.2.2 application.yaml
spring:
redis:
# 指定 redis 服务器的地址
host: 192.168.213.100
5.2.3 redis自动配置原理
- RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis的配置
- 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
- 自动注入了RedisTemplate<Object, Object> : xxxTemplate;
- 自动注入了StringRedisTemplate;k:v都是String
- key:value
- 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
5.2.4 测试,redis运行的时候,才能对其进行访问
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testRedis(){
// 1.获取用来操作String类型数据的ValueOperations对象
ValueOperations<String, String> operations = redisTemplate.opsForValue();
// 2.借助ValueOperations对象存入数据
operations.set("hello","world");
// 3.读取刚才设置的数据
String readValue = operations.get("hello");
System.out.println(hello);
}
StringRedisTemplate、RedisTemplate二者的关系:子父类的关系
5.3 整合Thymeleaf
5.3.1 视图模板技术
我们熟悉的JSP其实只是视图模板技术的一种,除了JSP还有很多其他技术也可以帮助我们实现服务器端渲染。例如: Freemarker、Thymeleaf、Velocity等等。其中最优秀的是Thymeleaf。而JSP因为不容易从jar包中读取而不被SpringBoot推荐。
5.3.2 JSP相对于Thymeleaf的巨大缺陷
前端工程师拿到JSP文件后,没法直接放在浏览器上查看布局、动态效果等等显示效果。所以,基于JSP无法和前端工程师进行协同工作。而Thymeleaf中所有的动态效果都不影响原本HTML标签的显示。当一个前端工程师拿到Thymeleaf文件后,可以直接放在浏览器上查看显示效果,看代码时忽略动态部分即可。
5.3.3 在SpringBoot环境下加入使用Thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
#在html标签中加入Thymeleaf的名称空间
<html xmlns:th="http://www.thymeleaf.org">
application.yaml
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
cache: false #开发的时候禁用缓存
thymeleaf语法用法:
# 修改标签文本值
<h3>替换标签体</h3>
<p th:text="新值">原始值</p>
# 修改指定属性值
<h3>替换属性值</h3>
<input value="old-value" th:value="new-value" />
# 在表达式中访问属性域
<h3>访问请求域</h3>
<p th:text="${reqAttrName}">aaa</p>
或
<p th:text="${#httpServletRequest.getAttribute('reqAttrName')}">这里注意给属性名加引号</p>
<h3>访问session域</h3>
<p th:text="${session.ssAttrName}">bbb</p>
<h3>访问application域</h3>
<p th:text="${application.appAttrName}">ccc</p>
# 解析URL地址
<h3>解析url地址</h3>
<p th:text="@{/aaa/bbb/ccc}">页面上看到的是:/contextPath/aaa/bbb/ccc</p>
# 直接执行表达式
<h3>直接执行表达式</h3>
<p>有转义效果:[[${reqAttrName}]]</p>
<p>无转义效果:[(${reqAttrName})]</p>
# 分支与迭代
# 条件判断
<h3>测试if判断</h3>
<p th:if="${not #strings.isEmpty(reqAttrName)}">if判断为真时输出的内容</p>
~<p th:if="${#strings.isEmpty(reqAttrName)}">if判断为真时输出的内容</p>~
# 遍历集合
<h3>测试循环</h3>
<!--使用th:each进行集合数据迭代-->
<!--th:each="声明变量 : ${集合}"-->
<!--th:each用在哪个标签上,哪个标签就会出现多次-->
<div>
<p th:text="${card}" th:each="card : ${cardList}"></p>
</div>
# 包含其他模板文件
# 声明代码片段part.html,位于templates/include/part.html
<div th:fragment="myFirstPart">
<p>被包含的内容1</p>
</div>
<div th:fragment="mySecondPart">
<p>被包含的内容2</p>
</div>
<div th:fragment="myThirdPart">
<p>被包含的内容3</p>
</div>
# 包含
xxx部分是访问模板文件时的逻辑视图名称,要求这个xxx拼接yaml里配置的前后缀之后得到访问模板文件的物理视图。
比如:想要访问templates/include/aaa.html页面,那么xxx就是include/aaa,因为templates是前缀,.html是后缀
表达式 作用
th:insert="~{xxx::zzz}" 以插入方式引入(使用代码片段整体插入当前标签内)
th:replace="~{xxx::zzz}" 以替换方式引入(使用代码片段整体替换当前标签)
th:include="~{xxx::zzz}" 以包含方式引入(使用代码片段内容包含到当前标签内)
# 具体代码:
<h3>包含页面的片段</h3>
<!--"::"左边的值拼前后缀后必须能够找到要包含的文件-->
<!--"::"右边的值是代码片段的名字-->
<div th:insert="~{include/part :: myFirstPart}"></div>
<div th:replace="~{include/part :: mySecondPart}"></div>
<div th:include="~{include/part :: myThirdPart}"></div>
6. 开发小技巧
6.1 Lombok,简化JavaBean开发
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
===============================简化JavaBean开发===================================
@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {
private String name;
private Integer age;
private Pet pet;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
================================简化日志开发===================================
@Slf4j
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
log.info("请求进来了....");
return "Hello, Spring Boot 2!"+"你好:"+name;
}
}
7. SpringBoot的使用补充
7.1 web的注解版view-controller实现无业务逻辑跳转
@Configuration
public class CrowdWebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器访问的地址
String urlPath = "/auth/member/to/reg/page.html";
// 目标视图的名称,将来拼接视图的前后缀
String viewName = "member-reg";
// 添加一个view-controller
registry.addViewController(urlPath).setViewName(viewName);
}
}
// 效果相当于以下方法:
@RequestMapping("/auth/member/to/reg/page.html")
public String memberToRegPage(){
return "member-reg";
}