【SpringBoot】的学习

目录

简介:

01.Spring Boot 入门程序

​​02.常用注解【spring的java配置】

@Controller   --控制器

传参方式

03. Spring Boot热部署

04.Spring Boot的自动配置原理以及启动分析(难点)

 点进去spring-boot-starter-parent以后这依赖管理里面写和很从版本

 05.Spring Boot的两种配置文件语法【重中之重】

 06.@Value读取配置文件以及验证

07.Profile配置文件详解

7.1为什么要使用profiles

 08.配置文件加载优先级和外部配置文件

09.自动配置原理以及 @Conditional派生注解

 10. 整合logback

11.AOP开发 

12.WEB静态资源访问规则

 13.Thymeleaf模板的使用

14. 内嵌tomcat加载原理分析

15.启动内嵌jetty服务器【了解】

16.注册Web三大组件【重点】

17.数据源配置和自动管理【重中之中】

18.集成JdbcTemplate【熟悉】

19.集成mybatis【重点】

 20.集成mybatisplus【重点】

21.集成swagger3.0【熟悉】

22.集成shiro非前后端分离【重点】

23.集成shiro前后端分离

24.普通缓存(非redis)

25.Spring Boot定时任务 

 26.Spring Boot邮件发送


简介:

 Spring SpringMVC和SpringBoot 区别

1、Spring

Spring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。

2、SpringMVC

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。SpringMVC是一种web层mvc框架,用于替代servlet(处理|响应请求,获取表单参数,表单校验等。SpringMVC是一个MVC的开源框架,SpringMVC=struts2+spring,springMVC就相当于是Struts2加上Spring的整合。

3、SpringBoot

Springboot是一个微服务框架,延续了spring框架的核心思想IOC和AOP,简化了应用的开发和部署。Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题—>习惯大于约定

区别与总结
1.简单理解为:Spring包含了SpringMVC,而SpringBoot又包含了Spring或者说是在Spring的基础上做得一个扩展。

spring mvc < spring < springboot

2、Spring Boot 对比Spring的一些优点包括:

提供嵌入式容器支持

使用命令java -jar独立运行jar

在外部容器中部署时,可以选择排除依赖关系以避免潜在的jar冲突

部署时灵活指定配置文件的选项

用于集成测试的随机端口生成

3、结论

Spring Boot只是Spring本身的扩展,使开发,测试和部署更加方便。

01.Spring Boot 入门程序

创建第一个springboot项目

【SpringBoot】的学习_第1张图片

【SpringBoot】的学习_第2张图片【SpringBoot】的学习_第3张图片

【SpringBoot】的学习_第4张图片

 【SpringBoot】的学习_第5张图片

 
            org.springframework.boot
            spring-boot-starter-web
        

这个spring-boot-starter-web 是web的启动器,封装了springmv所需要的jar包

【SpringBoot】的学习_第6张图片
 创建一个controller

【SpringBoot】的学习_第7张图片

启动测试

【SpringBoot】的学习_第8张图片

 启动项目访问http://localhost:8080/index/hello

 【SpringBoot】的学习_第9张图片

 ​​​​​jar包启动测试,先打包

【SpringBoot】的学习_第10张图片

【SpringBoot】的学习_第11张图片​ 

【SpringBoot】的学习_第12张图片【SpringBoot】的学习_第13张图片

 测试访问

 【SpringBoot】的学习_第14张图片

 项目结构

【SpringBoot】的学习_第15张图片

 彩蛋

spring Boot启动的时候会有一个默认的启动图案。

【SpringBoot】的学习_第16张图片

在src/main/resources路径下新建一个banner.txt文件,并输入想要的内容即可

【SpringBoot】的学习_第17张图片02.常用注解【spring的java配置】

类上面的注解:

@Controller   --控制器

@RestController   --返回json的控制器 =Controller  +ResponseBody

@Service --标记服务接口

@Respority --标记Dao接口

@Component --标记组件

@RequestMapping --请求映射 (也可在方法上)

方法上的注解:

|--GetMapping

|--PostMapping

 |--DeleteMapping

|--PutMapping

|--PatchMapping

@ResponseBody --返回JSON对象

参数上的注解:

@RequestBody --入参是JSON对象

@PathVariable(value=””) --将路径上面的参数映射到入参里面

http://127.0.0.1:8080/car/user/1

@RequestParam   如果Controller里面使用map接收参数

|--如果controller不知道页面要传多少参数,那么可以使用Map去接收。 必须加@RequestParam的注解

属性上面的注解

@Autowried --自动注入(首选按照类型)  byName byType

@Resource --自动注入(首选按照名字)

传参方式

http://127.0.0.1:8080/car/user?id=1
http://127.0.0.1:8080/car/user/1    加@pathvarible注解

 @Configuration

作用于类上,相当于一个xml配置文件;|--application-dao.xml

@Bean  作用于方法上,相当于xml配置中的

【SpringBoot】的学习_第18张图片​ 

@Qualifier注解 

qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,我们修改调用代码,添加@Qualifier注解,需要注意的是@Qualifier的参数名称必须为我们之前定义@Bean注解的名称之一

【SpringBoot】的学习_第19张图片

 @Primary 主候选的

当IOC容器里面有多个同类型的对象时,就会发生冲突,标注了该注解的就作为主候选对象

​ 

03. Spring Boot热部署

 什么是热部署

spring为开发者提供了一个名为spring-boot-devtools的模块来使springboot应用支持热部署,提高开发的效率,修改代码后无需重启应用



    org.springframework.boot
    spring-boot-devtools
    
    runtime
    
    true

配置idea的启动面板

如果不配置面板,那么可直接使用ctrl+F9去刷新

 【SpringBoot】的学习_第20张图片 

04.Spring Boot的自动配置原理以及启动分析(难点)

4.​​​​​​1.pom的依赖分析



        org.springframework.boot
        spring-boot-starter-parent
        2.6.4
         
    

 点进去spring-boot-starter-parent以后这依赖管理里面写和很从版本

 
    org.springframework.boot
    spring-boot-dependencies
    2.6.4
  

【SpringBoot】的学习_第21张图片

【SpringBoot】的学习_第22张图片

 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了

4.2.启动器 spring-boot-starter


  org.springframework.boot
  spring-boot-starter
  2.6.4

springboot-boot-starter-xxx:就是spring-boot的场景启动器



    org.springframework.boot
    spring-boot-starter-web

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ,也可以自己自定义 starter;

4.3原理分析

  1. 为什么启动类所在的所及其子包能被默认扫描
  2. 为什么引入spring-boot-starter-web之后能直接集成springmvc

注解功能划分

【SpringBoot】的学习_第23张图片

如何自动扫描(默认扫描启动类所在的包以下面的及子包)

@Import(AutoConfigurationPackages.Registrar.class)

【SpringBoot】的学习_第24张图片

AutoConfigurationPackages中registerBeanDefinitions

【SpringBoot】的学习_第25张图片

 如图:

【SpringBoot】的学习_第26张图片

 疑问:这是springmvc中的写法,我们要这么用,必须配置相关xml

为什么引入spring-boot-starter-web之后能直接集成springmvc

回顾SSM的配置:

  1. 包扫描
  2. 注解
  3. 配置适配器
    1. Contoller的实方式
      • 实现Controller接口
      • 实现HttpRequestHandler
      • 使用注解
        1. Controller
        2. RestController
    2. 总结,就是为了适配Controller的不同实现方式
  4. 配置映射器
    1. 处理用户请求地址和Controller里方法的映射关系的
  5. 配置视图解释器
    1. org.springframework.web.servlet.view.InternalResourceViewResolver
  6. 配置拦截器
    1. 拦截请求 Inteceptor
  7. 配置文件上传管理器
  8. 配置前端控制器

springmvc.xml ​【SpringBoot】的学习_第27张图片

 web.xml

【SpringBoot】的学习_第28张图片

是因为上面配置是springboot帮我们做了

如何加载自动配置类

找到@Import(AutoConfigurationImportSelector.class)

【SpringBoot】的学习_第29张图片

在这个类里面的getAutoConfigurationEntry()方法

springboot在启动的时候默认加载了所有的配置 

【SpringBoot】的学习_第30张图片

进入getCandidateConfigurations()方法查看究竟

这个方法是spring的加载工厂去筛选所有引入(link)EnableAutoConfiguration的配置类

EnableAutoConfiguration是Key 返回的List是vlaue

接着进入loadFactoryNames()方法

【SpringBoot】的学习_第31张图片

【SpringBoot】的学习_第32张图片

【SpringBoot】的学习_第33张图片

 最后回到getAutoConfigurationEntry()方法里面往下执行

剔除不需要的(pom里面没有引入starter的自动配置类)

【SpringBoot】的学习_第34张图片

至此自动加载配置类的初探就结束了

(从加载到的配置类中选讲)如何加载前端控制器,在ssm里面,我们需要手动去创建DispatcherServlet对象,然后注入到Servlet容器中,在springboot里面,已经帮我们自动配置了

 查看DispatcherServletAutoConfiguration这个自动配置类

【SpringBoot】的学习_第35张图片

 创建DispatcherServlet对象【SpringBoot】的学习_第36张图片

【SpringBoot】的学习_第37张图片

 设置servlet的名称,loadonstart 启动时加载  路径【SpringBoot】的学习_第38张图片

Springboot的run方法到底执行了什么

【SpringBoot】的学习_第39张图片

【SpringBoot】的学习_第40张图片

Springboot默认提供了哪些候选的starter

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

【SpringBoot】的学习_第41张图片

 05.Spring Boot的两种配置文件语法【重中之重】

引入依赖



    org.springframework.boot
    spring-boot-configuration-processor
    true

创建Hero 类

【SpringBoot】的学习_第42张图片

properties方式:

hero.id=2
hero.name=LJH
hero.age=18
hero.birth=2012/12/31
hero.hobby=LOL,DNF,CF
hero.list=C#,Python,PS
hero.set=football,basketball,swim
hero.map.k1=v1
hero.map.k2=v2

 yml方式:

hero:
  id: 1
  age: 18
  name: 超人
  birth: 2012/12/31
  hobby:
    - LOL
    - DNF
    - CF
  list:
    - JAVA
    - JS
    - C++
  set:
    - 足球
    - 篮球
    - 排球
  map:
    k1: v1
    k2: v2

 测试

【SpringBoot】的学习_第43张图片

 配置文件取值

【SpringBoot】的学习_第44张图片

hero.name=${hero.age}

两种配置文件的用法说明

  1. 如果properties里面配置了就不会去yml里面去取值,如果没有配置就会去yml里面去取
  2. 两种配置文件是互补的存在

注入对象 

【SpringBoot】的学习_第45张图片 【SpringBoot】的学习_第46张图片

 06.@Value读取配置文件以及验证

【SpringBoot】的学习_第47张图片

总结说明 

1,@Value只能注入普通的属性[也就是基本数据类型和String] 其它的复杂类型是不能取到值的[如果yaml配置是array:LOL,DNF]这样的配置是可以取 或者在perpeties里面配置

2,如果属性是使用驼峰命名法则不能使用属性名注入,要使用@Value("${hero.class-name}")来取值

不能使用@Value("${hero.className}")来取值

 ​​​​​​​@Value和@ConfigurationProperties取值比较

【SpringBoot】的学习_第48张图片

注解验证 

引入依赖



    org.springframework.boot
    spring-boot-starter-validation

【SpringBoot】的学习_第49张图片

 

【SpringBoot】的学习_第50张图片

 ​​​​​​​常用的验证注解

@NotNull --不能为null,但可以空着不写 (name:)

@NotEmpty --不能为空,也不能为null,但可以是空格 “  ”

@NotBlank --不能为空格,也不能为null,也不能为空格

@Min     最大值

@Max    最小值

@Size(min = 1, max = 6)   长度限制

@Range(min = 1,max = 2)   范围限制

@Pattern(regexp"[0,1]{1}")   正则限制

全局异常

package com.bjsxt.exception;

import com.bjsxt.vo.ResultObj;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = {BindException.class})
    public ResultObj bindException(BindException e){
        BindingResult result = e.getBindingResult();
        return getResultObj(result);
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public ResultObj methodArgumentNotValidException(MethodArgumentNotValidException e){
        BindingResult result = e.getBindingResult();
        return getResultObj(result);
    }

    private ResultObj getResultObj(BindingResult result) {
        List> resList=new ArrayList<>();
        //取出errors
        List allErrors = result.getAllErrors();
        for (ObjectError allError : allErrors) {
            //取出对象名
            String objectName = allError.getObjectName();
            String defaultMessage = allError.getDefaultMessage();
            FieldError fieldError= (FieldError) allError;
            String field = fieldError.getField();
            Map map=new HashMap<>();
            map.put("objectName",objectName);
            map.put("field",field);
            map.put("defaultMessage",defaultMessage);
            resList.add(map);
        }
        return new ResultObj(400,"数据验证不通过",resList);
    }
}

