SpringBoot学习总结
SpringBoot笔记
SpringBoot文档
- 官网: https://spring.io/projects/spring-boot
- 学习文档: https://docs.spring.io/spring-boot/docs/current/reference/html/
- 在线API: https://docs.spring.io/spring-boot/docs/current/api/
Spring Boot 是什么
- Spring Boot 可以轻松创建独立的、生产级的基于Spring 的应用程序
- Spring Boot 直接嵌入Tomcat、Jetty 或Undertow ,可以"直接运行" SpringBoot 应用程序
- Spring Boot有Web场景启动器,会自动导入和Web相关的依赖
Springboot/Srping/SpringMVC之间的关系
- 关系大概是: Spring Boot > Spring > Spring MVC
- Spring MVC 只是Spring 处理WEB 层请求的一个模块/组件, Spring MVC 的基石是Servlet
- Spring 的核心是IOC 和AOP, IOC 提供了依赖注入的容器, AOP 解决了面向切面编程
- Spring Boot 是为了简化开发, 推出的封神框架(约定优于配置[COC],简化了Spring 项目的配置流程), SpringBoot 包含很多组件/架,Spring 就是最核心的内容之一,也包含SpringMVC
- Spring 家族,有众多衍生框架和组件例如boot、security、jpa 等, 他们的基础都是Spring
约定优于配置
- 约定优于配置(Convention over Configuration/COC),又称按约定编程,是一种软件设计规范, 本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)
- 例如在模型中存在一个名为User 的类,那么对应到数据库会存在一个名为user 的表,只有在偏离这个约定时才需要做相关的配置(例如你想将表名命名为t_user 等非user 时才需要写关于这个名字的配置)
- 简单来说就是假如你所期待的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待时, 才需要对约定进行替换配置
- 约定其实就是一种规范,遵循了规范,那么就存在通用性,存在通用性,那么事情就会变得相对简单,程序员之间的沟通成本会降低,工作效率会提升,合作也会变得更加简单
依赖管理
-
spring-boot-starter-parent 还有父项目, 声明了开发中常用的依赖的版本号
-
并且进行自动版本仲裁, 即如果程序员没有指定某个依赖jar 的版本,则以父项目指
定的版本为准
如何修改版本仲裁?
修改版本仲裁-方法一
在pom.xml中显示的配置
修改版本仲裁-方法二
在spring-boot-starter-parent 的父项目 中修改即可
场景启动器Starter
- 开发中我们引入了相关场景的starter,这个场景中所有的相关依赖都引入进来了,比如我们做web 开发引入了,该starter 将导入与web 开发相关的所有包
- SpringBoot 也支持第三方starter。第三方starter 不要从spring-boot 开始,因为这是官方spring-boot 保留的命名方式的。第三方启动程序通常以项目名称开头。
- 也就是说:xxx-spring-boot-starter 是第三方为我们提供的简化开发的场景启动器
SpringBoot 自动配置了哪些?
- 自动配置Tomcat
- 自动配置SpringMVC
- 自动配置Web 常用功能: 比如字符过滤器
- 【默认扫描主程序所在包以及其子包】
如何修改默认扫描包结构?
设置 @SpringBootApplication(scanBasePackages="com.xxx")
如何修改SpringBoot默认配置?
在 resources/application.properties 文件中修改
- 默认配置最终会映射到某个类【属性类】
在application.properties 自定义配置
-
通过 @Value(${ })获取值
-
例如 my.website=http://www.baidu.com
@value(${my.website})//获取方式: private String bdUrl(){ }
-
SpringBoot 所有的自动配置功能都在spring-boot-autoconfigure 包里面
-
在SpringBoot 的自动配置包, 一般是XxxAutoConfiguration.java, 对应XxxxProperties.java
容器功能
Spring注解
Spring中传统的注解依然可以使用
@Configuration注解
案例一
@Configuration//表示这是一个配置类【类似spring的bean.xml配置文件】
//当一个类被 @Configuration 标识 ,该类【bean】自己本身也会注入到容器中
public class BeanConfig {//配置类可以有多个, 就和Spring 可以有多个beans.xnl配置文件是一个道理.
@Bean // 程序员可以通过@Bean 注解注入bean对象到容器
// 给容器添加组件,就是Monster bean;
// 方法名(monster01),作为 bean 的 id/名字 ;
// 返回值(Monster) 表示 注入类型 是 Monster 类型 ;
// new Monster(200,"nmw",500,"fmq"); 就是注入到容器的bean的具体信息
public Monster monster01() {
return new Monster(200, "nmw", 500, "fmq");
}
- 方法名(monster01),作为 bean 的 id/名字 ;
- 返回值(Monster) 表示 注入类型 是 Monster 类型 ;
- new Monster(200,"nmw",500,"fmq"); 就是注入到容器的bean的具体信息
案例二
- 可以通过在注解 @Bean 中设置属性 来指定bean的名字 @Bean("monster_nmw")
@Bean("monster_nmw")
// 也可以通过在注解 @Bean 中设置属性 来指定bean的名字 @Bean("monster_nmw")
public Monster monster02() {
return new Monster(200, "nmw", 500, "fmq");
}
案例三
- @Configuration(proxyBeanMethods = true) ---proxyBeanMethods:代理bean 的方法
@Configuration(proxyBeanMethods = true)
public class BeanConfig {
@Bean
//默认单例注入
//多例模式 添加注解: @Scope("prototype")
public Monster monster01() {
return new Monster(200, "nmw", 500, "fmq");
}
- proxyBeanMethods:代理bean 的方法
- Full(proxyBeanMethods = true) 【保证每个@Bean 方法被调用多少次返回的组件都是单实例的, 是代理方式】
- Lite(proxyBeanMethods = false)【每个@Bean 方法被调用多少次返回的组件都是新创建的, 是非代理方式】
- 特别说明: proxyBeanMethods 是在调用@Bean 方法才生效,因此,需要先获取BeanConfig 组件,再调用方法而不是直接通过SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean,proxyBeanMethods 值并没有生效
- 如何选择: 组件依赖必须使用Full 模式默认。如果不需要组件依赖使用Lite 模式 ;
- Lite 模式也称为轻量级模式,因为不检测依赖关系,运行速度快
@Import注解
@Configuration
@Import({Dog.class, Cat.class})//可以指定Class数组,可以注入指定类型的Bean ;默认组件 id 是 对应的类型的全类名
public class BeanConfig {}
- 可以指定Class数组,可以注入指定类型的Bean ;
- 默认组件 id 是 对应的类型的全类名(com.xxx.xx...)
@Conditional注解
- 条件装配,满足Conditional 指定的条件,才进行组件注入
- @Conditional 是一个根注解,下面有很多扩展注解
- 例如 @ConditionalOnBean
@Bean
@ConditionalOnBean(name = "monster_nmw")
//【条件装配】表示 只有在ioc容器中存在一个名字 为 monster_nmw 的bean,才能注入 dog01
public Dog dog01() {
return new Dog();
}
-
只有在ioc容器中存在一个名字 为 monster_nmw 的bean,才能注入 dog01
-
对name约束,对其他无关
-
有 其他扩展注解,对应不同的约束例如 :@ConditionalOnMissingBeanName(name="monster-nmw")
表示 没有name="monster-nmw" 才会注入容器
-
如果@ConditionalOnBean 表示在某个类上,则该类所有的组件都要受到约束
@ImportResource
- 作用:原生配置文件引入, 也就是可以直接导入Spring 传统的beans.xml ,可以认为是SpringBoot 对Spring 容器文件的兼容.
@Configuration
//导入bean.xml文件
@ImportResource("classpath:beans.xml")
public class BeanConfig2 {
}
将beans.xml导入到配置类
配置绑定@ConfigurationProperties
- 使用Java 读取到SpringBoot 核心配置文件application.properties 的内容,并且把它封装到JavaBean 中
1.在application.properties 文件中设置 属性 k-v 【属性需要和JavaBean的属性对应】
furn01.id=100
furn01.name=soft_chair!!
furn01.price=45678.9
2.在JavaBean上添加注解 @ConfigurationProperties(prefix = "furn01")
@Component
@ConfigurationProperties(prefix = "furn01")
public class Furn {
private Integer id;
private String name;
private Double price;
}
注意事项
- 另一种绑定方式:在配置类上加上 @EnableConfigurationProperties(Furn.class)
- 如果application.properties 有中文, 需要转成unicode 编码写入, 否则出现乱码
yaml介绍
- yaml以数据作为中心,不以标记语言为中心
- yaml仍然是一种标记语言,但是以数据作为中心
- yaml非常适合用来做以数据为中心的配置文件
yaml基本语法
- 形式为 key: value 【注意: 后有空格】
- 区分大小写
- 使用缩进表示层级关系
- 缩进推荐使用空格
- 缩进空格数不重要,只要相同层级的元素左对齐即可
- 字符串无需加引号
- yaml注释使用 #
yaml数据类型
-
字面量:date,boolean,string,number,null
-
对象:键值对的集合 map,hash,set,object
行内写法
k: {k1: v1,k2: v2,k3: v3}
换行写法
k: k1: v2 k2: v2 k3: v3
-
数组 array,list,queue
行内写法
k: [v1,v2,v3]
换行写法
k: -v2 -v2 -v3
注意事项:
- application.properties 的优先级比 application.yml 高,因此要避免前缀相同
- 字符串无需加引号,加引号不影响
静态资源的访问
基本介绍
-
只要静态资源放在 类路径下 :“classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/”
-
访问方式:默认 项目根路径/静态资源名
注意事项
-
静态资源访问原理:静态映射的 /**,也就是拦截所有请求。当请求进来,先判断Controller能否处理,不能处理的请求交给静态资源处理器,如果静态资源找不到,响应404页面
-
修改静态资源访问前缀【当与Controller路径冲突时】
在application.yml配置‘
spring: mvc: static-path-pattern: /zyres/** #静态资源前缀
-
修改静态资源访问路径
在application.yml配置‘
web: resources: static-locations: [classpath:/img/] #添加默认静态资源路径
原先的默认路径会被覆盖,需要再配
Rest风格请求
-
Rest风格请求的核心Filter:HiddenHttpMethodFilter,表单请求会被HiddenHttpMethodFilter 拦截,获取表单的 _method值,再判断是PUT/PELETE/PATCH
-
如果需要Springboot支持 页面表单的rest风格请求功能,需要在application.yml 启用filter功能,否则无效
spring: mvc: hiddenmethod: filter: enabled: true #页面表单支持rest风格
视图解析器的配置
spring:
mvc:
view:
suffix: .html
prefix: /zyres/ #视图解析器 <-这里如果修改了static-path-pattern 需要保持一致
- 如果配置了视图解析器,而controller的路径和视图路径一致时,会走视图解析器
接收参数注解
@PathVaiable
请求 :
<a href="/monster/100/king"></a>
@RequestMapping("/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 + ", name= " + name);
System.out.println("map= " + map);
return "success";
}
- @RequestMapping("/monster/{id}/{name}") 和 @PathVariable("id")相对应
- Integer id形参无所谓
- Map<String, String> map) ,map会存放所有 @PathVariable的 k-v
@RequestHeader
@RequestMapping("/requestHeader")
@ResponseBody
public String requestHeader(@RequestHeader("Host") String host,
@RequestHeader Map<String, String> header) {
System.out.println("host= " + host);
System.out.println("header= " + header);
return "success";
}
- @RequestHeader("Host")大小写无所谓
- @RequestHeader Map<String, String> header),map获取 @RequestHeader的 所有请求头
@RequestParam
请求:
<a href="/hi?name=zy&fruit=apple&fruit=pear"></a>
@GetMapping("/hi")
@ResponseBody
public String hi(@RequestParam("name") String userName,
@RequestParam("fruit") List<String> fruits,//一个参数名对应多个值时 用List接收
@RequestParam Map<String, String> params) {
System.out.println("userName= " + userName);
System.out.println("fruits= " + fruits);
System.out.println("params= " + params);
return "success";
}
- @RequestParam("fruit") List
fruits,//一个参数名对应多个值时 用List接收 - <a href="/hi?name=zy 中的name 要和 @RequestParam("name") 注解中对应
- String userName形参无所谓’
- @RequestParam Map<String, String> params ,map获取所有请求参数
- 但是k-v中k是唯一,所以获取相同参数只有一个值会被放入 map
@CookieValue
@GetMapping("/cookie")
@ResponseBody
public String cookie(@CookieValue(value = "cookie_key",required = false) String cookie_value,
@CookieValue(value = "username",required = false) Cookie cookie) {
System.out.println("cookie_value= "+cookie_value);
System.out.println("username= "+cookie.getName()+"-"+cookie.getValue());
return "success";
}
- 当形参为 String 类型,会取出cookie对应的value
- 当形参为 Cookie 类型,会取出封装好的cookie
@RequestBody
@PostMapping("/save")
@ResponseBody
public String save(@RequestBody String content) {
System.out.println("content= "+content);
return "success";
}
- 可以接收客户端 json格式数据
- 可以获取post请求体
@RequestAttribute
@PostMapping("/login")
@ResponseBody
public String login(@RequestAttribute String content) {
System.out.println("content= "+content);
return "success";
}
- 用来获取Request域中的属性
@SessionAttribute
-
用来获取Session域中的属性
-
用法和@RequestAttribute一致
复杂参数
- Springboot在响应客户端请求时,也支持复杂参数。例如:map,model等
- map,model数据会放在request域
- RedirectAttribute 重定向携带数据
//响应注册请求
@GetMapping("/register")
public String register(Map<String, Object> map,
Model mondel,
HttpServletResponse response) {
//将数据封装到map或model
map.put("user", "zy");
map.put("job", "java后端开发");
mondel.addAttribute("sal", "8000");
//请求转发
return "forward:/registerOk";
}
自定义对象参数-自动封装
- springboot在响应客户端请求时,也支持自定义对象参数
- 完成自动类型转换与格式化
- 支持级联封装
转换器
- Springboot在响应客户端请求时,将提供的数据封装成对象时,使用了内置的转换器
- springboot也支持自定义转换器,springboot提供了124内置转换器
自定义转换器
自定义转换器--- 方式一
@Configuration(proxyBeanMethods = false)//Lite模式
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
// 在addFormatters 中 添加自定义转换器String->Car
// 添加的自定义转换器 会注册到 converters 容器中
// converters 底层是 ConcurrentHashMap 内置124个转换器
registry.addConverter(new Converter<String, Car>() {
@Override
public Car convert(String s) {//s 就是传入的需要转换的数据
//加入自定义的转换的业务代码
if (!ObjectUtils.isEmpty(s)){
String[] split = s.split(",");
Car car = new Car();
car.setName(split[0]);
car.setPrice(Double.parseDouble(split[1]));
return car;
}
return null;
}
});
}
};
}
}
自定义转换器--- 方式二
@Configuration(proxyBeanMethods = false)//Lite模式
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
//方式二
//1.先创建自定义的转换器
Converter<String, Car> zyConverter = new Converter<String, Car>() {
@Override
public Car convert(String s) {//s 就是传入的需要转换的数据
//加入自定义的转换的业务代码
if (!ObjectUtils.isEmpty(s)) {
String[] split = s.split(",");
Car car = new Car();
car.setName(split[0]);
car.setPrice(Double.parseDouble(split[1]));
return car;
}
return null;
}
};
registry.addConverter(zyConverter);//添加
//这里可以添加多个自定义转换器,但如果泛型一样,后面的转换器会覆盖
//原因: 因为容器底层时CurrentHashMap,key是唯一,二key时泛型组合成的
}
};
}
}
Thymeleaf
Thymeleaf是什么?
- Thymeleaf 是一个跟Velocity,FreeMarker类似的模板引擎,可完全替代
- Thymeleaf是一个java类库,它是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层
Thymeleaf的优点
- 实现JSTL,OGNL表达式的效果,语法相似
- Thymeleaf 模板页面无需服务器渲染,也可以被浏览器运行,页面简洁
- Springboot支持FreeMarker,Thymeleaf,Velocity
Thymeleaf的缺点
- 并不是一个高性能引擎,适用于单体应用
- 高并发应用依然选择前后端分离更好
Thymeleaf机制
- Thymeleaf是服务器渲染技术,页面数据是在服务端进行渲染的
- 当用户请求页面,Thymeleaf模板引擎完成处理【服务端】,将页面返回
- Thymeleaf并非前后端分离,是在服务器端完成渲染
Thymeleaf语法
表达式
${...}
变量表达式,获取请求域,session域,对象等值
@{...}
链接表达式,生成链接
#{...}
消息表达式,获取国际化等值
~{...}
代码块表达式,类似jsp中include作用,映入公共片段
*{...}
选择变量表达式,获取上下文对象值
字面量
- 文本值:'zy','hello',...;数字:10,15.3,...;布尔值:true,false
- 空值: null
- 变量:name,age...
文本操作
字符串拼接 +
变量替换 |age=${age}|
运算符
-
数学运算 +,-,*,/,%
-
布尔运算 and,or
一元运算 !,not
-
比较运算
比较 ><,>=,<=(gt,lt,ge,le)
等式 ==,!=(eq,ne)
条件运算
- if-then:(if)?(then)
- if-then-else:(if)?(then):(else)
- Default : (value)?:(defaul value)
th属性
1)th:text:文本替换;
2)th:utext:支持html的文本替换。
3)th:value:属性赋值
4)th:each:遍历循环元素
5)th:if:判断条件,类似的还有th:unless,th:switch,th:case
6)th:insert:代码块引入,类似的还有th:replace,th:include,常用于公共代码块提取的场景
7)th:fragment:定义代码块,方便被th:insert引用
8)th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。
9)th:attr:设置标签属性,多个属性可以用逗号分隔
拦截器Interceptor
- springboot项目,拦截器是开发中常用的手段,登陆验证,性能检查,日志记录等
基本介绍
- 编写一个拦截器实现HandleInterceptor接口
- 拦截器注册到配置类【实现WebMvcConfiguration 的 addInterceptors】
- 也可以直接实现WebMvcConfigurer接口
- 指定拦截规则
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")//拦截所有
.excludePathPatterns("/","/login","/images/**","/upload.html","/register"
,"/css/**");//指定放行
}
};
}
}
注意事项
- URI和URL的区别:URI唯一标识一个资源【网站内部】;URL可以提供找到该资源的路径【带主机和端口】
- URI = /manage.html
- URL = http://localhost:8080/manage.html
异常处理
基本介绍
- 默认情况下,springboot提供 /error 处理所有错误的映射
- 当出现错误时,springboot底层会请求转发到 /error映射
- 比如当浏览器访问不存在的路径,响应一个"whitelable"错误视图,以HTML格式呈现给用户
- springboot底层默认由DefaultErrorViewResolver
拦截器和过滤器的区别
拦截器和过滤器使用范围不同
- 过滤器实现的是javax.servlet.Filter接口,而这个接口实在Servlet规范中定义的,也就是说过滤器Filter的使用要依赖与Tomcat等容器,Filter只能在Web程序使用
- 拦截器是一个Spring组件,并由Spring容器管理,并不依赖Tomcat容器,是可以单独使用的,不仅能应用在web工程,还能用于Application等程序
拦截器和过滤器触发时机不同
-
过滤器Filter是在请求进入容器后,但在进入Servlet之前进行预处理,请求结果实在Servlet处理完以后。
-
拦截器实在请求进入Servlet后,进入Controller之前进行预处理,Controller渲染了对应的视图后结束
-
请求
↓
Tomcat
↓
Filter
↓
Servlet
↓
Interceptor
↓
Controller
↓
-
过滤器不会处理 请求转发,拦截器会处理请求转发
自定义异常页面
- 如果是用模板引擎,需要将自定义异常的页面放在 /templates/error/路径
- 如果没有使用模板引擎,就放在静态资源路径下【四个默认】,"/resource/static/errpr/路径"
- “classpath:/META-INF/resources/error/路径","classpath:/resources/error/路径", "classpath:/static/error/路径", "classpath:/public/error/路径”
- 先会根据状态码精确查找 (例如 404.html),若找不到,会去找 4xx.html
- 如果都没有,会new 一个默认页面返回
- 前端可以使用 th:text="${status}"取出状态码
全局异常
基本介绍
- @ControllerAdvice + @ExceptionHandler 处理全局异常
- 底层是ExceptionHandlerExceptionResolver支持的
- 作用:当发生异常时,不使用默认异常机制匹配xxx.html,而是通过全局异常机制,显示指定的错误页面
- 对比:自定义异常是根据状态码来决定 返回的html页面;全局异常是根据Java异常类型来决定 返回的html页面
如何获取到异常发生的地方?
用HandlerMethod在形参上获取
具体实现
@ControllerAdvice //标识全局异常处理器/对象 ,GlobalExceptionHandler 会被注入到spring容器
@Slf4j
public class GlobalExceptionHandler {
//编写方法处理指定的异常
//Exception e 表示异常发生后,传递的异常对象
//Model model 可以存放错误信息到model,再放入到request域,带到目标页面显示
// Class<? extends Throwable>[] value() default {};
@ExceptionHandler({ArithmeticException.class, NullPointerException.class,AccessException.class})
public String handleAritException(Exception e, Model model){
log.info("异常信息={}",e.getMessage());
model.addAttribute("msg",e);
return "/error/global";
}
}
- Exception e 表示异常发生后,传递的异常对象
- Model model 可以存放错误信息到model,再放入到request域,带到目标页面显示
- @ControllerAdvice //标识全局异常处理器/对象 ,GlobalExceptionHandler 会被注入到spring容器
- @ExceptionHandler({ArithmeticException.class, NullPointerException.class,AccessException.class})指定捕获哪些异常
自定义异常
基本介绍
- springboot提供的异常不满足需求,程序员可以自定义异常
- @ResponseStatus + 自定义异常
- 底层是 ResponseStatusExceptionResolver ,底层调用response.sendError
- 当抛出自定义异常后,仍然会根据状态码去匹配xxx.html显示
- 可以将自定义异常放在全局异常处理器处理,但仍然走全局异常
具体实现
//自定义异常,需要继承Exception (编译异常)/RuntionException(运行异常)
// 一般继承RuntionException(运行异常)
@ResponseStatus(value = HttpStatus.BAD_GATEWAY)
public class AccessException extends RuntimeException{
//提供构造器,可以指定信息
public AccessException(String message){
super(message);
}
public AccessException(){
}
}
Springboot文件上传
前端 enctype="multipart/form-data"
<form action="#" th:action="@{/register}" method="post" 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>
类型一 将上传的文件保存到指定目录
@Controller
@Slf4j
public class UploadController {
//处理请求转发-用户注册 可以完成文件上传
@GetMapping("/upload.html")
public String upload() {
return "upload";
}
//处理用户注册
@PostMapping("/register")
@ResponseBody
public String register(@RequestParam("name") String name,
@RequestParam("email") String email,
@RequestParam("age") Integer age,
@RequestParam("job") String job,
@RequestParam("header") MultipartFile header,
@RequestParam("photos") MultipartFile[] photos) throws IOException {
//1.将上传的文件保存到指定目录
if (!header.isEmpty()) {
String originalFilename = header.getOriginalFilename();
File file = new File("d:\\temp_upload\\" + originalFilename);
header.transferTo(file);
}
//处理图片
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
File file = new File("d:\\temp_upload\\" + originalFilename);
photo.transferTo(file);
}
}
}
return "注册用户成功/文件上传成功";
}
}
类型二 将上传的文件保存到指定目录
@Controller
@Slf4j
public class UploadController {
//处理请求转发-用户注册 可以完成文件上传
@GetMapping("/upload.html")
public String upload() {
return "upload";
}
//处理用户注册
@PostMapping("/register")
@ResponseBody
public String register(@RequestParam("name") String name,
@RequestParam("email") String email,
@RequestParam("age") Integer age,
@RequestParam("job") String job,
@RequestParam("header") MultipartFile header,
@RequestParam("photos") MultipartFile[] photos) throws IOException {
//2.动态创建目录并保存
// D:\zy_springboot\springboot-thymeleaf-usersys\target\classes\static\images\xupload\\
//获取当前年月日【解决分目录】
LocalDate now = LocalDate.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String format = now.format(dtf);
System.out.println("format= "+format);
String path = ResourceUtils.getURL("classpath:").getPath();
path = path + "static/images/upload/"+format+"/";
File file = new File(path);
if (!file.exists()) {
//目录不存在 就创建
file.mkdirs();
}
//【解决文件覆盖问题】
String prefix = UUID.randomUUID().toString();
prefix = prefix +"_"+ System.currentTimeMillis()+"_";
if (!header.isEmpty()) {
String originalFilename = header.getOriginalFilename();
header.transferTo( new File(file.getAbsolutePath()+"/"+prefix+originalFilename));
}
//处理图片
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo( new File(file.getAbsolutePath()+"/"+prefix+originalFilename));
}
}
}
return "注册用户成功/文件上传成功";
}
}
Servlet/Filter/Listener在springboot如何注入
基本介绍
- 考虑到兼容,springboot支持将Servlet/Filter/Listener注入spring容器,成为spring 的 bean
- 即 springboot开放了和原生web组件的兼容
- 如果要使用原生web组件的开发,需要在主启动器添加注解@ServletComponentScan(basePackages = "com.xxx.xx")指定要扫描的包,才会注入容器
- Servlet/Filter/Listener在springboot注入有两种方式,
- 方式一是通过@WebServlet,@WebFilter,@WebListener注解注入
- 方式二是通过RegistrationBean方式注入
通过@WebServlet,@WebFilter,@WebListener注解注入
注入Servlet 【@WebServlet】
//有@WebServlet标识 表示: 将 Servlet_ 对应的bean 注入到容器
//@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_~");
}
}
- 注意:注入的原生的Servlet 不会被 SpringBoot拦截器 拦截 ---原因走原生的Servlet处理,而不是springboot的DispatchServlet
- 原因:
- 当多个Servlet都能处理同一层路径,精确优先/最长前缀匹配原则,所以当请求/servlet时,会直接匹配注入的servlet
注入Filter【@WebFilter】
@WebFilter(urlPatterns = {"/css/*","/images/*"})
public class Filter_ implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("hello Filter_~");
HttpServletRequest request = (HttpServletRequest)servletRequest ;
System.out.println("URI= "+request.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("destroy...");
}
}
- 过滤器会被SpringBoot拦截器 拦截
注入Listener 【@WebListener 】
@WebListener
public class Listener_ implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
system.out.println("项目初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContextListener.super.contextDestroyed(sce);
}
}
通过RegistrationBean方式注入
RegistrationBean方式-注入Servlet
// //注入Servlet
@Bean
public ServletRegistrationBean servlet_(){
Servlet_ servlet = new Servlet_();//创建原生的Servlet对象
//把servlet 和 ServletRegistrationBean 关联
//"servlet01","servlet02" 是url-pattern
return new ServletRegistrationBean(servlet,"/servlet01","/servlet02");
}
RegistrationBean方式-注入Filter
//注入Filter
@Bean
public FilterRegistrationBean filter_(){
Filter_ filter = new Filter_();//创建原生的filter对象
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(filter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*","/images/*"));
return filterRegistrationBean;
}
RegistrationBean方式-注入Listener
//注入Listener
@Bean
public ServletListenerRegistrationBean listener_(){
Listener_ listener = new Listener_();//创建原生的listener对象
ServletListenerRegistrationBean servletListenerRegistrationBean =
new ServletListenerRegistrationBean(listener);
return servletListenerRegistrationBean;
}
内置的WebServlet【服务器】切换
基本介绍
- Springboot支持的WebServlet:Tomcat,Jetty,Undertom
- Springboot启动,web场景启动器自动导入tomcat
- 支持切换WebServlet
如何配置内置的tomcat
-
可以通过application.yml完成配置【配置信息和ServletProperties.java关联】
-
通过配置类进行配置
-
@Component public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(8080);//配置端口 } }
切换WebServlet
-
修改pom.xml文件,删除tomcat依赖,引入undertow
-
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--排除tomcat starter--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
-
引入undertow
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
数据库操作
- springboot默认使用JDBC+HikariDataSource
如何使用默认JDBC+HikariDataSource进行数据库操作
-
引入data-jdbc starter
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency>
-
springboot并不知道具体要操作哪一种数据库,需要在pom.xml中指定导入数据库驱动,并指定对应版本
-
<!-- 引入mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
-
在application.yml中配置数据源
-
spring: datasource: url: jdbc:mysql://localhost:3306/furn_ssm2?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username: root password: zy driver-class-name: com.mysql.jdbc.Driver
-
完成测试
-
@SpringBootTest//需要引入 spring-boot-starter-test 依赖 public class ApplicationTests { @Resource private JdbcTemplate jdbcTemplate; @Test public void contextLoads(){ BeanPropertyRowMapper<Furn> rowMapper = new BeanPropertyRowMapper<>(Furn.class); List<Furn> furns = jdbcTemplate.query("SELECT * FROM `furn`", rowMapper); for (Furn furn : furns) { System.out.println("furn= "+furn); } System.out.println(jdbcTemplate.getDataSource().getClass()); } }
整合Druid 和 springboot
基本介绍
-
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。
-
Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
-
可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
-
数据库密码加密。DruidDruiver和DruidDataSource都支持PasswordCallback。
-
SQL执行日志,,监控你应用的数据库访问情况。
整合Druid 和 springboot【2种方式】
方式一 自定义方式整合
-
引入druid依赖
-
<!--引入druid依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.17</version> </dependency>
-
在配置类注入DataSource
-
@Configuration public class DruidDataSourceConfig { //编写方法注入 DruidDataSource @ConfigurationProperties(prefix = "spring.datasource")//就可以和 application.yml相关联【DataSource】 @Bean public DataSource dataSource() throws SQLException { DruidDataSource druidDataSource = new DruidDataSource(); //加入监控功能 druidDataSource.setFilters("stat,wall"); return druidDataSource; } //配置druid的监控功能 @Bean public ServletRegistrationBean statViewServlet() { StatViewServlet statViewServlet = new StatViewServlet(); ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*"); //设置init-parameter [设置用户名和密码] registrationBean.addInitParameter("loginUsername", "zy"); registrationBean.addInitParameter("loginPassword", "666666"); return registrationBean; } //配置 WebStatFilter 用于Web的监控功能 @Bean public FilterRegistrationBean webStatFilter() { WebStatFilter webStatFilter = new WebStatFilter(); FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>(webStatFilter); //默认对所有请求进行监控 registrationBean.setUrlPatterns(Arrays.asList("/*")); //排除指定的url registrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return registrationBean; } }
方式二 引入druid starter方式整合
-
引入druid starter
-
<!-- 引入druid starter--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
-
在application.yml中配置druid和监控功能
-
#配置druid 和 监控功能 druid: stat-view-servlet: enabled: true login-username: jack 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: slow-sql-millis: 1000 #慢查询 log-slow-sql: true enabled: true #开启防火墙监控 wall: enabled: true config: drop-table-allow: false select-all-column-allow: false
为什么注入自己的DataSource,默认的HikariDataSource会失效?
原因: 因为默认的数据源是根据@ConditionalOnMissingBean判断,如果自己没有配,默认使用HikariDataSource;如果配置了,就用配置了的DataSource。
Springboot整合MyBatis
-
需要在application.yml种指定mapper.xml扫描的路径
-
mybatis: # 指定要扫描的 Xxxmapper.xml mapper-locations: classpath:mapper/*.xml
-
在Mapper接口上可以通过添加@Mapper注解,注入容器
-
@Mapper //@Mapper就会扫描 并 将接口对象注入容器 public interface MonsterMapper { //根据id 返回monster Monster getMonsterById(Integer id); }
-
在application.yml中,可以通过 config-location 可以指定 mybatis-config.xml,来进行传统mybatis方式的配置
-
mybatis: config-location: classpath:mybatis-config.xml
-
也可以直接在application.yml中配置mybatis【推荐】
-
mybatis: # 配置类型别名 type-aliases-package: com.zy88.springboot.bean configuration: # 配置日志 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
如果配置内容少,建议直接在application.yml中配置mybatis,如果内容很多,才考虑单独做一个 mybatis-config.xml
Springboot整合MyBatis-Plus
基本介绍
- MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
- 内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
Springboot整合MyBatis-Plus
-
需要引入MyBatis-Plus starter【在meven仓库中】
-
<!-- 引入Mybatis-Plus starter--> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency>
-
在application.yml配置数据源
-
server: port: 9090 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hsp_mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username: root password: zy
-
在配置类注入DruidDataSource
-
@Configuration public class DruidDataSourceConfig { //编写方法注入 DruidDataSource @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); return druidDataSource; } }
整合mapper【MyBatis-Plus与MyBatis的不同】
- mapper接口需要继承BeseMapper<>
@Mapper
public interface MonsterMapper extends BaseMapper<Monster> {
//自定义方法
int insertSelective(Monster monster);
}
- BaseMapper 已经默认提供了很多crud方法,可以直接使用
- 如果 BaseMapper 提供的方法不能满足业务需求,我们可以开发新的方法 ,并在MonsterMapper.xml配置
整合service【MyBatis-Plus与MyBatis的不同】
- service需要继承父接口 ISercice<>
public interface MonsterService extends IService<Monster> {
//自定义方法
}
- IService接口声明了很多方法,crud
- 如果不能满足需求,可以再声明需要的方法,然后在实现类实现即可
- 注意:MyBatis-Plus开发实现类 需要继承 ServletImpl
@Service
public class MonsterServiceImpl
extends ServiceImpl<MonsterMapper, Monster>
implements MonsterService{
}
指定扫描某个包下的所有mapper接口
在主启动器上添加@MapperScan(basePackages = {"com.xxx.xxx.xx"})
@MapperScan(basePackages = {"com.zy88.springboot.mybatisplus.mapper"})
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class,args);
}
}
@TableName作用
- 默认情况:如果类名首字母小写和表名不一致,可以映射
- 当类名首字母小写和表名不一致时,通过@TableName,在实体类上加@TableName("表名")进行映射
@TableName("monster_")
public class Monster {
private Integer id;
private Integer age;
private String name;
private String email;
//解决时区问题
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")//GMT 是格林尼治标准时间 ,如果需要时分秒加上 HH:mm:ss
private Date birthday;
private double salary;
private Integer gender;
}
- 为了开发方便可以添加MyBatisX插件
本文学习内容来自韩顺平老师的课程
仅供个人参考学习