Spring Boot学习
一、Spring Boot基本介绍
1.官方文档[以3.2.8为例]
- 官网网址:https://spring.io/projects/spring-boot
- 学习文档:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/
- PDF:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/pdf/spring-boot-reference.pdf
- API:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/api/
2.Spring Boot是什么
- Spring Boot 可以轻松创建独立的、生产级的基于Spring的应用程序
- Spring Boot 直接嵌入Tomcat、Jetty或Undertow,可以“直接运行” SpringBoot应用程序
3.Spring Boot快速入门
3.1 maven项目下的pom.xml文件配置
<?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>com.leon</groupId>
<artifactId>spring-boot-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<!--导入Spring Boot 父工程-规定写法-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<!--解决报错问题,错误:
maven'parent.relativePath' of POM com.leon:spring-boot-quickstart:1.0-SNAPSHOT
(C:\software\javacodelibrary\distributed-microservices\spring-boot-quickstart\pom.xml)
points at com.leon:distributed-microservices instead of
org.springframework.boot:spring-boot-starter-parent,
please verify your project structure
-->
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--导入web项目场景启动器:会自动导入和web开发相关的依赖[库/jar]-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.3</version>
</dependency>
</dependencies>
</project>
3.2 创建一个MainAPP.java
package com.leon.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* ClassName:MainApp
* Package:com.leon.springboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/6 21:47
* @Version: 1.0
*/
//@SpringBootApplication 表示这是一个Spring Boot应用/项目
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
//启动Spring Boot 应用程序/项目
SpringApplication.run(MainApp.class,args);
}
}
3.3 在MainApp.java包的子包下创建controller包,在创建HelloController.java
package com.leon.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* ClassName:HelloController
* Package:com.leon.springboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/6 21:51
* @Version: 1.0
*/
@RequestMapping("/hello")
@Controller
public class HelloController {
@RequestMapping("/test")
@ResponseBody
public String hello() {
return "Hello World";
}
}
3.4 快速入门小结
Spring Boot 比较传统的SSM开发,简化整合步骤,提高开发效率
简化了Maven项目的pom.xml依赖导入,可以说是一键导入,如图:
引入一个spring-boot-starter-web,到底发生了什么,如图:
内置Tomcat,简化服务器的配置
4.Spring SpringMVC Spring Boot 的关系
4.1 梳理关系
- 关系:Spring Boot > Spring > Spring MVC
- Spring MVC 只是Spring处理WEB层请求的一个模块/组件,SpringMVC 的基石是Servlet
- Spring 的核心是IOC和AOP,IOC提供了依赖注入的容器,AOP解决了面向切面编程
- Spring Boot 是为了简化开发,推出的封神框架(约定优于配置[COC],简化了Spring项目的配置流程),Spring Boot包含很多组件/框架,Spring就是最核心的内容之一,也包含Spring MVC
- Spring家族,有众多衍生框架和组件例如Boot、security、jpa等,它们的基础都是Spring
4.2 如何理解-约定优于配置
- 约定优于配置(Convention over Configuration / COC),又称按约定编程,是一种软件设计规范,本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)
- 例如在模型中存在一个名为User的类,那么对应到数据库会存在一个名为user的表,只有在偏离这个约定时才需要做相关的配置(例如你想将表名命名为t_user等非user时才需要写关于这个名字的配置)
- 简单来说就是假如你所期望的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待值时,才需要对约定进行替换配置
- 约定优于配置理念[为什么要搞一个约定优于配置]:约定其实就是一种规范,遵守了规范,那么就存在通用性,存在通用性,那么事情就会变得相对简单,程序员之间的沟通成本会降低,工作效率就会提升,合作也会变得更加简单
- 所谓的约定优于配置并不是说约定的优先级比配置高,而是如果是符合约定,则按约定,如果不合符约定,则按配置来。需要通过规范性、通用性、效率等方面去理解,不要理解成做了配置,配置的优先级更低。
二、依赖管理和自动配置
1. 依赖管理
1.1 什么是依赖管理
spring-boot-starter-parent还有父项目,声明了开发中常用的依赖的版本号
并且进行自动版本仲裁,即如果程序员没有指定某个依赖jar的版本,则父项目指定的版本为准
1.2 修改自动仲裁/默认版本号
需求说明:将Spring Boot Mysql驱动修改成5.1.49
查看spring-boot-dependencies.pom里面规定当前依赖的版本对应的key,这里是mysql.version
方案一:修改项目的pom.xml配置文件,当刷新Maven时,就会更新到新的mysql驱动
方案二:使用spring-boot-dependencies.pom.xml中的配置方式,因为Maven对于依赖的配置使用的是就近原则
2.starter场景启动器
2.1 starter场景启动器基本介绍
开发中我们引入了相关场景的starter,这个场景中所有的相关依赖都引入进来,比如我们做WEB开发引入了,该starter将导入语WEB相关的所有包
依赖树:可以看到spring-boot-starter-web,帮我们引入了spring-webmvc,spring-web开发模块,还引入了spring-boot-starter-tomcat场景,spring-boot-starter-json场景,这些场景下面又引入了一大堆相关的包,这些依赖可以快速启动和运行一个项目,提高开发效率
所有场景启动器最基本的依赖就是spring-boot-starter,前面的依赖树分析可以看到,这个依赖也就是Spring Boot自动配置的核心依赖
2.2 官方提供的starter
地址: https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/using.html#using.build-systems.starters
介绍
在开发中我们经常会用到spring-boot-starter-xxx,比如spring-boot-starter-web,该场景是用作web开发,也就是说xxx是某种开发场景
只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
Spring Boot 支持的场景如下:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/using.html#using.build-systems.starters
2.3 第三方starter
- Spring Boot 也支持第三方starter
- 第三方starter不是从spring-boot开始,因为这是官方spring-boot保留的命名方式的。第三方启动程序通常以项目名称开头。例如,名为thirdpartyproject的第三方启动程序项目被命名为thirdpartyproject-spring-boot-starter。
- 也就是说:xxx-spring-boot-starter是第三方为我们提供的简化开发的场景启动器
3. 自动配置
3.1 自动配置基本介绍
前面学习SSM整合时,需要配置Tomcat、配置SpringMVC、配置如何扫描包、配置字符过滤器、配置视图解析器、文件上传等[如下图],非常麻烦。而在Spring Boot中,存在自动【配置机制,提高开发效率。
简单回顾以前SSM整合
3.2 Spring Boot 自动配置了哪些
自动配置Tomcat
自动配置Spring MVC
自动配置Web常用功能:比如字符过滤器,提示:可以通过获取IOC容器,查看容器创建的组件来验证
方式一:通过代码的方式查看
package com.leon.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; /**
ClassName:MainApp
Package:com.leon.springboot
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/6 21:47
@Version: 1.0
*/
//@SpringBootApplication 表示这是一个Spring Boot应用/项目
@SpringBootApplication
public class MainApp {public static void main(String[] args) {
//启动Spring Boot 应用程序/项目 ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args); //获取注入的bean的名称 String[] beanDefinitionNames = ioc.getBeanDefinitionNames(); //循环遍历数组 for (String beanDefinitionName : beanDefinitionNames) { System.out.println( "beanDefinitionName---" + beanDefinitionName); }
}
}