07.Profile配置文件详解​​​​​​​

7.1为什么要使用profiles

 在开发中,一般有两种环境

        1,生产环境 [项目上线,客户在使用中,就是生产环境]

        2,开发环境 [就是开发环境,自己开发测试的环境]

有时候开发环境和生产环境的配置方法是不一样的,那么如何快速的切换呢,这里就要使用profiles文件

​​​​​​​7.2使用方法

​​​​​​​创建application.yml

【SpringBoot】的学习_第51张图片

创建application-dev.yml

【SpringBoot】的学习_第52张图片

创建application-pro.yml

​​​​​​​运行测试

​​​​​​​总结

在application.yml的主配置文件中,激活哪个配置文件,就会使用该配置文件进行运行

 08.配置文件加载优先级和外部配置文件

​​​​​​​8.1项目内部配置文件

spring boot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件

其中同一目录下的properties文件的优先级大于yml文件

​​​​​​​配置文件可以放的位置和优先级

classpath:/           --优先级4        默认放的位置

classpath:/config/    --优先级3

file:./                 --优先级2            跟jar包同级别的目录

file:./config/          --优先级1

 测试前两个

【SpringBoot】的学习_第53张图片

 测试前三个

先打包

【SpringBoot】的学习_第54张图片

【SpringBoot】的学习_第55张图片

启动jar包运行

【SpringBoot】的学习_第56张图片

 在jar包同级路面建config包,里面application.yml端口为8004

启动测试:

【SpringBoot】的学习_第57张图片

【SpringBoot】的学习_第58张图片

Win10 下关闭已运行jar包的方法,本文直接查找端口杀之

查找端口命令    netstat -aon|findstr “8081”

使用命令杀掉:taskkill /pid 8080 -f 杀掉端口 

【SpringBoot】的学习_第59张图片

​​​​​​​查看ConfigFileApplicationListener

【SpringBoot】的学习_第60张图片

8.2外部的配置文件 

在D盘放一个application.yml文件 端口指定为8009

java -jar 01springboot-hello-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.yml

【SpringBoot】的学习_第61张图片

09.自动配置原理以及 @Conditional派生注解

9.1​​​​​​​自动配置

