二、SpringBoot基础篇
2.1什么是SpringBoot
- SpringBoot是目前流行的微服务框架,倡导“约定优于配置”,其目的是用来简化新Spring应用的初始化搭建以及开发过程。
- SpringBoot提供了很多核心的功能,比如自动化配置starter(启动器)简化Maven配置、内嵌Servlet容器、应用监控等功能,让我们可以快速构建企业级应用程序。
- 特性:
- 创建独立的Spring应用程序
- 嵌入式Tomcat、Jetty、Undertow容器(jar)
- 提供的starters简化构建配置(简化依赖管理和版本控制)
- 尽可能自动配置spring应用和第三方库
- 提供生产指标,例如指标、健壮检查和外部化配置
- 没有代码生成,无需XML配置
- SpringBoot同时提供“开箱即用”,“约定优于配置”的特性。
- 开箱即用:SpringBoot应用无需从0开始,使用脚手架创建项目。基础配置已经完成。集成大部分第三方库对象,无需配置就能使用。例如在SpringBoot项目中使用MyBatis,可以直接使用XXXMapper对象,调用方法执行sql语句。
- 约定优于配置:SpringBoot定义了常用类,包的位置和结构,默认的设置。代码不需要做调整,项目能够按照预期运行。比如启动类在根包的路径下,使用了@SpringBooApplication注解,创建了默认的测试类,controller、service、dao应该放到根包的子包中,application为默认的配置文件。
- 脚手架(spring提供的一个web应用,帮助开发人员,创建springboot项目)
- SpringBoot3最小JDK17,支持17-20。
- SpringBoot理念”约定优于配置“,也可以称为按约定编程。
2.1.1SpringBoot与Spring关系
- SpringBoot创建的是Spring应用,这里的Spring是指Spring Framework。
- 我们常说的Spring,一般指的是Spring家族,包括Spring Boot、Spring Framework、Spring Data、Spring Security、Spring Batch、Spring Shell、Spring for Apache Kafka…。
- SpringBoot是包含了Spring、SpringMVC的高级框架,提供了自动功能。能够更好的创建Spring应用,消除了Spring的XML配置文件,提高了开发效率,消除Spring应用的臃肿,避免了大量的模板代码。
- SpringBoot是现在Java开发人员必须掌握的框架,SpringBoot是掌握SpringCloud的基础。
2.1.2SpringBoot3新特性
- JDK最小Java17,能够支持17-20。
- SpringBoot3已将所有底层依赖项从JavaEE迁移到了Jakarta EE API。原来javax开头的包名修改为jakarta。
- 支持GraalVM原生镜像,将Java应用编译为本机代码,提供显著的内存和启动性能改进。
- 对第三方库更新了版本支持。
- 自动配置文件的修改。
- 提供新的声明式Http服务,在接口方法上声明@HttpExchange获取http远程访问的能力。
- Spring HTTP客户端提供基于Micrometer的可观察性跟踪服务,记录服务运行状态等。
- 支持AOT预先编译。
- Servlet6.0规范。
- 支持Jackson2.14。
- SpringMVC:默认情况下使用的PathPatternParser,删除过时的文件和FreeMarker、JSP支持。
2.2脚手架
- 脚手架是辅助创建程序的工具,Spring Initializr是创建Spring Boot项目的脚手架,快速建立Spring Boot项目的最好方式。
- 它是一个web应用,能够在浏览器中使用。
- IDEA中继承了此工具,用来快速创建SpringBoot项目已经SpringCloud项目。
- Spring Initializr 脚手架的 web 地址: https://start.spring.io/
2.2.1使用脚手架创建项目
- 浏览器访问脚手架,创建项目
- IDEA中使用脚手架
2.2.2IDEA创建SpringBoot项目
2.3代码结构
2.3.1单一模块
- 一个工程一个模块的完整功能实现,例如实现订单功能。
com.example.模块名称
±—Application.java 启动类
±—controller 控制器包
—StudentController.java
—ScoreController.java
±—service 业务层包
—inter 业务层接口
—impl 接口实现包
±—repository 持久层包
±—model 模型包
—entity 实体类包
—dto 数据传输包
—vo 视图数据包
2.3.2多个模块
- 一个SpringBoot中多个模块,在根包下面创建每个模块的子包,子包中可以按照”单一模块“包结构定义。
2.3.3spring-boot-starter-parent
-
pom中的指定spring-boot-starter-parent作为坐标,表示继承SpringBoot提供的父项目。
-
父项目提供以下功能:
- JDK的基准版本,比如<java.version>17</java.version>
- 源码使用UTF-8格式编码
- 公共依赖的版本
- 自动化的资源过滤:默认把src/main/resources目录下的文件进行资源打包
- maven的占位符为’@’
- 对多个Maven插件做了默认配置,如maven-compile-plugin,maven-jar-plugin
-
快速创建SpringBoot项目,同时能够使用父项目带来的便利性,可以采用如下两种方式:
- 在项目中继承spring-boot-starter-parennt
<!--parent:表示父项目--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.2</version> <relativePath/> </parent>
- pom.xml不继承,单独加入spring-boot-dependencies依赖
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.1.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
2.4starter
- starter是一组依赖的描述,应用中包含starter,可以获取spring相关技术的一站式的依赖和版本。不必复制、粘贴代码,通过starter能够快速启动并运行项目。
- starter包含:
- 直接依赖坐标、版本
- 传递依赖的坐标、版本
- 配置类、配置项
pom.xml加入spring Web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
查看Maven依赖
Mybatis配置类
2.5@SpringBootApplication核心注解
/**
* 核心注解的功能
* @SpringBootConfiguration:包含@Configuration注解的功能
* @Configuration:JavaConfig的功能,配置类,结合@Bean能够将对象注入到spring的IOC容器。
* 有@SpringBootConfiguration标注的类是配置类,Springboot06PackageApplication就是配置类。
*
* @EnableAutoConfiguration:开启自动配置,将spring和第三方库中的对象创建好,注入到spring容器。
* 避免写xml,去掉样例代码,需要使用的对象由框架提供。
*
* @ComponentScan:组件扫描器,<context:component-scan base-package="xxx包"/>
* 扫描@Controller、@Service、@Repository、@Component注解,创建他们的对象注入到容器
* springboot约定:启动类作为扫描包的根(起点),@ComponentScan扫描com.hhb.pk这个包和它的子包中所有的类
*
*/
@SpringBootApplication
public class Springboot06PackageApplication {
@Bean
public Date myDate(){
return new Date();
}
public static void main(String[] args) {
//run方法的第一个参数是 源(配置类),从这里加载bean,找到bean注入到spring的容器。
//run方法的返回值是容器对象
ApplicationContext applicationContext= SpringApplication.run(Springboot06PackageApplication.class, args);
//可以从容器获取对象
Date date = applicationContext.getBean(Date.class);
}
}
@RestController
public class HelloController {
@Autowired
private Date date;
@GetMapping("/hello")
public String hello() {
return "欢迎使用SpringBoot3"+date;
}
}
2.6运行SpringBoot项目方式
-
开发工具,例如IDEA执行main方法
-
Maven插件
-
java -jar
-
Spring Boot项目可以打包为jar或者war文件,因为SpringBoot内嵌了web服务器,例如tomcat。能够以jar方式运行web应用,无需安装tomcat程序。
普通的jar与SpringBoot jar的区别
项目 | springboot jar | 普通的jar |
---|---|---|
目录 | BOOT-INF:应用的class和依赖jar META-INF:清单 org.springframeword.boot.loader:spring-boot-loader模板类 | META-INF:清单 class的文件夹:jar中的所有类 |
BOOT-INF | class:应用的类 lib:应用的依赖 | 没有BOOT-INF |
spring-boot-loader | 执行jar的spring boot类 | 没有此部分 |
可执行 | 能 | 否 |
2.7外部化配置
2.7.1配置文件基础
2.7.1.1 配置文件格式
- 配置文件由两个种格式,分别是:properties和yaml(yml)。
- properties是Java种常用的一种配置文件格式,基本的语法 key=value。可以是唯一的,文件扩展名是properties。
- yaml也看做yml,是一种做配置文件的数据格式,基本的语法 key: value。yml文件扩展名是yaml或yml(常用)。
- yaml基本语法规则:
- 大小写敏感
- 使用缩进表示层级关系
- 缩进只可以使用空格,不允许使用Tab键
- 缩进的空格数目不重要,相同层级的元素左侧对齐即可
- #字符表示注释,只支持单行注释。#放在注释行的第一个字符
- YAML缩进必须使用空格,而且区分大小写,建议编写YAML文件只用小写和空格。
- YAML支持三种数据结构:对象、数组、标量
2.7.1.2application文件
- SpringBoot同时支持properties和yml格式的配置文件,配置文件名称默认是application,我们可以使用application.properties,application.yml。
- SpringBoot建议使用一种格式的配置文件,如果properties和yml都存在,优先读取properties文件,但是推荐使用yml文件。
- application配置文件的名称和位置都可以修改,约定名称为application,位置为resources目录。
2.7.1.3application.properties
1.在application.properties自定义配置项目
#默认的配置文件:application.properties
app.name=springboot07
app.owner=hhb
app.port=8091
2.创建SomeService类读取key
- @Value读取单个值,语法为 ${key:默认值}
@Service
public class SomeService {
//使用@Value("${key:默认值}")
@Value("${app.name}")
private String name;
@Value("${app.owner}")
private String owner;
@Value("${app.port:8080}")
private Integer port;
public void printValue() {
StringJoiner joiner = new StringJoiner(";");
String result = joiner.add(name).add(owner).add(String.valueOf(port)).toString();
System.out.println("result=" + result);
}
}
3.单元测试
@SpringBootTest
class Springboot07ConfigApplicationTests {
@Autowired
private SomeService someService;
@Test
void test() {
someService.printValue();
}
}
2.7.1.4application.yml
1.编写application.yml
#编写配置项 key: 值
app:
name: springboot07
owner: hhb07
port: 9000
- SpringBoot对yml文件的内容进行扁平化处理,将yml中的内容看做properties中的格式一样处理。也就是app: name 看做是app.name。所以在SpringBoot中认为properties和yml是一样的。
2.创建SomeService类读取key
- @Value读取单个值,语法为 ${key:默认值}
@Service
public class SomeService {
//使用@Value("${key:默认值}")
@Value("${app.name}")
private String name;
@Value("${app.owner}")
private String owner;
@Value("${app.port:8080}")
private Integer port;
public void printValue() {
StringJoiner joiner = new StringJoiner(";");
String result = joiner.add(name).add(owner).add(String.valueOf(port)).toString();
System.out.println("result=" + result);
}
}
3.单元测试
@SpringBootTest
class Springboot07ConfigApplicationTests {
@Autowired
private SomeService someService;
@Test
void test() {
someService.printValue();
}
}
2.7.1.5Environment
- Environment是外部化的抽象,是多种数据来源的集合。从中可以读取application配置文件、环境变量、系统属性。
- 使用方式在Bean中注入Environment,调用它的getProperty(key)方法。
1.创建ReadConfig类,注入Environment
@Service
public class ReadConfig {
//注入环境对象
@Autowired
private Environment environment;
public void print() {
//获取某个key的值
String name = environment.getProperty("app.name");
//判断key是否存在
if( environment.containsProperty("app.owner")){
System.out.println("app.owner是存在的");
}
//读取key的值,转为期望的类型,同时提供默认值
Integer port = environment.getProperty("app.port", Integer.class, 9001);
String format = String.format("读取的key值,name=%s,port=%d", name, port);
System.out.println("str=" + format);
}
}
单元测试
@SpringBootTest
class ReadConfigTest {
@Autowired
private ReadConfig readConfig;
@Test
void test01() {
readConfig.print();
}
}
2.7.1.6组织多文件
- 大型集成的第三方框架,中间件比较多,每个框架的配置细节相对复杂,如果都将配置集中到一个application文件,导致文件内容多,不易阅读。我们将每个框架独立一个配置文件,最后将多个文件集中到application,我们使用导入文件的功能。
1.在resources创建自定义conf目录,在conf中创建redis.yml,db.yml
2.application.yml导入多个配置
#导入其他的配置 , 多个文件使用",“ 作为分隔符
spring:
config:
import: conf/db.yml,conf/redis.yml
3.创建类,读取两个文件的配置项
@Service
public class MultiConfigService {
@Value("${spring.redis.host}")
private String redisHostName;
@Value("${spring.datasource.url}")
private String jdbcUrl;
public void print() {
String format = String.format("redisHostName=%s,url=%s", redisHostName, jdbcUrl);
System.out.println(format);
}
}
4.单元测试
@SpringBootTest
class MultiConfigServiceTest {
@Autowired
private MultiConfigService multiConfigService;
@Test
void test01() {
multiConfigService.printConfig();
}
}
2.7.1.7多环境配置
- SpringBoot规定环境文件的名称 application-{profile}.properties(yml)。其中profile为自定义的环境名称,推荐使用dev表示开发,test表示测试,prod表示生产,feature表示特性。
- SpringBoot会加载application以及application-{profile}两类文件,不是只单独加载application-{profile}。
1.在resources创建环境配置文件
2.激活环境
- spring.profiles.active: 环境名称
spring:
#激活某个配置文件(环境)
profiles:
active: dev
3.创建读取配置项的类
@Service
public class MultiEnvService {
@Value("${myapp.memo}")
private String memo;
public void print() {
System.out.println("memo=" + memo);
}
}
4.单元测试
@SpringBootTest
class MulitEnvServiceTest {
@Autowired
private MulitEnvService service;
@Test
void test01() {
service.print();
}
}
2.7.2绑定Bean
- @Value绑定单个属性,当属性较多不方便时,SpringBoot提供了另一种绑定属性的方法。
- 基本原则:标准的Java Bena有无参数构造方法,包含属性的访问器,配合@ConfigurationProperties注解一起使用,Bean的static属性不支持。
- @ConfigurationProperties能够配置多个简单类型属性,同时支持Map、List、数组类型。对属性还能验证基本格式。
2.7.2.1简单的属性绑定
1.查看application配置
#编写配置项 key: 值
app:
name: springboot07
owner: hhb07
port: 9000
2.创建Bean,定义name、owner、port属性
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "app")
public class AppBean {
private String name;
private String owner;
private Integer port;
// set | get 方法 ,toString()
}
- @ConfigurationProperties声明在类上,表示绑定属性到此类。prefix表示前缀,是配置文件中多个key的公共前缀。prefix=“app”,将文件中app开始的key都找到,调用与key相同名称的setXXX方法,如果有给属性赋值成功,没有的忽略。
单元测试
@Autowired
private AppBean appBean;
@Test
void test05() {
System.out.println("appBean = " + appBean.toString());
System.out.println("appBean.getClass() = " + appBean.getClass());
}
2.7.2.2嵌套Bean
- Bean中包含其它Bean作为属性
1.定义两个Bean
public class Security {
private String username;
private String password;
// set | get 方法 ,toString()
}
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "app")
public class NestAppBean {
private String name;
private String owner;
private Integer port;
private Security security;
// set | get 方法 ,toString()
}
2.定义application.yml
#yml 配置文件 key: 值
app:
name: Lession07-yml
owner: bjpowernode
port: 8002
# app.security.username=root
# app.security.password=123456
security:
username: root
password: 123456
2.7.2.3扫描注解
- @ConfigurationProperties注解起作用,还需要@EnableConfigurationProperties 或@ConfigurationPropertiesScan。这个注解是专门寻找@ConfigurationProperties 注解的,将他的对象注入到 Spring 容器。在启动类上使用扫描注解。
//扫描注解的包名,其中绑定Bean注入到Spring容器
@ConfigurationPropertiesScan(basePackages = {"com.hhb.config.pk6","com.hhb.config.pk8"})
//启用ConfigurationProperties,属性是类的名字
//@EnableConfigurationProperties({NestAppBean.class})
@SpringBootApplication
public class Springboot07ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot07ConfigApplication.class, args);
}
}
2.7.2.4处理第三方库对象
- 上面的例子都是在源代码中使用@ConfigurationProperties 注解,如果某个类需要在配置文件中提供数据,但是没有源代码。此时@ConfigurationProperties 结合@Bean 一起在方法上面使用。
1.application.yml添加新的配置
security:
username: common
password: abc123
2.创建配置类
@Configuration
public class ApplicationConfig {
//创建bean对象,属性值来自配置文件
@ConfigurationProperties(prefix = "security")
@Bean
public Security createSecurity(){
return new Security();
}
}
3.单元测试
@Autowired
private Security security;
@Test
void test07() {
System.out.println("security.toString() = " + security.toString());
}
2.7.2.5集合Map、List以及Array
- Map、List以及Array都能提供配置数据。
1.创建保存数据的Bean
public class User {
private String name;
private String sex;
private Integer age;
//set | get ,toString
}
public class MyServer {
private String title;
private String ip;
//set | get ,toString
}
@Configuration
@ConfigurationProperties
public class CollectionConfig {
private List<MyServer> servers;
private Map<String,User> users;
private String [] names;
//set | get ,toString
}
2.修改application.yml,配置数据
#配置集合
#数组和List一样,使用“-”表示一个成员
names:
- 李四
- 张三
##List<MyServer> servers
servers:
- title: 服务器1
ip: 101.12.36.2
- title: 服务器2
ip: 202.90.23.263
#Map<String,User> users
users:
user1:
name: 张三
sex: 男
age: 22
user2:
name: 李四
sex: 女
age: 23
- “-”表示集合一个成员,因为成员是对象,需要属性名称指定属性值。
- List与数组前面加入“-”表示一个成员。
- Map直接指定key和value,无需“-”。
3.单元测试
@Autowired
private CollectionConfig collectionConfig;
@Test
void test08() {
System.out.println("collectionConfig.toString() = " + collectionConfig.toString());
}
2.7.2.7指定数据源文件
- application做配置是经常使用的,除此之外我们能够指定某个文件作为数据来源。
- @PropertySource是注解 ,用以加载指定的properties文件、yml文件。@PropertySource与@Configuration一同使用,其它注解还有@Value,@ConfigurationProperties。
1.创建Group类,表示组织
@Configuration
@ConfigurationProperties(prefix = "group")
@PropertySource(value = "classpath:/group-info.properties")
public class Group {
private String name;
private String leader;
private Integer members;
//set | get ,toString
}
2.在resources目录下的任意位置创建properties文件
group.name=IT学习专栏
group.leader=无名
group.members=20
总结
- @ConfigurationProperties绑定Bean对于使用配置文件的数据十分方便、灵活的。
2.8创建对象三种方式
-
将对象注入到Spring容器,可以通过如下方式:
- 传统的XML配置文件
- Java Config技术,@Configuration与@Bean
- 创建对象的注解,@Controller ,@Service , @Repository ,@Component
-
Spring Boot不建议使用XML文件的方式,自动配置已经解决了大部分XML中的工作了。如果需要XML提供bean的声明,@ImportResource加载XML注册Bean。
1.创建Person类,对象由容器管理
public class Person {
private String name;
private Integer age;
//set | get ,toString
}
2.resources目录下创建XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--声明Bean对象-->
<bean id="myPerson" class="com.hhb.config.pk10.Person">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
</beans>
3.启动类,从容器中获取Person对象
//在配置类加入注解@ImportResource
@ImportResource(locations = {"classpath:/applicationContext.xml"})
@SpringBootApplication
public class Springboot07ConfigApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(Springboot07ConfigApplication.class, args);
Person bean = applicationContext.getBean(Person.class);
System.out.println("bean = " + bean);
}
}
- @ImportResource(locations = {“classpath:/applicationContext.xml”}),加载类路径下的applicationContext.xml文件。location或者value属性都能指定文件路径。
2.9AOP
-
AOP:面向切面编程,保持原有代码不变,能够给原有的业务逻辑增加更多功能。
-
AOP增加的功能是开发人员自己编写的,底层是动态代理实现功能的增强,对于扩展功能十分有利。
-
Spring的事务功能就是在AOP基础上实现的,业务方法在执行前开启事务,再执行业务方法,最后提交或回滚失败。
-
主要包括五个注解:@Before、@After、@AfterReturning、@AfterThrowing、@Around。注解来自aspectj框架。
- @Before:在切点方法之前执行。
- @After:在切点方法之后执行。
- @AfterReturning:切点方法返回后执行。
- @AfterThrowing:切点方法抛异常执行。
- @Around:属于环绕增强,能控制切点执行前、执行后,功能最强的注解。
-
需求:项目中的业务方法都需要在日志中输出方法调用的时间以及参数明细。业务方法多,使用AOP最合适。
1.Maven添加aop依赖
- 包含了aop和aspectj依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建业务类SomeService在aop的service子包
public interface SomeService {
void query(Integer id);
void save(String name, Integer age);
}
@Service
public class SomeServiceImpl implements SomeService {
@Override
public void query(Integer id) {
System.out.println("SomeService业务方法query");
}
@Override
public void save(String name, Integer age) {
System.out.println("SomeService业务方法save");
}
}
3.创建切面类
- @Aspect说明当前类是切面类,包含了功能增强的代码和通知注解。
- @Component将切面类对象注入到spring容器。spring会根据目标类和切面类创建代理对象,织入切面功能。
@Component
@Aspect
public class LogAspect {
//功能增强的方法
@Before("execution(* com.example.aop.service.*.*(..))")
public void sysLog(JoinPoint joinPoint) {
StringJoiner log = new StringJoiner("|", "{", "}");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.add(sdf.format(new Date()));
//当前执行的业务方法名称
String name = joinPoint.getSignature().getName();
log.add(name);
//方法的参数
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
log.add(arg == null ? "-" : arg.toString());
}
System.out.println("日志:" + log);
}
}
4.单元测试
@SpringBootTest
class Springboot08AopApplicationTests {
@Autowired
private SomeService someService;
@Test
void testAspect() {
someService.query(null);
someService.save("张三", 20);
}
}