SpringBoot学习总结

SpringBoot笔记

SpringBoot文档

  1. 官网: https://spring.io/projects/spring-boot
  2. 学习文档: https://docs.spring.io/spring-boot/docs/current/reference/html/
  3. 在线API: https://docs.spring.io/spring-boot/docs/current/api/

Spring Boot 是什么

  1. Spring Boot 可以轻松创建独立的、生产级的基于Spring 的应用程序
  2. Spring Boot 直接嵌入Tomcat、Jetty 或Undertow ,可以"直接运行" SpringBoot 应用程序
  3. Spring Boot有Web场景启动器,会自动导入和Web相关的依赖

Springboot/Srping/SpringMVC之间的关系

  1. 关系大概是: Spring Boot > Spring > Spring MVC
  2. Spring MVC 只是Spring 处理WEB 层请求的一个模块/组件, Spring MVC 的基石是Servlet
  3. Spring 的核心是IOC 和AOP, IOC 提供了依赖注入的容器, AOP 解决了面向切面编程
  4. Spring Boot 是为了简化开发, 推出的封神框架(约定优于配置[COC],简化了Spring 项目的配置流程), SpringBoot 包含很多组件/架,Spring 就是最核心的内容之一,也包含SpringMVC
  5. Spring 家族,有众多衍生框架和组件例如boot、security、jpa 等, 他们的基础都是Spring

约定优于配置

  1. 约定优于配置(Convention over Configuration/COC),又称按约定编程,是一种软件设计规范, 本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)
  2. 例如在模型中存在一个名为User 的类,那么对应到数据库会存在一个名为user 的表,只有在偏离这个约定时才需要做相关的配置(例如你想将表名命名为t_user 等非user 时才需要写关于这个名字的配置)
  3. 简单来说就是假如你所期待的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待时, 才需要对约定进行替换配置
  4. 约定其实就是一种规范,遵循了规范,那么就存在通用性,存在通用性,那么事情就会变得相对简单,程序员之间的沟通成本会降低,工作效率会提升,合作也会变得更加简单

依赖管理

  1. spring-boot-starter-parent 还有父项目, 声明了开发中常用的依赖的版本号

  2. 并且进行自动版本仲裁, 即如果程序员没有指定某个依赖jar 的版本,则以父项目指
    定的版本为准


如何修改版本仲裁?

修改版本仲裁-方法一

在pom.xml中显示的配置

修改版本仲裁-方法二

在spring-boot-starter-parent 的父项目 中修改即可

场景启动器Starter

  1. 开发中我们引入了相关场景的starter,这个场景中所有的相关依赖都引入进来了,比如我们做web 开发引入了,该starter 将导入与web 开发相关的所有包
  2. SpringBoot 也支持第三方starter。第三方starter 不要从spring-boot 开始,因为这是官方spring-boot 保留的命名方式的。第三方启动程序通常以项目名称开头。
  3. 也就是说:xxx-spring-boot-starter 是第三方为我们提供的简化开发的场景启动器

SpringBoot 自动配置了哪些?

  1. 自动配置Tomcat
  2. 自动配置SpringMVC
  3. 自动配置Web 常用功能: 比如字符过滤器
  4. 【默认扫描主程序所在包以及其子包】

如何修改默认扫描包结构?

设置 @SpringBootApplication(scanBasePackages="com.xxx")

如何修改SpringBoot默认配置?

在 resources/application.properties 文件中修改

  • 默认配置最终会映射到某个类【属性类】

