1. Spring Boot特性:
- 快速创建独立的Spring应用程序。(Spring支持的SpringBoot都支持,也就是说SpringBoot全方位支持IoC,AOP等)
- 嵌入式的Tomcat、Jetty、Undertow容器。(web服务器本身就是几个jar包,Spring Boot框架自动嵌入了。)
- 需要什么功能时只需要引入对应的starter启动器即可。(启动器可以自动管理这个功能相关的依赖,自动管理依赖版本的控制)
- 尽最大努力,最大可能的自动配置Spring应用和第三方库。(例如:如果要进行事务的控制,不用做任何事务相关的配置,只需要在service类上添加@Transactional注解即可。)
- 没有代码生成,没有XML配置。(Spring Boot的应用程序在启动后不会动态地创建新的Java类,所有逻辑都是在编译期就已经确定好的)
- 提供了生产监控的支持,例如健康检查,度量信息,跟踪信息,审计信息等。也支持集成外部监控系统。
1.1 第一个SpringBoot程序
<!--使用SpringBoot框架,首先要继承SpringBoot这个父工程。-->
<!--这里为什么不是直接引入springboot依赖,而是继承SpringBoot父工程的方式??????-->
<!--确保所有Spring Boot项目使用相同版本的依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
</parent>
<!--依赖-->
<dependencies>
<!--引入web启动器,这样的话自动会将web开发相关的所有依赖全部引入,例如:json、tomcat、springmvc等,包括这些依赖的版本也不需要我们管理,自动管理。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
1.2 Spring Boot主入口程序
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
1.3 打jar包运行
Spring Boot提供了打包插件,可以将Spring Boot项目打包为可执行 jar 包。
Web服务器(Tomcat)也会连同一块打入jar包中。
<!--这是一个能够创建可执行jar包的springboot插件。-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
执行package打包命令,生成可执行jar包:
单独的将这个 jar 包可以拷贝到任何位置运行,通过
java -jar sb3-01-first-web-1.0-SNAPSHOT.jar
命令来启动 Spring Boot 项目:
Spring Boot框架提供了非常灵活的配置,在可执行jar包的同级目录下新建配置文件:application.properties,并配置以下信息:
server.port=8888
1.4 SpringBoot的jar包和普通jar包的区别
Spring Boot 打包成的 JAR 文件与传统的 Java 应用程序中的 JAR 文件相比确实有一些显著的区别,主要体现在依赖管理
和可执行性
上。
依赖管理:
1. Spring Boot 的 JAR 包通常包含了应用程序运行所需的所有依赖项,也就是说它是一个“fat jar”(胖 JAR 包),这种打包方式使得应用可以独立运行,而不需要外部的类路径或应用服务器上的其他依赖。
2. 普通的 JAR 文件一般只包含一个类库的功能,并且需要依赖于特定的类路径来找到其他的类库或者框架,这些依赖项通常在部署环境中已经存在,比如在一个应用服务器中。
可执行性:
1. Spring Boot 的 JAR 文件可以通过直接执行这个 JAR 文件来启动应用程序,也就是说它是一个可执行的 JAR 文件。通过 java -jar your-application.jar
命令就可以直接运行应用程序。
2. 普通的 JAR 文件通常是不可直接执行的,需要通过指定主类(main class)的方式或者其他方式来启动一个应用程序,例如使用 -cp
或 -classpath
加上类路径以及主类名来执行。
2. Spring Boot启动器
2.1. 启动器实现原理
依赖聚合:
每个启动器通常对应一个特定的功能集或者一个完整的应用模块,如 spring-boot-starter-web
就包含了构建 Web 应用所需的所有基本依赖项,如 Spring MVC, Tomcat 嵌入式容器等。
依赖传递:
当在项目中引入一个启动器时,它不仅会把自身作为依赖加入到你的项目中,还会把它的所有直接依赖项(transitive dependencies)也加入进来。这意味着你不需要单独声明这些依赖项,它们会自动成为项目的一部分。
版本管理:
启动器内部已经指定了所有依赖项的具体版本,这些版本信息存储在一个公共的 BOM(Bill of Materials,物料清单)文件中,通常是 spring-boot-dependencies
。当引入启动器时,实际上也间接引用了这个 BOM,从而确保了所有依赖项版本的一致性。
自动配置:
许多启动器还提供了自动配置(Auto-configuration),这是一种机制,允许 Spring Boot 根据类路径上的可用组件自动设置应用程序。例如,如果类路径上有 Spring MVC 和嵌入式 Tomcat,则 Spring Boot 会自动配置它们,并准备好一个 web 应用程序。
2.2 Springboot的启动器
2.2.1. 官方提供的启动器
启动器命名特点:spring-boot-starter-*
2.2.2. 非官方的启动器
启动器命名特点:*-spring-boot-starter
3. @SpringBootApplication注解
3.1 @SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
注解被@Configuration
标注,说明主入口
程序是一个配置类。也就是说主入口中的方法可以被@Bean
注解标注,被@Bean
注解的标注的方法会被Spring容器自动调用,并且将该方法的返回对象纳入IoC容器的管理。
1. Bean是Spring管理的对象,可以是任何Java类(如Service、Repository、Controller等)
2. Spring容器会自动创建这些Bean,并管理它们的生命周期(初始化、销毁)
3. Bean之间可以通过依赖注入相互调用
SpringBoot主入口类实际上就是一个配置类
。
这个配置类
也可以称为源
,起源的意思,SpringBoot从这个配置类开始加载项目中所有的bean。
3.2 @EnableAutoConfiguration注解
该注解表示启用自动配置;
Spring Boot 会根据你引入的依赖自动配置好一系列的 Bean,无需手动编写复杂的配置代码。
例如:在SpringBoot项目中进行了如下配置:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=1234
在依赖中引入了mybatis依赖
/mybatis启动器
,那么SpringBoot框架将为你自动化配置以下bean:
- SqlSessionFactory: MyBatis的核心工厂SqlSessionFactory会被自动配置。这个工厂负责创建SqlSession实例,后者用来执行映射文件中的SQL语句。
- TransactionManager: DataSourceTransactionManager会被自动配置来管理与数据源相关的事务。
3.3 @ComponentScan注解
启动组件扫描功能,代替spring框架xml文件中这个配置:
<context:component-scan base-package="com.powernode.sb305core"/>
@SpringBootApplication
注解标注之后,会启动组件扫描功能,扫描的包是主入口程序所在包及子包
,因此如果一个bean要纳入IoC容器的管理则必须放到主入口程序所在包及子包下。
4. Spring Boot的单元测试
4.1 不使用单元测试
public class User {
private String username;
private String password;
// 全参构造器
// toString
// getter
// setter
}
public interface UserService {
User findUser();
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public User findUser() {
return new User("jackson", "123456");
}
}
主入口程序
手动获取Spring上下文对象
ConfigurableApplicationContext
,然后调用getBean方法从Spring容器中获取service对象,然后调用方法。
@SpringBootApplication
public class Springboot305JunitApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Springboot305JunitApplication.class, args);
UserService userService = applicationContext.getBean("userService", UserService.class);
User user = userService.findUser();
System.out.println(user); // User{username='jackson', password='123456'}
applicationContext.close();
}
}
4.2 使用单元测试
引入单元测试启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
// springboot项目中使用单元测试junit,那么单元测试类必须使用这个注解进行标注。
@SpringBootTest
class Springboot305JunitApplicationTests {
@Autowired
private UserService userService;
// 单元测试方法,单元测试方法使用 @Test 注解标注。
@Test
void test01() {
User user = userService.findUser();
System.out.println(user);
}
}
4.2.1 @SpringBootTest注解
@SpringBootTest
会创建一个完整的 Spring 应用程序上下文(Application Context),这个上下文包含了应用程序的所有组件和服务。以下是 @SpringBootTest
做的一些主要工作:
创建 ApplicationContext:
@SpringBootTest
使用SpringApplication
的run()
方法来启动一个 Spring Boot 应用程序上下文。这意味着它会加载应用程序的主配置类和其他相关的配置类。
加载配置文件:
- 它会查找并加载默认的配置文件,如
application.properties
自动配置:
- 如果应用程序依赖于 Spring Boot 的自动配置特性,
@SpringBootTest
会确保这些自动配置生效。这意味着它会根据可用的类和bean来自动配置一些组件,如数据库连接、消息队列等。
注入依赖:
- 使用
@SpringBootTest
创建的应用程序上下文允许你在测试类中使用@Autowired
注入需要的 bean,就像在一个真实的 Spring Boot 应用程序中一样。
5. 外部化配置
外部化配置是指:将配置信息
存储在应用程序代码
之外的地方。这样配置信息
可以独立于代码进行管理。这样方便了配置的修改,并且修改后不需要重新编译代码,也不需要重新部署项目。
5.1 外部化配置的方式
SpringBoot支持多种外部化配置方式,包括但不限于:
- properties文件
- YAML文件
- 系统环境变量
- 命令行参数
5.1.1 外部化配置的优势
灵活性:配置文件可以独立于应用程序部署,这使得可以根据运行环境的不同来调整配置,而无需修改代码。
易于维护:配置变更不需要重新构建和部署应用程序,降低了维护成本。
安全性:敏感信息如数据库密码、API密钥等可以存储在外部,并且可以限制谁有权限访问这些配置信息。
共享性:多实例或多服务可以共享相同的配置信息,减少重复配置的工作量。
版本控制:配置文件可以存放在版本控制系统中,便于跟踪历史版本和回滚配置。
5.2 application.properties
Spring Boot 框架在启动时会尝试从以下位置加载 application.properties
配置文件:
1. file:./config/:首先在Spring Boot 当前工作目录下的 config
文件夹中查找。
注意:如果没有找到application.properties
会继续找application.yml
,如果这两个都没有找到,才会进入以下位置查找,以此类推。
2. file:./:如果在当前工作目录下config
目录中找不到时,再从当前工作目录中查找。
3. classpath:/config/:如果从工作目录中找不到,会从类路径中找,先从类路径的 /config/
目录下寻找配置文件。
4. classpath:/:如果在 /config/
下没有找到,它会在类路径的根目录下查找。
Spring Boot 会按照这个顺序来加载配置文件,如果在多个位置有相同的属性定义,那么最先检查的位置中的属性值将优先使用。
// 使用这个也是可以的。因为这样也可以纳入IoC容器的管理
// 并且还有另一个作用,就是表示以下的类是一个配置类。
//@Configuration
// 纳入IoC容器的管理
@Component
public class AppConfig {
@Value("${myapp.path}")
private String appPath;
public void printInfo(){
System.out.println(appPath);
}
}
@SpringBootApplication
public class Springboot306PropertiesLocationApplication {
// args 就是接收命令行参数的。
public static void main(String[] args) {
// 打印命令行参数。
for (String arg : args) {
System.out.println(arg); // 输出内容就是上面最后配置的路径
}
ConfigurableApplicationContext applicationContext = SpringApplication.run(Springboot306PropertiesLocationApplication.class, args);
AppConfig appConfig = applicationContext.getBean(AppConfig.class);
appConfig.printInfo();
applicationContext.close();
}
}
如果想要指定其他的配置文件位置或者改变默认的行为,可以通过 --spring.config.location=
后跟路径的方式来指定配置文件的具体位置。
java -jar sb3-01-first-web-1.0-SNAPSHOT.jar --spring.config.location=file:///E:\a\b\application.properties
Spring Boot 将会首先从 E:\a\b\
这个路径加载配置文件。
5.3 @Value注解
@Value注解可以将application.properties
/application.yml
文件中的配置信息注入/绑定到java对象的属性上。
语法格式:@Value("${key}")
myapp.username=jackson
myapp.email=jackson@123.com
#myapp.age=20
@Service
public class SystemService {
@Value("${myapp.username}")
private String username;
@Value("${myapp.email}")
private String email;
// 注意:当使用 @Value注解的时候,如果这个key不存在,并且没有指定默认值,则报错。
// key存在,但没有指定默认值,那么赋值为null
//@Value("${myapp.age}")
@Value("${myapp.age:50}")
private Integer age;
public void printInfo(){
System.out.println(username);
System.out.println(email);
System.out.println(age);
}
}
5.4 YAML
YAML文件的扩展名可以是.yaml
或.yml
。
5.4.1 常见的数据存储和交换格式
properties
、XML
、JSON
、YAML
这几种格式确实是用来存储和交换数据的常见方式,但它们各有特点和适用场景:
Properties
- 这种格式主要用于Java应用程序中的配置文件。它是键值对的形式,每一行是一个键值对,使用等号或冒号分隔键和值。
- 特点是简单易懂,但在处理复杂结构的数据时显得力不从心。
XML (eXtensible Markup Language)
- XML是一种标记语言,用来描述数据的格式。它支持复杂的数据结构,包括嵌套和属性。
- XML文档具有良好的结构化特性,适合传输和存储结构化的数据。但是,XML文档通常体积较大,解析起来也比较耗资源。
JSON (JavaScript Object Notation)
- JSON是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript的一个子集,支持多种数据类型,如数字、字符串、布尔值、数组和对象。
- JSON因为简洁和高效而广泛应用于Web应用程序之间进行数据交换。
YAML (YAML Ain't Markup Language)
- YAML设计的目标之一就是让人类更容易阅读。它支持类似JSON的数据序列化,但提供了更多的灵活性,例如缩进来表示数据结构。
- YAML非常适合用来编写配置文件,因为它允许以一种自然的方式组织数据,并且可以包含注释和其他人类可读的元素。
5.4.2 YAML的语法规则
YAML的语法规则如下:
1. 数据结构:YAML支持多种数据类型,包括:
- 字符串、数字、布尔值
- 数组、list集合
- map键值对 等。
2. YAML使用一个空格
来分隔属性名
和属性值
,例如:
properties
文件中这样的配置:name=jackyaml
文件中需要这样配置:name: jack
3. YAML用换行+空格
来表示层级关系。注意不能使用tab,必须是空格,空格数量无要求,大部分建议2个或4个空格。
4. 同级元素左对齐。
5. 键必须是唯一的:在一个映射中,键必须是唯一的。
6. 注释:使用#
进行注释。
7. 大小写敏感
5.4.3 YAML的小细节
第一:普通文本 可以 使用单引号或双引号括起来:
- 单引号括起来:单引号内所有的内容都被当做普通文本,不转义(例如字符串中有\n,则\n被当做普通的字符串)
- 双引号括起来:双引号中有 \n 则会被转义为换行符
第二:保留文本格式
- | 将文本写到这个符号的下层,会自动保留格式。
第三:文档切割
- --- 这个符号下面的配置可以认为是一个独立的yaml文件。便于庞大文件的阅读。
5.4.4. application.yml
Spring Boot框架同时支持properties
和yaml
。
在同一个目录下同时存在application.properties
和application.yml
时,SpringBoot优先解析application.properties
文件。
把resources/config
目录下application.properties
名字修改为application2.properties
,这样Spring Boot才会解析resources/config/application.yml;
5.5 配置文件合并
application-mysql.properties
spring.datasource.username=root
spring.datasource.password=123456
application-redis.properties
spring.data.redis.host=localhost
spring.data.redis.port=6379
application.properties
spring.config.import=classpath:application-mysql.properties,classpath:application-redis.properties
application.yml
spring:
config:
import:
- classpath:/config/application-mysql.yml
- classpath:/config/application-redis.yml
import: [classpath:/config/application-mysql.yml, classpath:/config/application-redis.yml]
5.6 多环境切换
在Spring Boot中,多环境切换是指在一个应用程序中支持多种运行环境配置的能力。这通常用于区分开发(development)、测试(testing)、预生产(staging)和生产(production)等不同阶段的环境。
这种功能使得开发者能够在不同的环境中使用不同的配置,比如数据库连接信息、服务器端口、环境变量等,而不需要更改代码。这对于维护一个可移植且易于管理的应用程序非常重要。
如果希望该项目使用生产环境的配置,可以这样做:
- 第一种方式:在
application.properties
文件中添加这个配置:spring.profiles.active=prod - 第二种方式:在命令行参数上添加:--spring.profiles.active=prod
5.7 将配置绑定到bean
5.7.1 绑定简单bean
将配置信息一次性赋值给Bean对象的属性;
# 将这里的配置信息一次性的绑定到bean对象的属性上。比 @Value 注解好用,方便。
myapp.a.username=jackson
myapp.a.password=123456
myapp.a.age=20
myapp.a.gender=true
myapp:
a:
username: jacksonyml
password: 123456yml
age: 30
gender: false
这样的bean需要使用@Component
注解进行标注,纳入IoC容器的管理。
@Component
注解负责创建Bean对象;
@ConfigurationProperties(prefix = "app")
注解负责给bean对象的属性赋值。
bean的属性需要是非static
的属性。
// 纳入IoC容器的管理。
//@Component
// 这个注解标注的类一定会被纳入IoC容器的管理,同时这个类也表示是一个配置类。
@Configuration
// 将配置文件中的属性值一次性的绑定到bean对象的属性上。
@ConfigurationProperties(prefix = "myapp.a")
public class AppConfig {
// 强调:要实现这种一次性的绑定功能,配置文件中的属性名和bean对象的属性名要一致。
private String username;
private String password;
private Integer age;
private Boolean gender;
// 底层在实现给对象属性赋值的时候,调用了setter方法,因此必须保证每个属性提供了setter方法。
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAge(Integer age) {
this.age = age;
}
public void setGender(Boolean gender) {
this.gender = gender;
}
@Override
public String toString() {
return "AppConfig{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
}
注入数组,集合,Map类型配置
# 数组的数据结构
app2.abc.names[0]=jack
app2.abc.names[1]=lucy
app2.abc.addrArray[0].city=BeiJing
app2.abc.addrArray[0].street=ChaoYang
app2.abc.addrArray[1].city=TianJin
app2.abc.addrArray[1].street=NanKai
app2.abc.addrList[0].city=BeiJing_List
app2.abc.addrList[0].street=ChaoYang_List
app2.abc.addrList[1].city=TianJin_List
app2.abc.addrList[1].street=NanKai_List
# add1指的是key,city和street指的是value
app2.abc.addrs.addr1.city=BeiJing_Map
app2.abc.addrs.addr1.street=ChaoYang_Map
app2.abc.addrs.addr2.city=TianJin_Map
app2.abc.addrs.addr2.street=NanKai_Map
app2:
abc:
names:
- tom2
- smith2
# names: [jack, lucy]
# addrArray:
addr-array:
# 指定数组中的每一个bean之前必须要有一个 -
- city: BeiJing2
street: ChaoYang2
- city: TianJin2
street: NanKai2
# addrList:
addr-list:
- city: BeiJing_List2
street: ChaoYang_List2
- city: TianJin_List2
street: NanKai_List2
addrs:
addr1:
city: BeiJing_Map2
street: ChaoYang_Map2
addr2:
city: TianJin_Map2
street: NanKai_Map2
@ConfigurationProperties(prefix = "app2.abc")
public class AppBean {
// 数组:数组中元素是简单类型
private String[] names;
// 数组:数组中元素是bean
private Address[] addrArray;
// List集合:集合中的元素是bean
private List<Address> addrList;
// Map集合:String,Bean
private Map<String, Address> addrs;
public void setNames(String[] names) {
this.names = names;
}
public void setAddrArray(Address[] addrArray) {
this.addrArray = addrArray;
}
public void setAddrList(List<Address> addrList) {
this.addrList = addrList;
}
public void setAddrs(Map<String, Address> addrs) {
this.addrs = addrs;
}
@Override
public String toString() {
return "AppBean{" +
"names=" + Arrays.toString(names) +
", addrArray=" + Arrays.toString(addrArray) +
", addrList=" + addrList +
", addrs=" + addrs +
'}';
}
}
5.7.2 @Configuration注解
被该注解标注的类,该Bean对象的属性对应的就是配置文件中的配置信息;
底层会创建类的代理对象,但是生成代理对象会影响效率,所以需要配置取消代理机制;
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "app")
public class AppBean {
private String name;
private Integer age;
private String email;
//setter and getter
}
5.7.3 绑定嵌套bean
@Component // 第一种
//@Configuration // 第二种
@ConfigurationProperties(prefix = "app.xyz")
public class User {
private String name;
private Address addr;
public void setName(String name) {
this.name = name;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", addr=" + addr +
'}';
}
}
public class Address {
private String city;
private String street;
public void setCity(String city) {
this.city = city;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
app:
xyz:
name: lucy_yml
addr:
city: TJ
street: NanKai
5.7.4 @EnableConfigurationProperties与@ConfigurationPropertiesScan
// 如果项目中有这种老的Spring XML配置文件,使用以下注解可以让XML生效。
// 在SpringBoot主入口类上添加以下注解,让XML配置文件生效。
@ImportResource("classpath:/applicationContext.xml")
@EnableConfigurationProperties({User.class, AppBean.class})
// 将配置信息绑定到bean的第三种方式:在主入口程序上添加以下注解。
// 以下注解的作用是:启用将配置信息绑定到bean,以下代码的意思就是将配置信息绑定到User这个bean上。
//@EnableConfigurationProperties(User.class)
// 将配置信息绑定到bean的第四种方式:在主入口程序上添加以下注解。
//@ConfigurationPropertiesScan(basePackages = "com.hnlg.springboot.bean")
@SpringBootApplication
public class Springboot310ConfigtobeanApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot310ConfigtobeanApplication.class, args);
}
}
5.7.5 配置绑定到第三方对象
将配置文件中的信息绑定到某个Bean对象上,如果这个Bean对象没有源码,是第三方库提供的,怎么办?
此时可以单独编写一个方法,在方法上使用以下两个注解进行标注:
- @Bean
- @ConfigurationProperties
只知道有这样一个字节码
Address.class;怎么把属性配置绑定到指定Bean.
other:
abc:
city: BEIJING
street: tongzhouqu
// 指定该类是一个配置类。
@Configuration
public class AppConfig2 {
// 假设Address是第三方库提供的类。使用以下方式可以完成配置(数据)到bean对象的属性的绑定。
@Bean // 纳入IoC容器的管理
@ConfigurationProperties(prefix = "other.abc") // 将配置文件中凡是以 other.abc 开头的配置数据绑定到Address对象的属性上。
public Address address(){
return new Address();
}
}
5.7.6 指定数据源
在resources
目录下新建a
目录,在a
目录下新建b
目录,b
目录中新建group-info.properties
文件,进行如下的配置:
group.name=IT
group.leader=LaoDu
group.count=20
- @Configuration:指定该类为配置类,纳入Spring容器的管理
- @ConfigurationProperties(prefix = "group"):将配置文件中的值赋值给Bean对象的属性
- @PropertySource("classpath:a/b/group-info.properties"):指定额外的配置文件
@Configuration
@ConfigurationProperties(prefix = "group")
// 用这个注解来指定数据来源。
@PropertySource("classpath:/a/b/group-info.properties")
public class Group {
private String name;
private String leader;
private Integer count;
@Override
public String toString() {
return "Group{" +
"name='" + name + '\'' +
", leader='" + leader + '\'' +
", count=" + count +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setLeader(String leader) {
this.leader = leader;
}
public void setCount(Integer count) {
this.count = count;
}
}
5.8 @ImportResource注解
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在resources
目录下新建 applicationContext.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 id="person" class="com.hnlg.springboot.bean.Person">
<property name="name" value="jackson"/>
<property name="age" value="20"/>
</bean>
</beans>
主入口类
// 如果项目中有这种老的Spring XML配置文件,使用以下注解可以让XML生效。
// 在SpringBoot主入口类上添加以下注解,让XML配置文件生效。
@ImportResource("classpath:/applicationContext.xml")
@SpringBootApplication
public class Springboot310ConfigtobeanApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot310ConfigtobeanApplication.class, args);
}
}
5.9 Environment
SpringBoot框架在启动的时候会将系统配置,环境信息全部封装到Environment
对象中,如果要获取这些环境信息,可以调用Environment
接口的方法。
在Spring Boot中,Environment
接口提供了访问应用程序环境信息的方法,比如活动配置文件、系统环境变量、命令行参数等。Environment
接口由Spring框架提供,Spring Boot应用程序通常会使用Spring提供的实现类AbstractEnvironment
及其子类来实现具体的环境功能。
Environment
对象封装的主要数据包括:
Active Profiles: 当前激活的配置文件列表。Spring Boot允许应用程序定义不同的环境配置文件(如开发环境、测试环境和生产环境),通过激活不同的配置文件来改变应用程序的行为。
System Properties: 系统属性,通常是操作系统级别的属性,比如操作系统名称、Java版本等。
System Environment Variables: 系统环境变量,这些变量通常是由操作系统提供的,可以在启动应用程序时设置特定的值。
Command Line Arguments: 应用程序启动时传递给主方法的命令行参数。
Property Sources:
Environment
还包含了一个PropertySource
列表,这个列表包含了从不同来源加载的所有属性。PropertySource
可以来自多种地方,比如配置文件、系统属性、环境变量等。
app:
xyz:
name: lucy_yml
addr:
city: TJ
street: NanKai
@Component
public class SomeBean {
@Autowired
private Environment environment;
public void doSome(){
// 直接使用这个环境对象,来获取环境信息,配置信息等。
String[] activeProfiles = environment.getActiveProfiles();
for (String activeProfile : activeProfiles) {
System.out.println(activeProfile);
}
// 获取配置信息
String street = environment.getProperty("app.xyz.addr.street");
System.out.println(street);
}
}
6. Spring Boot中开发AOP
当引入aop启动器
之后,会引入aop依赖
和aspectj依赖
。
- aop依赖:如果只有这一个依赖,也可以实现AOP编程,这种方式表示使用了纯Spring AOP实现aop编程。
- aspectj依赖:一个独立的可以完成AOP编程的AOP框架,属于第三方的,不属于Spring框架。
6.1 aop启动器
<!--aop启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
6.2 service提供方法
public interface OrderService {
/**
* 生成订单
*/
void generate(Integer id, String name);
/**
* 订单详情
*/
void detail(Integer id);
}
@Service("orderService")
public class OrderServiceImpl implements OrderService {
@Override
public void generate(Integer id, String name) {
System.out.println("生成订单");
}
@Override
public void detail(Integer id) {
System.out.println("订单细节");
}
}
6.3 编写切面
@Aspect
@Component
public class LoggingAspect {
// 记录日志的工具
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
// 定义切入点,匹配所有以 "service" 结尾的包下的所有方法
// 切入点表达式
@Pointcut("execution(* com.hnlg.springboot.service..*(..))")
// 为这个切入点表达式创建一个可重用的标识符
// serviceMethods()方法名成为这个切入点的引用名称
public void serviceMethods() {
}
// 在切入点的方法执行前执行此方法
// 前置通知
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("joinPoint:" + joinPoint);
System.out.println("切点签名:" + joinPoint.getSignature());
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
logger.info("方法 [{}] 携带的 [{}] 被调用了.", methodName, args);
}
}
测试
@SpringBootApplication
public class Springboot311AopApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Springboot311AopApplication.class, args);
OrderService orderService = applicationContext.getBean(OrderService.class);
orderService.generate(111, "jackson");
orderService.detail(111);
applicationContext.close();
}
}
7. 持久层框架整合
7.1 Vip
public class Vip {
private Long id;
private String name;
private String cardNumber; // 数据库字段 card_number
private String birth;
}
7.2 接口interface
7.2.1 Mapper接口
// VipMapper通过@MapperScan被MyBatis创建了代理实现
public interface VipMapper {
/**
* 保存会员信息
* @param vip 会员信息
* @return 1表示保存成功。
*/
int insert(Vip vip);
/**
* 获取所有会员信息
* @return 会员列表
*/
List<Vip> selectAll();
}
7.2.2 Service接口
public interface VipService {
/**
* 保存会员信息
* @param vip 会员信息
* @return true表示成功,false表示失败
*/
boolean save(Vip vip);
/**
* 查看会员列表
* @return 会员列表
*/
List<Vip> findAll();
}
@Service("vipService")
public class VipServiceImpl implements VipService {
@Autowired
private VipMapper vipMapper;
@Override
public boolean save(Vip vip) {
return vipMapper.insert(vip) == 1;
}
@Override
public List<Vip> findAll() {
return vipMapper.selectAll();
}
}
7.3 MyBatis配置
# 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot
username: root
password: 1234
type: com.zaxxer.hikari.HikariDataSource
# mybatis相关配置
# 1. 起别名
# 2. 告诉springboot,mapper配置文件的位置
# 3. 启用自动映射:数据库表的列名和javabean的属性名。card_number-cardNumber
mybatis:
type-aliases-package: com.hnlg.springboot.bean
mapper-locations: classpath:/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
7.4 Mapper的扫描(主入口程序)
// 通过这个指定去哪里找Mapper接口(配置Mapper的扫描)
// 使用@MapperScan指定Mapper接口扫描路径
// Spring 需要知道哪些 Mapper 需要被管理
@MapperScan(basePackages = {"com.hnlg.springboot.repository"})
@SpringBootApplication
public class Springboot312MybatisApplication {
// Spring Boot 读取配置文件,创建数据源
// MyBatis 根据配置创建 SqlSessionFactory
// 扫描 Mapper 接口并创建实现类
public static void main(String[] args) {
// 启动Spring Boot应用,返回应用上下文对象
// 加载所有配置(包括application.yml/properties)
// 初始化数据源(根据配置文件中的数据库连接信息)
// 创建SqlSessionFactory(MyBatis的核心工厂类)
// 扫描并注册所有Bean(包括@Service, @Repository等)
// 启动内嵌的Web服务器(如果有Web依赖)
ConfigurableApplicationContext applicationContext = SpringApplication.run(Springboot312MybatisApplication.class, args);
// 从应用上下文中获取VipService的Bean实例
VipService vipService = applicationContext.getBean(VipService.class);
/*// 保存会员
Vip vip1 = new Vip("jack1", "1234567892", "1999-10-11");
vipService.save(vip1);
// 保存会员
Vip vip2 = new Vip("lucy1", "1234567893", "1999-10-12");
vipService.save(vip2);*/
// 查看会员列表
vipService.findAll().forEach(System.out::println);
applicationContext.close();
}
}
8. Lombok库
Lombok 自动生成构造函数、getter、setter、equals、hashCode、toString 方法等,从而避免了手动编写这些重复性的代码。
@Data:
- 等价于
@ToString
,@EqualsAndHashCode
,@Getter
,@Setter
,@RequiredArgsConstructor
. - 用于生成:必要参数的构造方法、getter、setter、toString、equals 和 hashcode 方法。
@Getter / @Setter:
- 分别用于生成所有的 getter 和 setter 方法。
- 可以作用于整个类,也可以作用于特定的字段。
@NoArgsConstructor:
- 生成一个无参构造方法。
@AllArgsConstructor:
- 生成一个包含所有实例变量的构造器。
@RequiredArgsConstructor:
- 生成包含所有被
final
修饰符修饰的实例变量的构造方法。 - 如果没有
final
的实例变量,则自动生成无参数构造方法。
@ToString / @EqualsAndHashCode:
- 用于生成 toString 和 equals/hashCode 方法。
- 这两个注解都有
exclude
属性,通过这个属性可以定制toString、hashCode、equals方法。
@Data
// 无参数构造方法
@NoArgsConstructor
// 全参数构造方法
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}
public class Test {
public static void main(String[] args) {
// 创建User对象
User user = new User();
user.setId(111L);
user.setName("jackson");
user.setAge(20);
System.out.println(user);
// 创建User对象
user = new User(120L, "lucy", 30);
System.out.println(user);
}
}
@NoArgsConstructor
@AllArgsConstructor
// 生成必须的构造方法。
// 如果类中有final修饰的实例变量,则生成带有该实例变量的构造方法。
// 如果类中没有final修饰的实例变量,则自动添加无参数构造方法。
//@RequiredArgsConstructor
@Data
public class Person {
private String name;
private int age;
// final修饰的实例变量必须在构造方法中为其初始化。
//private final String email;
/*public Person(String email){
this.email = email;
}*/
}
8.1 Lombok的其他注解
8.1.1 @Value注解
该注解会给所有属性添加final
,给所有属性提供 getter
方法,自动生成toString
、hashCode
、equals
通过这个注解可以创建不可变对象。
8.1.2 @Builder注解
GoF23种设计模式之一:建造模式
建造模式(Builder Pattern)属于创建型设计模式。GoF23种设计模式之一。
用于解决对象创建时参数过多的问题。它通过将对象的构造过程与其表示分离,使得构造过程可以逐步完成,而不是一次性提供所有参数。
建造模式的主要目的是让对象的创建过程更加清晰、灵活和可控。
简而言之,建造模式用于:
- 简化构造过程:通过逐步构造对象,避免构造函数参数过多。
- 提高可读性和可维护性:让构造过程更加清晰和有序。
- 增强灵活性:允许按需配置对象的不同部分。
// 建造模式。23种设计模式之一。GoF之一。
public class Person {
// 一般建造模式的bean属性使用final进行修饰
private final String name;
private final int age;
// 提供一个私有的全参数的构造方法
private Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 通过这个公开的静态方法获取建造器对象。
public static PersonBuilder builder() {
return new PersonBuilder();
}
// 静态的内部类:建造者
public static class PersonBuilder {
private String name;
private int age;
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder age(int age) {
this.age = age;
return this;
}
public Person build() {
return new Person(name, age);
}
}
public static void main(String[] args) {
Person person = Person.builder()
.name("jackson")
.age(20)
.email("jackson@123.com")
.build();
System.out.println(person);
}
}
@Builder注解自动生成建造模式代码
8.1.3 @Singular
@Singular注解是辅助@Builder注解的。
当被建造的对象的属性是一个集合,这个集合属性使用@Singular注解进行标注的话,可以连续调用集合属性对应的方法完成多个元素的添加。
如果没有这个注解,则无法连续调用方法完成多个元素的添加。
@Data
@Builder // 帮助这个类生成符合建造模式的代码。
public class Person {
private String name;
private int age;
@Singular("addPhone")
private List<String> phones;
}
public class Test {
public static void main(String[] args) {
/*Person person = Person.builder()
.name("jack")
.age(30)
.build();
System.out.println(person);*/
/*没有singular注解时的代码*/
/*List<String> phones = new ArrayList<>();
phones.add("12345678912");
phones.add("12345678913");
phones.add("12345678914");
Person person = Person.builder()
.name("jack")
.age(30)
.phones(phones)
.build();
System.out.println(person);*/
Person person = Person.builder()
.name("jack")
.age(30)
.addPhone("12345678912")
.addPhone("12345678913")
.addPhone("12345678914")
.build();
System.out.println(person);
}
}
8.1.4 @Slf4j
1. @Log4j
:
自动生成一个 org.apache.log4j.Logger
对象。
适用于 Apache Log4j 1.x 版本。
2. @Slf4j
:
自动生成一个 org.slf4j.Logger
对象。
适用于 SLF4J(Simple Logging Facade for Java),这是一种日志门面,可以与多种实际的日志框架(如 Logback、Log4j 等)集成。
3. @Log4j2
:
自动生成一个 org.apache.logging.log4j.Logger
对象。
适用于 Apache Log4j 2.x 版本。
@Slf4j
public class UserService {
public void saveUser(){
log.info("保存用户信息....");
}
}
public class UserServiceTest {
public static void main(String[] args) {
UserService userService = new UserService();
userService.saveUser();
}
}
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<!--Slf4j日志规范-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<!--slf4j日志实现:logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
</dependencies>
9. SSM整合
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Vip implements Serializable {
private Long id;
private String name;
private String cardNumber;
private String birth;
public Vip(String name, String cardNumber, String birth) {
this.name = name;
this.cardNumber = cardNumber;
this.birth = birth;
}
private static final long serialVersionUID = 1L;
}
@RestController
public class VipController {
@Autowired
private VipService vipService;
@GetMapping("/detail")
public Vip detail(@RequestParam("cn") String cardNumber){
return vipService.findByCardNumber(cardNumber);
}
}
@Repository
public interface VipMapper {
int deleteByPrimaryKey(Long id);
int insert(Vip record);
int insertSelective(Vip record);
Vip selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(Vip record);
int updateByPrimaryKey(Vip record);
Vip selectByCardNumber(String cardNumber);
}
public interface VipService {
/**
* 根据会员的卡号查询会员的信息。
* @param cardNumber 会员的卡号
* @return 会员信息
*/
Vip findByCardNumber(String cardNumber);
}
@Service("vipService")
public class VipServiceImpl implements VipService {
@Autowired
private VipMapper vipMapper;
@Override
public Vip findByCardNumber(String cardNumber) {
return vipMapper.selectByCardNumber(cardNumber);
}
}
@MapperScan(basePackages = "com.hnlg.ssm.repository")
@SpringBootApplication
public class Springboot314SsmApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot314SsmApplication.class, args);
}
}
# 数据源
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=1234
# mybatis配置
mybatis.type-aliases-package=com.hnlg.ssm.bean
mybatis.mapper-locations=classpath:/mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true