我们就了解到了Spring Boot在启动的时候:

  • 去扫描加载了所有引入依赖的jar包下面的MATE-INF/spring.factories文件,
  • 然后通过EnableAutoConfiguration为key,下面所有自动配置类的全限定类名作为value,(List
  • 经过筛选排除掉不符合要求的(pom中没有添加依赖的配置类)
  • 最后把(List:符合条件的自配配置类的全限定类名)添加到IOC容器去管理,从而实现了自动配置原理

@Conditional派生注解

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效;(条件之间是并且的关系)

【SpringBoot】的学习_第62张图片

 10. 整合logback

10.1​​​​​​​​​​​​​​概述

在搭建新的系统时候必不可少的是需要日志的,日志的作用就不用多说了吧,可以用来调试程序,记录程序运行的状态,最重要的是可以用来排查线上的问题。

那我们该如何在项目中使用日志呢?

SpringBoot内部集成了LogBack日志依赖,SpringBoot默认使用LogBack记录日志信息,默认根据base.xml配置内容来输出到控制台和文件之中,这是默认的配置不能达到企业级项目的要求

 创建logback-spring.xml








    

    logback
    
    

    
    
    
    
    
    
    


    
    
        
        
            info
        
        
            ${CONSOLE_LOG_PATTERN}
            
            UTF-8
        
    


    

    
    
        
        ${log.path}/log_debug.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
        
            
            ${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            debug
            ACCEPT
            DENY
        
    

    
    
        
        ${log.path}/log_info.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8
        
        
        
            
            ${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            info
            ACCEPT
            DENY
        
    

    
    
        
        ${log.path}/log_warn.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
        
            ${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            warn
            ACCEPT
            DENY
        
    


    
    
        
        ${log.path}/log_error.log
        
        
            %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            UTF-8 
        
        
        
            ${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log
            
                100MB
            
            
            15
        
        
        
            ERROR
            ACCEPT
            DENY
        
    

    
    
    
    


    

    
    
        
    

    
        
        
        
        
        
    

    
    
    
    
    
    
    
    
    
    


创建启动类测试

【SpringBoot】的学习_第63张图片

 ​​​​​​​查看本地日志

【SpringBoot】的学习_第64张图片

11.AOP开发 

​​​​​​​11.1概述

aop是spring的两大功能模块之一,功能非常强大,为解耦提供了非常优秀的解决方案。SpringBoot集成aop是非常方便的,下面使用aop来拦截业务组件的方法

Aop的作用:在不修改源代码的情况下,对类里面的方法进行增强

(前置,后置,环绕,异常)

​​​​​​​11.2使用方法​​​​​​​


    org.springframework.boot
    spring-boot-starter-aop

创建Man测试

@Component
public class TestAop {
    public void eat(String food){
       // int a = 10/0;
        System.out.println("吃"+food);

    }
}

 创建切面类

package com.wxz.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * TODO
 *
 * @author wxz
 * @date 2022/3/2014:16
 */
@Component
@Aspect
public class TestAspect {

    public static final String pc ="execution(* com.wxz.domain.*.*(..))";

   // @Before(pc)
    public  void before(){
        System.out.println("饭前来点水果");
    }
   // @After(pc)
    public  void after(){
        System.out.println("饭后来一把");
    }
    @Around(pc)
    public Object around(ProceedingJoinPoint point){
        before();
        Object[] args = point.getArgs();
        String game = args[0].toString();
        System.out.println(game);
        //执行目标方法,获得返回值
        Object proceed = null;
        try {
            proceed = point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        after();
        //返回出去
        return proceed;
    }

    @AfterThrowing(value = pc, throwing = "tw")
    public void afterThrowing(Throwable tw) {
        System.out.println("出现异常" + tw.getMessage());
    }

}

【SpringBoot】的学习_第65张图片

12.WEB静态资源访问规则

  • classpath:META-INF/resources   优先级最高
  • classpath:resources   其次
  • classpath:static
  • classpath:public

查看WebMvcAutoConfiguration里面的静态类WebMvcAutoConfigurationAdapter

关于资源管的方法addResourceHandlers

【SpringBoot】的学习_第66张图片

如果这四个目录下面都有相同的文件,那么访问的优先级为:

META-INF/resources>resources>static>public

 13.Thymeleaf模板的使用

​​​​​​​Thymeleaf概述

 Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:

1、Thymeleaf 在有网络和无网络的环境下皆可

运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。

2、Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。

3、Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能


    org.springframework.boot
    spring-boot-starter-thymeleaf

Spring Boot项目Thymeleaf模板页面存放位置

查看ThymeleafAutoConfiguration自动配置类,找到模板存放的位置 

【SpringBoot】的学习_第67张图片

 【SpringBoot】的学习_第68张图片

 禁用缓存

【SpringBoot】的学习_第69张图片

 templates目录不能直接访问,需要controller进行页面跳转

【SpringBoot】的学习_第70张图片

【SpringBoot】的学习_第71张图片

 测试访问:

【SpringBoot】的学习_第72张图片

 Thymeleaf的相关语法:​​​​​​​

1,简单表达式   

  1、变量的表达式:${...}   取作用域里面的值(request,session,applicationContext)
  2、选择变量表达式:*{...}
  3、信息表达式:#{...}    取IOC容器里面的值  
  4、链接URL表达式:@{...}          US.

5,实用工具对象 
#dates: java.util的实用方法。对象:日期格式、组件提取等.
#calendars:类似于#日期,但对于java.util。日历对象
#numbers:格式化数字对象的实用方法。
#strings:字符串对象的实用方法:包含startsWith,将/附加等。

 Thymeleaf读取Model里面的对象

创建类Hero:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private String country;
    private String phone;
    private Date birth;
    private Double salary;
}
@RequestMapping("helloHero")
public String helloHero(Model model){
    Hero hero=new Hero(1,"后羿","男",18,"中国","110",new Date());
    model.addAttribute("hero",hero);
    return "showHero";
}

修改页面




    
    Title
    
    


英雄面板
英雄id
英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日

测试访问

【SpringBoot】的学习_第73张图片

 Thymeleaf读取Model里面的集合

@RequestMapping("helloHeroList")
public String helloHeroList(Model model){
    List list=new ArrayList<>();
    for (int i = 1; i <=5 ; i++) {
        list.add(new Hero(i,"后羿"+i,"男",18,"中国","110",new Date()))
    }
    model.addAttribute("heros",list);
    return "showHeroList";
}



    
    Title
    
    


英雄面板
英雄id
英雄名字
英雄性别
英雄年龄
英雄年龄
英雄国家
英雄电话
英雄生日
英雄生日

 测试访问

【SpringBoot】的学习_第74张图片

 ThymeleafObjects的使用

【SpringBoot】的学习_第75张图片




    
    Title
    
    


model:

request:

session:

session:

servletContext:

14. 内嵌tomcat加载原理分析

我们在使用springboot项目的时候并没有使用外部的tomcat,那么springboot是如何帮我们管理内置的服务器的呢?

​​​​​​​查看启动原理

查看ServletWebServerFactoryAutoConfiguration

发现包含了3个内嵌的服务器,默认是使用了内嵌的tomcat

【SpringBoot】的学习_第76张图片

点击EmbeddedTomcat 进入

【SpringBoot】的学习_第77张图片

继续往下走查看TomcatServletWebServerFactory

【SpringBoot】的学习_第78张图片

【SpringBoot】的学习_第79张图片

点击getTomcatWebServer

 点击TomcatWebServer

【SpringBoot】的学习_第80张图片

【SpringBoot】的学习_第81张图片

15.启动内嵌jetty服务器【了解】

在springboot里面默认使用内嵌的tomcat 可以改成jetty


    org.springframework.boot
    spring-boot-starter-web
    
    
        
            org.springframework.boot
            spring-boot-start-tomcat
        
    




    org.springframework.boot
    spring-boot-starter-jetty

@Configuration
public class JettyConfig {

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        JettyServletWebServerFactory jettyServletWebServerFactory = new JettyServletWebServerFactory();
        return jettyServletWebServerFactory;
    }
}

启动查看

【SpringBoot】的学习_第82张图片

16.注册Web三大组件【重点】

web的三大件有哪些

Servlet     ---web.xml    @WebServlet

Filter       --web.xml   @WebFilter

Listener    --web.xml   @WebListener

16.1首先查看注册组件的结构 

【SpringBoot】的学习_第83张图片

16.2注册自己的Servlet 

我们可以模仿DispatcherServlet的注册方式

创建UserServlet

package com.wxz.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


public class UserServlet  extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.println("hello-userServlet");
        writer.flush();
        writer.close();
        System.out.println("hello-UserServlet");
    }
}

在配置类注册自己的servlet

@Configuration
public class WebConfig {
    @Bean
    public UserServlet userServlet() {
        return new UserServlet();
    }

    @Bean
    public ServletRegistrationBean userServletServletRegistrationBean(UserServlet userServlet) {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>();
        servletRegistrationBean.setServlet(userServlet);
        servletRegistrationBean.setUrlMappings(Arrays.asList("/user1.action", "/user2.action"));
        return servletRegistrationBean;
    }
}

16.3注册自己的Filter

创建MyFilter


public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("前置过滤");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("后置过滤");
    }

    @Override
    public void destroy() {

    }
}

在配置类注册自己的Filter

 @Bean
public MyFilter myFilter() {
    return new MyFilter();
}

/**
 * 注册自己的filter
 *
 * @param myFilter
 * @return
 */
@Bean
public FilterRegistrationBean myFilterFilterRegistrationBean(MyFilter myFilter) {
    FilterRegistrationBean myFilterFilterRegistrationBean = new FilterRegistrationBean<>();
    myFilterFilterRegistrationBean.setFilter(myFilter);
    //设置所有的请求都走过滤器
    myFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
    return myFilterFilterRegistrationBean;
}

16.4注册自己的Listener

创建MyListener

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContextEvent被创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContextEvent被销毁");
    }
}

在配置类注册自己的Listener


@Bean
public MyListener myListener() {
    return new MyListener();
}

@Bean
public ServletListenerRegistrationBean listenerRegistrationBean(MyListener myListener) {
    ServletListenerRegistrationBean listenerRegistrationBean = new ServletListenerRegistrationBean<>();
    listenerRegistrationBean.setListener(myListener);
    return listenerRegistrationBean;
}

17.数据源配置和自动管理【重中之中】

选择依赖

【SpringBoot】的学习_第84张图片

 17.1使用DriverManagerDataSource

​​​​​​​修改配置文件