方式二:Debug查看
自动配置:默认扫描包结构,官方文档: https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/using.html#using.structuring-your-code.using-the-default-package
3.3 如何修改默认配置
3.3.1 如何修改默认扫描结构
需求:要求能扫描com.leon 包下的HiController.java
创建:在com.leon包下创建HiController.java,进行测试
package com.leon; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /**
ClassName:HiController
Package:com.leon
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/8 20:55
@Version: 1.0
*/
@RequestMapping(“/hi”)
@Controller
public class HiController {@RequestMapping(“/test”)
@ResponseBody
public String hi() {return "Hi, Spring Boot";
}
}
修改MainAPP.java,增加扫描包
package com.leon.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; /**
ClassName:MainApp
Package:com.leon.springboot
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/6 21:47
@Version: 1.0
/
/
- @SpringBootApplication 表示这是一个Spring Boot应用/项目
- @SpringBootApplication(scanBasePackages = {“com.leon”})其中的scanBasePackages = {“com.leon”} 表示指定
Spring Boot 要扫描的包和子包,scanBasePackages属性是一个数组,可以有多个值,也就是说可以写多个要扫描的包,例如:
scanBasePackages={"com.leon","xxx.yyy.ccc",...}
*/
@SpringBootApplication(scanBasePackages = {“com.leon”})
public class MainApp {public static void main(String[] args) {
//启动Spring Boot 应用程序/项目
ConfigurableApplicationContext ioc =SpringApplication.run(MainApp.class, args);
//获取注入的bean的名称
String[] beanDefinitionNames = ioc.getBeanDefinitionNames();
//循环遍历数组
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println( “beanDefinitionName—” + beanDefinitionName);
}}
}
3.3.2 application.properties 配置大全
- Sppring Boot 项目最重要也是最核心的配置文件就是application.properties,所有的框架配置都可以在这个配置文件中说明
- 官方网址:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/application-properties.html#appendix.application-properties.core
- 其它参考网址1:https://blog.csdn.net/u014135369/article/details/112172329 网址2:https://blog.csdn.net/pbrlovejava/article/details/82659702
3.3.3 application.properties 修改配置
各种配置都有默认,可以在 resources/application.properties修改,application.properties文件我们可以手动创建
#默认server.port=8080 #设置Tomcat监听端口号 server.port=8080 #默认spring.servlet.multipart.max-file-size=1MB #该属性可以指定Spring Boot 上传文件大小的限制-体现约定优于配置 #默认配置最终都是映射到某个类上,比如spring.servlet.multipart.max-file-size配置会映射到MultipartProperties #可以通过ctrl+b,然后单击就可以到映射类 spring.servlet.multipart.max-file-size=10MB
3.3.4 application.properties 常用配置
常用配置一览
#端口号 server.port=10000 #应用的上下文路径(项目路径) server.servlet.context-path=/allModel 韩顺平Java 工程师 #指定POJO扫描包来让mybatis自动扫描到自定义的POJO mybatis.type-aliases-package=com.cxs.allmodel.model #指定mapper.xml 的路径 #(application 上配置了@MapperScan(扫面 mapper 类的路径)和 pom.xml 中放行了mapper.xml后, #配置 mapper-locations 没有意义。如果 mapper 类和 mapper.xml 不在同一个路径下时, mapper-locations 就有用了) mybatis.mapper-locations=classpath:com/cxs/allmodel/mapper #session 失效时间(单位s) spring.session.timeout=18000 #数据库连接配置 #mysql 数据库url #mysql 数据库用户名 mysql.one.jdbc-url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai&useSSL=false mysql.one.username= #数据库密码 mysql.one.password= 韩顺平Java 工程师 #线程池允许的最大连接数 mysql.one.maximum-pool-size=15 #日志打印:日志级别 trace<debug<info<warn<error<fatal 默认级别为 info,即默认打印 info 及其以 上级别的日志 #logging.level 设置日志级别,后面跟生效的区域,比如root表示整个项目,也可以设置为某个包下, 也可以具体到某个类名(日志级别的值不区分大小写) logging.level.com.cxs.allmodel.=debug logging.level.com.cxs.allmodel.mapper=debug logging.level.org.springframework.web=info logging.level.org.springframework.transaction=info logging.level.org.apache.ibatis=info logging.level.org.mybatis=info logging.level.com.github.pagehelper = info logging.level.root=info #日志输出路径 logging.file=/tmp/api/allmodel.log #配置pagehelper 分页插件 韩顺平Java 工程师 pagehelper.helperDialect=mysql pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.params=count=countSql #jackson 时间格式化 spring.jackson.serialization.fail-on-empty-beans=false #指定日期格式,比如yyyy-MM-ddHH:mm:ss,或者具体的格式化类的全限定名 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss #指定日期格式化时区,比如America/Los_Angeles或者GMT+10 spring.jackson.time-zone=GMT+8 #设置统一字符集 spring.http.encoding.charset=utf8 #redis 连接配置 # redis 所在主机ip地址 spring.redis.host= #redis 服务器密码 韩顺平Java 工程师 spring.redis.password= #redis 服务器端口号 spring.redis.port= #redis 数据库的索引编号(0到15) spring.redis.database=14 ## 连接池的最大活动连接数量,使用负值无限制 #spring.redis.pool.max-active=8 # ## 连接池的最大空闲连接数量,使用负值表示无限数量的空闲连接 #spring.redis.pool.max-idle=8 # #spring.redis.pool.max-wait=-1ms ## 连接池最大阻塞等待时间,使用负值表示没有限制 # ## 最小空闲连接数量,使用正值才有效果 #spring.redis.pool.min-idle=0 韩顺平Java 工程师 # ## 是否启用SSL连接. ##spring.redis.ssl=false # ## 连接超时,毫秒为单位 #spring.redis.timeout= 18000ms # ## 集群模式下,集群最大转发的数量 #spring.redis.cluster.max-redirects= # ## 集群模式下,逗号分隔的键值对(主机:端口)形式的服务器列表 #spring.redis.cluster.nodes= # ## 哨兵模式下,Redis主服务器地址 #spring.redis.sentinel.master= # ## 哨兵模式下,逗号分隔的键值对(主机:端口)形式的服务器列表 #spring.redis.sentinel.nodes= 127.0.0.1:5050,127.0.0.1:5060
3.3.5 application.properties 自定义配置
还可以在application.properties文件中自定义配置,通过@Value(“{}“)获取对应属性值
application.properties文件 my.website=https://www.baidu.com 某个Bean @Value("${my.website}") private String bdUrl;
3.4 解读Spring Boot读取配置文件application.properties文件
打开ConfigFileApplicationListener.java,查看一下源码
3.5 自动配置遵守按需加载原则
3.5.1 基本说明
自动配置遵守按需加载原则:也就是说,引入哪个场景starter就会加载该场景关联的jar包,没有引入的starter则不会加载其相关联jar
Spring Boot 所有的自动配置功能都在spring-boot-autoconfigure包里面
在Spring Boot 的自动配置包,一般是XxxAutoConfiguration.java,对应XxxProperties.java,如图
3.5.2 案例演示
以MultipartProperties,MultipartAutoConfiguration和application.properties来说明
三、容器功能
1. Spring注入组件的注解
@Component、@Controller、@Service、@Repository,说明:这些在Spring中的传统注解仍然有效,通过这些注解可以给容器注入组件
案例演示
创建一个A.java
在MainApp.java中获取,A类的对象
package com.leon.springboot; import com.leon.springboot.bean.A; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; /** * ClassName:MainApp * Package:com.leon.springboot * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/6 21:47 * @Version: 1.0 */ /* * 1. @SpringBootApplication 表示这是一个Spring Boot应用/项目 * 2. @SpringBootApplication(scanBasePackages = {"com.leon"})其中的scanBasePackages = {"com.leon"} 表示指定 * Spring Boot 要扫描的包和子包,scanBasePackages属性是一个数组,可以有多个值,也就是说可以写多个要扫描的包,例如: * scanBasePackages={"com.leon","xxx.yyy.ccc",...} * */ @SpringBootApplication(scanBasePackages = {"com.leon"}) public class MainApp { public static void main(String[] args) { //启动Spring Boot 应用程序/项目 ConfigurableApplicationContext ioc = SpringApplication.run(MainApp.class, args); ////获取注入的bean的名称 //String[] beanDefinitionNames = ioc.getBeanDefinitionNames(); ////循环遍历数组 //for (String beanDefinitionName : beanDefinitionNames) { // System.out.println( "beanDefinitionName---" + beanDefinitionName); //} //======演示Spring中传统的注解在Spring Boot中依然可以使用@Controller @Service @Repository @Component start //Object a = ioc.getBean("a"); A aBean = ioc.getBean(A.class); System.out.println(aBean ); //======演示Spring中传统的注解在Spring Boot中依然可以使用@Controller @Service @Repository @Component end } }
2. @Configuration 注解
2.1 应用实例
@Configuration应用实例需求
演示在Spring Boot ,如何通过@Configuration 创建配置类来注入组件
回顾传统方式如何通过配置文件注入组件
创建Monster.java
package com.leon.springboot.bean; /** * ClassName:Monster * Package:com.leon.springboot.bean * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 11:15 * @Version: 1.0 */ public class Monster { private Integer id; private String name; private Integer age; private String skill; public Monster() { } public Monster(Integer id, String name, Integer age, String skill) { this.id = id; this.name = name; this.age = age; this.skill = skill; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSkill() { return skill; } public void setSkill(String skill) { this.skill = skill; } @Override public String toString() { return "Monster{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", skill='" + skill + '\'' + '}'; } }
创建beans.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="monster01" class="com.leon.springboot.bean.Monster"> <property name="id" value="100"/> <property name="age" value="5000"/> <property name="name" value="牛魔王"/> <property name="skill" value="芭蕉扇"/> </bean> </beans>
MainApp.java
//======演示在Spring Boot项目中,依然可以使用Spring的配置bean/注入bean/获取bean方式 start ApplicationContext springIOc = new ClassPathXmlApplicationContext("beans.xml"); Monster monster01 = springIOc.getBean("monster01", Monster.class); System.out.println("monster01--" + monster01); //======演示在Spring Boot项目中,依然可以使用Spring的配置bean/注入bean/获取bean方式 end
使用Spring Boot 的@Configuration添加/注入组件
BeanConfig.java类
package com.leon.springboot.config; import com.leon.springboot.bean.Monster; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * ClassName:BeanConfig * Package:com.leon.springboot.config * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 15:23 * @Version: 1.0 */ /* * 1. 被@Configuration注解标识的类称之为配置类,等价于Spring的配置文件,例如beans.xml * 程序员可以通过@Bean注解,注入bean对象到容器中 * 2. 当一个类被@Configuration 标识,该类-Bean也会被注入到容器中 * 3. proxyBeanMethods:表示代理bean的方法,是Spring Boot2新增的特性,指定Full模式(属性值为true)和Lite模式(属性值为false) * 4. Full(proxyBeanMethods=true):保证每个@Bean方法无论被调用多少次返回的组件都是单实例的,是代理方式 * 5. Lite(proxyBeanMethods=false):每个@Bean方法无论被调用多少次返回的组件都是新创建的,是非代理方式 * 6. 特别说明:proxyBeanMethods是在调用@Bean方法才生效,因此,需要现先获取BeanConfig组件,再调用方法,而不是 * 直接通过Spring Boot主程序得到的容器来获取Bean,如果直接通过ioc.getBean() 获取Bean,proxyBeanMethods值是并没有生效的 * 如果没有被@Bean注解修饰也是不会生效的 * 7. Lite模式也称轻量级模式,因为不检测依赖关系,运行速度快 * */ @Configuration(proxyBeanMethods = false) public class BeanConfig { /* * 1. @Bean注解 作用:给容器添加组件,就是本方法返回的Monster bean * 2. 如果没有指定名称,被标注的方法的名称,默认会被作为Bean在容器中的名称/id * 3. 被标注的方法的返回类型,就是注入Bean类型, * 4. new Monster(200,"牛魔王",500,"封魔拳") 这条语句可以理解为注入到容器中具体的Bean信息,类似于传统Spring的配置文件中的beans.xml中的 * <property name="id" value="100"/> * <property name="age" value="5000"/> * <property name="name" value="牛魔王"/> * <property name="skill" value="芭蕉扇"/> * 5. @Bean(name="monster_nmw"):中的name或value属性值是用来指定注入容器中的Bean的名称/id * 6. 注入的Bean默认是单例的 * 7. 如果想每次获取的都是新创建的对象,需要使用@Scope("prototype")设置value或scopeName属性值为prototype * */ @Bean public Monster monster01(){ return new Monster(200,"牛魔王",500,"封魔拳"); } public Monster monster02(){ return new Monster(300,"白骨精",500,"易容术"); } }
MainApp.java
//======演示@Configuration start Monster monster01 = ioc.getBean("monster01", Monster.class); Monster monster02 = ioc.getBean("monster01", Monster.class); System.out.println("monster01---"+monster01+"--"+monster01.hashCode()); System.out.println("monster02---"+monster02+"--"+monster02.hashCode()); //======演示@Configuration end
通过Debug的方式来查看是否被注入
2.2 @Configuration注意事项和细节
配置类本身也是组件,因此也可以获取
Spring Boot2 新增特性:proxyBeanMethods 指定Full模式和Lite模式
BeanConfig.java
package com.leon.springboot.config; import com.leon.springboot.bean.Monster; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /**
ClassName:BeanConfig
Package:com.leon.springboot.config
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/9 15:23
@Version: 1.0
/
/
- 被@Configuration注解标识的类称之为配置类,等价于Spring的配置文件,例如beans.xml
程序员可以通过@Bean注解,注入bean对象到容器中
- 当一个类被@Configuration 标识,该类-Bean也会被注入到容器中
- proxyBeanMethods:表示代理bean的方法,是Spring Boot2新增的特性,指定Full模式(属性值为true)和Lite模式(属性值为false)
- Full(proxyBeanMethods=true):保证每个@Bean方法无论被调用多少次返回的组件都是单实例的,是代理方式
- Lite(proxyBeanMethods=false):每个@Bean方法无论被调用多少次返回的组件都是新创建的,是非代理方式
- 特别说明:proxyBeanMethods是在调用@Bean方法才生效,因此,需要现先获取BeanConfig组件,再调用方法,而不是
直接通过Spring Boot主程序得到的容器来获取Bean,如果直接通过ioc.getBean() 获取Bean,proxyBeanMethods值是并没有生效的
如果没有被@Bean注解修饰也是不会生效的
- Lite模式也称轻量级模式,因为不检测依赖关系,运行速度快
*/
@Configuration(proxyBeanMethods = false)
public class BeanConfig {/*
- @Bean注解 作用:给容器添加组件,就是本方法返回的Monster bean
- 如果没有指定名称,被标注的方法的名称,默认会被作为Bean在容器中的名称/id
- 被标注的方法的返回类型,就是注入Bean类型,
- new Monster(200,“牛魔王”,500,“封魔拳”) 这条语句可以理解为注入到容器中具体的Bean信息,类似于传统Spring的配置文件中的beans.xml中的
- @Bean(name=“monster_nmw”):中的name或value属性值是用来指定注入容器中的Bean的名称/id
- 注入的Bean默认是单例的
- 如果想每次获取的都是新创建的对象,需要使用@Scope(“prototype”)设置value或scopeName属性值为prototype
*/
@Bean
public Monster monster01(){return new Monster(200,“牛魔王”,500,“封魔拳”);
}public Monster monster02(){
return new Monster(300,“白骨精”,500,“易容术”);
}}
MainApp.java
//======演示@Configuration(proxyBeanMethods = false) start //通过获取容器中的对象来检验是否生效 //Monster monster01 = ioc.getBean("monster01", Monster.class); //Monster monster02 = ioc.getBean("monster01", Monster.class); // //System.out.println("monster01---"+monster01+"--"+monster01.hashCode()); //System.out.println("monster02---"+monster02+"--"+monster02.hashCode()); //通过先获取容器中的BeanConfig来检验是否生效 BeanConfig beanConfig = ioc.getBean("beanConfig", BeanConfig.class); Monster monster01 = beanConfig.monster01(); Monster monster02 = beanConfig.monster01(); System.out.println("monster01---"+monster01+"--"+monster01.hashCode()); System.out.println("monster02---"+monster02+"--"+monster02.hashCode()); Monster monster03 = beanConfig.monster02(); Monster monster04 = beanConfig.monster02(); System.out.println("monster03---"+monster03+"--"+monster03.hashCode()); System.out.println("monster04---"+monster04+"--"+monster04.hashCode()); //======演示@Configuration(proxyBeanMethods = false) end
配置类可以有多个,与Spring可以有多个ioc配置文件是一个道理
创建BeanConfig2.java
package com.leon.springboot.config; import com.leon.springboot.bean.Monster; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * ClassName:BeanConfig2 * Package:com.leon.springboot.config * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 16:36 * @Version: 1.0 */ @Configuration public class BeanConfig2 { /* * 1.在配置其他配置类时,要注意Bean的名称/id不能重复,也就是在没指定名称时方法名不能重复,指定名称时也不能重复 * 要保证名称/id是唯一的 * */ @Bean public Monster monster02(){ return new Monster(800,"蚂蚁精",200,"咬人"); } }
MainApp.java
//======演示可以有多个配置类 start Monster monster02 = ioc.getBean("monster02", Monster.class); Monster monster01 = ioc.getBean("monster01", Monster.class); System.out.println("monster01---"+monster01+"--"+monster01.hashCode()); System.out.println("monster02---"+monster02+"--"+monster02.hashCode()); //======演示可以有多个配置类 end
Debug方式查看
3. @Import注解
3.1 应用实例
创建两个类,分别是Cat.java和Dog.java
修改BeanConfig类,使用Import将Cat和Dog注入到容器中
BeanConfig.java
package com.leon.springboot.config; import com.leon.springboot.bean.Cat; import com.leon.springboot.bean.Dog; import com.leon.springboot.bean.Monster; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Scope; /** * ClassName:BeanConfig * Package:com.leon.springboot.config * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 15:23 * @Version: 1.0 */ /* * 1. 被@Configuration注解标识的类称之为配置类,等价于Spring的配置文件,例如beans.xml * 程序员可以通过@Bean注解,注入bean对象到容器中 * 2. 当一个类被@Configuration 标识,该类-Bean也会被注入到容器中 * 3. proxyBeanMethods:表示代理bean的方法,是Spring Boot2新增的特性,指定Full模式(属性值为true)和Lite模式(属性值为false) * 4. Full(proxyBeanMethods=true):保证每个@Bean方法无论被调用多少次返回的组件都是单实例的,是代理方式 * 5. Lite(proxyBeanMethods=false):每个@Bean方法无论被调用多少次返回的组件都是新创建的,是非代理方式 * 6. 特别说明:proxyBeanMethods是在调用@Bean方法才生效,因此,需要现先获取BeanConfig组件,再调用方法,而不是 * 直接通过Spring Boot主程序得到的容器来获取Bean,如果直接通过ioc.getBean() 获取Bean,proxyBeanMethods值是并没有生效的 * 如果没有被@Bean注解修饰也是不会生效的 * 7. Lite模式也称轻量级模式,因为不检测依赖关系,运行速度快 * 8. @Import注解,有一个value属性其类型是一个Class数组,通过Class数组中的Class值,注入指定类型的Bean,例如Dog.class,注入的是Dog类型的Bean * public @interface Import { * Class<?>[] value(); * } * 9. 通过@Import方式注入组件,默认组件名称/id就是对应类型的全类名 * */ @Import({Dog.class, Cat.class}) @Configuration public class BeanConfig { /* * 1. @Bean注解 作用:给容器添加组件,就是本方法返回的Monster bean * 2. 如果没有指定名称,被标注的方法的名称,默认会被作为Bean在容器中的名称/id * 3. 被标注的方法的返回类型,就是注入Bean类型, * 4. new Monster(200,"牛魔王",500,"封魔拳") 这条语句可以理解为注入到容器中具体的Bean信息,类似于传统Spring的配置文件中的beans.xml中的 * <property name="id" value="100"/> * <property name="age" value="5000"/> * <property name="name" value="牛魔王"/> * <property name="skill" value="芭蕉扇"/> * 5. @Bean(name="monster_nmw"):中的name或value属性值是用来指定注入容器中的Bean的名称/id * 6. 注入的Bean默认是单例的 * 7. 如果想每次获取的都是新创建的对象,需要使用@Scope("prototype")设置value或scopeName属性值为prototype * */ @Bean public Monster monster01(){ return new Monster(200,"牛魔王",500,"封魔拳"); } public Monster monster02(){ return new Monster(300,"白骨精",500,"易容术"); } }
在mainApp.java获取
MainApp.java
//======演示通过Import将Bean注入到容器中 start Dog dogBean = ioc.getBean(Dog.class); Cat catBean = ioc.getBean(Cat.class); System.out.println("catBean---"+ catBean); System.out.println("dogBean---" + dogBean); //======演示通过Import将Bean注入到容器中 end
Debug查看
4.@Conditional注解
4.1 @Conditional介绍
条件装配:满足Conditional指定的条件,则进行组件注入
@Conditional是一个根注解,下面有很多扩展注解
| @Conditional扩展注解 | 作用(判断是否满足是当前指定条件) |
| ——————————- | ———————————————— |
| @ConditionalOnJava | 系统的java版本是否符合要求 |
| @ConditionalOnBean | 容器中存在指定Bean |
| @ConditionalOnMissingBean | 容器中不存在指定Bean |
| @ConditionalOnExpression | 满足SpEL表达式指定 |
| @ConditionalOnClass | 系统中有指定的类 |
| @ConditionalOnMissingClass | 系统中没有指定的类 |
| @ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
| @ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
| @ConditionalOnResource | 类路径下是否存在指定资源文件 |
| @ConditionalOnWebApplication | 当前是web环境 |
| @ConditionalOnNotWebApplication | 当前不是web环境 |
| @ConditionalOnJndi | JNDI存在指定项 |
4.2 应用实例
要求:演示在Spring Boot,如何通过@ConditionalOnBean来注入组件,只有在容器中有name=monster_nmw组件时才注入dog01。
BeanConfig.java
package com.leon.springboot.config; import com.leon.springboot.bean.Cat; import com.leon.springboot.bean.Dog; import com.leon.springboot.bean.Monster; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Scope; /** * ClassName:BeanConfig * Package:com.leon.springboot.config * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 15:23 * @Version: 1.0 */ /* * 1. 被@Configuration注解标识的类称之为配置类,等价于Spring的配置文件,例如beans.xml * 程序员可以通过@Bean注解,注入bean对象到容器中 * 2. 当一个类被@Configuration 标识,该类-Bean也会被注入到容器中 * 3. proxyBeanMethods:表示代理bean的方法,是Spring Boot2新增的特性,指定Full模式(属性值为true)和Lite模式(属性值为false) * 4. Full(proxyBeanMethods=true):保证每个@Bean方法无论被调用多少次返回的组件都是单实例的,是代理方式 * 5. Lite(proxyBeanMethods=false):每个@Bean方法无论被调用多少次返回的组件都是新创建的,是非代理方式 * 6. 特别说明:proxyBeanMethods是在调用@Bean方法才生效,因此,需要现先获取BeanConfig组件,再调用方法,而不是 * 直接通过Spring Boot主程序得到的容器来获取Bean,如果直接通过ioc.getBean() 获取Bean,proxyBeanMethods值是并没有生效的 * 如果没有被@Bean注解修饰也是不会生效的 * 7. Lite模式也称轻量级模式,因为不检测依赖关系,运行速度快 * 8. @Import注解,有一个value属性其类型是一个Class数组,通过Class数组中的Class值,注入指定类型的Bean,例如Dog.class,注入的是Dog类型的Bean * public @interface Import { * Class<?>[] value(); * } * 9. 通过@Import方式注入组件,默认组件名称/id就是对应类型的全类名 * */ @Import({Dog.class, Cat.class}) @Configuration public class BeanConfig { /* * 1. @Bean注解 作用:给容器添加组件,就是本方法返回的Monster bean * 2. 如果没有指定名称,被标注的方法的名称,默认会被作为Bean在容器中的名称/id * 3. 被标注的方法的返回类型,就是注入Bean类型, * 4. new Monster(200,"牛魔王",500,"封魔拳") 这条语句可以理解为注入到容器中具体的Bean信息,类似于传统Spring的配置文件中的beans.xml中的 * <property name="id" value="100"/> * <property name="age" value="5000"/> * <property name="name" value="牛魔王"/> * <property name="skill" value="芭蕉扇"/> * 5. @Bean(name="monster_nmw"):中的name或value属性值是用来指定注入容器中的Bean的名称/id * 6. 注入的Bean默认是单例的 * 7. 如果想每次获取的都是新创建的对象,需要使用@Scope("prototype")设置value或scopeName属性值为prototype * */ @Bean(name = "monster_nmw") public Monster monster01(){ return new Monster(200,"牛魔王",500,"封魔拳"); } @Bean @ConditionalOnBean(name = "monster_nmw") public Dog dog01(){ return new Dog(); } public Monster monster02(){ return new Monster(300,"白骨精",500,"易容术"); } }
MainApp.java
//======演示 @ConditionalOnBean的使用 start Dog dogBean = ioc.getBean("dog01", Dog.class); System.out.println("dogBean--"+dogBean); //======演示 @ConditionalOnBean的使用 end
4.3 注意事项和细节
- @ConditionalOnBean(name = “monster_nmw”) 表示当容器中存在一个Bean的名称/id是monster_nmw时,则注入目标Bean,如果不存在Bean的名称/id是monster_nmw时,则不注入目标Bean
- @ConditionalOnMissingBean(name = “monster_nmw”)则与@ConditionalOnBean(name = “monster_nmw”)相反
- 当@ConditionalOnBean(name = “monster_nmw”)标识一个类时,表示该类的所有要注入的组件都需要满足该约束条件
- 注意@ConditionalOnBean(name = “monster_nmw”)标识的目标方法必须在满足约束条件的方法之后,否则目标Bean是没有被注入的,也就是说目标Bean被注入时IOC容器中必须要有满足约束条件的Bean,否则目标Bean是注入不进去的,标识一个类时也是如此。
5. @ImportResource注解
5.1 作用
- 原生配置文件引入,也就是可以直接导入Spring传统的beans.xml,可以认为是Spring Boot对Spring容器文件的兼容
5.2 应用实例
需求:将beans.xml导入到BeanConfig3.java配置类,并测试是否可以获得beans.xml注入/配置的组件
在BeanConfig3.java使用@ImportResource导入beans.xml
BeanConfig3.java
package com.leon.springboot.config; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; /** * ClassName:BeanConfig3 * Package:com.leon.springboot.config * Description: * * @Author: leon-->ZGJ * @Create: 2024/7/9 21:46 * @Version: 1.0 */ @Configuration @ImportResource(locations = "classpath:beans.xml") public class BeanConfig3 { }
MainApp.java
//======演示@ImportResopurce的使用 start Monster monster04 = ioc.getBean("monster04", Monster.class); System.out.println("monster04==="+monster04); //======演示@ImportResopurce的使用 end
5.3 注意事项和细节
- @ImportResource注解可以导入多个Spring的IOC容器文件,因为locations或value属性是一个字符串数组类型,查看源码可以知道读取配置文件的默认路径是:{classpath:},{file:}
- 注意如果被导入的Spring的IOC容器文件中的Bean的id/名称,和Spring Boot容器的Bean重复了,会将已注入的数据覆盖掉
- 要判断容器中是否存在指定名称/id的Bean时,可以使用containsBean()方法来判断,如果存在与之对应的id/名称的Bean时返回true,反之则为false
6. 配置绑定
6.1 介绍
- 可以理解为使用java读取到SpringBoot核心配置文件application.properties的内容,并且把它封装到JavaBean中
6.2 应用实例
需求:将application.properties指定的k-v键值对和javaBean绑定
#设置Furn的属性k-v #前面的furn01是用来指定/区别不同的绑定对象,这样可以在绑定Furn Bean属性值时,通过furn01这个前缀进行区分 #注意furn01.id中的id就是你要绑定的Furn Bean 的属性名 furn01.id=100 furn01.name=baidu furn01.price=1000.0
创建Furn.java
package com.leon.springboot.bean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /**
ClassName:Furn
Package:com.leon.springboot.bean
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/9 22:11
@Version: 1.0
/
@Component
/
- @ConfigurationProperties(prefix = “furn01”)表示获取哪个配置绑定的数据,通过前缀来区分,也就是prefix属性值来区分
*/
@ConfigurationProperties(prefix = “furn01”)
public class Furn {private Integer id;
private String name;
private Double price;
public Furn() {
}public Furn(Integer id, String name, Double price) {
this.id = id; this.name = name; this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {return "Furn{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price + '}';
}
}还有第二种配置绑定方式,就是在配置类的头部配置@EnableconfigurationProperties(Furn.class),如果这样配置,则要注销掉Furn类头部的@Component注解,因为不注销掉@Component注解容器中会有两个带有相同配置绑定数据的Furn Bean,一个id/名称为Frun的全类名,一个id/名称为Furn类名首字母小写(前提是没有设置名称/id),所以为了避免错误建议注销掉Furn头部的@Component注解。
BeanConfig2.java
package com.leon.springboot.config; import com.leon.springboot.bean.Furn; import com.leon.springboot.bean.Monster; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /**
ClassName:BeanConfig2
Package:com.leon.springboot.config
Description:
*@Author: leon–>ZGJ
@Create: 2024/7/9 16:36
@Version: 1.0
/
@Configuration
/1.@EnableConfigurationProperties({Furn.class}) 表示开启Furn配置绑定功能,把Furn组件自动注入到容器中
2.@EnableConfigurationProperties 的value属性是一个数组,也就是说可以开启多个Bean
*/
@EnableConfigurationProperties({Furn.class})
public class BeanConfig2 {/*
1.在配置其他配置类时,要注意Bean的名称/id不能重复,也就是在没指定名称时方法名不能重复,指定名称时也不能重复
要保证名称/id是唯一的
*/
@Bean
public Monster monster02(){return new Monster(800,“蚂蚁精”,200,“咬人”);
}
}
6.3 注意事项和细节
Spring Boot 核心配置文件application.properties中的配置绑定数据,使用的是K-V键值对,其中K中包含有一个前缀是用来指定或区别不同的绑定对象的,这样可以在绑定Bean属性时,通过前缀进行区分。
注意前缀之后的部分就是Bean的属性名,如果和Bean的属性名不一样会绑定失败,Bean中没被绑定到的属性则是默认值。
在使用第二种配置方式时,注意要把Bean头部的@Component注解注销掉,不然会有两相同数据,但id/名称不同的Bean对象。
在application.properties文件中会有中文乱码,需要转换成Unicode编码才可以
如果在使用@ConfigurationProperties注解时,提示了警告或错误,则需要导入该依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
四、分析Spring Boot底层机制【Tomcat启动分析+Spring容器初始化+Tomcat如何关联Spring容器】
1. SpringApplication.run()进行Debug
package com.leon.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
/**
* ClassName:MainApp
* Package:com.leon.realizespringboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/14 20:42
* @Version: 1.0
*/
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
/*
* 启动spring boot 应用程序/项目
* 1.提出问题:当执行run方法时,怎么就启动我们的内置的Tomcat?
* 2.在分析run方法的底层机制的基础上,我们直接尝试实现
* */
ConfigurableApplicationContext
ioc = SpringApplication.run(MainApp.class, args);
/*
* 这里开始追源码,也就是对SpringApplication.run()
* 1.SpringApplication.java
* public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
* return run(new Class[]{primarySource}, args);
* }
*
2. SpringApplication.java :创建返回ConfigurableApplicationContext对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
3.SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
Throwable ex;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();//严重分析:创建容器
context.setApplicationStartup(this.applicationStartup);
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);//严重分析:刷新应用程序的上下文,初始化默认设置/注入相关Bean/启动Tomcat
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
ex = var10;
this.handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
ex = var9;
this.handleRunFailure(context, ex, (SpringApplicationRunListeners)null);
throw new IllegalStateException(ex);
}
}
4. SpringApplication.java 容器类型很多,会跟据你的this.webApplicationType创建对应的容器
默认this.webApplicationType 是SERVLET也就是web容器/可以处理Servlet
* protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
5. ApplicationContextFactory.java
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET: //默认进入该分支
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
} catch (Exception var2) {
Exception ex = var2;
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", ex);
}
};
* 6.SpringApplication.java
* private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
this.refresh(context);//严重分析:真正执行的方法
}
* 7.SpringApplication.java
* protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
* 8.ServletWebServerApplicationContext.java
* public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
} catch (RuntimeException var3) {
RuntimeException ex = var3;
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
* 9.AbstractApplicationContext.java
* @Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh(); //严重分析:当父类完成通用的工作后,再重新动态绑定机制回到子类
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
* 10.ServletWebServerApplicationContext.java
* protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer(); //创建webServer 可以理解成会创建指定web服务-Tomcat
} catch (Throwable var2) {
Throwable ex = var2;
throw new ApplicationContextException("Unable to start web server", ex);
}
}
* 11.ServletWebServerApplicationContext.java
* private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); //严重分析:使用TomcatServletWebServerFactory创建一个Tomcat
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
ServletException ex = var5;
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
this.initPropertySources();
}
* 12.TomcatServletWebServerFactory.java 会创建Tomcat 并设置Tomcat,然后启动Tomcat
* public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat(); //创建Tomcat对象
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
//对Tomcat进行一些设置
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat); //严重分析该方法
}
13.TomcatServletWebServerFactory.java 这里做了校验 创建TomcatWebServer
* protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
* 14. TomcatWebServer.java
* public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.monitor = new Object();
this.serviceConnectors = new HashMap();
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
this.initialize(); //严重分析该方法,做了初始化,并启动Tomcat
}
* 15. TomcatWebServer.java
* private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
synchronized(this.monitor) {
try {
this.addInstanceIdToEngineName();
Context context = this.findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && "start".equals(event.getType())) {
this.removeServiceConnectors();
}
});
this.tomcat.start(); //启动Tomcat
this.rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
} catch (NamingException var5) {
}
this.startDaemonAwaitThread();
} catch (Exception var6) {
Exception ex = var6;
this.stopSilently();
this.destroySilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
* */
System.out.println("==============================");
}
}
2. 实现Spring Boot 部分机制【之前实现过Spring和SpringMVC底层机制所以简化了很多】
2.1 创建Tomcat
package com.leon.realizespringboot;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
/**
* ClassName:RealizeSpringBoot
* Package:com.leon.realizespringboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 15:49
* @Version: 1.0
*/
public class RealizeSpringBoot {
//创建tomcat对象,并关联Spring容器,然后启动
public static void run(){
try {
//创建Tomcat对象
Tomcat tomcat = new Tomcat();
//让Tomcat可以将请求,转发到Spring Web 容器,因此需要进行关联
//"/hspboot" 就是项目的 application context,就是我们原来配置Tomcat时,指定的application context
//"C:\\software\\javacodelibrary\\distributed-microservices\\realize-spring-boot"指定项目所在的目录
tomcat.addWebapp("","C:\\software\\javacodelibrary\\distributed-microservices\\realize-spring-boot");
//设置监听端口
tomcat.setPort(9090);
//启动Tomcat
tomcat.start();
//等待接入
System.out.println("===========9090===========等待请求===========");
//让Tomcat等待连接
tomcat.getServer().await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2.2 创建Spring 容器
package com.leon.realizespringboot;
import com.leon.realizespringboot.config.BeanConfig;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
/**
* ClassName:RealizeWebApplicationInitializer
* Package:com.leon.realizespringboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 16:12
* @Version: 1.0
*/
public class RealizeWebApplicationInitializer implements WebApplicationInitializer {
/*
* 1.创建自己的Spring容器
* 2.加载/关联Spring容器的配置-按照注解的方式
* 3.完成Spring容器配置的Bean的创建,依赖注入
* 4.创建前端控制器DispatcherServlet,并让其持有Spring容器
* 5.当DispatcherServlet持有容器,就可以进行分发映射。可以回忆之前自己实现SpringMVC底层机制
* 6.这里onStartup是Tomcat调用,并把ServletContext对象传入
* */
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//加载Spring Web application configuration
AnnotationConfigWebApplicationContext acwa
= new AnnotationConfigWebApplicationContext();
//在AnnotationConfigWebApplicationContext中注册BeanConfig配置类
acwa.register(BeanConfig.class);
//进行刷新,完成Bean的创建和配置
acwa.refresh();
//创建一个非常重要的前端控制器 DispatcherServlet
/*
* 1.让DispatcherServlet持有容器,这样就可以进行映射分发
* */
DispatcherServlet dispatcherServlet = new DispatcherServlet(acwa);
//将DispatcherServlet放入到tomcat的ServletContext中,然后返回ServletRegistration.Dynamic对象
ServletRegistration.Dynamic registration
= servletContext.addServlet("app", dispatcherServlet);
//设置加载优先级,也就是当Tomcat启动时加载DispatcherServlet
registration.setLoadOnStartup(1);
//拦截请求,并进行分发处理
registration.addMapping("/");
}
}
2.3 创建主程序
package com.leon.realizespringboot;
/**
* ClassName:RealizeMainApp
* Package:com.leon.realizespringboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 15:56
* @Version: 1.0
*/
public class RealizeMainApp {
public static void main(String[] args) {
//启动RealizeSpringBoot项目/程序
RealizeSpringBoot.run();
}
}
2.4 创建Bean
package com.leon.realizespringboot.bean;
/**
* ClassName:Dog
* Package:com.leon.realizespringboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 16:06
* @Version: 1.0
*/
public class Dog {
}
2.5 创建配置类
package com.leon.realizespringboot.config;
import com.leon.realizespringboot.bean.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* ClassName:BeanConfig
* Package:com.leon.realizespringboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 16:07
* @Version: 1.0
*/
@Configuration
//在配置类中可以指定要扫描的包
@ComponentScan("com.leon.realizespringboot")
public class BeanConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
2.6 创建Controller
package com.leon.realizespringboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* ClassName:HiController
* Package:com.leon.realizespringboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/15 16:06
* @Version: 1.0
*/
@Controller
public class HiController {
@RequestMapping("/hi")
@ResponseBody
public String hi() {
return "hi realize-spring-boot-----";
}
}
五、Lombok
1.Lombok介绍
- Lombok作用
- 简化JavaBean开发,可以使用Lombok的注解让代码更加简洁
- java项目中,很多没有技术含量又必须存在的代码:POJO的getter/setter/toString;异常处理;I/o流的关闭操作等,这些代码既没有技术含量,又影响着代码的美观,Lombok应运而生
- Spring Boot和IDEA官方支持
- IDEA 2020以及内置了Lombok插件
- Spring Boot 2.1.x之后的版本也在Starter中内置了Lombok依赖
2. Lombok常用注解
- @Data :注解在类上:提供所有属性的getter和setter方法,此外还提供了equals、canEqual、hashCode、toString方法
- @Setter:注解在属性上:为属性提供setter方法,注解在类上:为所有属性提供setter方法
- @Getter:注解在属性上:为属性提供getter方法,注解在类上:为所有属性提供getter方法
- @Log4j:注解在类上,为类提供一个属性名为log的log4j日志对象
- @NoArgsConstructor:注解在类上,为类提供一个无参的构造器
- @AllArgsConstructor:注解在类上,为类提供一个全参的构造器
- @Cleanup:可以关闭流
- @Builder:被注解的类加个构造者模式
- @Synchronized:加个同步锁
- @SneakyThrows:等同于try/catch捕获异常
- @NonNull:如果给参数加上这个注解,参数为Null会抛出空指针异常
- @Value注解和@Data注解类似,区别在于它会把所有成员变量默认定义为private final 修饰,并且不会生成setter方法
- @RequiredArgsConstructor
- 在我们写Controller或者Service层的时候,需要注入很多的mapper接口或者另外的Service接口,这时候就会写很多的@Autowired注解,代码看起来很乱,Lombok提供了一个注解:@RequiredArgsConstructor(onConstructor=@_(@Autowired))写在类上可以代替@Autowired注解,需要注意的是在注入时需要使用final定义,或者使用@notnull注解
3.lombok应用实例
package com.leon.springboot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* ClassName:Furn
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/9 22:11
* @Version: 1.0
*/
@Component
/*
* 1. @ConfigurationProperties(prefix = "furn01")表示获取哪个配置绑定的数据,通过前缀来区分,也就是prefix属性值来区分
* */
@ConfigurationProperties(prefix = "furn01")
//在编译时,生成toString,默认情况下,会生成一个无参构造器
//@ToString
/*
* Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
* 1.使用了Data注解等价使用了 如下注解:@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode
* */
//@Data
/*
* 1.@NoArgsConstructor在编译时,会生成无参构造器,但是在默认情况下,也会生成一个无参构造器
* 2.当使用其他构造器时,默认的无参构造器会被覆盖,如果希望仍然有无参构造器就需要使用@NoArgsConstructor指定一下,
* 否则就会因为无参构造器被覆盖,而产生一系列的错误
* */
@NoArgsConstructor
//1.@NoArgsConstructor在编译时,会生成无参构造器,但是在默认情况下,也会生成一个全参构造器
@AllArgsConstructor
public class Furn {
private Integer id;
private String name;
private Double price;
}
4.idea中安装lombok插件,使用扩展功能
六、Spring Initailizr
1. Spring Intitailizr介绍
- Spring Initailzr作用
- 程序员通过开发Maven Archetype 来生成Maven项目,这种项目的原型相对简陋,需要手动配置,比较灵活
- 通过Spring官方提供的Spring Initailizr 来构建Maven项目,能完美支持IDEA和Eclipse,让程序员来选择需要的开发场景(starter),还能自动生成启动类和单元测试代码
- Spring Initailizr对IDEA版本有要求同时还要走网络,本人比较喜欢使用Maven Archetype来生成Maven项目,因为比较灵活。
2. Spring Inlitailizr使用演示
2.1 方式1:IDEA创建
2.1.1 创建项目
2.1.1 选择类型
2.1.1 选择场景(starter)
2.2 方式2:start.spring.io创建
2.2.1 网址
- 网址:https://start.spring.io/
2.2.2 选择类型和场景
七、yaml
1. yaml介绍
- YAML是"YAML Ain`t a Markup Languaga”(YAML不是一种标记语言)的递归缩写。在开发这种语言时,YAML的意思其实是"Yet Another Markup Languaga”(仍是一种标记语言),是为了强调这种语言以数据作为中心,而不是标记语言为重点,而用反向缩略语重命名【百度百科】
- 解读
- YAML以数据作为中心,而不是以标记语言为重点
- YAML仍然是一种标记语言,但是和传统的标记语言不一样,是以数据为中心的标记语言
- YAML非常适合用来做以数据为中心的配置文件
2. 使用文档
- 官方网址:https://yaml.org/
- 关于java操作yaml:https://www.cnblogs.com/strongmore/p/14219180.html
3.YAML基本语法
- 形式为key: value;注意:后面有空格
- 区分大小写
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格【有些地方也识别tab,推荐使用空格】
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- 字符串无需加引号【也可以加上,不过意义不大】
- YAML中,使用#注释
4.数据类型
4.1 字面量
- 字面量:单个的、不可再分的值。例如:date、boolean、string、number、null
- 保存形式为key: value
4.2 对象
对象:键值对的集合,比如 map、hash、set、object
行内写法:K: {k1: v1,k2: v2,k3: v3} #或者 k: k1: v1 k2: v2 k3: v3
4.3 数组
数组:一组按次序排列的值,比如array、List、queue
行内写法:k: {v1,v2,v3} #或者 k:
v1
v2
v3
5.yaml使用细节
如果application.properties和application.yaml有相同的前缀绑定,则application.properties优先级高,开发时,应当避免
字符串无需加引号,如果使用”“或者''包起来也可以
解决YAML配置文件,不提示字段信息问题
在pom文件中添加如下配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
也可以在IDEA中安装YAML插件
如果有提示功能的前提下,配置过的属性不会在提示
6. 代码
monster:
id: 100
name: 牛魔王
age: 100
is-married: true
birth: 2024/7/30
# 对象
# car: {name: 兰博基尼,price: 200} 行内风格
car:
name: 兰博基尼
price: 200
# 数组
# skill: [法天象地,芭蕉扇] #行内风格
skill:
- 法天象地
- 芭蕉扇
# 数组
# hobby: [泡妞,吹牛,吃肉] #行内风格
hobby:
- 泡妞
- 吹牛
- 吃肉
# 对象
# wife: {no1: 铁扇公主,no2: 狐狸精} #行内风格
wife:
no1: 铁扇公主
no2: 狐狸精
salaries:
- 1000
- 20000
cars:
c1: [{name: 路虎,price: 3000},{name: 小米SUV,price: 3000},{name: suv,price: 2000}]#行内风格
c2:
- {name: 法拉利 ,price: 2000} #行内风格
- name: 五菱宏光
price: 300
八、WEB开发-静态资源访问
1. 官方文档
- 网址:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/html/web.html#web.servlet.spring-mvc.auto-configuration
2. 基本介绍
只要静态资源放在类路径下:/static、 /public、 /resources 、/META-INF/resources可以被直接访问-对应文件WebProperties.java
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
常见静态资源:JS、CSS、图片(.jpg .png .gif .bmp .svg)、字体文件(Fonts)等
访问方式:默认:项目根路径/+静态资源。例如 http://localhost/hi.html。可以通过WebMvcProperties.java中的属性来设置访问映射
/** * Path pattern used for static resources. */ private String staticPathPattern = "/**";
3.快速入门
3.1 代码
package com.leon.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* ClassName:MainApp
* Package:com.leon.springboot
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/30 20:04
* @Version: 1.0
*/
@SpringBootApplication
public class MainApp {
public static void main(String[] args) {
/*
* private static final String[] CLASSPATH_RESOURCE_LOCATIONS =
* { "classpath:/META-INF/resources/","classpath:/resources/",
* "classpath:/static/", "classpath:/public/" };
* */
SpringApplication.run(MainApp.class,args);
}
}
4. 静态资源访问注意事项和使用细节
静态资源访问原理:静态映射是 /** ,也就是对所有请求拦截,请求进来,先看Controller能不能处理,不能处理的请求再交给静态资源处理,如果静态资源找不到则响应404页面
改变静态资源访问前缀,应用场景:静态资源访问前缀和控制器请求路径冲突
创建一个yaml文件,来修改静态资源访问前缀
spring: mvc: static-path-pattern: /resource/** #修改静态资源访问的路径/前缀
改变默认的静态资源路径
配置yaml文件,修改static-locations
spring: mvc: static-path-pattern: /resource/** #修改静态资源访问的路径/前缀 web: resources: #修改/指定 静态资源的访问路径/位置 #如果新配置了static-locations,原来的访问路径就会被覆盖,如果需要保留,需要再次指定一下 static-locations: [classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/,classpath:/img/] #这是一个字符串数组
九、Rest风格请求处理
1.基本介绍
- Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 举例说明:
- GET-获取信息
- DELETE-删除信息
- PUT-修改信息
- PSOT-保存信息
2. 快速入门
package com.leon.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* ClassName:MonsterController
* Package:com.leon.springboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/7/30 20:43
* @Version: 1.0
*/
@RestController
public class MonsterController {
@GetMapping("/monster")
//等价于@RequestMapping(value = "/monster", method = RequestMethod.GET)
public String getMonster() {
return "GET-获取妖怪信息";
}
@DeleteMapping("/monster")
public String deleteMonster() {
return "DELETE-删除妖怪信息";
}
@PutMapping("/monster")
public String putMonster() {
return "PUT-修改妖怪信息";
}
@PostMapping("/monster")
public String postMonster() {
return "POST-保存妖怪信息";
}
}
3.注意事项和细节
客户端是PostMan可以直接发送PUT、DELETE等请求方式,可不设置Filter
如果要SpringBoot支持页面表单的Rest功能,则需要注意如下细节
Rest风格请求核心Filter:HiddenHttpMethodFilter,表单请求会被HiddenHttpMethodFilter拦截,获取到表单_method的值,再判断是PUT/DELETE/PATCH(老师注释:PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行局部更新:https://segmentfault.com/q/1010000005685904)
如果要SpringBoot 支持页面表单的Rest功能,需要在application.yml启用Filter功能,否则无效
设置application.yml启用Filter功能
spring: mvc: hiddenmethod: filter: enabled: true #启用HiddenHttpMethodFilter,开启页面表单Rest功能
配置视图解析器
spring: mvc: #static-path-pattern: /resource/** #修改静态资源访问的路径/前缀 view: #配置视图解析器 suffix: .html #这里需要注意: # 1. 如果没有指定static-path-pattern: 的值 则 prefix: / # 2. 如果指定了static-path-pattern: 的值,则 prefix: 的值等于static-path-pattern: 的值 # 去除后面的**,也就是需要和static-path-pattern的值保持一致 # 3. 因为SpringBoot有一个约定由于配置的思想,所以建议不要指定static-path-pattern: 的值 #prefix: /resource/ prefix: /
十、接收参数相关注解
1. 基本介绍
- Spring Boot接收客户端提交数据/参数会使用到相关注解
- 详解@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@CookieValue、@RequestBody
2. 接收参数相关注解应用实例
2.1 @PathVariable
/*
* 1. /monster/{id}/{name} 解读
* 1.1 /monster/{id}/{name} 构成完整请求路径
* 1.2 {id}{name} 就是占位变量
* 1.3 @PathVariable("name"): 这里name和占位变量{name}必须相同,否则会报错
* 1.4 而方法中的形参可以和注解中的value值不一样
* 1.5 @PathVariable Map<String,String> map 就是把所有参数都封装进map集合中,
* key值是占位变量的名称,value是请求参数值
* */
@GetMapping("/monster/{id}/{name}")
@ResponseBody
public String pathVariable(@PathVariable("id") Integer id,
@PathVariable("name") String name,
@PathVariable Map<String, String> map){
System.out.println("id---"+id);
System.out.println("name---"+name);
System.out.println("map---"+map);
return "success";
}
2.2 @RequestHeader
@GetMapping("/requestHeader")
@ResponseBody
public String requestHeader(@RequestHeader("Host") String host,
@RequestHeader("Accept") String accept,
@RequestHeader Map<String, String> map){
System.out.println("host---"+host);
System.out.println("accept---"+accept);
System.out.println("map---"+map);
return "success";
}
2.3 @ModelAttribute
/*
* 1. 当Handler的方法被标识@ModelAttribute,就视为一个前置方法,
* 每当这个类中的其他方法被执行前都会执行这个方法
* 2. 这个方法类似于AOP中的前置通知
* */
@ModelAttribute
public void modelAttribute(){
System.out.println("----------------modelAttribute被执行了----------------");
}
2.4 @RequestParam
/*
* 1.如果是多个相同名称的请求参数,可以通过List集合进行封装
* 2.如果使用Map集合来接收所有请求参数的话,相同参数名称的多个请求参数只能获取到一个,也就是说只有多个相同参数名称的请求参数中的一个被封装进map集合
* */
@GetMapping("/requestParam")
@ResponseBody
public String requestParam(@RequestParam(value = "name",required = false) String name,
@RequestParam(value = "job",required = false) String job,
@RequestParam(value = "hobby",required = false) List<String> hobby,
@RequestParam Map<String, String> map){
System.out.println("name---"+name);
System.out.println("job---"+job);
System.out.println("hobby---"+hobby);
System.out.println("hobbyMap---"+map);
return "success";
}
2.5 @CookieValue
/*
* 1.因为浏览目前没有Cookie,可以自己在浏览器中设置Cookie
* 2.@CookieValue("cookie_key") String cookieValue
* 表示将接收到Cookie的key值为cookie_key的value值赋值给cookieValue
* 3.@CookieValue("username") Cookie cookie 表示将接收到中cookie的key值为username的cookie对象赋值给cookie
* 4.如果浏览器携带来对应的cookie,那么就会将对应的值或者封装好的cookie对象赋值给对应的形参
* */
@GetMapping("/cookie")
@ResponseBody
public String cookieValue(@CookieValue("cookie_key") String cookieValue,
@CookieValue("username") Cookie cookie,
HttpServletRequest request){
System.out.println("cookieValue---"+cookieValue);
System.out.println("cookie---[ "+cookie.getName()+"--"+cookie.getValue()+" ]");
//获取所有的cookie
Cookie[] cookies = request.getCookies();
//循环遍历所有的cookie
for (Cookie cookie1 : cookies) {
System.out.println("cookie---[ "+cookie1.getName()+"--"+cookie1.getValue()+" ]");
}
return "success";
}
2.6 @RequestBody
/*
* 1.@RequestBody String content 将表单发出的Post请求中的请求数据封装赋值给content
* */
@PostMapping("/save")
@ResponseBody
public String requestBody(@RequestBody String content){
System.out.println("content---"+content);
return "success";
}
2.7 @RequestAttribute和@SessionAttribute
@GetMapping("/requestAttribute")
@ResponseBody
public String requestAttribute(@RequestAttribute(value = "user",required = false) String user,
@RequestAttribute(value = "age",required = false) String age,
HttpServletRequest request,
@SessionAttribute(value = "id",required = false) String id){
System.out.println("user--"+user);
System.out.println("age---"+age);
System.out.println("通过servlet API获取request域中的数据:"+request.getAttribute("user"));
System.out.println("id--"+id);
System.out.println("通过servlet API获取session域中的数据:"+request.getSession().getAttribute("id"));
return "success";
}
@GetMapping("/login")
public String login(HttpServletRequest request){
request.setAttribute("user","牛魔王");
request.setAttribute("age","100");
request.getSession().setAttribute("id","109022");
//这里需要启用视图解析器
return "forward:/requestAttribute";
}
2.7 前端测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>接收参数注解测试</title>
</head>
<body>
<h1>hello,欢迎来到SpringBoot接收参数注解测试</h1>
基本注解:
<hr/>
<a href="/monster/100/king">@PathVariable-路径变量 /monster/100/king</a><br/><br/>
<a href="/requestHeader">@RequestHeader-获取Http请求头</a><br/><br/>
<a href="/requestParam?name=jack&job=java&hobby=羽毛球&hobby=乒乓球">@RequestParam-获取请求参数</a><br/><br/>
<a href="/cookie">@CookieValue-获取Cookie值</a><br/><br/>
<a href="/login">@RequestAttribute-获取request域属性</a><br/><br/>
<form action="/save" method="post" >
用户:<input type="text" name="username" value=""/><br/>
年龄:<input type="text" name="age" value=""/><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
3. 复杂参数
3.1 基本介绍
- 在开发中,Spring Boot 在响应客户端请求时,也支持复杂参数
- Map、Model、Errors/BindingResult、RedirectAttributes、ServletResponse、SessionStatus、UriComponentBuilder、ServletUriComponentsBuilder、HttpSession
- Map、Model数据会被放在request域
- RedirectAttributes重定向携带数据
3.2 复杂参数应用
@GetMapping("/register")
public String register(Map<String, Object> map,
Model model,
HttpServletResponse response) {
//Map中的数据会被放到,request域中
map.put("user","jack");
map.put("job","java工程师");
map.put("age","32");
map.put("hobby","乒乓球");
//model中的数据也会被放入到request域中
model.addAttribute("id","10091");
//添加cookie
response.addCookie(new Cookie("salary","10000"));
return "forward:/registerOk";
}
@GetMapping("/registerOk")
@ResponseBody
public String registerOk(HttpServletRequest request) {
System.out.println("user---"+request.getAttribute("user"));
System.out.println("job---"+request.getAttribute("job"));
System.out.println("age---"+request.getAttribute("age"));
System.out.println("hobby---"+request.getAttribute("hobby"));
System.out.println("id---"+request.getAttribute("id"));
return "success";
}
4.自定义对象参数-自动封装
4.1基本介绍
- 在开发中,SpringBoot在响应客户端/浏览器请求时,也支持自定义对象参数
- 完成自动类型转换与格式化
- 支持级联封装
4.2自定义对象参数-应用实例
4.2.1 后端代码
@PostMapping("/saveMonster")
@ResponseBody
public String saveMonster(Monster monster){
System.out.println("monster---"+monster);
return "success";
}
4.2.2 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义对象参数</title>
</head>
<body>
<h1>添加妖怪-坐骑[测试封装POJO]</h1>
<form action="/saveMonster" method="post">
编号:<input type="text" name="id" value=""><br/>
姓名:<input type="text" name="name" value=""><br/>
年龄:<input type="text" name="age" value=""><br/>
婚否:<input type="text" name="isMarried" value=""><br/>
生日:<input type="text" name="birthdays" value=""><br/>
坐骑名称:<input type="text" name="car.name" value=""><br/>
坐骑价格:<input type="text" name="car.price" value="">
<input type="submit" value="保存">
</form>
</body>
</html>
十一、自定义转换器
1. 基本介绍
- Spring Boot 在响应客户端请求时,将提交的数据封装成对象时,使用了内置的转换器
- Spring Boot 也支持自定义转换器,其中Spring Boot提供了124个内置转换器(Spring Boot 版本为2.5.3),通过查看GenericConverter-ConvertiblePair
2.自定义转换器-应用实例
2.1 后端代码
package com.leon.springboot.config;
import com.leon.springboot.bean.Car;
import com.leon.springboot.bean.Monster;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* ClassName:WebConfig
* Package:com.leon.springboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/7 22:15
* @Version: 1.0
*/
/*
* 1.proxyBeanMethods = false 表示使用Lite模式
* */
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
/*
* 1.在addFormatters 方法中,增加一个自定义的转换器,转换的类型是由String-->Car
* 2.增加的自定义转换器会注册到converters容器中
* 3.converters底层结构是ConcurrentHashMap内置有124个转换器
* */
//方式1
//registry.addConverter(new Converter<String, Car>() {
//
// /*
// * 1.source是通过表单请求过来的数据:避水金晶兽,666.6
// * */
// @Override
// public Car convert(String source) {
// //这里写主要业务代码
//
// //判断source是否为空
// if(StringUtils.hasText(source)){
//
// //创建一个Car对象
// Car car = new Car();
// //进行字符串操作
// String[] split = source.split(",");
// //设置名称
// car.setName(split[0]);
// //设置价格
// car.setPrice(Double.parseDouble(split[1]));
//
// return car;
// }
//
//
// return null;
// }
//});
//方式2
Converter<String, Car> converter = new Converter<String,Car>() {
@Override
public Car convert(String source) {
//这里写主要业务代码
//判断source是否为空
if (StringUtils.hasText(source)) {
//创建一个Car对象
Car car = new Car();
//进行字符串操作
String[] split = source.split(",");
//设置名称
car.setName(split[0]);
//设置价格
car.setPrice(Double.parseDouble(split[1]));
return car;
}
return null;
}
};
Converter<String, Monster> converter2 = new Converter<String, Monster>() {
@Override
public Monster convert(String source) {
return null;
}
};
//可以添加多个自定义转换器
registry.addConverter(converter);
registry.addConverter(converter2);
}
};
}
}
2.2 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义对象参数</title>
</head>
<body>
<h1>添加妖怪-坐骑[测试封装POJO]</h1>
<form action="/saveMonster" method="post">
编号:<input type="text" name="id" value=""><br/>
姓名:<input type="text" name="name" value=""><br/>
年龄:<input type="text" name="age" value=""><br/>
婚否:<input type="text" name="isMarried" value=""><br/>
生日:<input type="text" name="birthdays" value=""><br/>
坐骑:<input type="text" name="car" value="避水金晶兽,666.6">
<!-- 坐骑名称:<input type="text" name="car.name" value=""><br/>-->
<!-- 坐骑价格:<input type="text" name="car.price" value="">-->
<input type="submit" value="保存">
</form>
</body>
</html>
十二、处理JSON
1.应用实例
@GetMapping("/getMonster")
@ResponseBody
public Monster getMonster() {
Monster monster = new Monster();
monster.setId(100);
monster.setName("奔波霸");
monster.setAge(1000);
monster.setMarried(false);
monster.setBirthdays(new Date());
monster.setCar(new Car("拖拉机",10));
return monster;
}
十三、内容协商
1.基本介绍
- 根据客户接收能力不同,Spring Boot返回不同媒体类型的数据
- 比如:客户端Http请求Accept:application/xml则返回xml数据,客户端Http请求Accept:application/json则返回json数据
2.内容协商-应用实例
2.1 测试代码
@GetMapping("/getMonster")
@ResponseBody
public Monster getMonster() {
Monster monster = new Monster();
monster.setId(100);
monster.setName("奔波霸");
monster.setAge(1000);
monster.setMarried(false);
monster.setBirthdays(new Date());
monster.setCar(new Car("拖拉机",10));
return monster;
}
3.注意细节
如果想要让SpringBoot返回xml格式,需要导入xml处理包,版本号则使用SpringBoot的版本仲裁
<!--导入处理返回xml类型的依赖--> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
为什么通过浏览器请求,总是返回xml格式呢?(前提导入了依赖)
因为浏览器的Http请求头中,Accept中的参数设置了权重【 text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng, / ;q=0.8】,其中text/html,application/xhtml+xml,application/xml的权重为0.9,而image/avif,image/webp,image/apng, * / *;q=0.8的权重为0.8,所以xml的权重比JSON格式的权重大,权重越大优先级越高,因而总是返回xml格式
Postman可以通过修改Accept的值,来返回不同的数据格式
对于浏览器,我们无法修改其Accept的值,如何实现修返回不同数据格式
解决方案:开启支持基于请求参数的内容协商功能
设置application.yml,开启基于请求参数的内容协商功能
spring: mvc: contentnegotiation: favor-parameter: true #开启基于请求参数的内容协商功能
请求方式
注意,参数format是规定好的,在开启请求参数的内容协商功能后,SpringBoot底层ParameterContentNegotiationStrategy会通过format来接收参数,然后返回对应的媒体类型/数据格式,当然format=媒体类型/数据类型,这个媒体类型/数据格式,是SpringBoot可以进行处理的才行,不能乱写
如果要修改format这个参数名称,要在application.yml中进行设置
spring: mvc: contentnegotiation: favor-parameter: true #开启基于请求参数的内容协商功能 parameter-name: leonformat #指定一个内容协商的参数名
请求方式
十四、Thymeleaf
1.官方文档
- 在线文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
- pdf文件网址:https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.pdf
2.基本介绍
- Thymeleaf是什么
- Thymeleaf是一个跟Velocity、FreeMarker类似的模板引擎,可完全替代JSP
- Thymeleaf是一个java类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层
- Thymeleaf的优点
- 实现JSTL、OGNL表达式效果,语法相似,java程序员上手快
- Thymeleaf模板页面无需服务器渲染,也可以被浏览器运行,页面简洁
- SpringBoot支持FreeMarker、Thymeleaf、veocity
- Thymeleaf的缺点
- Thymeleaf:Thymeleaf is a modern server-side Java template engine for both web and standlone environments
- 缺点:并不是一个高性能的引擎,适用用于单体应用
- 如果要做一个高并发的应用,选择前后端分离更好,但是作为SpringBoot推荐的模板引擎,还是推荐学习一下。
3.Thymeleaf机制说明
- Thymeleaf是服务器渲染技术,页面数据是在服务器端进行渲染的
- 比如:manage.html中一段Thymeleaf代码,是在用户请求该页面时,有Thymeleaf模板引擎完成处理的(在服务器端完成),并将结果页面返回
- 因此使用Thymeleaf,并不是前后端分离
4.Thymeleaf语法
4.1表达式
表达式一览
| 表达式名字 | 语法 | 用途 |
| ———- | —— | ————————————- |
| 变量取值 | ${…} | 获取request域、Session域,对象等值 |
| 选择变量 | *{…} | 获取上下文对象值 |
| 消息 | #{…} | 获取国际化等值 |
| 链接 | @{…} | 生成链接 |
| 片段表达式 | ~{…} | 类似jsp:include作用,引入公共页面片段 |字面量
文本值:'hello','hhh',….
数字:10,23,6…
布尔值:true,false
空值:null
变量:name,age,…[变量不能有空格]
文本操作
字符串拼接:+
变量替换:|age=${age}|
4.2运算符
- 数学运算符
- 运算符:+,-,*,/,%
- 布尔运算
- 运算符:and,or
- 一元运算符:!,not
- 比较运算符
- 比较:<,>,>= ,<= (gt,lt,ge,le)
- 等式:==,!= (eq,ne)
- 条件运算符
- If-then:(if) ? (then)
- If-then-else:(if) ? (then) : (else)
- Default:(value) ?: (defaultvalue)
4.3th属性
- html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先从1~8,数字越低优先级越高
- th:text 设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。优先级不高:order=7
- th:value 设置当前元素的value值,类似修改指定属性的还有th:src,th:href。优先级不高:order=6
- th:each 遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置,详细往后看。优先级很高:order=2
- th:if 条件判断,类似的还有th:unless,th:switch,th:case。优先级较高:order=3
- th:insert 代码块引入,类似的还有th:replace,th:include,三者区别较大,若使用不恰当会被破坏html结构,常用于公共代码块提取场景。优先级最高:order=1
- th:fragment 定义代码块,方便被th:insert引用。优先级最低:order=8
- th:object 声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4
- th:attr 修改任意属性,实际开发中用的较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。优先级一般:order=5
4.4迭代
<tr th:each="prod:${prods}">
<td th:text="${pord.name}">Onions</td>
<td th:text="${pord.price}">2.14</td>
<td th:text="${pord.inStock} ? #{true} : #{false}">yes</td>
<tr/>
<tr th:each="prod,iterStat:${prods}" th:class="${iterStat.odd} ? 'odd'">
<td th:text="${pord.name}">Onions</td>
<td th:text="${pord.price}">2.14</td>
<td th:text="${pord.inStock} ? #{true} : #{false}">yes</td>
<tr/>
4.5条件运算符
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #list.isEmpty(prod.comments)}">view</a>
<div th:swicth="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is an manager</p>
<p th:case="*">User is some other thing</p>
</div>
4.6使用Thymeleaf属性需要注意的点
- 若要使用Thymeleaf语法,首先要先声明名称空间:xmlns:th=“http://www.thymeleaf.org”
- 设置文本内容th:text,设置input的值th:value,循环输出th:each,条件判断th:if,插入代码块th:insert,定义代码块th:fragment,声明变量th:object
- th:each 的用法需要格外注意,举个例子 :如果你要循环一个div中的p标签,则th;each属性必须放在p标签上。若你将th:each属性放在div上,则循环的是整个div
- 变量表达式中提供了很多的内置方法,该内置方法使用#开头,请不要与#{}消息表达式弄混
5.应用实例
5.1 IndexController.java
package com.leon.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* ClassName:IndexController
* Package:com.leon.springboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/8 21:17
* @Version: 1.0
*/
@Controller
public class IndexController {
@GetMapping({"/","/login"})
public String login(){
//作为一个中转
/*
* 1.因为我们引入了starter-thymeleaf,这里就会直接使用视图解析到Thymeleaf下的模板文件adminLogin.html
* */
return "adminLogin";
}
}
5.2 AdminController.java
package com.leon.springboot.controller;
import com.leon.springboot.bean.Admin;
import com.leon.springboot.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
/**
* ClassName:AdminController
* Package:com.leon.springboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/8 21:28
* @Version: 1.0
*/
@Controller
public class AdminController {
@PostMapping("/login")
public String manage(Admin admin, HttpSession session, Model model) {
//判断用户是否合法
if (StringUtils.hasText(admin.getUsername()) && "666".equals(admin.getPassword())) {
//设置用户登录
session.setAttribute("admin", admin);
//跳转到目标方法
return "redirect:/manage.html";
}
//设置错误信息
model.addAttribute("error", "用户密码不正确");
return "adminLogin";
}
@GetMapping("/manage.html")
public String manage(HttpSession session,Model model) {
//判断用户是否登录
if(session.getAttribute("admin") != null){
ArrayList<User> users = new ArrayList<>();
users.add(new User(1,"关羽","6666666",20,"gy@shouhu.com"));
users.add(new User(2,"张飞","6666666",20,"zf@shouhu.com"));
users.add(new User(3,"赵云","6666666",20,"zy@shouhu.com"));
users.add(new User(4,"马超","6666666",20,"mc@shouhu.com"));
users.add(new User(5,"黄忠","6666666",20,"hz@shouhu.com"));
model.addAttribute("users", users);
//如果登录则跳转到管理页面
return "manage";
}
//设置错误信息
model.addAttribute("error","请先登录");
return "adminLogin";
}
}
5.3adminLogin.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>用户登陆</h1>
<form action="#" th:action="@{/login}" method="post">
<label style="color: red" th:text="${error}"></label><br/>
用户名:<input type="text" style="width:150px" name="username"/><br/><br/>
密 码 :<input type="password" style="width:150px" name="password"/><br/><br/>
<input type="submit" value="登录"/>
<input type="reset" value="重新填写"/>
</form>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
5.4manage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>管理后台</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<a href='#'>返回管理界面</a> <a href='#' th:href="@{/login}">安全退出</a> 欢迎您:[[${session.admin.username}]]
<hr/>
<div style="text-align: center">
<h1>管理雇员~</h1>
<table border="1px" cellspacing="0" bordercolor="green" style="width:800px;margin:auto">
<tr bgcolor="pink">
<td>id</td>
<td>name</td>
<td>pwd</td>
<td>age</td>
<td>email</td>
</tr>
<tr bgcolor="#ffc0cb" th:each="user:${users}">
<td th:text="${user.id}">a</td>
<td th:text="${user.name}">b</td>
<td th:text="${user.password}">c</td>
<td th:text="${user.age}">d</td>
<td th:text="${user.email}">e</td>
</tr>
</table>
<br/>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
5.5Admin.java
package com.leon.springboot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* ClassName:Admin
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/8 21:26
* @Version: 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
//用户名
private String username;
//密码
private String password;
}
5.6User.java
package com.leon.springboot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* ClassName:User
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/8 21:26
* @Version: 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private String password;
private Integer age;
private String email;
}
十五、拦截器-HandlerInterceptor
1.基本介绍
- 在Spring Boot 项目中,拦截器是开发中常用手段,要来做登录验证、性能检查、日志记录等。
- 基本步骤
- 编写一个拦截器实现HandlerInterceptor接口
- 拦截器注册到配置类中(实现WebMvcConfig的addInterceptor)
- 指定拦截规则
2.应用实例
2.1 LoginInterceptor.java
package com.leon.springboot.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* ClassName:LoginInterceptor
* Package:com.leon.springboot.interceptor
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/10 20:06
* @Version: 1.0
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/*
* 1.目标方法执行前被调用
* */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求的URI
String requestURI = request.getRequestURI();
log.info("requestURI: {}", requestURI);
//进行登录校验
Object admin = request.getSession().getAttribute("admin");
//判断用户是否登录
if (null != admin) {
//放行
return true;
}
//拦截,重新返回登录页面
request.setAttribute("error", "请登录");
//进行请求转发
request.getRequestDispatcher("/").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle被执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion被执行了");
}
}
2.2 WebConfig.java
package com.leon.springboot.config;
import com.leon.springboot.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* ClassName:WebConfig
* Package:com.leon.springboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/10 20:20
* @Version: 1.0
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//将自定的LoginInterceptor注册到容器中
//添加拦截规则,和排除哪些拦截
registry.addInterceptor(new LoginInterceptor()).
addPathPatterns("/**").//这里表示拦截所有请求
excludePathPatterns("/","/login","/images/**");//表示排除哪些请求,这里/images/**前面不用加/static的原因是静态资源默认请求
}
}
3.注意事项
URI和URL
URI = Universal Resource Identifier
URL = Universal Resource Locator
Identifier:标识符,Localtor:定位器。从字面上来看,URI可以唯一标识一个资源,URL可以可提供到该资源的路径
举例
String requestURI = request.getRequestURI(); String requestURL = request.getRequestURL().toString();
第二种注册方式
@Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()). addPathPatterns("/**"). excludePathPatterns("/","/login","/images/**"); } }; }
十六、文件上传
1. 应用实例
1.1 后端代码
package com.leon.springboot.controller;
import com.leon.springboot.bean.Employee;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* ClassName:UploadController
* Package:com.leon.springboot.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/10 21:06
* @Version: 1.0
*/
@Controller
public class UploadController {
private static final Logger log = LoggerFactory.getLogger(UploadController.class);
@GetMapping("/upload.html")
public String toUpload() {
return "upload";
}
@PostMapping("/upload")
@ResponseBody
public String upload(Employee employee, @RequestParam(value = "header",required = false) MultipartFile header,
@RequestParam(value = "photos",required = false) MultipartFile[] photos) throws IOException {
//获取项目的类路径
String filePath = ResourceUtils.getURL("classpath:").getFile();
//创建一个新的目录
File file = new File(filePath + "static/images/upload/");
//判断文件是否存在
if(!file.exists()){
//创建文件
file.mkdirs();
}
//C:\software\javacodelibrary\distributed-microservices\springboot-usersys\target\classes
//判断文件对象是否为空
if(!header.isEmpty()){
//获取文件名称
String filename = header.getOriginalFilename();
//保存文件
//header.transferTo(new File("C:\\software\\javacodelibrary\\distributed-microservices\\springboot-usersys\\target\\classes\\"+filename));
//log.info("绝对路径:{}",file.getAbsoluteFile());
header.transferTo(new File(file.getAbsoluteFile() + "/" + filename));
}
//判断头像是否为空
if(photos.length>0){
//循环遍历
for (MultipartFile photo : photos) {
//判断文件对象是否为空
if (!photo.isEmpty()){
//获取文件名
String filename = photo.getOriginalFilename();
////保存文件
//photo.transferTo(new File("C:\\software\\javacodelibrary\\distributed-microservices\\springboot-usersys\\target\\classes\\" + filename));
photo.transferTo(new File(file.getAbsoluteFile() + "/" + filename));
}
}
}
return "success";
}
}
1.2 前端代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>upload</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>注册用户~</h1>
<form action="#" method="post" th:action="@{/upload}" enctype="multipart/form-data">
用户名:<input type="text" style="width:150px" name="name"/><br/><br/>
电 邮:<input type="text" style="width:150px" name="email"/><br/><br/>
年 龄:<input type="text" style="width:150px" name="age"/><br/><br/>
职 位:<input type="text" style="width:150px" name="job"/><br/><br/>
头 像:<input type="file" style="width:150px" name="header"><br/><br/>
宠 物:<input type="file" style="width:150px" name="photos" multiple><br/><br/>
<input type="submit" value="注册"/>
<input type="reset" value="重新填写"/>
</form>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
2. 注意事项
注意如果单个文件上传超过1MB,批量上传超过10MB,需要设置application.yml文件,配置如下
spring: servlet: multipart: max-file-size: 10MB #单个文件最大请求数 max-request-size: 50MB #最大文件请求数
十七、异常处理
1. 基本介绍
默认情况下,Spring Boot 提供 /error处理所有错误(异常)的映射,也就是说当出现错误(异常)时,Spring Boot底层会请求转发到/error这个映射路径。
比如使用浏览器访问不存在接口(路径映射),响应一个“whitelabel”错误视图,以HTML格式呈现给用户
Spring Boot底层默认由DefaultErrorViewResolver处理错误(异常)
2. 拦截器VS过滤器
使用范围不同
过滤器:实现的是javax.servlet.Filter接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter的使用要依赖于Tomcat等容器,Filter只能在web程序中使用
拦截器(Interceptor):它是是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application等程序中
过滤器和拦截器的出发时机也不同,如图
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
拦截器Interceptor是在请求进入servlet后,在进入Controller之前进行预处理的,Controller中渲染了对应的视图之后请求结束
说明:过滤器不会处理请求转发,拦截器会处理请求转发
3. 自定义异常页面说明
3.1 学习文档
- 学习网址:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/htmlsingle/#web.servlet.spring-mvc.error-handling
3.2 自定义异常页面说明
如何找到目标位置
4. 自定义异常页面实现
4.1 需求
- 自定义404.html,500.html,4xx.html,5xx.html当发生相应错误时,显示自定义的页面信息
4.2 代码实现
4.2.1 404.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>4o4 Not Found</h1>
<a href='#' th:href="@{/}">返回主页面</a></br>
状态码:<h1 th:text="${status}"></h1></br>
异常信息:<h1 th:text="${error}"></h1>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
4.2.2 500.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>500 内部服务器出现了问题</h1><br/>
状态码:<h1 th:text="${status}"></h1></br>
异常信息:<h1 th:text="${error}"></h1>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
4.2.3 4xx.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>4xx 发生错误了:)</h1>
错误状态码: <h1 th:text="${status}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
4.2.4 5xx.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>5xx 发生错误了:)</h1><br/>
错误状态码: <h1 th:text="${status}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
5.全局异常
5.1 说明
- @ControllerAdvice+@ExceptionHandler处理全局异常
- 底层是ExceptionHandlerExceptionResolver支持的
5.2 全局异常-应用实例
需求:演示全局异常使用,当发生ArithmeticException、NullPointerException时,不使用默认异常机制匹配的xxx.html,而显示全局异常机制指定的错误页面
5.2.1 Controller
package com.leon.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
/**
* ClassName:GlobalExceptionHandler
* Package:com.leon.springboot.exception
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/24 17:25
* @Version: 1.0
*
* @ControllerAdvice :使用它标识一个全局异常处理器/对象,会注入到Spring容器
*/
//@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
/*
* 1.这个方法是用来处理指定异常的,例如算数异常和空指针异常,这里处理的异常可以由程序员来指定
* 2.Exception ex:表示异常发生后,传递的异常对象
* 3.Model model:可以将我们的异常信息,放入model然后传递到显示页面
* */
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public String handleArithmeticException(Exception ex, Model model, HandlerMethod handlerMethod){
//输出异常信息
System.out.println("异常信息==="+ex.getMessage());
//将异常信息放入到model,然后到错误页面显示
model.addAttribute("err",ex.getMessage());
//获取异常发生的方法
System.out.println(handlerMethod.getMethod());
return "/error/global";
}
}
5.2.2 前端代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>全局异常</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
韩顺平Java 工程师
<h1>全局异常/错误 发生了:)</h1><br/>
异常/错误信息:<h1 th:text="${err}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
6.自定义异常
6.1 说明
- 如果Spring Boot提供的异常不能满足开发需求,程序员也可以自己定义异常
- @ResponseStatus+自定义异常
- 底层是ResponseStatusExceptionResolver,底层调用response.sendError(statusCode,resolvedReason)
- 当抛出自定义异常后,仍然会根据状态码,去匹配使用xxx.html显示,当然也可以将自定义异常,放在全局异常处理器去处理
6.2 需求说明
- 自定义一个异常AccessException,当用户访问某个无权访问的路径时,抛出该异常,显示自定义状态码
6.3 应用实例
6.3.1 代码
package com.leon.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* ClassName:AccessException
* Package:com.leon.springboot.exception
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/24 22:08
* @Version: 1.0
*/
/*
* 1.本类是一个自定义异常
* 2. value = HttpStatus.FORBIDDEN:表示发生AccessException异常,我们通过Http协议返回的状态码 403
* */
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException {
//提供一个构造器可以指定错误信息
public AccessException(String message) {
super(message);
}
//提供一个无参构造器,
public AccessException(){
}
}
6.4 注意事项和细节
- 如果把自定义异常类型,放在全局异常处理器,那么仍然走全局异常处理机制
- 异常处理优先级:全局异常 > 默认异常机制
十八、Spring Boot中注入Servlet、Filter、Listenter
1. 官方文档
- 官方学习网址:https://docs.spring.io/spring-boot/docs/3.2.8-SNAPSHOT/reference/htmlsingle/#web.servlet.embedded-container.servlets-filters-listeners
2. 基本介绍
- 考虑到实际开发业务非常复杂和兼容,Spring Boot支持将Servlet、Filter、Listener注入Spring容器,成为Spring bean
- 也就是说明Spring Boot开放了和原生WEB组件(Servlet、Filter、Listener)的兼容
3. 应用实例
3.1 注解方式注入
3.1.1 注入Servlet
package com.leon.springboot.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* ClassName:Servlet_
* Package:com.leon.springboot.servlet
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/26 20:24
* @Version: 1.0
*/
/*
* 1.通过继承HttpServlet来开发原生的Servlet
* 2.@WebServlet注解是将Servlet对象注入到Spring Boot容器中
* 3.(urlPatterns = {"/servlet01","/servlet02"}),对Servlet对象配置url-pattern,也就是配置映射路径
* 4.注意:注入的原生的Servlet被请求时,不会被Spring Boot的拦截器拦截
* 5.对于开发原生的Servlet,需要使用@ServletComponent注解来指定扫描原生Servlet,才会被
* 注入到Spring Boot容器中
* */
@WebServlet(urlPatterns = {"/servlet01","/servlet02"})
public class Servlet_ extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello Servlet_ ------");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
3.1.2 注入Filter
package com.leon.springboot.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* ClassName:Filter_
* Package:com.leon.springboot.servlet
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/26 20:48
* @Version: 1.0
*/
/*
* 1.@WebFilter注解表示被标识的是Filter,并注入到容器中
* 2.urlPatterns = {"/css/*","/images/*"},当请求/css/和/images/下的目录或资源时,会经过过滤器
* 3.在过滤器放行之后,拦截器是否放行,需要根据拦截器的规则来确定的
* 4.过滤器配置的urlPattern也会经过Spring Boot拦截器(要同时配置了相同的映射路径),在Servlet中匹配全部是[/单*],在Spring Boot中是[/双*]
* 5.
*/
@Slf4j
@WebFilter(urlPatterns = {"/css/*","/images/*"})
public class Filter_ implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("===Filter_init===");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//打印信息
log.info("===Filter_doFilter===");
//为了方便观察过滤器处理的资源,这里输出请求的uri
HttpServletRequest request = (HttpServletRequest)servletRequest;
log.info("请求的URI==={}",request.getRequestURI());
//放行,在实际开发中按照自己的实际业务来决定如何处理
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
log.info("===Filter_destroy===");
}
}
3.1.3 注入Listener
package com.leon.springboot.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* ClassName:Listener_
* Package:com.leon.springboot.servlet
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/26 21:19
* @Version: 1.0
*/
@Slf4j
@WebListener
public class Listener_ implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
log.info("---contextInitialized============");
}
public void contextDestroyed(ServletContextEvent sce) {
log.info("---contextDestroyed============");
}
}
3.2 使用RegistrationBean方式注入
3.2.1 注入Servlet
//使用RegistrationBean方式注入
//注入原生Servlet
@Bean
public ServletRegistrationBean servlet_(){
//创建原生Servlet对象
Servlet_ servlet = new Servlet_();
/*
* 1.把Servlet对象关联到ServletRegistrationBean对象
* 2."/servlet01","/servlet02" 就是注入的Servlet的url-pattern
* */
return new ServletRegistrationBean(servlet,"/servlet01","/servlet02");
}
3.2.2 注入Filter
//注入Filter
@Bean
public FilterRegistrationBean filter_(){
//创建原生的filter
Filter_ filter = new Filter_();
//创建FilterRegistrationBean对象,并将filter对象关联到FilterRegistrationBean对象
FilterRegistrationBean filterRegistrationBean =
new FilterRegistrationBean(filter);
//添加过滤的映射路径
filterRegistrationBean.addUrlPatterns("/css/*");
return filterRegistrationBean;
}
3.2.3 注入Listener
//注入Listener
@Bean
public ServletListenerRegistrationBean listener_(){
//创建一个listener对象
Listener_ listener = new Listener_();
//将Listener对象关联到ServletListenerRegistrationBean对象
return new ServletListenerRegistrationBean(listener);
}
4.请求Servlet为什么不会到拦截器
请求原生Servlet时,不会到达DispatcherServlet,因此也不会到达拦截器,因为原生Servlet和DispatcherServlet都是Servlet接口的实现类,属于同一级别
原因分析
注入的原生Servlet和DispatcherServlet都会存在Spring Boot容器中
说明
多个Servlet都能处理同一个映射路径时,精确优先原则或最长前缀匹配原则
优先级遵守:精确匹配 > 目录匹配 > 扩展名匹配 > /* > /
在Spring Boot中,去调用@Controller目标方法是按照DispatcherServlet分发匹配的机制
十九、内置Tomcat配置和切换
1. 基本介绍
- Spring Boot支持的WebServer:Tomcat,Jetty,Undertow
- Spring Boot 应用启动Web应用时,需要导入Web场景-导入Tomcat
- Spring Boot支持对Tomcat(也可以是Jetty、Undertow)的配置和切换
2. 内置Tomcat的配置
2.1 通过application.yml完成配置
server:
#配置监听的端口号
port: 9999
tomcat: #对Tomcat进行配置
threads:
max: 10 #表示设置最大的工作线程,默认是200
min-spare: 5 #表示设置最小工作线程,默认是10
accept-count: 200 #表示设置Tomcat启动的线程达到最大值,接受排队的请求数,默认是100
max-connections: 2000 #表示设置Tomcat最大并发数(连接数),默认是8192
connection-timeout: 10000 #表示设置Tomcat建立连接的超时时间,默认单位是:毫秒
#等等.....
2.2 通过类来配置Tomcat
package com.leon.springboot.config;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
/**
* ClassName:CustomizationBean
* Package:com.leon.springboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 15:43
* @Version: 1.0
*/
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
//设置服务端口
factory.setPort(9090);
}
}
3.切换Tomcat和Undertow
3.1 修改pom.xml文件
<dependencies>
<!--导入web项目场景启动器:会自动导入和web开发相关的依赖[库/jar]-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.3</version>
<!--如果想要切换其他的服务器,例如Undertow,需要将Tomcat场景排除-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--引入Thymeleaf-start:会进行默认配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--引入Undertow-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
3.2 注意细节
- 排除掉Tomcat后,使用相关Servlet的类都会报错,可以注销掉或者引入新的服务器
二十、数据库操作
1. JDBC+HikarDataSource
1.1 应用实例-需求
- 演示Spring Boot如何通过JDBC+HikariDataSource完成对Mysql操作
- 说明:HikariDataSource是目前市面上非常优秀的数据源,是Spring Boot2默认的数据源
1.2应用实例-代码实现
1.2.1 数据库操作
--创建furns_ssm
DROP DATABASE IF EXISTS spring_boot;
CREATE DATABASE spring_boot;
USE spring_boot;##创建家居表
CREATE TABLE furn(
`id`INT(11)PRIMARY KEY AUTO_INCREMENT, ##id
`name`VARCHAR(64)NOT NULL, ##家居名
`maker`VARCHAR(64)NOT NULL, ##厂商
`price`DECIMAL(11,2)NOT NULL, ##价格
`sales`INT(11)NOT NULL, ##销量
`stock`INT(11)NOT NULL, ##库存
`img_path` VARCHAR(256) NOT NULL ## 照片路径
);
-- 初始化家居数据
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL,'北欧风格小桌子','熊猫家居' , 180 , 666 , 7 ,'assets/images/product-image/1.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 简约风格小椅子 ' , ' 熊 猫 家 居 ' , 180 , 666 , 7 ,'assets/images/product-image/2.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 典 雅 风 格 小 台 灯 ' , ' 蚂 蚁 家 居 ' , 180 , 666 , 7 ,'assets/images/product-image/3.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , ' 温 馨 风 格 盆 景 架 ' , ' 蚂 蚁 家 居 ' , 180 , 666 , 7 ,'assets/images/product-image/4.jpg');
SELECT * FROM furn;
1.2.2 pom.xml操作
<!--进行数据库开发,引入JDBC开发场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!--引入mysql的驱动
使用版本仲裁
-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入测试场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
1.2.3 application.yml操作
#配置数据源
datasource:
#说明:有的版本没有设置useSSL=true启动项目会报红警告
url: jdbc:mysql://localhost:3306/spring_boot?useSSL=true&userUnicode=true&characterEncoding=UTF-8
#配置连接用户
username: root
#配置连接密码
password: root
#配置驱动类
driver-class-name: com.mysql.cj.jdbc.Driver
1.2.4 javaBean
package com.leon.springboot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* ClassName:Furn
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 16:25
* @Version: 1.0
*/
public class Furn {
private Integer id;
private String name;
private String maker;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath = "assets/images/product-image/1.jpg";
public Furn() {
}
public Furn(Integer id, String name, String maker, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.sales = sales;
this.stock = stock;
this.imgPath = imgPath;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
@Override
public String toString() {
return "Furn{" +
"id=" + id +
", name='" + name + '\'' +
", maker='" + maker + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}
1.2.5 测试
package com.leon.springboot.test;
import com.leon.springboot.bean.Furn;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.annotation.Resource;
import java.util.List;
/**
* ClassName:HikariDataSourceTest
* Package:com.leon.springboot.test
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 16:27
* @Version: 1.0
*/
/*
* 1.在Spring Boot项目中,使用测试类,需要引入测试场景以及在测试类上标注@SpringBootTest注解
* */
@SpringBootTest
public class HikariDataSourceTest {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void jdbcHikariDataSource(){
//创建一个BeanPropertyRowMapper对象对查询到的数据封装到JavaBean
BeanPropertyRowMapper<Furn> rowMapper =
new BeanPropertyRowMapper<>(Furn.class);
//查询所有数据
List<Furn> query = jdbcTemplate.query("SELECT * FROM furn", rowMapper);
//循环输出
for (Furn furn : query) {
System.out.println(furn);
}
//输出是什么数据源
System.out.println(jdbcTemplate.getDataSource().getClass());
}
}
2.整合Druid到Spring Boot
2.1 官方文档
- 官方网址:https://github.com/alibaba/druid
- 中文学习文档:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
- Spring Boot中Druid场景启动器学习网址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
2.2 基本介绍
- HikariCP:是目前市面上非常优秀的数据源,是Spring Boot2默认的数据源
- Druid:性能优秀,Druid提供性能卓越的连接池功能外,还集成了SQL监控,黑名单拦截等功能,强大的监控特性,通过Druid提供的监控功能,可以清楚知道连接池和SQL的工作情况,所以根据项目需要,需要掌握Druid和SpringBoot整合
- 整合Druid到Spring Boot有两种方式
- 自定义方式
- 引入starter方式
2.3 基本使用
2.3.1 pom.xml配置
<!--引入Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
2.3.2 创建配置类
package com.leon.springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ClassName:DruidDataSourceConfig
* Package:com.leon.springboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 17:04
* @Version: 1.0
*/
@Configuration
public class DruidDataSourceConfig {
/*
* 1.为什么注入DruidDataSource后默认的HikariDataSource就会失效?
* 1.1 @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
* 表示只有在Spring Boot容器中没有DataSource时才会注入HikariDataSource
* */
@ConfigurationProperties("spring.datasource")
@Bean
public DruidDataSource druidDataSource() {
/*
* 1.配置了@ConfigurationProperties("spring.datasource")就可以读取到application.yml的配置
* */
//创建一个DruidDataSource对象
DruidDataSource druidDataSource =
new DruidDataSource();
return druidDataSource;
}
}
2.4 Druid监控功能-SQL监控
2.4.1 需求
- 配置Druid的监控功能,包括SQL监控、SQL防火墙、Web应用、Session监控等
2.4.2 SQL监控
- 增加Druid监控功能
- 参考手册:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
2.4.2.1 配置Druid监控页和SQL监控
package com.leon.springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;
/**
* ClassName:DruidDataSourceConfig
* Package:com.leon.springboot.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 17:04
* @Version: 1.0
*/
@Configuration
public class DruidDataSourceConfig {
/*
* 1.为什么注入DruidDataSource后默认的HikariDataSource就会失效?
* 1.1 @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
* 表示只有在Spring Boot容器中没有DataSource时才会注入HikariDataSource
* */
@ConfigurationProperties("spring.datasource")
@Bean
public DruidDataSource druidDataSource() {
/*
* 1.配置了@ConfigurationProperties("spring.datasource")就可以读取到application.yml的配置
* */
//创建一个DruidDataSource对象
DruidDataSource druidDataSource =
new DruidDataSource();
try {
//添加SQL监控功能
druidDataSource.addFilters("stat");
} catch (SQLException e) {
throw new RuntimeException(e);
}
return druidDataSource;
}
//配置Druid的监控页功能
@Bean
public ServletRegistrationBean statViewServlet() {
//创建一个StatViewServlet对象
StatViewServlet statViewServlet =
new StatViewServlet();
//将StatViewServlet对象关联到ServletRegistrationBean对象中
ServletRegistrationBean servletRegistrationBean =
new ServletRegistrationBean(statViewServlet, "/druid/*");
//设置init-parameter
//设置用户
servletRegistrationBean.addInitParameter("loginUsername", "admin");
//设置密码
servletRegistrationBean.addInitParameter("loginPassword", "666");
return servletRegistrationBean;
}
}
2.5 Druid监控功能-Web关联监控
2.5.1 需求
- 配置Web关联控制配置:Web应用、URI监控
- 官方文档:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
2.5.2 Web关联监控配置:Web应用、URI监控
@Bean
public FilterRegistrationBean filterRegistrationBean() {
//创建一个WebStatFilter对象
WebStatFilter webStatFilter = new WebStatFilter();
//创建一个FilterRegistrationBean对象,然后将WebStatFilter关联
FilterRegistrationBean filterRegistrationBean =
new FilterRegistrationBean(webStatFilter);
//设置过滤映射路径
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
//设置排除哪里映射路径
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
2.6 Druid监控功能-SQL防火墙
2.6.1 需求
- 配置SQL防火墙
- 官方文档:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
2.6.2 SQL防火墙
/*
* 1.为什么注入DruidDataSource后默认的HikariDataSource就会失效?
* 1.1 @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
* 表示只有在Spring Boot容器中没有DataSource时才会注入HikariDataSource
* */
@ConfigurationProperties("spring.datasource")
@Bean
public DruidDataSource druidDataSource() {
/*
* 1.配置了@ConfigurationProperties("spring.datasource")就可以读取到application.yml的配置
* */
//创建一个DruidDataSource对象
DruidDataSource druidDataSource =
new DruidDataSource();
try {
//添加SQL监控功能,加入防火墙功能
druidDataSource.addFilters("stat,wall");
} catch (SQLException e) {
throw new RuntimeException(e);
}
return druidDataSource;
}
2.7 Druid监控功能-Session监控
2.7.1 需求
- 配置Session监控
- 官方文档:https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
2.7.2 Session监控
- 默认会开启不需要配置
2.8 Druid Spring Boot Stater
2.8.1 基本介绍
- 前面我们使用的是自己引入druid+配置类方式整合Druid和监控
- Druid Spring Boot Starter可以让程序员在Spring Boot项目中更加轻松集成Druid和监控
2.8.2 应用实例
2.8.2.1 需求
- 使用Druid Spring Boot Starter方式完成Druid集成和监控
2.8.2.2 具体实现
- application.yml文件配置
#配置Druid
druid:
#开启监控页功能
stat-view-servlet:
enabled: true
#设置登录账号
login-username: admin
#设置登录密码
login-password: 666
#关闭重置
reset-enable: false
#配置Web监控
web-stat-filter:
enabled: true
#设置过滤映射路径
url-pattern: /*
#设置排除哪里映射路径
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
#配置SQL监控
filter:
stat:
enabled: true
#设置慢查询时间条件,如果低于1秒则就是慢查询,单位时间:毫秒秒
slow-sql-millis: 1000
#启用慢查询日志
log-slow-sql: true
#配置SQL防火墙
wall:
enabled: true
#拦截配置
config:
#如果是删除语句就不执行
drop-table-allow: false
#如果是select * from furn 则不执行
select-all-column-allow: false
- pom.xml文件配置
<!--引入Druid spring boot starter-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
二十一、Spring Boot整合Mybatis
1. 代码实现
1.1 DruidDataSourceConfig.java
package com.leon.springboot.mybatis.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* ClassName:DruidDataSourceConfig
* Package:com.leon.springboot.mybatis.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 22:26
* @Version: 1.0
*/
@Configuration
public class DruidDataSourceConfig {
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
}
1.2 application.yml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_boot?useSSL=true&userUnicode=true&characterEncoding=UTF-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
#配置Mybatis
#说明:配置Mybatis的两种方式的选择:如果配置比较简单,就直接在application.yml中配置
#如果非常复杂,就使用传统的方式,在mybatis-config.xml文件中配置
mybatis:
#配置指定要扫描的Mapper.xml文件
mapper-locations: classpath:/mapper/*.xml
# #配置指定去扫描mybatis.xml文件,可以通过传统方式来配置mybatis
# config-location: classpath:mybatis-config.xml
#配置TypeAliases
type-aliases-package: com.leon.springboot.mybatis.bean
#配置日志输出
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#启用自动驼峰配置
map-underscore-to-camel-case: true
1.3 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置Mybatis自带的日志
1.注意这个标签要放在最上面
-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置别名-->
<typeAliases>
<!--
1.如果一个包下面有很多的类,可以直接引入包,这样该包下的所有类,可以直接使用类名
-->
<package name="com.leon.springboot.mybatis.bean"/>
</typeAliases>
</configuration>
1.4 FurnMapper.xml配置
<?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.leon.springboot.mybatis.dao.FurnMapper">
<select id="queryFurnById" resultType="Furn" parameterType="java.lang.Integer">
select * from furn where id = #{id};
</select>
</mapper>
1.5 Service
package com.leon.springboot.mybatis.service;
import com.leon.springboot.mybatis.bean.Furn;
/**
* ClassName:FurnService
* Package:com.leon.springboot.mybatis.service
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 11:01
* @Version: 1.0
*/
public interface FurnService {
public Furn queryFurnById(Integer id);
}
======================================================================================
package com.leon.springboot.mybatis.service.impl;
import com.leon.springboot.mybatis.bean.Furn;
import com.leon.springboot.mybatis.dao.FurnMapper;
import com.leon.springboot.mybatis.service.FurnService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* ClassName:FurnServiceImpl
* Package:com.leon.springboot.mybatis.service.impl
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 11:02
* @Version: 1.0
*/
@Service
public class FurnServiceImpl implements FurnService {
@Resource
private FurnMapper furnMapper;
@Override
public Furn queryFurnById(Integer id) {
return furnMapper.queryFurnById(id);
}
}
1.6 dao
package com.leon.springboot.mybatis.dao;
import com.leon.springboot.mybatis.bean.Furn;
import org.apache.ibatis.annotations.Mapper;
/**
* ClassName:FurnMapper
* Package:com.leon.springboot.mybatis.dao
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 10:33
* @Version: 1.0
*/
/*
*
* 1.在Mapper接口使用@Mapper注解,Spring Boot就会将Mapper接口扫描并注入到容器中
* */
@Mapper
public interface FurnMapper {
public Furn queryFurnById(Integer id);
}
1.7 javaBean
package com.leon.springboot.mybatis.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* ClassName:Furn
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 16:25
* @Version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Furn {
private Integer id;
private String name;
private String maker;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath = "assets/images/product-image/1.jpg";
}
1.8 controller层
package com.leon.springboot.mybatis.controller;
import com.leon.springboot.mybatis.bean.Furn;
import com.leon.springboot.mybatis.service.FurnService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
/**
* ClassName:FurnController
* Package:com.leon.springboot.mybatis.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 11:04
* @Version: 1.0
*/
@Controller
public class FurnController {
@Resource
private FurnService furnService;
@GetMapping("/getFurn")
@ResponseBody
public Furn getFurn(@RequestParam("id") int id) {
return furnService.queryFurnById(id);
}
}
1.9 注意事项和细节
解决时间格式以及时间偏移
在时间属性添加一个import com.fasterxml.jackson.annotation包下的注解:@JsonFormat
//通过@JsonFormat注解来解决时区问题 //GMT 就是格林尼治标准时间 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date birth;
二十二、Spring Boot整合Mybatis-plus
1. 官方文档
- 官网:https://baomidou.com/
2. 基本介绍
- Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生
- 强大的CRUD操作:内置通用Mapper、通用Service,通过少量配置即可以实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求
3.代码实现
3.1application.yml配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/spring_boot?useSSL=true&userUnicode=true&characterEncoding=UTF-8
#配置MybatisPlus
mybatis-plus:
#配置输出日志
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.2 javaBean类
package com.leon.springboot.mybatisplus.bean;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* ClassName:Furn
* Package:com.leon.springboot.bean
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/27 16:25
* @Version: 1.0
*/
/*
* 1.如果javaBean的类名和对应的数据库表名一致,则可以映射,@TableName注解可以省略
* 2.如果javaBean的类名和对应的数据库表名不一致,则不能映射,需要通过@TableName注解来指定
* */
@TableName(value = "furn")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Furn {
private Integer id;
private String name;
private String maker;
////通过@JsonFormat注解来解决时区问题
////GMT 就是格林尼治标准时间
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
//private Date birth;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath = "assets/images/product-image/1.jpg";
}
3.3 DruidDataSourceConfig.java
package com.leon.springboot.mybatisplus.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* ClassName:DruidDataSourceConfig
* Package:com.leon.springboot.mybatisplus.config
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 15:26
* @Version: 1.0
*/
@Configuration
public class DruidDataSourceConfig {
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
3.4 Controller
package com.leon.springboot.mybatisplus.controller;
import com.leon.springboot.mybatisplus.bean.Furn;
import com.leon.springboot.mybatisplus.service.FurnService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
/**
* ClassName:FurnController
* Package:com.leon.springboot.mybatis.controller
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 11:04
* @Version: 1.0
*/
@Controller
public class FurnController {
@Resource
private FurnService furnService;
@GetMapping("/getFurn")
@ResponseBody
public Furn getFurn(@RequestParam("id") int id) {
return furnService.getById(id);
}
@GetMapping("/all")
@ResponseBody
public List<Furn> getAllFurn() {
return furnService.list();
}
}
3.5 Mapper
package com.leon.springboot.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.leon.springboot.mybatisplus.bean.Furn;
import org.apache.ibatis.annotations.Mapper;
/**
* ClassName:FrunMapper
* Package:com.leon.springboot.mybatisplus.mapper
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 15:29
* @Version: 1.0
*/
/*
* 1.BaseMapper已经默认提供了很多的crud方法,可以直接使用
* 2.如果BaseMapper提供的方法不能满足业务需求,可以在添加新的方法,并在FurnMapper.xml进行配置
* */
//@Mapper
public interface FurnMapper extends BaseMapper<Furn> {
//自定义方法
}
3.6 Service
package com.leon.springboot.mybatisplus.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.leon.springboot.mybatisplus.bean.Furn;
/**
* ClassName:FurnService
* Package:com.leon.springboot.mybatisplus.service
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 15:36
* @Version: 1.0
*/
/*
* 1.传统方式在接口中定义方法或声明方法,然后在实现类中进行实现
* 2.在mybatis-plus中,只需要继承父接口IService,这个接口声明了很多crud的方法
* 3.如果提供的方法不能满足,则可以增加方法,然后在实现类中实现即可
* */
public interface FurnService extends IService<Furn> {
//自定义方法
}
======================================================================================
package com.leon.springboot.mybatisplus.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leon.springboot.mybatisplus.bean.Furn;
import com.leon.springboot.mybatisplus.mapper.FurnMapper;
import com.leon.springboot.mybatisplus.service.FurnService;
import org.springframework.stereotype.Service;
/**
* ClassName:FurnServiceImpl
* Package:com.leon.springboot.mybatisplus.service.impl
* Description:
*
* @Author: leon-->ZGJ
* @Create: 2024/8/28 15:40
* @Version: 1.0
*/
/*
* 1.传统方式:在实现类中直接进行实现 FurnService接口
* 2.在mybatis-plus中,开发Service实现类,需要继承ServiceImpl
* 3.ServiceImpl实现了IService接口,FurnService接口它继承了IService接口,因此这里可以认为
* FurnServiceImpl实现了FurnService接口,这样FurnServiceImpl就可以使用IService的接口方法,也可以理解成可以
* 使用FurnService的方法
* 4.如果FurnService接口中声明了其他方法或自定义了方法,依然需要再FurnServiceImpl中实现
*
* */
@Service
public class FurnServiceImpl extends ServiceImpl<FurnMapper, Furn> implements FurnService {
}
3.7 pom.xml
<?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>com.leon</groupId>
<artifactId>springboot-mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<!--导入Spring Boot 父工程-规定写法-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--导入web项目场景启动器:会自动导入和web开发相关的依赖[库/jar]-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.3</version>
</dependency>
<!--引入Mysql启动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入配置处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--引入lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--引入Test starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--引入Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
<!--引入Mybatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
</dependencies>
</project>
4. 注意事项和细节
- 如果javaBean的类名和对应的数据库表名一致,则可以映射,@TableName注解可以省略
- 如果javaBean的类名和对应的数据库表名不一致,则不能映射,需要通过@TableName注解来指定