在application.properties 自定义配置

  1. 通过 @Value(${ })获取值

  2. 例如 my.website=http://www.baidu.com

     	@value(${my.website})//获取方式:
    	private String bdUrl(){
    	
    	}
    
  3. SpringBoot 所有的自动配置功能都在spring-boot-autoconfigure 包里面

  4. 在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");
    }
  1. 方法名(monster01),作为 bean 的 id/名字 ;
  2. 返回值(Monster) 表示 注入类型 是 Monster 类型 ;
  3. 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");
    }
  1. proxyBeanMethods:代理bean 的方法
  2. Full(proxyBeanMethods = true) 【保证每个@Bean 方法被调用多少次返回的组件都是单实例的, 是代理方式】
  3. Lite(proxyBeanMethods = false)【每个@Bean 方法被调用多少次返回的组件都是新创建的, 是非代理方式】
  4. 特别说明: proxyBeanMethods 是在调用@Bean 方法才生效,因此,需要先获取BeanConfig 组件,再调用方法而不是直接通过SpringBoot 主程序得到的容器来获取bean, 注意观察直接通过ioc.getBean() 获取Bean,proxyBeanMethods 值并没有生效
  5. 如何选择: 组件依赖必须使用Full 模式默认。如果不需要组件依赖使用Lite 模式 ;
  6. Lite 模式也称为轻量级模式,因为不检测依赖关系,运行速度快

@Import注解

@Configuration
@Import({Dog.class, Cat.class})//可以指定Class数组,可以注入指定类型的Bean ;默认组件 id 是 对应的类型的全类名
public class BeanConfig {}
  1. 可以指定Class数组,可以注入指定类型的Bean ;
  2. 默认组件 id 是 对应的类型的全类名(com.xxx.xx...)

@Conditional注解

  1. 条件装配,满足Conditional 指定的条件,才进行组件注入
  2. @Conditional 是一个根注解,下面有很多扩展注解
  • 例如 @ConditionalOnBean
@Bean
    @ConditionalOnBean(name = "monster_nmw")
    //【条件装配】表示 只有在ioc容器中存在一个名字 为 monster_nmw 的bean,才能注入 dog01
    public Dog dog01() {
        return new Dog();
    }
  1. 只有在ioc容器中存在一个名字 为 monster_nmw 的bean,才能注入 dog01

  2. 对name约束,对其他无关

  3. 有 其他扩展注解,对应不同的约束例如 :@ConditionalOnMissingBeanName(name="monster-nmw")

    表示 没有name="monster-nmw" 才会注入容器

  4. 如果@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;
}

注意事项

  1. 另一种绑定方式:在配置类上加上 @EnableConfigurationProperties(Furn.class)
  2. 如果application.properties 有中文, 需要转成unicode 编码写入, 否则出现乱码

yaml介绍

  1. yaml以数据作为中心,不以标记语言为中心
  2. yaml仍然是一种标记语言,但是以数据作为中心
  3. yaml非常适合用来做以数据为中心的配置文件

yaml基本语法

  1. 形式为 key: value 【注意: 后有空格】
  2. 区分大小写
  3. 使用缩进表示层级关系
  4. 缩进推荐使用空格
  5. 缩进空格数不重要,只要相同层级的元素左对齐即可
  6. 字符串无需加引号
  7. yaml注释使用 #

yaml数据类型

  1. 字面量:date,boolean,string,number,null

  2. 对象:键值对的集合 map,hash,set,object

    行内写法

    k: {k1: v1,k2: v2,k3: v3}
    

    换行写法

    k:
     k1: v2
     k2: v2
     k3: v3
    
  3. 数组 array,list,queue

    行内写法

    k: [v1,v2,v3]
    

    换行写法

    k:
     -v2
     -v2
     -v3
    

注意事项:

  1. application.properties 的优先级比 application.yml 高,因此要避免前缀相同
  2. 字符串无需加引号,加引号不影响

静态资源的访问

基本介绍

  1. 只要静态资源放在 类路径下 :“classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/”

  2. 访问方式:默认 项目根路径/静态资源名