spring:
  datasource: #数据源的配置
    driver-class-name: com.mysql.cj.jdbc.Driver
    url:  jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    type: org.springframework.jdbc.datasource.DriverManagerDataSource   #spring自带的数据源

测试连接

【SpringBoot】的学习_第85张图片

​​​​​​​17,2使用Druid数据源【自己配置】

数据源

数据源的监控页面

添加durid的依赖


    com.alibaba
    druid
    1.1.21

添加MyDruidProperties配置文件类


@Data
@ConfigurationProperties(prefix = "my.druid")
public class MyDruidProperties {

    private String url;
    private String username;
    private String password;
    private String driverClassName;
    //初始化数量
    private Integer initialSize;
    //最大活跃数
    private Integer maxActive;
    //最小连接数
    private Integer minIdle;
    //检查连接的
    private String validationQuery;

    private StatView statView;
    /**
     * 监控配置
     */
    @Data
    static class StatView {
        //监控登录名
        private String loginUsername;
        //监控登录密码
        private String loginPassword;
        //白名单
        private String allow;
        //黑名单
        private String deny;
        //映射路径
        private String[] urlMapping;
    }
}

添加MyDruidAutoConfiguration自动配置类


@Data
@Configuration
@Log4j2
@EnableConfigurationProperties(MyDruidProperties.class)
@ConditionalOnClass(value = {DruidDataSource.class}) //必须有DruidDataSource的这个类
public class MyDruidAutoConfiguration {

//    @Autowired  如果不想使用Autowired就必须提交一个构造方法,如下
    private MyDruidProperties myDruidProperties;

    public MyDruidAutoConfiguration(MyDruidProperties myDruidProperties) {
        this.myDruidProperties = myDruidProperties;
    }

    /**
     * 创建数据源对象
     *
     */
    @Bean(initMethod = "init",destroyMethod = "close")
    public DruidDataSource druidDataSource(){
        if (myDruidProperties.getUrl() == null) {
            log.error("URL can not be null");
            throw new RuntimeException("URL can not be null");
        }
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setDriverClassName(myDruidProperties.getDriverClassName());
        dataSource.setUrl(myDruidProperties.getUrl());
        dataSource.setUsername(myDruidProperties.getUsername());
        dataSource.setPassword(myDruidProperties.getPassword());
        dataSource.setMaxActive(myDruidProperties.getMaxActive());
        dataSource.setMinIdle(myDruidProperties.getMinIdle());
        dataSource.setValidationQuery(myDruidProperties.getValidationQuery());
        return dataSource;
    }

    /**
     * 注册StatViewServlet
     */
    @Bean
    @ConditionalOnClass(StatViewServlet.class)
    public ServletRegistrationBean statViewServletServletRegistrationBean(){
        StatViewServlet  statViewServlet=new StatViewServlet();
        ServletRegistrationBean registrationBean=new ServletRegistrationBean<>();
        registrationBean.setServlet(statViewServlet);
        registrationBean.addInitParameter("loginUsername",myDruidProperties.getStatView().getLoginUsername());
        registrationBean.addInitParameter("loginPassword",myDruidProperties.getStatView().getLoginPassword());
        registrationBean.addInitParameter("allow",myDruidProperties.getStatView().getAllow());
        registrationBean.addInitParameter("deny",myDruidProperties.getStatView().getDeny());

        registrationBean.addUrlMappings(myDruidProperties.getStatView().getUrlMapping());

        return registrationBean;
    }
}

修改yml配置文件

my:
  druid:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    initial-size: 5
    max-active: 10
    min-idle: 2
    validation-query: select 'x'
    stat-view:
      login-username: admin
      login-password: admin
      allow:
      deny:
      url-mapping:
        - /druid/*
        - /druid2/*

测试访问

【SpringBoot】的学习_第86张图片

​​​​​​​17.3使用Druid数据源【官方starter】

添加durid starter的依赖



    com.alibaba
    druid-spring-boot-starter
    1.1.21

 修改配置文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
#    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      max-active: 10
      min-idle: 2
      validation-query: select 'x'
      stat-view-servlet:
        login-username: admin
        enabled: true  #启用监控页
        login-password: admin
        allow:
        deny:
        url-pattern: /druid/*

18.集成JdbcTemplate【熟悉】

我们之前在学spring的时候接触过JdbcTemplate,这个东西是官方的,那么在springboot里面怎么用,需要配置什么呢,答案:只要有数据源就行了

测试

因为数据源已经搞定了,所以直接测试即可

【SpringBoot】的学习_第87张图片

原理

找到JdbcTemplateAutoConfiguration 

【SpringBoot】的学习_第88张图片

找到JdbcTemplateConfiguration 

【SpringBoot】的学习_第89张图片

 从上面的代码可以看出,只要有数据源,就直接创建了,所以我们可以直接使用

19.集成mybatis【重点】

​​​​​​​​​​​​​​19.1注解方式的整合

创建项目

【SpringBoot】的学习_第90张图片



    com.alibaba
    druid-spring-boot-starter
    1.1.21

 创建数据库

【SpringBoot】的学习_第91张图片

 创建User对象

package com.wxz.domain;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    /**
    * 用户编号
    */
    private Integer id;

    /**
    * 用户姓名
    */
    private String name;

    /**
    * 用户地址
    */
    private String address;

    /**
    * 出生时间
    */
    private Date birth;

    /**
    * 是否删除1删除0未删除
    */
    private Integer flag;
}

创建UserMapper

package com.wxz.mapper;

import com.wxz.domain.User;
import org.apache.ibatis.annotations.*;


public interface UserMapper {
    @Delete("delete from sys_user where id= #{id}")
    int deleteByPrimaryKey(Integer id);

    @Insert("insert  into sys_user(id,name,address,birth,flag) values(#{user.id},#{user.name},#{user.address} ,#{user.birth},#{user.flag})")
    int insert(@Param("user") User record);

    @Select("select * from sys_user where id=#{id} ")
    User selectByPrimaryKey(Integer id);

    @Update("update  sys_user set name=#{user.name},address=#{user.address} ,birth=#{user.birth} ,flag=#{user.flag} where id=#{user.id} ")
    int updateByPrimaryKey(@Param("user") User user);
}

 修改yml配置文件

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
#    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      max-active: 10
      min-idle: 2
      validation-query: select 'x'
      stat-view-servlet:
        login-username: admin
        enabled: true  #启用监控页
        login-password: admin
        allow:
        deny:
        url-pattern: /druid/*
#配置mybatis
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #输出sql

启动了上扫描mapper所在的包 

【SpringBoot】的学习_第92张图片

测试查询

【SpringBoot】的学习_第93张图片

19.2配置文件方式整合(推荐)

生成UserMapper接口


public interface UserMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(User record);

    User selectByPrimaryKey(Integer id);

    int updateByPrimaryKey(User record);

    List selectAll();
}

在resources/mapper生成UserMapper.xml




  
    
    
    
    
    
    
    
  
  
    
    id, `name`, address, birth, flag
  
  
  
    
    delete from sys_user
    where id = #{id,jdbcType=INTEGER}
  
  
    
    insert into sys_user (`name`, address, birth, 
      flag)
    values (#{name,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{birth,jdbcType=TIMESTAMP}, 
      #{flag,jdbcType=INTEGER})
  
  
    
    update sys_user
    set `name` = #{name,jdbcType=VARCHAR},
      address = #{address,jdbcType=VARCHAR},
      birth = #{birth,jdbcType=TIMESTAMP},
      flag = #{flag,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  
  

修改yml配置文件

#配置mybatis
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #输出sql
  mapper-locations:
    - classpath:mapper/*Mapper.xml

启动类添加mapper扫描

【SpringBoot】的学习_第94张图片

19.3配置PageHelper插件分页

依赖pageHelper的starter 


    com.github.pagehelper
    pagehelper-spring-boot-starter
    1.2.13

【SpringBoot】的学习_第95张图片

 测试:

​​​​​​​19.4事务管理

和spring里面一样的,但是在boot里面只要在XXXXServiceImpl上Transactionl

【SpringBoot】的学习_第96张图片

 20.集成mybatisplus【重点】

创建项目引入依赖

【SpringBoot】的学习_第97张图片


    com.baomidou
    mybatis-plus-boot-starter
    3.4.0



    com.alibaba
    druid-spring-boot-starter
    1.1.21

配置yml

server:
  port: 8080
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: 123456
      initial-size: 5
      max-active: 10
      min-idle: 2
      validation-query: select 'x'
      stat-view-servlet:
        login-username: admin
        login-password: 123456
        enabled: true  #启用监控页面
        url-pattern: /druid/*
#配置mybatis-plus
mybatis-plus:
  configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations:
     - classpath:mapper/*.xml

生成User UserMapper  UserMapper.xml

修改启动类

【SpringBoot】的学习_第98张图片

测试

【SpringBoot】的学习_第99张图片 配置分页


package com.wxz.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}
@SpringBootTest
class ApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void queryForPage() {
        Integer pageNum=1;
        Integer pageSize=10;
        Page page=new Page<>(pageNum,pageSize);
        this.userMapper.selectPage(page,null);
        List records = page.getRecords();
        long total = page.getTotal();
        System.out.println("总条数:"+total);
        for (User record : records) {
            System.out.println(record);
        }
    }
}

21.集成swagger3.0【熟悉】

​​​​​​​使用步骤

创建项目加入依赖



    io.springfox
    springfox-boot-starter
    3.0.0

创建SwaggerProperties信息配置类


@Data
@ConfigurationProperties(prefix = "swagger2")
public class SwaggerProperties {
    //作者姓名
    private String name;
    //作者主页链接
    private String url;
    //作者邮箱
    private String email;
    //版本号
    private String version;
    //分组名称
    private String groupName;
    //文档标题
    private String title;
    //文档描述
    private String description;
    //组织地址
    private String termsOfServiceUrl;
}

创建SwaggerAutoConfiguration自动配置类


package com.wxz.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;


@Configuration
@EnableConfigurationProperties(value = {SwaggerProperties.class})
@EnableOpenApi  //启用swagger
public class SwaggerAutoConfiguration {

    private SwaggerProperties swaggerProperties;

    public SwaggerAutoConfiguration(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }

    /**
     * 创建文档
     *
     * @return
     */
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .groupName(swaggerProperties.getGroupName())
                .select()
                //要扫描的包
                .apis(RequestHandlerSelectors.basePackage("com.wxz"))
