SpringBoot基础
# 1 基础
# 1.1 Springboot介绍
# 1.1.1 回顾以前的SSM,SSH项目
- 配置web.xml,加载spring和spring mvc
- 配置数据库连接、配置spring事务
- 配置加载配置文件的读取,开启注解 ..... 配置完成之后部署tomcat 调试
# 1.1.2 Sping的优缺点分析
优点: Spring是java企业级(J2EE)的轻量级代替品,为企业级开发提供了一种相对简单的方法,通过一来入驻和面向切面编程,用简单的java对象实现了EJB的功能。
缺点: 虽然Spring是轻量级的,但它的配置是重量级的。一开始,Spring用XML配置,而且很多XML配置,Spring2.5引入了基于注解的组件扫描,消除了大量针对应用程序自身组件的显示XML配置。 Spring3.0开始引入了基于java的配置,这是一种类型安全的可重构配置方式,可以替代XML。所有这些配置都代表了开发时的损耗,挤占了编写程序逻辑的时间。 除此之外,项目依赖管理也是一件损时耗力的事情,需要分析要导入哪些依赖。依赖之间的关系,版本之间不兼容等问题
# 1.1.3 什么是SpringBoot
从本质上来说,springboot是一个专注于框架的框架。Spring Boot就是对各种框架的整合,让他们集成在一起更加简单,简化了我们在集成过程中的模板化配置, 它做了那些没有它你自己也会去做的Spring Bean配置。你不用再写这些样板配置了,可以专注于应用程序的逻辑,这些才是应用程序独一无二的东西。
SpringBoot对Spring的缺点进行改善和优化,基于约定优于配置的思想,开发人员不必在配置与逻辑业务之间进行思维的切换,专注于业务逻辑的开发,提高开发效率。
起步依赖
本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起支持某项功能。简单的说,就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
自动配置
指的是Springboot会自动将一些配置类的bean注册到IOC容器中,我们可以在需要的地方使用@Autowired或者@Resource等注解来使用它。 “自动”的表现形式就是我们只需要引入我们要用的功能的包,相关的陪着我们完全不用管,SpringBoot会自动注入这些配置bean,我们直接使用这些bean即可。
SpringBoot的简单、快速、方便的搭建项目,对主流开发框架的无配置集成,极大提高开发,部署效率。
# 1.2 约定优于配置
这是形容springBoot最常用的描述,也有人解读为:约定大于配置,约定好于配置,习惯大于配置等。
用springBoot框架开发程序时,框架提供的默认值会让我们的项目开发起来效率更快,如果默认值满足不了我们的需求,我们可以使用Properties配置文件和YAML配置文件来重写默认值来满足我们的需求,所以约定大于配置,是说通过约定来较少配置,从而提升开发效率。
而且约定大于配置,并不是一种新的思想,在JDK5.0发布,采用元数据 ,引入注解的概念(也称之为标注),就代表简化配置的开始,就是初期的一种 “约定优于配置” 的体现;所以约定优于配置这一设计理念,从 Spring 的注解版本就已经开始了。引入注解就是为了减少一些默认配置,引入注解也就代表着简化配置的开始,官方说基于 spring 的基础就是这个事实。
# 1.3 快速上手SpringBoot
# 1.4 配置文件和注解配置
# 1.4.1 SpringBoot配置文件的加载顺序
- 工程根目录:./config/
- 工程根目录:./
- classpath:/config/
- classpath:/
优先级顺序是从上向下加载,所有的文件都会被加载,高优先级的内容会覆盖低优先级的内容,形成互补配置
# 1.4.2 常用属性
- --spring.config.name: 修改配置文件前缀
- --spring.config.location:定制路径,加载配置文件
- --spring.profiles.active :可以使用不同profile来区分环境。格式:application-{profile}.properties
- --spring.profiles.include :,引入其他配置文件,包含
# 1.4.2 application.propertites和application.yml共存
当application.properties和yml文件在并存时(同一目录下),application.properties优先级更好,会先读它,若它没有,再去读yml中的值。
# 1.4.3 拆分多环境配置
通过---可以把一个yml文档分割为多个或者创建 application-{profile}.properties/yml
# 1.4.4 多配置文件加载问题
使用spring.profile.include
引入其他配置文件,可propertites和yml混合使用,也可单独使用
(1)application.propertites格式问题
a.前后空格、等号左右空格是否有影响? 无影响
b.换行是否报错? key换行不能正常读取属性值不报错,值换行正常读取(除带特殊符号的)
c.指定include中的文件不存在是否报错? 不报错
d.命名得是application-profile的形式,eg:application-test.properties
(2)加载顺序问题
spring.profiles.include
属性配置顺序为主,后加载的会覆盖前面的
如:spring.profiles.include =redis,sys,sql,dev
redis和sql中同時配置了端口号,最后使用的是sql配置文件中的值
(3)外部配置文件 外部的配置文件优先加载 加载顺序也是按include属性中配置的顺序加载, 如果有相同的属性,外部配置文件会覆盖内部文件属性。
命令参考: a)jar、war包和配置文件不在同级目录下
java -jar demo.war-1.0.0-SNAPSHOT.war
--spring.config.location=C:\Users\lumingfu\Desktop\test\protest\application-mysql.properties,C:\Users\lumingfu\Desktop\test\protest\application.yml
2
3
或者
java -jar demo.war-1.0.0-SNAPSHOT.war
--spring.config.location=C:\Users\lumingfu\Desktop\test\protest\
2
或者
#加载内部的yml
--spring.config.location=classpath:/config/application.yml,C:\Users\lumingfu\Desktop\test\application-mysql.properties
2
b)jar,war包和配置文件在同级目录下或者同级/config目录下 可加载到内部配置文件,如需要引入外部配置文件,可不写或者用--spring.profiles.active 或--spring.profiles.include引入
# 1.4.5 常用条件注解
我们可以通过debug=true來打印自动配置报告,可以很方便的知道哪些自动配置生效
# 1.5 AOP处理
# 1.5.1 什么是AOP
面向切面编程,相对于OOP面向对象编程,Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。 在OOP中只能继承和实现接口,且类继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。还有就是为了清晰的逻辑,让业务逻辑关注业务本身, 不用去关心其它的事情,比如事务。Spring的AOP是通过JDK的动态代理和CGLIB实现的。
# 1.5.2 Aop术语
aop 有一堆术语,非常难以理解,简单说一下
通知(有的地方叫增强)(Advice): 需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用
连接点(Join point): 就是spring中允许使用通知的地方,基本上每个方法前后抛异常时都可以是连接点
切点(Poincut): 其实就是筛选出的连接点,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点做为切点。如果说通知定义了切面的动作或者执行时机的话,切点则定义了执行的地点
切面(Aspect): 其实就是通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行
引入(Introduction): 在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去
目标(target): 被通知的对象。也就是需要加入额外代码的对象,也就是真正的业务逻辑被组织织入切面。
织入(Weaving): 把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:
编译期: 切面在目标类编译时被织入,这种方式需要特殊的编译器
类加载期: 切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
运行期: 切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。
# 1.5.3常用的Aop增强类型
before(前置通知): 在方法开始执行前执行 after(后置通知): 在方法执行后执行 afterReturning(返回后通知): 在方法返回后执行 afterThrowing(异常通知): 在抛出异常时执行 around(环绕通知): 在方法执行前和执行后都会执行
执行顺序:around > before > around > after > afterReturning
# 1.5.4 实战
(1)引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.5.5</version>
</dependency>
2
3
4
5
(2)切面类
/**
* @author Mr.Fire -lmf
* @desc Aop切面类
*
* Spring AOP常用于拦截器、事务、日志、权限验证等方面
*/
@Aspect
@Component
public class AopAspect {
@Pointcut("execution(* com.springboot.demo.web.TestContoller.query())")
public void pointQuery(){
}
@Pointcut("execution(* com.springboot.demo.dao.UserJpaRepository.queyNameLike(..))")
public void pointQueryByName(){
}
/**
* 目标方法执行之前
*/
@After(value = "pointQuery()")
public void after(){
System.out.println("after query point processed");
}
/**
* 目标方法执行之后
*/
@Before(value = "pointQuery()")
public void before(){
System.out.println("before query point processed");
}
/**
* @Around的作用
*
* 既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
* 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
* 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
*/
@Around(value = "pointQuery()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("around query point processed");
try {
//果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
Object result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
//如果这里不返回result,则目标对象实际返回值会被置为null
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
/**
* 目标方法返回之后执行
* @param user
*/
@AfterReturning(value = "pointQuery()",returning = "user")
public void afterQueryToDo(Object user){
System.out.println("afterReturning query point processed");
System.out.println("执行结果:"+user.toString());
}
@AfterReturning(value = "pointQueryByName()",returning = "user")
public void afterQueryToDo(JoinPoint joinPoint,Object user){
System.out.println("参数:"+joinPoint.getArgs()[0]);
System.out.println("执行结果:"+user.toString());
}
/**
* 目标方法抛异常之后执行
* @param ex
*/
@AfterThrowing(value = "pointQueryByName()",throwing = "ex")
public void afterThrows(Throwable ex){
System.out.println(ex.getCause());
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 1.6 单元测试和热部署
(1)引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional><!--依赖不会进行传递-->
</dependency>
2
3
4
5
6
7
8
9
10
(2)热部署需要配置自动编译 快捷键ctrl + alt + shift + / 选择Register 勾选下面两个
(3)测试用例 编写测试用例类
@SpringBootTest //标记为SpringBoot单元测试类,并加载项目的上下文环境
class BootDemoApplicationTests {
@Autowired
TestContoller testContoller;
@Autowired
UserJpaRepository userJpaRepository;
@Test
void query() {
User user = testContoller.query();
System.out.println(user.toString());
System.out.println("hello word");
}
@Test
void queryAll() {
List<User> users = testContoller.queryList();
users.forEach(user -> System.out.println(user.toString()));
System.out.println("hello word");
}
@Test
void queryByLike(){
List<User> users = userJpaRepository.queyNameLike("cc");
users.forEach(user -> System.out.println(user.toString()));
}
@Test
void update(){
userJpaRepository.updatePwdName("123456","cc");
}
}
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
32
33
34
35
36
# 1.7 打包部署
(1)引入插件
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
2
3
4
5
6
(2)pom文件中定义打包类型
<packaging>jar</packaging>
(3)运行
jar包:
java -jar boot-demo.jar
war包:中间件部署
# 2 进阶
# 2.1 SpringBoot启动流程及自动装配及原理
默认启动类在最包的上层,否则在启动类之上的不会自动扫描到
# 2.2 Starter组件及命名规范
Starter是什么?
Starter 是SpringBoot四大组件之一,是一组惯例依赖描述资源,可以包含在应用中。从 starter 中,您可以获得所需的所有 Spring 和相关技术的一站式支持,无须通过示例代码和复制粘贴来获取依赖。比如,如果您要使用 Spring 和 JPA 进行数据库访问,那么只需要在项目中包含 spring-boot-starter-data-jpa 依赖项即可。 starter 包含了许多您需要用于使项目快速启动和运行,并且需要一组受支持的可传递依赖关系的依赖。
命名含义
官方的所有 starter 都遵循类似的命名规则:spring-boot-starter-*,其中 * 是特定类型的应用。这个命名结构旨在帮助您找到 starter。 许多 IDE 中 Maven 集成允许您按名称搜索依赖。例如,安装了 Eclipse 或者 STS 插件后,您可以简单地在 POM 编辑器中按下 ctrl-space 并输入 spring-boot-starter 来获取完整的列表。 第三方的 starter 命名不应该以 spring-boot 开头,因为它是官方 Spring Boot 构件所保留的规则。例如,有一个第三方 starter 项目叫做 thirdpartyproject, 它通常会命名为 thirdpartyproject-spring-boot-starter。
SpringBoot自动装配原理移步:https://javaessay.cn/pages/7fb4a5/ (opens new window)
# 2.3 SpingBoot整合Spring Data JPA
(1)引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2
3
4
(2)编写Repoistory接口
/**
* @author Mr.Fire -lmf
* @date 2021/10/9 15:39
* @desc Jpa接口
* <p>Repository接口 ,提供了方法名称命名查询方式,findBy</p>
* <p>JpaRepository接口,基于@Query注解查询与更新</p>
* <p>CrudRepository接口,主要是完成一些增删改查的操作。注意:CrudRepository接口继承了Repository接口</p>
* <p>JpaSpecificationExecutor接口,自定义多条件查询</p>
*
*/
public interface UserJpaRepository extends Repository<User,Integer>,
JpaRepository<User,Integer>,
CrudRepository<User,Integer>,
PagingAndSortingRepository<User,Integer>,
JpaSpecificationExecutor<User> {
/**
*
* @param name
* @return
*/
User findByUserName(String name);
/**
*
* @return
*/
@Query(value = "from User where userName = ?1")
User queryByNameHQL(String name);
/**
* 查询
* @return
*/
@Query(value = "select *from user where user_name like ?1%",nativeQuery = true)
List<User> queyNameLike(String name);
/**
* 修改
* @param password
* @param userName
*/
@Query(value = "update user set password = :password where user_name = :userName ",nativeQuery = true)
@Modifying
void updatePwdName(@Param("password") String password, @Param("userName") String userName);
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
(3)调用接口
@RestController
@RequestMapping("/test")
public class TestContoller {
@Autowired
UserJpaRepository jpaRepository;
@Autowired
ChildrenService childrenService;
@GetMapping("/hello")
public String helloWorld(){
return "Hello SpringBoot!!!";
}
@RequestMapping(value = "/query",method = RequestMethod.GET)
public User query(){
return jpaRepository.findByUserName("cc");
}
@RequestMapping(value = "/queryList",method = RequestMethod.GET)
public List<User> queryList(){
return jpaRepository.findAll();
}
}
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