注意事项

  1. 静态资源访问原理:静态映射的 /**,也就是拦截所有请求。当请求进来,先判断Controller能否处理,不能处理的请求交给静态资源处理器,如果静态资源找不到,响应404页面

  2. 修改静态资源访问前缀【当与Controller路径冲突时】

    在application.yml配置‘

    spring:
      mvc:
        static-path-pattern: /zyres/** #静态资源前缀
    
  3. 修改静态资源访问路径

    在application.yml配置‘

      web:
        resources:
          static-locations: [classpath:/img/]  #添加默认静态资源路径
    

    原先的默认路径会被覆盖,需要再配

Rest风格请求

  1. Rest风格请求的核心Filter:HiddenHttpMethodFilter,表单请求会被HiddenHttpMethodFilter 拦截,获取表单的 _method值,再判断是PUT/PELETE/PATCH

  2. 如果需要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";
    }
  1. @RequestMapping("/monster/{id}/{name}") 和 @PathVariable("id")相对应
  2. Integer id形参无所谓
  3. 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";
    }
  1. @RequestHeader("Host")大小写无所谓
  2. @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";
    }
  1. ​ @RequestParam("fruit") List fruits,//一个参数名对应多个值时 用List接收
  2. <a href="/hi?name=zy 中的name 要和 @RequestParam("name") 注解中对应
  3. String userName形参无所谓’
  4. @RequestParam Map<String, String> params ,map获取所有请求参数
  5. 但是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";
    }
  1. 当形参为 String 类型,会取出cookie对应的value
  2. 当形参为 Cookie 类型,会取出封装好的cookie

@RequestBody

@PostMapping("/save")
@ResponseBody
public String save(@RequestBody String content) {
    System.out.println("content= "+content);
    return "success";
}
  1. 可以接收客户端 json格式数据
  2. 可以获取post请求体

@RequestAttribute

   @PostMapping("/login")
    @ResponseBody
    public String login(@RequestAttribute String content) {
        System.out.println("content= "+content);
        return "success";
    }
  1. 用来获取Request域中的属性

@SessionAttribute

  • 用来获取Session域中的属性

  • 用法和@RequestAttribute一致

复杂参数

  1. Springboot在响应客户端请求时,也支持复杂参数。例如:map,model等
  2. map,model数据会放在request域
  3. 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";
    }

自定义对象参数-自动封装

  1. springboot在响应客户端请求时,也支持自定义对象参数
  2. 完成自动类型转换与格式化
  3. 支持级联封装

转换器

  1. Springboot在响应客户端请求时,将提供的数据封装成对象时,使用了内置的转换器
  2. 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是什么?

  1. Thymeleaf 是一个跟Velocity,FreeMarker类似的模板引擎,可完全替代
  2. Thymeleaf是一个java类库,它是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层

Thymeleaf的优点

  1. 实现JSTL,OGNL表达式的效果,语法相似
  2. Thymeleaf 模板页面无需服务器渲染,也可以被浏览器运行,页面简洁
  3. Springboot支持FreeMarker,Thymeleaf,Velocity

Thymeleaf的缺点

  1. 并不是一个高性能引擎,适用于单体应用
  2. 高并发应用依然选择前后端分离更好

Thymeleaf机制

  1. Thymeleaf是服务器渲染技术,页面数据是在服务端进行渲染的
  2. 当用户请求页面,Thymeleaf模板引擎完成处理【服务端】,将页面返回
  3. Thymeleaf并非前后端分离,是在服务器端完成渲染

Thymeleaf语法

表达式

${...} 变量表达式,获取请求域,session域,对象等值

@{...} 链接表达式,生成链接

#{...} 消息表达式,获取国际化等值

~{...} 代码块表达式,类似jsp中include作用,映入公共片段

*{...} 选择变量表达式,获取上下文对象值

字面量
  1. 文本值:'zy','hello',...;数字:10,15.3,...;布尔值:true,false
  2. 空值: null
  3. 变量:name,age...
文本操作

字符串拼接 +

变量替换 |age=${age}|

运算符
  1. 数学运算 +,-,*,/,%

  2. 布尔运算 and,or

    一元运算 !,not

  3. 比较运算

    比较 ><,>=,<=(gt,lt,ge,le)

    等式 ==,!=(eq,ne)

条件运算
  1. if-then:(if)?(then)
  2. if-then-else:(if)?(then):(else)
  3. 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项目,拦截器是开发中常用的手段,登陆验证,性能检查,日志记录等

基本介绍

  1. 编写一个拦截器实现HandleInterceptor接口
  2. 拦截器注册到配置类【实现WebMvcConfiguration 的 addInterceptors】
  3. 也可以直接实现WebMvcConfigurer接口
  4. 指定拦截规则
@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/**");//指定放行
            }
        };
    }

}

注意事项

  1. URI和URL的区别:URI唯一标识一个资源【网站内部】;URL可以提供找到该资源的路径【带主机和端口】
  2. URI = /manage.html
  3. URL = http://localhost:8080/manage.html

异常处理

基本介绍

  1. 默认情况下,springboot提供 /error 处理所有错误的映射
  2. 当出现错误时,springboot底层会请求转发到 /error映射
  3. 比如当浏览器访问不存在的路径,响应一个"whitelable"错误视图,以HTML格式呈现给用户
  4. springboot底层默认由DefaultErrorViewResolver

拦截器和过滤器的区别

拦截器和过滤器使用范围不同

  1. 过滤器实现的是javax.servlet.Filter接口,而这个接口实在Servlet规范中定义的,也就是说过滤器Filter的使用要依赖与Tomcat等容器,Filter只能在Web程序使用
  2. 拦截器是一个Spring组件,并由Spring容器管理,并不依赖Tomcat容器,是可以单独使用的,不仅能应用在web工程,还能用于Application等程序

拦截器和过滤器触发时机不同

  1. 过滤器Filter是在请求进入容器后,但在进入Servlet之前进行预处理,请求结果实在Servlet处理完以后。

  2. 拦截器实在请求进入Servlet后,进入Controller之前进行预处理,Controller渲染了对应的视图后结束

  3. 请求

    Tomcat

    Filter

    Servlet

    Interceptor

    Controller

  4. 过滤器不会处理 请求转发,拦截器会处理请求转发

自定义异常页面

  1. 如果是用模板引擎,需要将自定义异常的页面放在 /templates/error/路径
  2. 如果没有使用模板引擎,就放在静态资源路径下【四个默认】,"/resource/static/errpr/路径"
  3. “classpath:/META-INF/resources/error/路径","classpath:/resources/error/路径", "classpath:/static/error/路径", "classpath:/public/error/路径”
  4. 先会根据状态码精确查找 (例如 404.html),若找不到,会去找 4xx.html
  5. 如果都没有,会new 一个默认页面返回
  6. 前端可以使用 th:text="${status}"取出状态码

全局异常

基本介绍

  1. @ControllerAdvice + @ExceptionHandler 处理全局异常
  2. 底层是ExceptionHandlerExceptionResolver支持的
  3. 作用:当发生异常时,不使用默认异常机制匹配xxx.html,而是通过全局异常机制,显示指定的错误页面
  4. 对比:自定义异常是根据状态码来决定 返回的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";
    }
}
  1. Exception e 表示异常发生后,传递的异常对象
  2. Model model 可以存放错误信息到model,再放入到request域,带到目标页面显示
  3. @ControllerAdvice //标识全局异常处理器/对象 ,GlobalExceptionHandler 会被注入到spring容器
  4. @ExceptionHandler({ArithmeticException.class, NullPointerException.class,AccessException.class})指定捕获哪些异常

自定义异常

基本介绍

  1. springboot提供的异常不满足需求,程序员可以自定义异常
  2. @ResponseStatus + 自定义异常
  3. 底层是 ResponseStatusExceptionResolver ,底层调用response.sendError
  4. 当抛出自定义异常后,仍然会根据状态码去匹配xxx.html显示
  5. 可以将自定义异常放在全局异常处理器处理,但仍然走全局异常

具体实现

//自定义异常,需要继承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如何注入

基本介绍

  1. 考虑到兼容,springboot支持将Servlet/Filter/Listener注入spring容器,成为spring 的 bean
  2. 即 springboot开放了和原生web组件的兼容
  3. 如果要使用原生web组件的开发,需要在主启动器添加注解@ServletComponentScan(basePackages = "com.xxx.xx")指定要扫描的包,才会注入容器
  4. Servlet/Filter/Listener在springboot注入有两种方式,
  5. 方式一是通过@WebServlet,@WebFilter,@WebListener注解注入
  6. 方式二是通过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_~");
    }
}
  1. 注意:注入的原生的Servlet 不会被 SpringBoot拦截器 拦截 ---原因走原生的Servlet处理,而不是springboot的DispatchServlet
  2. 原因:
  3. 当多个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【服务器】切换

基本介绍

  1. Springboot支持的WebServlet:Tomcat,Jetty,Undertom
  2. Springboot启动,web场景启动器自动导入tomcat
  3. 支持切换WebServlet

如何配置内置的tomcat

  1. 可以通过application.yml完成配置【配置信息和ServletProperties.java关联】

  2. 通过配置类进行配置

  3. @Component
    public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    
    
        @Override
        public void customize(ConfigurableServletWebServerFactory factory) {
            factory.setPort(8080);//配置端口
        }
    }
    

切换WebServlet

  1. 修改pom.xml文件,删除tomcat依赖,引入undertow

  2. <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>
    
  3. 引入undertow

  4. <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
    

数据库操作

  • springboot默认使用JDBC+HikariDataSource

如何使用默认JDBC+HikariDataSource进行数据库操作

  1. 引入data-jdbc starter

  2. <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    
  3. springboot并不知道具体要操作哪一种数据库,需要在pom.xml中指定导入数据库驱动,并指定对应版本

  4. <!--    引入mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    
  5. 在application.yml中配置数据源

  6. 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
    
  7. 完成测试

  8. @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

基本介绍

  1. Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。

  2. Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。

  3. 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。

  4. 数据库密码加密。DruidDruiver和DruidDataSource都支持PasswordCallback。

  5. SQL执行日志,,监控你应用的数据库访问情况。

整合Druid 和 springboot【2种方式】

方式一 自定义方式整合

  1. 引入druid依赖

  2. <!--引入druid依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.17</version>
    </dependency>
    
  3. 在配置类注入DataSource

  4. @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方式整合

  1. 引入druid starter

  2. <!--    引入druid starter-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
    
  3. 在application.yml中配置druid和监控功能

  4. #配置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

  1. 需要在application.yml种指定mapper.xml扫描的路径

  2. mybatis:
    #  指定要扫描的 Xxxmapper.xml
      mapper-locations: classpath:mapper/*.xml
    
  3. 在Mapper接口上可以通过添加@Mapper注解,注入容器

  4. @Mapper //@Mapper就会扫描 并 将接口对象注入容器
    public interface MonsterMapper {
        //根据id 返回monster
        Monster getMonsterById(Integer id);
    
    }
    
  5. 在application.yml中,可以通过 config-location 可以指定 mybatis-config.xml,来进行传统mybatis方式的配置

  6. mybatis:
      config-location: classpath:mybatis-config.xml
    
  7. 也可以直接在application.yml中配置mybatis【推荐】

  8. mybatis:
    # 配置类型别名
      type-aliases-package: com.zy88.springboot.bean
      configuration:
      # 配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  9. 如果配置内容少,建议直接在application.yml中配置mybatis,如果内容很多,才考虑单独做一个 mybatis-config.xml

Springboot整合MyBatis-Plus

基本介绍

  1. MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
  2. 内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

Springboot整合MyBatis-Plus

  1. 需要引入MyBatis-Plus starter【在meven仓库中】

  2. <!--    引入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>
    
  3. 在application.yml配置数据源

  4. 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
    
  5. 在配置类注入DruidDataSource

  6. @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);

}
  1. BaseMapper 已经默认提供了很多crud方法,可以直接使用
  2. 如果 BaseMapper 提供的方法不能满足业务需求,我们可以开发新的方法 ,并在MonsterMapper.xml配置

整合service【MyBatis-Plus与MyBatis的不同】

  • service需要继承父接口 ISercice<>
public interface MonsterService extends IService<Monster> {
    //自定义方法
}
  1. IService接口声明了很多方法,crud
  2. 如果不能满足需求,可以再声明需要的方法,然后在实现类实现即可
  • 注意: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作用

  1. 默认情况:如果类名首字母小写和表名不一致,可以映射
  2. 当类名首字母小写和表名不一致时,通过@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插件

本文学习内容来自韩顺平老师的课程

仅供个人参考学习

热门相关:麻辣婆媳斗   快乐探究生活   锦衣夜行   离婚后三个大佬要娶我   来到古代当厨神