//                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
//                .apis(RequestHandlerSelectors.withMethodAnnotation(GetMapping.class))
                .build();
    }

    /**
     * 自定义文档信息
     *
     * @return
     */
    private ApiInfo apiInfo() {
        return new ApiInfo(swaggerProperties.getTitle(),    //文档标题
                swaggerProperties.getDescription(),         //文档描述
                swaggerProperties.getVersion(),             //版本号
                swaggerProperties.getTermsOfServiceUrl(),   //组织信息
                new Contact(swaggerProperties.getName(),    //个人信息
                        swaggerProperties.getUrl(),
                        swaggerProperties.getEmail()
                ),
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }
}

修改yml文件

swagger2:
  name: "小白"
  description: "测试的接口"
  email: [email protected]
  title: "后台"
  version: 1.0
  group-name: leige
  terms-of-service-url: https://gitee.com/smiledouble
  url: https://gitee.com/smiledouble
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  mvc:
    format:
      date-time: yyyy-MM-dd HH:mm:ss

创建Hero类


@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "Hero", description = "英雄对象")
public class Hero {
    @ApiModelProperty(value = "英雄的id")
    private Integer id;
    @ApiModelProperty(value = "英雄的名称")
    private String name;
    @ApiModelProperty(value = "英雄的地址")
    private String address;
    @ApiModelProperty(value = "英雄的生日")
    private Date birth;
    @ApiModelProperty(value = "英雄的爱好")
    private List hobby;
    @ApiModelProperty(value = "英雄的map")
    private Map map;
}

创建Controller


@Api(tags = "英雄的管理接口")  //标记一个controller信息
@RestController
public class TestController {
    /**
     * 获取一个英雄
     *
     * @param id 使用restful风格,讲参数拼接在url上
     * @return
     */
    @GetMapping("getOneHero/{id}")
    @ApiOperation(value = "获取一个英雄", notes = "根据id获取一个英雄对象")
    @ApiImplicitParam(name = "id", value = "英雄编号(必填)", required = true, dataType = "Integer", paramType = "path")
    public Hero getOne(@PathVariable(value = "id") Integer id) {
        Map map = new HashMap<>(4);
        map.put("q", "哈萨克");
        return new Hero(id, "亚索", "峡谷", new Date(), Arrays.asList("吹风,笛子"), map);
    }

    /**
     * 添加英雄
     *
     * @param hero
     * @return
     */
    @PostMapping("addHero")
    @ApiOperation(value = "创建英雄", notes = "根据Hero创建英雄对象")
    @ApiImplicitParam(name = "hero", value = "英雄对象", required = true, dataTypeClass = Hero.class, paramType = "body")
    public Map addHero(@RequestBody Hero hero) {
        System.out.println(hero);
        Map res=new HashMap<>();
        res.put("code",200);
        res.put("msg","添加成功");
        return res;
    }

    /**
     * 删除一个英雄
     *
     * @param id
     * @return
     */
    @DeleteMapping("deleteHero")
    @ApiOperation(value = "删除一个英雄", notes = "根据删除一个英雄对象")
    @ApiImplicitParam(name = "id", value = "英雄编号(必填)", required = true, dataType = "Integer", paramType = "query")
    public Map deleteHero(@RequestParam("id") Integer id) {
        System.out.println(id);
        Map res=new HashMap<>();
        res.put("code",200);
        res.put("msg","删除成功");
        return res;
    }
}

​​​​​​​测试访问文档页面

启动项目访问 http://localhost:8080/swagger-ui/index.html 

【SpringBoot】的学习_第100张图片

​​​​​​​测试接口

【SpringBoot】的学习_第101张图片

【SpringBoot】的学习_第102张图片

 补充注解说明

https://gumutianqi1.gitbooks.io/specification-doc/content/tools-doc/spring-boot-swagger2-guide.html

 更换页面ui



    com.github.xiaoymin
    swagger-bootstrap-ui
    1.9.6

【SpringBoot】的学习_第103张图片

 注解总结

model(实体类)   的注解 
|--@ApiModel(value = "Hero", description = "英雄对象")  标记这个类的描述
|--@ApiModelProperty(value = "英雄的id") 标记类的属性的作用

controller上的注解
	|--@Api(tags = "英雄测试类接口",description = "描述")
		|--作用在类上,标记这个contorller的作用
	|--@ApiOperation(value = "根据ID获取英雄对象",notes = "根据id获取一个英雄对象")  //当前接口的描述
	|--作用在方法上,标记这个方法的作用			

	|--@ApiImplicitParam(name = "hero", value = "英雄对象", required = true, dataTypeClass = Hero.class, paramType = "body")
		|--作用在方法上,标记这个方法需要哪些参数 及需要的数据格式

配置类
	|--@EnableOpenApi  //启用swagger

22.集成shiro非前后端分离【重点】

​​​​​​​之前SSM的配置

  1. UserRealm
  2. shiro.xml
  3. springmvc.xml
  4. web.xml

导入sql建表 

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50724
 Source Host           : localhost:3306
 Source Schema         : shiro

 Target Server Type    : MySQL
 Target Server Version : 50724
 File Encoding         : 65001

 Date: 15/12/2020 14:53:39
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `perid` int(11) NOT NULL AUTO_INCREMENT,
  `pername` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `percode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`perid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户查询', 'user:query');
INSERT INTO `permission` VALUES (2, '用户添加', 'user:add');
INSERT INTO `permission` VALUES (3, '用户修改', 'user:update');
INSERT INTO `permission` VALUES (4, '用户删除', 'user:delete');
INSERT INTO `permission` VALUES (5, '导出用户', 'user:export');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `roleid` int(11) NOT NULL AUTO_INCREMENT,
  `rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`roleid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员');
INSERT INTO `role` VALUES (2, 'CEO');
INSERT INTO `role` VALUES (3, '保安');

-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`  (
  `perid` int(255) NOT NULL,
  `roleid` int(11) NOT NULL,
  PRIMARY KEY (`perid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 2);
INSERT INTO `role_permission` VALUES (3, 1);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (4, 1);
INSERT INTO `role_permission` VALUES (5, 3);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `userid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `userpwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', '639ffb0cbcca39d4fff8348844b1974e', '男', '武汉');
INSERT INTO `user` VALUES (2, 'lisi', '0d303fa8e2e2ca98555f23a731a58dd9', '女', '北京');
INSERT INTO `user` VALUES (3, 'wangwu', '473c41db9af5cc0d90e7adfd2b6d9180', '女', '成都');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `userid` int(11) NOT NULL,
  `roleid` int(11) NOT NULL,
  PRIMARY KEY (`userid`, `roleid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (2, 2);
INSERT INTO `user_role` VALUES (3, 3);

SET FOREIGN_KEY_CHECKS = 1;

分析表的数据

zhangsan/123456  超级管理员  1234号权限user:query user:add user:update  user:delete

lisi/123456   CEO   123号权限user:query user:add user:update

wangwu/123456  保安  15号权限  user:query user:export

创建项目选择依赖

【SpringBoot】的学习_第104张图片

 【SpringBoot】的学习_第105张图片

 【SpringBoot】的学习_第106张图片

   
        
            com.alibaba
            druid-spring-boot-starter
            1.1.21
        
        
        
            org.apache.shiro
            shiro-spring
            1.5.3
        
        
        
            com.github.theborakompanioni
            thymeleaf-extras-shiro
            2.0.0
        

 ​​​​​​​修改主启动类

【SpringBoot】的学习_第107张图片

​​​​​​​创建yml

#配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/0817-shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      #      filters: log4j,stat
      max-active: 20
      min-idle: 5
      validation-query: select x
      initial-size: 3
      max-wait: 5000
      stat-view-servlet:
        login-username: root
        login-password: 123456
        allow:
        deny:
        url-pattern: /druid/*
        enabled: true   #启用数据源监控

#mybatis的配置
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  type-aliases-package: com.wxz.domain  #配置字别名

#shiro的配置
shiro:
  hash-iterations: 2
  hash-algorithm-name: md5
  login-url: /index.html
  unauthorized-url: /unauthorized.html
  log-out-url: /login/logout
  anon-url:  #这些地址不用认证
    - /index.html*
    - /login.html*
    - /login/toLogin*
    - /login/doLogin*
  authc-url:  #下面的需要认证
    - /**

 生成User

package com.wxz.domain;

public class User {
    /*
     *用户id
     */
    private Integer userid;
    /*
     *用户名
     */
    private String username;
    /*
     *密码
     */
    private String userpwd;
    /*
     *性别
     */
    private String sex;
    /*
     *地址
     */
    private String address;

  

    public Integer getUserid() {
        return userid;
    }

    public void setUserid(Integer userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserpwd() {
        return userpwd;
    }

    public void setUserpwd(String userpwd) {
        this.userpwd = userpwd;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
}

UserMapper

package com.wxz.mapper;

import com.wxz.domain.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper {

    User queryUserByUserName(@Param("username") String username);

}

UserMapper.xml




  
    
    
    
    
    
    
    
  
  
    
    userid, username, userpwd, sex, address
  

  

UserService

package com.wxz.service;

import com.wxz.domain.User;
public interface UserService{


    /**
     * 据用户名去数据库查询用户
     * @param username
     * @return
     */
    User queryUserByUserName(String username);
}

UserServiceImpl

package com.wxz.service.impl;

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.domain.User;
import com.wxz.mapper.UserMapper;
import com.wxz.service.UserService;
@Service
public class UserServiceImpl implements UserService{

    @Resource
    private UserMapper userMapper;

    @Override
    public User queryUserByUserName(String username) {
        return this.userMapper.queryUserByUserName(username);
    }
}

生成Role

package com.wxz.domain;

public class Role {
    /*
     *角色id
     */
    private Integer roleid;
    /*
     *角色名
     */
    private String rolename;

    public Integer getRoleid() {
        return roleid;
    }

    public void setRoleid(Integer roleid) {
        this.roleid = roleid;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }
}

RoleMapper

package com.wxz.mapper;

import com.wxz.domain.Role;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface RoleMapper {


    List queryRolesByUserId(@Param("userid") Integer userid);
}

RoleMapper.xml




  
    
    
    
    
  
  
    
    roleid, rolename
  

  

RoleService

package com.wxz.service;

import com.wxz.domain.Role;

import java.util.List;

public interface RoleService{

    /**
     * 根据用户ID查询用户拥有的角色
     * @param userid
     * @return
     */
    List queryRolesByUserId(Integer userid);
}

RoleServiceImpl

package com.wxz.service.impl;

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.mapper.RoleMapper;
import com.wxz.domain.Role;
import com.wxz.service.RoleService;

import java.util.ArrayList;
import java.util.List;

@Service
public class RoleServiceImpl implements RoleService{

    @Resource
    private RoleMapper roleMapper;

    @Override
    public List queryRolesByUserId(Integer userid) {
        List roleList=this.roleMapper.queryRolesByUserId(userid);

        List roles=new ArrayList<>();
        for (Role role : roleList) {
            roles.add(role.getRolename());
        }
        return roles;
    }
}

生成Permission

package com.wxz.domain;

public class Permission {
    /*
     *权限id
     */
    private Integer perid;
    /*
     *权限名
     */
    private String pername;

    private String percode;

    public Integer getPerid() {
        return perid;
    }

    public void setPerid(Integer perid) {
        this.perid = perid;
    }

    public String getPername() {
        return pername;
    }

    public void setPername(String pername) {
        this.pername = pername;
    }

    public String getPercode() {
        return percode;
    }

    public void setPercode(String percode) {
        this.percode = percode;
    }
}

PermissionMapper

package com.wxz.mapper;

import com.wxz.domain.Permission;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface PermissionMapper {

    List queryPermissionsByUserId(@Param("userid") Integer userid);
}

PermissionMapper.xml




  
    
    
    
    
    
  
  
    
    perid, pername, percode
  

    

PermissionService

package com.wxz.service;

import java.util.List;

public interface PermissionService{


    /**
     * 根据用户ID查询用户拥有的权限
     * @param userid
     * @return
     */
    List queryPermissionsByUserId(Integer userid);
}

 PermssionServiceImpl

package com.wxz.service.impl;

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import com.wxz.mapper.PermissionMapper;
import com.wxz.domain.Permission;
import com.wxz.service.PermissionService;

import java.util.ArrayList;
import java.util.List;

@Service
public class PermissionServiceImpl implements PermissionService{

    @Resource
    private PermissionMapper permissionMapper;


    @Override
    public List queryPermissionsByUserId(Integer userid) {
        List permissionList=this.permissionMapper.queryPermissionsByUserId(userid);

        List permissions=new ArrayList<>();
        for (Permission permission : permissionList) {
            permissions.add(permission.getPercode());
        }
        return permissions;
    }
}

创建ActiveUser

package com.wxz.common;

import com.wxz.domain.User;
import com.wxz.domain.User;

import java.util.List;

public class ActiveUser {

    private User user;
    private List roles;

    private List permissions;

    public ActiveUser() {
        this.user = user;
        this.roles = roles;
        this.permissions = permissions;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List getRoles() {
        return roles;
    }

    public void setRoles(List roles) {
        this.roles = roles;
    }

    public List getPermissions() {
        return permissions;
    }

    public void setPermissions(List permissions) {
        this.permissions = permissions;
    }
}

创建UserRealm

package com.wxz.realm;

import com.wxz.common.ActiveUser;
import com.wxz.domain.User;
import com.wxz.service.PermissionService;
import com.wxz.service.RoleService;
import com.wxz.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

public class UserRealm  extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;


    /**
     * 做认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //得到用户名
        String username = token.getPrincipal().toString();
        //根据用户名去数据库查询用户
        User user=this.userService.queryUserByUserName(username);
        if(null!=user){
            ActiveUser activeUser = new ActiveUser();
            activeUser.setUser(user);
            //根据用户ID查询用户拥有的角色
            List roles=this.roleService.queryRolesByUserId(user.getUserid());
            //根据用户ID查询用户拥有的权限
            List permissions=this.permissionService.queryPermissionsByUserId(user.getUserid());
            activeUser.setRoles(roles);
            activeUser.setPermissions(permissions);
            //进行密码匹配
            ByteSource salt=ByteSource.Util.bytes(user.getUsername()+user.getAddress());
            SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser,user.getUserpwd(),salt,this.getName());
            return info;
        }else{
            //用户名不存在
            return null;
        }
    }


    /**
     * 作授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        //取出activeUser
        ActiveUser activeUser= (ActiveUser) principals.getPrimaryPrincipal();
        List roles = activeUser.getRoles();
        List permissions = activeUser.getPermissions();
        if(null!=roles&&!roles.isEmpty()){ //一定要判断,如果roles为空shiro会出异常
            info.addRoles(roles);
        }
        if(permissions!=null&&!permissions.isEmpty()){
            info.addStringPermissions(permissions);
        }
        return info;
    }

}

创建ShiroProperties配置信息类


package com.wxz.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;


@Data
@ConfigurationProperties("shiro")
public class ShiroProperties {
    //散列次数
    private Integer hashIterations=2;
    //加密方式
    private String hashAlgorithmName="MD5";
    //登录
    private String loginUrl;
    //未授权证
    private String unauthorizedUrl;
    //登出
    private String logOutUrl;
    //放行的请求
    private String[] anonUrl;
    //需要认证的请求
    private String[] authcUrl;
}

创建ShiroAutoConfiguration配置类


package com.wxz.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.wxz.realm.UserRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


@Configuration
@EnableConfigurationProperties(value = ShiroProperties.class)
public class ShiroAutoConfiguration {

    private static final String SHIRO_FILTER = "shiroFilter";

    private ShiroProperties properties;

    public ShiroAutoConfiguration(ShiroProperties properties) {
        this.properties = properties;
    }

    /**
     * 创建凭证匹配器
     * @return
     */
    @Bean
    public CredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        //设置加密算法
        credentialsMatcher.setHashAlgorithmName(properties.getHashAlgorithmName());
        //设置散列次数
        credentialsMatcher.setHashIterations(properties.getHashIterations());
        return credentialsMatcher;
    }

    /**
     * 配置自定义的realm
     * @param credentialsMatcher
     * @return
     */
    @Bean
    public UserRealm userRealm(CredentialsMatcher credentialsMatcher){
        UserRealm userRealm=new UserRealm();
        //注入一个凭证匹配器
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }

    /**
     * 配置shiro的核心管理器
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 配置过滤器链
     */
    @Bean(SHIRO_FILTER)
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
        //注入安全管理器
        filterFactoryBean.setSecurityManager(securityManager);
        //设置登陆页面
        filterFactoryBean.setLoginUrl(properties.getLoginUrl());
        //设置未授权的页面
        filterFactoryBean.setUnauthorizedUrl(properties.getUnauthorizedUrl());
        //注入过滤器链
        Map map=new HashMap<>();
        String[] anonUrl = properties.getAnonUrl();//不用认证
        String[] authcUrl = properties.getAuthcUrl();//需要认证之后才能访问的
        //注入不用认证的路径
        if(null!=anonUrl&&anonUrl.length>0){
            for (String anon : anonUrl) {
                map.put(anon,"anon");
            }
        }
        //注入要认证的路径
        if(null!=authcUrl&&authcUrl.length>0){
            for (String authc : authcUrl) {
                map.put(authc,"authc");
            }
        }
        filterFactoryBean.setFilterChainDefinitionMap(map);
        return filterFactoryBean;
    }
    /**
     * 替换web.xml的配置  注册过滤器
     */
    @Bean
    public FilterRegistrationBean delegatingFilterProxyFilterRegistrationBean(){
        DelegatingFilterProxy filterProxy=new DelegatingFilterProxy();
        FilterRegistrationBean filterProxyFilterRegistrationBean=new FilterRegistrationBean<>();
        filterProxyFilterRegistrationBean.setFilter(filterProxy);
        filterProxyFilterRegistrationBean.addInitParameter("targetBeanName",SHIRO_FILTER);
        filterProxyFilterRegistrationBean.addInitParameter("targetFilterLifecycle","true");
        //设置拦击的servlet  就是前端控制器
        filterProxyFilterRegistrationBean.setServletNames(Arrays.asList(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

        return filterProxyFilterRegistrationBean;
    }
    /**
     * 这里是为了能在html页面引用shiro标签,不然不会走授权方法
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

创建LoginController


package com.wxz.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
@RequestMapping("login")
public class LoginController {

    /**
     * 跳转到登陆页面
     */
    @GetMapping("toLogin")
    public String toLogin(){
        return "login";
    }

    /**
     * 做登陆
     */
    @PostMapping("doLogin")
    public String doLogin(String username, String password, Model model){
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try{
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            return "list";
        }catch (UnknownAccountException e){
            model.addAttribute("error","用户名不存在");
        }catch (IncorrectCredentialsException e){
            model.addAttribute("error","密码不正确");
        }catch (Exception e){
            model.addAttribute("error","登陆失败,原因:"+ e.getMessage());
        }
        return "login";
    }
}

创建static/index.html




    
    Title


创建template/login.html




    
    用户登陆


用户登陆

用户名:
密码:

创建template/list.html




    
    用户管理


用户查询

用户添加

用户修改

用户删除

用户导出

启动项目测试:

zhangsan/123456  超级管理员  1234号权限user:query user:add user:update  user:delete

lisi/123456   CEO   123号权限user:query user:add user:update

wangwu/123456  保安  15号权限  user:query user:export

zhangsan登录:

【SpringBoot】的学习_第108张图片

 【SpringBoot】的学习_第109张图片

lisi登录 

 【SpringBoot】的学习_第110张图片

 【SpringBoot】的学习_第111张图片

 wangwu登录:

【SpringBoot】的学习_第112张图片

【SpringBoot】的学习_第113张图片

23.集成shiro前后端分离

复制上一个项目

​​​​​​​创建ResultObj

【SpringBoot】的学习_第114张图片

 情况说明:

  1. 用户没有登陆,直接访问资源 返回一个json提示您没有登陆
  2. 用户已经登陆,访问有权限的资源,返回controller返回的数据
  3. 用户已登陆,没有资源访问权限,返回一个您没有权限的json串

创建全局异常监控

package com.wxz.exception;

import com.wxz.commons.ResultObj;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice  //如果出在异常,直接以json串的形式返回
public class GlobalExceptionHandler {
    /**
     * 捕获未授权的全局异常
     */
    @ExceptionHandler(value = {UnauthorizedException.class})
    public ResultObj unauthorizedExp(){
        return new ResultObj(302,"您没有访问权限","");
    }
}

创建LoginFormAuthenticationFilter 处理未登陆重定向的问题

package com.wxz.config;

import com.alibaba.fastjson.JSON;
import com.wxz.commons.ResultObj;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LoginFormAuthenticationFilter extends FormAuthenticationFilter {

    /**
     * @param servletRequest
     * @param servletResponse
     * @return 返回值  如果为false就代表拦截
     * 没有登陆才会走这个方法
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        ResultObj resultObj=new ResultObj(302,"您没有登陆","");
        String json= JSON.toJSONString(resultObj);
        servletResponse.setCharacterEncoding("UTF-8");
        servletResponse.setContentType("application/json");
        servletResponse.getWriter().write(json);
        return false;
    }
}

加入fastjson依赖

  
            com.alibaba
            fastjson
            1.2.56
        

修改ShiroAutoConfiguration

/**
 * shiro的filter
 */
@Bean(SHIRO_FILTER)
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
    ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
    filterFactoryBean.setSecurityManager(securityManager);

    filterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
    filterFactoryBean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());

    //覆盖之前的authc过滤器
    Map filters=new HashMap<>();
    filters.put("authc",new LoginFormAuthenticationFilter());
    filterFactoryBean.setFilters(filters);

    String[] anonUrl = shiroProperties.getAnonUrl();//不用认证
    String[] authcUrl = shiroProperties.getAuthcUrl();//需要认证之后才能访问的
    Map map=new HashMap<>();
    if(anonUrl!=null&&anonUrl.length>0){
        for (String url : anonUrl) {
            map.put(url,"anon");
        }
    }
    if(authcUrl!=null&&authcUrl.length>0){
        for (String url : authcUrl) {
            map.put(url,"authc");
        }
    }
    map.put(shiroProperties.getLogOutUrl(), "logout");
    filterFactoryBean.setFilterChainDefinitionMap(map);
    return filterFactoryBean;
}


/**
 * 替换web.xml的配置  注册过滤器
 */
@Bean
public FilterRegistrationBean delegatingFilterProxyFilterRegistrationBean(){
    DelegatingFilterProxy filterProxy=new DelegatingFilterProxy();
    FilterRegistrationBean filterProxyFilterRegistrationBean=new FilterRegistrationBean<>();
    filterProxyFilterRegistrationBean.setFilter(filterProxy);
    filterProxyFilterRegistrationBean.addInitParameter("targetBeanName",SHIRO_FILTER);
    filterProxyFilterRegistrationBean.addInitParameter("targetFilterLifecycle","true");

    //设置拦击的servlet  就是前端控制器
    filterProxyFilterRegistrationBean.setServletNames(Arrays.asList(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

    return filterProxyFilterRegistrationBean;
}
/*加入注解的使用,不加入这个注解不生效--开始*/

/**
 * @param securityManager
 *
 *     
 *
 *     
 *     
 *         
 *     
 * @return
 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    advisorAutoProxyCreator.setProxyTargetClass(true);
    return advisorAutoProxyCreator;
}
/*加入注解的使用,不加入这个注解不生效--结束*/

修改LoginController

package com.wxz.controller;

import com.wxz.commons.ActiverUser;
import com.wxz.commons.ResultObj;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("login")
public class LoginController {
    /**
     * 登陆
     */
    @GetMapping("doLogin")
    public ResultObj doLogin(String username, String password, Model model){
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        String msg="";
        try{
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            return new ResultObj(200,"登陆成功",username);
        }catch (UnknownAccountException e){
            model.addAttribute("error","用户名不存在");
            msg="用户名不存在";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("error","密码不正确");
            msg="密码不正确";
        }catch (Exception e){
            model.addAttribute("error","登陆失败,原因:"+ e.getMessage());
            msg="登陆失败,原因:"+ e.getMessage();
        }
        return new ResultObj(401,msg,"");
    }
}

修改UserController

package com.wxz.controller;

import com.wxz.commons.ResultObj;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("user")
public class UserController {

    @GetMapping("query")
    @RequiresPermissions(value = {"user:query"})
    public Object query(){
        return new ResultObj(200,"查询成功","");
    }

    @GetMapping("add")
    @RequiresPermissions(value = {"user:add"})
    public Object add(){
        return new ResultObj(200,"添加成功","");
    }

    @GetMapping("update")
    @RequiresPermissions(value = {"user:update"})
    public Object update(){
        return new ResultObj(200,"更新成功","");
    }

    @GetMapping("delete")
    @RequiresPermissions(value = {"user:delete"})
    public Object delete(){
        return new ResultObj(200,"删除成功","");
    }

    @GetMapping("export")
    @RequiresPermissions(value = {"user:export"})
    public Object export(){
        return new ResultObj(200,"导出成功","");
    }
}

测试

http://127.0.0.1:8080/login/doLogin?username=lisi&password=123456 
http://127.0.0.1:8080/user/query
http://127.0.0.1:8080/user/export

24.普通缓存(非redis)

在SpringBoot中可以使用注解式开发缓存,默认没有开启缓存中间件,那么使用的就是存储在Map中的原理,但是我们还可以配置自己的缓存中间件,比如redis

【SpringBoot】的学习_第115张图片


    com.alibaba
    druid-spring-boot-starter
    1.1.21

 
            org.springframework.boot
            spring-boot-starter-cache
        

 创建yaml

server:
  port: 8080
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      max-active: 10
      min-idle: 2
      validation-query: select 'x'
      stat-view-servlet:
        login-username: admin
        enabled: true  #启用监控页
        login-password: admin
        allow:
        deny:
        url-pattern: /druid/*
#配置mybatis
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #输出sql
  mapper-locations:
    - classpath:mapper/*Mapper.xml

开启缓存

【SpringBoot】的学习_第116张图片

创建UserController

package com.wxz.controller;

import com.wxz.domain.User;
import com.wxz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 查询ID查询一个用户
     */
    @GetMapping("queryUserById")
    public User queryUserById(Integer id){
        return this.userService.queryUserById(id);
    }

    /**
     * 添加一个用户
     */
    @GetMapping("addUser")
    public User addUser(User user){
        return this.userService.addUser(user);
    }

    /**
     * 修改一个用户
     */
    @GetMapping("updateUser")
    public User updateUser(User user){
        return this.userService.updateUser(user);
    }

    /**
     * 删除一个用户
     */
    @GetMapping("deleteUserById")
    public String deleteUserById(Integer id){
        int i=this.userService.deleteUserById(id);
        return i>0?"删除成功":"删除失败";
    }

}

 ​​​​​​​创建UserService

package com.wxz.service;

import com.wxz.domain.User;

public interface UserService {
    User queryUserById(Integer id);

    User addUser(User user);

    User updateUser(User user);

    Integer deleteUserById(Integer id);
}

创建UserServiceImpl

package com.wxz.service.impl;

import com.wxz.domain.User;
import com.wxz.mapper.UserMapper;
import com.wxz.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

  
    @Cacheable(value = "user",key = "#id",unless = "#result==null",sync = true)
    @Override
    public User queryUserById(Integer id) {
        return userMapper.selectByPrimaryKey(id);
    }

    @Override
    @CachePut(value = "user",key = "#user.id")
    public User addUser(User user) {
        userMapper.insertSelective(user);
        return user;
    }

    @Override
    @CachePut(value = "user",key = "#user.id")
    public User updateUser(User user) {
        userMapper.updateByPrimaryKeySelective(user);
        return this.userMapper.selectByPrimaryKey(user.getId());
    }

    @Override
    @CacheEvict(value = "user",key = "#id")
    public Integer deleteUserById(Integer id) {
        return this.userMapper.deleteByPrimaryKey(id);
    }
}

修改启动类

【SpringBoot】的学习_第117张图片

25.Spring Boot定时任务 

相关注解

@Scheduled()

@PostConstruct   这个注解不是定时任务的

当IOC容器里面的所有对象全部初始化完成之后会调用的方法

- sprignBoot定时任务是与quartz整合,不需要添加任何的依赖

- 在springBoot的启动类上添加`@EnableScheduling`注解开启定时调度

- 在需要定时调度的方法上添加`@Scheduled`这个注解即可,其中可以指定**cron表达式和其他的定时方式

 开启定时任务

【SpringBoot】的学习_第118张图片

执行定时任务

package com.wxz.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


@Component
public class MyTask {

//    @Scheduled(fixedDelay = 2000)  //固定延时2秒执行一次
//    @Scheduled(fixedRate=2000) //fixedRate 每过多少秒执行一次
//    @Scheduled(initialDelay = 2000, fixedRate = 5000) //第一次延迟两秒执行,后面按照fixedRate的规则执行
    @Scheduled(cron = "0 30 0 1/1 * ? *")  //每天晚上12点半执行一次https://www.matools.com/cron/
    public void task1(){
        System.out.println("定时任务执行了。。。。。。");
    }
}
https://cron.qqe2.com/

 26.Spring Boot邮件发送

SpringBoot实现邮件功能是非常的方便快捷的,因为SpringBoot默认有starter实现了Mail。

发送邮件应该是网站的必备功能之一,什么注册验证,忘记密码或者是给用户发送营销信息。

最早期的时候我们会使用JavaMail相关api来写发送邮件的相关代码,后来spring退出了JavaMailSender更加简化了邮件发送的过程,在之后springboot对此进行了封装就有了现在的spring-boot-starter-mail。

准备工作

先去qq邮箱设置smtp开启,并获得授权码

邮箱->设置->账户->POP3/SMTP服务:开启服务后会获得权码、

【SpringBoot】的学习_第119张图片

 【SpringBoot】的学习_第120张图片


    org.springframework.boot
    spring-boot-starter-mail

编写测试类:

package com.wxz;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;

@SpringBootTest
class SpringbootHelloApplicationTests {


    @Autowired
    private JavaMailSender javaMailSender;

    @Test
    void contextLoads() {
        System.out.println(javaMailSender);
    }

    @Test
    void sendSimpleMail(){
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        //设置发送人
        simpleMailMessage.setFrom("[email protected]");
        //设置接收人
        simpleMailMessage.setTo("[email protected]");
        //设置主题
        simpleMailMessage.setSubject("这是一个测试开发的邮件发送");
        //设置内容
        simpleMailMessage.setText("收到后请认真查看 http://www.leige.plus:8088?id=123456");
        javaMailSender.send(simpleMailMessage);
    }

    /**
     * 发送一个带图片,带附件的邮件
     */
    @Test
    void sendComplexMail() throws MessagingException {
        //创建mimeMessage
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        //创建一个MimeMessageHelper,来组装参数 true表示开启附件,utf-8表示编码
        MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
        //设置发送人
        mimeMessageHelper.setFrom("[email protected]");
        //设置接收人
        String[] to ={"[email protected]"};
        mimeMessageHelper.setTo(to);
        //设置主题
        mimeMessageHelper.setSubject("这是一个复杂的邮件,带html解析和附件的哦");
        //拼接内容参数
        StringBuilder sb = new StringBuilder();
        sb.append("  

springboot 测试邮件发送复杂格式o

"); sb.append("

哈哈哈

"); sb.append("

居中

"); sb.append(" "); //如果要插入图片src='cid:picture' //设置内容,可以被html解析 mimeMessageHelper.setText(sb.toString(), true); //添加内容图片 mimeMessageHelper.addInline("picture", new File("D:\\img\\bg_car.jpg")); //添加附件 mimeMessageHelper.addAttachment("美女大图.zip", new File("D:\\img\\dog.jpg")); javaMailSender.send(mimeMessage); } }

你可能感兴趣的:(spring,boot,学习,java)