学习笔记(狂神SpringBoot全部笔记含--源码--及员工管理系统整合数据库版)

学习网站:https://www.bilibili.com/video/BV1PE411i7CV

学习笔记源码:https://gitee.com/zy-hao/StudySpringBoot

SpringBoot基本概念:

  • 简化配置:Springboot是对Spring的进一步封装,基于注解开发,舍弃了笨重的xml配置,使用yml或者properties配置
  • 产品级独立运行:每一个工程都可以打包成一个jar包,内置了Tomcat和Servlet容器,可以独立运行
  • 强大的场景启动器:每一个特定场景下的需求都封装成了一个starter,只要导入了这个starter就有了这个场景所有的一切

一、Hello World

1、环境

  • JDK
  • Maven
  • MySQL
  • SpringBoot

2、Idea中创建

选择spring initalizr,初学勾选Web即可
在这里插入图片描述
在这里插入图片描述

3、启动

  • 必须和主启动类在同一级目录下创建包和方法才能生效,这里创建包controller并创建一个JavaController类
@RestController
public class HelloWordController {
    
    

    /*
        springBoot的contextPath默认是""空字符串,不用想SSM配置Tomcat时候配置工程名,简化了配置
        访问地址:http://localhost:8080/hello
     */
    @RequestMapping("/hello")
    public String hello() {
    
    
        return "hello world";
    }
}

4、打包

  • 在idea右边的工具栏中点击Maven 选择要打包的微服务点击LifeCycle选择package打包发布(target中就会出现xxx.jar包,显示打包成功)
    在这里插入图片描述

二、原理

1、依赖

  • 主要是是依赖一个父项目,是管理项目的资源过滤及插件!
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
    <relativePath/>
</parent>

  • 点spring-boot-starter-parent进去看,就会看到官方配置好的依赖和资源版本号
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>
  • 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;

2、启动器

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

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

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

3、主启动类

//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
    
    
   public static void main(String[] args) {
    
    
     //以为是启动了一个方法,没想到启动了一个服务
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

3.1、@SpringBootApplication

  • 作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;

我们点进去看一下
在这里插入图片描述
3.2、@ComponentScan

  • 作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

3.3、@SpringBootConfiguration

  • 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

我们继续点进去看一下
在这里插入图片描述
这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;

3.3、@EnableAutoConfiguration

作用:开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;

点进注解接续查看:

在这里插入图片描述
@AutoConfigurationPackage :自动配置包

@import :Spring底层注解@import , 给容器中导入一个组件

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

这个分析完了,退到上一步,继续看

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;

我们继续点进去看一下AutoConfigurationImportSelector,有如下的一个类
在这里插入图片描述
上面的这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法
在这里插入图片描述
我们继续点击查看 loadSpringFactories 方法
在这里插入图片描述
发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

在这里插入图片描述

我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
在这里插入图片描述
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  • SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值

  • 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;

  • 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;

  • 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;

  • 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

三、yml配置注入

1、对比传统xml语法是以标记语言为中心

<server>
    <port>8081<port>
</server>

2、yml配置以数据为中心

server:
  prot: 8080
  servlet:
    # 原先的Tomcat工程路径在这里修改
    context-path: /laosong      

3、yml语法

  • 空格不能省略,键值对中间必须要有一个空格
  • 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
  • 属性和值的大小写都是十分敏感的。

4、yml配置bean属性,导入依赖

  • 依赖
<!-- 1 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

  • 创建一个application.yml

  • @ConfigurationProperties(prefix = “person”)默认是从全局配置获取,文件名只能是application.yml

  • 测试

// 3 编写测试类
@SpringBootTest
class DemoApplicationTests {
    
    
    @Autowired
    Person person;
    @Test
    void personTest() {
    
    
        System.out.println(person);
    }
}

5、yml可以安装的位置

  • file:./config和file:./config
  • file:config/appplication.yml
  • file:application.yml
  • resources:/config/application.yml
  • resources:application.yml
    执行的优先级依次递减:

四、Web开发

1、静态资源的位置

优先级:resources > static(默认) > public

2、Thymeleaf

template/**下的任何页面都需要Controller来跳转才能访问,不能直接访问

3、导入依赖

<!--thymeleaf模版引擎在,都是基于3.x版本开发-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

4、Thymeleaf常用语法

手册网址:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf

5、实现过程

  • 5.1、静态资源目录下创建index.html
<!DOCTYPE html>
<!--thymeleaf约束-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>thymeleaf</title>
</head>
<body>
<!--所有的html元素都可以被Th接管:th:xx元素名-->
<!--test=默认是转义文本-->
<h1 th:text="${msg}"></h1>
<!--utest=默认是不转义文本-->
<h1 th:utext="${msg}"></h1>
<h1>
    <!--遍历往前写item-->
    遍历一 推荐这么使用:
    <h2 th:each="user:${users}" th:text="${user}"></h2><br>
    遍历二:
    <h2 th:each="user:${users}" >[[${user}]]</h2>
</h1>
</body>
</html>

  • 5.2、编写controller
@Controller
public class ThymeleafController {
    
    

    @RequestMapping("/thymeleaf")
    public String thy(Model model){
    
    
        model.addAttribute("msg","<p>hello Thymeleaf</p>");
        model.addAttribute("users", Arrays.asList("张三","李四","王五"));
        // 后缀默认是 .html
        return "thymeleafTest";
    }
}

5.3、启动

五、员工管理系统

基础项目结构
在这里插入图片描述

1、首页及静态页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Signin Template for Bootstrap</title>
    <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/asserts/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" th:action="@{/user/login}" method="post">
    <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" alt="" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <!--如果msg的值为空则不显示消息-->
    <p style="color: #ff0000" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    <input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
    <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
</form>
</body>
</html>

dashboard.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Template for Bootstrap</title>
    <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
    <style>
        @-webkit-keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

        @keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

        .chartjs-render-monitor {
      
      
            -webkit-animation: chartjs-render-animation 0.001s;
            animation: chartjs-render-animation 0.001s;
        }
    </style>
</head>

<body>
<div th:insert="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <!--侧边栏-->
        <!--传递参数给组件-->
        <div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <div class="chartjs-size-monitor"
                 style="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
                <div class="chartjs-size-monitor-expand"
                     style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
                    <div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0"></div>
                </div>
                <div class="chartjs-size-monitor-shrink"
                     style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
                    <div style="position:absolute;width:200%;height:200%;left:0; top:0"></div>
                </div>
            </div>
            <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
                <h1 class="h2">Dashboard</h1>
                <div class="btn-toolbar mb-2 mb-md-0">
                    <div class="btn-group mr-2">
                        <button class="btn btn-sm btn-outline-secondary">Share</button>
                        <button class="btn btn-sm btn-outline-secondary">Export</button>
                    </div>
                    <button class="btn btn-sm btn-outline-secondary dropdown-toggle">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                             stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                             class="feather feather-calendar">
                            <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
                            <line x1="16" y1="2" x2="16" y2="6"></line>
                            <line x1="8" y1="2" x2="8" y2="6"></line>
                            <line x1="3" y1="10" x2="21" y2="10"></line>
                        </svg>
                        This week
                    </button>
                </div>
            </div>
            <canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454"
                    style="display: block; width: 1076px; height: 454px;"></canvas>
        </main>
    </div>
</div>
<script type="text/javascript" th:src="@{asserts/js/jquery-3.2.1.slim.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/popper.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/feather.min.js}"></script>
<script>
    import {
      
      feather} from "../static/asserts/js/feather.min";

    feather.replace()
</script>
<script type="text/javascript" th:src="@{asserts/js/Chart.min.js}"></script>
<script>
    let ctx = document.getElementById("myChart");
    let myChart = new Chart(ctx, {
      
      
        type: 'line',
        data: {
      
      
            labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
            datasets: [{
      
      
                data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
                lineTension: 0,
                backgroundColor: 'transparent',
                borderColor: '#007bff',
                borderWidth: 4,
                pointBackgroundColor: '#007bff'
            }]
        },
        options: {
      
      
            scales: {
      
      
                yAxes: [{
      
      
                    ticks: {
      
      
                        beginAtZero: false
                    }
                }]
            },
            legend: {
      
      
                display: false,
            }
        }
    });
</script>
</body>
</html>

commons.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!--头部导航栏-->
<!--顶部导航栏-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">[[${session.loginUser}]]</a>
    <input class="form-control form-control-dark w-100" type="text" placeholder="搜索" aria-label="搜索">
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" th:href="@{/user/loginOut}">注销</a>
        </li>
    </ul>
</nav>
<!--侧边栏-->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
    <div class="sidebar-sticky">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main.html}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                         stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                         class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    首页 <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                         stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                         class="feather feather-users">
                        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                        <circle cx="9" cy="7" r="4"></circle>
                        <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                        <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                    </svg>
                    员工管理
                </a>
            </li>
        </ul>
    </div>
</nav>
</html>

add.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Template for Bootstrap</title>
    <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
    <style>
        @-webkit-keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

        @keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }
    </style>
</head>
<body>
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>
        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <form class="form-horizontal" th:action="@{/emp}" method="post">
                <div class="form-group">
                    <label class="col-sm-2 control-label">名字</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" placeholder="张三" name="ename" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">邮件</label>
                    <div class="col-sm-10">
                        <input type="email" class="form-control" placeholder="[email protected]" name="email" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">性别</label>
                    <div class="col-sm-offset-2 col-sm-10">
                        <label>
                            <input type="radio" name="gender" checked value="1">&nbsp;</label>
                        &nbsp;&nbsp;&nbsp;
                        <label>
                            <input type="radio" name="gender" value="0">&nbsp;</label>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">部门</label>
                    <div class="col-sm-10">
                        <select class="form-control" name="did">
                            <option th:each="dept:${departments}" th:text="${dept.getDname()}"
                                    th:value="${dept.getId()}"></option>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">生日</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" placeholder="2000-11-11" name="birthday" required>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button class="btn btn-sm btn-success" type="submit">添加</button>
                    </div>
                </div>
            </form>
        </main>
    </div>
</div>
<script type="text/javascript" th:src="@{asserts/js/jquery-3.2.1.slim.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/popper.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/feather.min.js}"></script>
<script>
    import {
      
      feather} from "../../static/asserts/js/feather.min";

    feather.replace()
</script>
<script type="text/javascript" th:src="@{asserts/js/Chart.min.js}"></script>
<script>
    let ctx = document.getElementById("myChart");
    let myChart = new Chart(ctx, {
      
      
        type: 'line',
        data: {
      
      
            labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
            datasets: [{
      
      
                data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
                lineTension: 0,
                backgroundColor: 'transparent',
                borderColor: '#007bff',
                borderWidth: 4,
                pointBackgroundColor: '#007bff'
            }]
        },
        options: {
      
      
            scales: {
      
      
                yAxes: [{
      
      
                    ticks: {
      
      
                        beginAtZero: false
                    }
                }]
            },
            legend: {
      
      
                display: false,
            }
        }
    });
</script>
</body>
</html>

list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Template for Bootstrap</title>
    <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
    <style>
        @-webkit-keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

        @keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }
    </style>
</head>

<body>
<div th:replace="~{commons/commons::topbar}"></div>

<div class="container-fluid">
    <div class="row">
        <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">

            <form class="form-inline" style="float: left;">
                <h2>员工列表</h2>
                <p style="width: 650px;"></p>
                <a class="btn btn-sm btn-success" th:href="@{/emp}">添加</a>
            </form>

            <div class="table-responsive">
                <table class="table table-striped table-sm">
                    <thead>
                    <tr>
                        <th>编号</th>
                        <th>姓名</th>
                        <th>邮箱</th>
                        <th>性别</th>
                        <th>部门</th>
                        <th>生日</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr th:each="emp:${emps}">
                        <td th:text="${emp.getId()}"></td>
                        <td th:text="${emp.getEname()}"></td>
                        <td th:text="${emp.getEmail()}"></td>
                        <td th:text="${emp.getGender()==0?'':''}"></td>
                        <td th:text="${emp.getDname()}"></td>
                        <td th:text="${emp.getBirthday()}"></td>
                        <td>
                            <a class="btn btn-sm btn-primary" th:href="@{/upemp/}+${emp.getId()}">编辑</a>
                            <a class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">删除</a>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </main>
    </div>
</div>
<script type="text/javascript" th:src="@{asserts/js/jquery-3.2.1.slim.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/popper.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/feather.min.js}"></script>
<script>
    import {
      
      feather} from "../../static/asserts/js/feather.min";

    feather.replace()
</script>

<script type="text/javascript" th:src="@{asserts/js/Chart.min.js}"></script>
<script>
    let ctx = document.getElementById("myChart");
    let myChart = new Chart(ctx, {
      
      
        type: 'line',
        data: {
      
      
            labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
            datasets: [{
      
      
                data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
                lineTension: 0,
                backgroundColor: 'transparent',
                borderColor: '#007bff',
                borderWidth: 4,
                pointBackgroundColor: '#007bff'
            }]
        },
        options: {
      
      
            scales: {
      
      
                yAxes: [{
      
      
                    ticks: {
      
      
                        beginAtZero: false
                    }
                }]
            },
            legend: {
      
      
                display: false,
            }
        }
    });
</script>
</body>
</html>

update.html

<!DOCTYPE html>
<!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Dashboard Template for Bootstrap</title>
    <!-- Bootstrap core CSS -->
    <link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link th:href="@{/asserts/css/dashboard.css}" rel="stylesheet">
    <style type="text/css">
        /* Chart.js */

        @-webkit-keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

        @keyframes chartjs-render-animation {
      
      
            from {
      
      
                opacity: 0.99
            }
            to {
      
      
                opacity: 1
            }
        }

    </style>
</head>
<body>
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
    <div class="row">
        <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <form class="form-horizontal" th:action="@{/updateEmp}" method="post">
                <input type="hidden" name="id" th:value="${emp.getId()}">
                <div class="form-group">
                    <label class="col-sm-2 control-label">名字</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" name="ename" th:value="${emp.getEname()}" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">邮件</label>
                    <div class="col-sm-10">
                        <input type="email" class="form-control" th:value="${emp.getEmail()}" name="email" required>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">性别</label>
                    <div class="col-sm-offset-2 col-sm-10">
                        <label>
                            <input th:checked="${emp.getGender()==1}" type="radio" name="gender" checked value="1">&nbsp;</label>
                        &nbsp;&nbsp;&nbsp;
                        <label>
                            <input th:checked="${emp.getGender()==0}" type="radio" name="gender" value="0">&nbsp;</label>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">部门</label>
                    <div class="col-sm-10">
                        <select class="form-control" name="did">
                            <option th:selected="${emp.getDid()==dept.getId()}"
                                    th:each="dept:${departments}" th:text="${dept.getDname()}"
                                    th:value="${dept.getId()}"></option>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">生日</label>
                    <div class="col-sm-10">
                        <input th:value="${emp.getBirthday()}" type="text"
                               class="form-control"
                               name="birthday">
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button class="btn btn-sm btn-success" type="submit">修改</button>
                    </div>
                </div>
            </form>
        </main>
    </div>
</div>
<script type="text/javascript" th:src="@{asserts/js/jquery-3.2.1.slim.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/popper.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/bootstrap.min.js}"></script>
<script type="text/javascript" th:src="@{asserts/js/feather.min.js}"></script>
<script>
    import {
      
      feather} from "../../static/asserts/js/feather.min";

    feather.replace()
</script>
<script type="text/javascript" th:src="@{asserts/js/Chart.min.js}"></script>
<script>
    let ctx = document.getElementById("myChart");
    let myChart = new Chart(ctx, {
      
      
        type: 'line',
        data: {
      
      
            labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
            datasets: [{
      
      
                data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
                lineTension: 0,
                backgroundColor: 'transparent',
                borderColor: '#007bff',
                borderWidth: 4,
                pointBackgroundColor: '#007bff'
            }]
        },
        options: {
      
      
            scales: {
      
      
                yAxes: [{
      
      
                    ticks: {
      
      
                        beginAtZero: false
                    }
                }]
            },
            legend: {
      
      
                display: false,
            }
        }
    });
</script>
</body>
</html>

2、国际化

  • Idea中setting中“file-encoding”修改项目编码、配置文件编码均为“utf-8”,否则配置文件中文会显示乱码

在这里插入图片描述

  • resources下配置i18n文件,index.html前端页面使用th:text="#{login.btn}"等接收配置文件里的参数
    在这里插入图片描述
    login.properties
login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名

在启动类的同级目录下创建config软件包

编写MyLocalResolver

package com.zhu.config;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

//国际化
public class MyLocaleResolver implements LocaleResolver {
    
    
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
    
    
        //获取请求中的语言参数
        String lan = request.getParameter("l");
        Locale locale = Locale.getDefault(); //如果没有就使用默认的
        
        //如果请求的链接携带了国际化的参数
        if (!StringUtils.isEmpty(lan)) {
    
    
            String[] split = lan.split("_");
            //国家,地区
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    
    
    }
}

MyMvcConfig

package com.zhu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    
    
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        registry.addViewController("/main.html").setViewName("dashboard");
    }

    //自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver() {
    
    
        return new MyLocaleResolver();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**").excludePathPatterns("/index.html", "/", "/user/login", "/asserts/**");
    }
}

3、拦截器

LoginHandlerInterceptor

package com.zhu.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 拦截器
 */
public class LoginHandlerInterceptor implements HandlerInterceptor {
    
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        //登录成功之后,应该有用户的session
        Object loginUser = request.getSession().getAttribute("loginUser");

        if (loginUser == null) {
    
    
            //没有登录,而是直接进入的首页,肯定是不让进的
            request.setAttribute("msg", "没有权限,请先登录");
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        } else {
    
    
            return true;
        }
    }
}

4、CRUD

  • 这里我链接的数据库和狂神原版视频不太一样,请参考一下文章内容

在这里插入图片描述

  • 创建数据库
create table department
(
    id    int         not null
        primary key,
    dname varchar(20) not null
);

create table employee
(
    id       int auto_increment
        primary key,
    ename    varchar(50) not null,
    email    varchar(50) null,
    gender   int         null,
    birthday datetime    null,
    did      int         null
);
  • 配置yml’
spring:
  thymeleaf:
    cache: false
  messages:
    basename: i18n.login
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/springbootweb?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC

mybatis:
  type-aliases-package: com.zhu.pojo
  mapper-locations: classpath:com/zhu/mapper/*.xml

导入依赖

 <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>

实体类

Department

package com.zhu.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
//部门表
public class Department {
    
    
    private Integer id;
    private String dname;
}

Employee

package com.zhu.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
//员工表
public class Employee {
    
    
    private Integer id;
    private String ename;
    private String email;
    private Integer gender;
    private int did;
    private String birthday;
    private String dname;
}

实现类接口

DepartmentMapper

package com.zhu.mapper;

import com.zhu.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface DepartmentMapper {
    
    
    //获得所有部门信息
    List<Department> getAll();

    //通过ID的到部门
    int getIdByDname(@Param("dname") String dname);
}

EmployeeMapper

package com.zhu.mapper;

import com.zhu.pojo.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface EmployeeMapper {
    
    
    //查询全部的员工信息
    List<Employee> getAll();

    //通过ID查询员工
    Employee getEmployeeById(@Param("id") int id);

    //通过ID删除员工
    int delete(@Param("id") int id);

    //增加一个员工
    int save(Employee employee);

    //修改员工
    int updateEmpById(Employee employee);
}

接口实现类

DepartmentService

package com.zhu.service;

import com.zhu.pojo.Department;

import java.util.List;

public interface DepartmentService {
    
    
    List<Department> getAll();
}

EmployeeService

package com.zhu.service;

import com.zhu.pojo.Employee;

import java.util.List;


public interface EmployeeService {
    
    
    List<Employee> getAll();

    //通过ID查询员工
    Employee getEmployeeById(int id);

    //通过ID删除员工
    int delete(int id);

    //增加一个员工
    int save(Employee employee);

    int updateEmpById(Employee employee);
}

DepartmentServiceImpl

package com.zhu.service;

import com.zhu.mapper.DepartmentMapper;
import com.zhu.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DepartmentServiceImpl implements DepartmentService {
    
    

    @Autowired
    private DepartmentMapper departmentMapper;

    @Override
    public List<Department> getAll() {
    
    
        return departmentMapper.getAll();
    }
}

EmployeeServiceImpl

package com.zhu.service;

import com.zhu.mapper.EmployeeMapper;
import com.zhu.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


@Service
public class EmployeeServiceImpl implements EmployeeService {
    
    
    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public List<Employee> getAll() {
    
    
        return employeeMapper.getAll();
    }

    @Override
    public Employee getEmployeeById(int id) {
    
    
        return employeeMapper.getEmployeeById(id);
    }

    @Override
    public int delete(int id) {
    
    
        return employeeMapper.delete(id);
    }

    @Override
    public int save(Employee employee) {
    
    
        return employeeMapper.save(employee);
    }

    @Override
    public int updateEmpById(Employee employee) {
    
    
        return employeeMapper.updateEmpById(employee);
    }
}

sql 的xml
在这里插入图片描述

DepartmentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.zhu.mapper.DepartmentMapper">

    <select id="getAll" resultType="Department">
        select *
        from springbootweb.department
    </select>
</mapper>

EmployeeMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.zhu.mapper.EmployeeMapper">

    <select id="getAll" resultType="Employee">
        select e.id, e.ename, e.email, e.gender, e.birthday, e.did, dname
        from springbootweb.employee e
                 left join springbootweb.department d on d.id = e.did
    </select>


    <select id="getEmployeeById" resultType="Employee">
        select e.id, e.ename, e.email, e.gender, e.birthday, e.did, dname
        from springbootweb.employee e
                 left join springbootweb.department d on d.id = e.did
        where e.id = #{id}
    </select>

    <delete id="delete">
        delete
        from springbootweb.employee
        where id = #{id}
    </delete>

    <insert id="save" parameterType="Employee">
        insert into springbootweb.employee (ename, email, gender, birthday, did)
        VALUES (#{ename}, #{email}, #{gender}, #{birthday}, #{did})
    </insert>

    <update id="updateEmpById" parameterType="Employee">
        update springbootweb.employee
        set ename=#{ename},
            email = #{email},
            gender = #{gender},
            did = #{did},
            birthday = #{birthday}
        where id = #{id}
    </update>
</mapper>

控制器
EmployeeController

package com.zhu.controller;

import com.zhu.pojo.Department;
import com.zhu.pojo.Employee;
import com.zhu.service.DepartmentServiceImpl;
import com.zhu.service.EmployeeServiceImpl;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
public class EmployeeController {
    
    
    private final DepartmentServiceImpl departmentService;

    final
    EmployeeServiceImpl employeeService;

    public EmployeeController(DepartmentServiceImpl departmentService, EmployeeServiceImpl employeeService) {
    
    
        this.departmentService = departmentService;
        this.employeeService = employeeService;
    }


    @RequestMapping("/emps")
    public String list(Model model) {
    
    
        List<Employee> employees = employeeService.getAll();
        model.addAttribute("emps", employees);
        return "emp/list";
    }

    @GetMapping("/emp")
    public String toAdd(Model model) {
    
    
        //查出部门的所有信息
        List<Department> departments = departmentService.getAll();
        model.addAttribute("departments", departments);
        return "emp/add";
    }

    @PostMapping("/emp")
    public String add(Employee employee) {
    
    
        //添加的操作
        employeeService.save(employee);
        return "redirect:/emps";
    }

    //去员工的修改页面
    @GetMapping("/upemp/{id}")
    public String toUpdateEmp(@PathVariable("id") int id, Model model) {
    
    
        //查出原来的数据
        Employee employee = employeeService.getEmployeeById(id);
        model.addAttribute("emp", employee);

        List<Department> departments = departmentService.getAll();
        model.addAttribute("departments", departments);

        return "emp/update";
    }

    @PostMapping("/updateEmp")
    public String UpdateEmp(Employee employee) {
    
    
        employeeService.updateEmpById(employee);
        return "redirect:/emps";
    }

    //删除员工
    @GetMapping("/deleteEmp/{id}")
    public String deleteEmp(@PathVariable("id") int id) {
    
    
        employeeService.delete(id);
        return "redirect:/emps";
    }
}

LoginController

package com.zhu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
public class LoginController {
    
    


    @RequestMapping("/user/login")
    public String login(
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            Model model,
            HttpSession session
    ) {
    
    
        //具体的业务
        if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
    
    
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        } else {
    
    
            //告诉用户你登录失败了
            model.addAttribute("msg", "用户名或者密码错误");
            return "index";
        }
    }

    @RequestMapping("/user/loginOut")
    public String loginOut(HttpSession session) {
    
    
        session.removeAttribute("loginUser");
        return "redirect:/index.html";
    }
}

六、整合JDBC

  • 对于数据访问层,无论是SQL还是NOSQL,SpringBoot都是采用Spring Data方式统一处理

  • 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

  • application.yml
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC
    # Springboot使用 com.mysql.cj.jdbc.Driver 针对Mysql8以上,5可能会有bug
    driver-class-name: com.mysql.cj.jdbc.Driver

  • 测试查看默认数据源:hikari,据说是目前最快的数据源连接
@SpringBootTest
class JdbcApplicationTests {
    
    
    /**
     * SpringBoot只要配置了数据源,就自动将数据源封装进IOC容器,用户无需配置数据源组件,直接取出
     */
    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
    
    
        // 查看使用的数据源:目前最快的数据源:hikari.HikariDataSource
        //  System.out.println(dataSource.getClass());
        // 获取数据库连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        // 关闭数据源
        connection.close();
    }
}

  • controller
@Controller
public class JdbcController {
    
    
    @Autowired
    JdbcTemplate jdbcTemplate;

    // 查询出user表中的所有信息,没有实体类,可以使用万能的map来存
    @GetMapping("/userList")
    @ResponseBody
    public List<Map<String, Object>> userList() {
    
    
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

七、整合Druid

  • Druid是阿里开源(现在是阿帕奇)的数据源,自动整合了日志监控

1、依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.22</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、DruidConfig

Druid内置了一个监控工页面,可以通过配置监控后台进行访问:http://localhost:8080/druid/index.html

@Configuration
public class DruidConfig {
    
    

    //将自定义的Druid配置进IOC容器
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource getDataSource() {
    
    
        return new DruidDataSource();
    }


    //配置 Druid 监控管理后台的Servlet;
    //内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
    @Bean
    public ServletRegistrationBean statViewServlet() {
    
    
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
        // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
        Map<String, String> initParams = new HashMap<>();
        initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
        initParams.put("loginPassword", "123456"); //后台管理界面的登录密码

        //后台允许谁可以访问
        //initParams.put("allow", "localhost"):表示只有本机可以访问
        //initParams.put("allow", ""):为空或者为null时,表示允许所有访问
        initParams.put("allow", "");
        //deny:Druid 后台拒绝谁访问
        //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问

        //设置初始化参数
        bean.setInitParameters(initParams);
        return bean;
    }


    //配置 Druid 监控 之  web 监控的 filter
    //WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
    @Bean
    public FilterRegistrationBean webStatFilter() {
    
    
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
        Map<String, String> initParams = new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
        bean.setInitParameters(initParams);
        //"/*" 表示过滤所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

八、整合Mybatis

1、依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

2、application.yml

# 整合数据源
spring:
  datasource:
    username: root
    password: admin
    url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chacacterEncoding=utf-8&serverTimezone=UTC
    # Springboot使用 com.mysql.cj.jdbc.Driver 针对Mysql8以上,5可能会有bug
    driver-class-name: com.mysql.cj.jdbc.Driver
# 整合mybatis
mybatis:
  type-aliases-package: com.ssl.bean
  # 解决绑定异常:mapper.xml最好和接口的包名路径一致
  mapper-locations: classpath:com.ssl.mapper/*.xml


3、接口和xml,bean

// 这个注解表示了这是一个mapper的注解类
@Mapper
@Repository
public interface UserMapper {
    
    

    List<User> getAllUser();

    User getUserById(@Param("id") int id);

    void addUser(User user);

    void deleteUser(@Param("id")int id);

    User updateUser(User user);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    
    private int id;
    private String name;
    private String pwd;
}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssl.mapper.UserMapper">

    <select id="getAllUser" resultType="User">
      SELECT * FROM USER;
    </select>

    <select id="getUserById" parameterType="int" resultType="User">
      select * from user where id=#{id};
    </select>

    <insert id="addUser" parameterType="User">
        insert into user(id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>

    <update id="updateUser" parameterType="User">
        update user set name=#{name},pwd=#{pwd} where id = #{id}
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

4、controller

@Controller
public class UserController {
    
    

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/users")
    @ResponseBody
    public List<User> getUsers() {
    
    
        return userMapper.getAllUser();
    }
}

九、整合SpringSecurity

  • 功能权限、访问权限、菜单权限…,我们使用过滤器,拦截器需要写大量的原生代码,这样很不方便
  • 所以在网址设计之初,就应该考虑到权限验证的安全问题,其中Shiro、SpringSecurity使用很多

SpringSecurity是Springboot底层安全模块默认的技术选型,它可以实现强大的Web安全机制,只需要少数的spring-boot–spring-security依赖,进行少量的配置,就可以实现

1、依赖

<!--security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、controller

@Controller
public class RouterController {
    
    

    @RequestMapping({
    
    "/", "/index"})
    public String index() {
    
    
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin() {
    
    
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id) {
    
    
        return "views/level1/" + id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id) {
    
    
        return "views/level2/" + id;
    }


    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id) {
    
    
        return "views/level3/" + id;
    }
}

3、MySecurityConfig

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    // url授权:  HttpSecurity
    @Override
    protected void configure(HttpSecurity security) throws Exception {
    
    
        // 首页所有人可以访问,但是功能也只有对有权限的人可以访问
        security
                .authorizeRequests() // 认证请求
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3")
        ;
        // 自带登录页面,http://localhost:8080/login
            // 定制登录页,loginPage("/toLogin")
            // 指定表单提交url:loginProcessingUrl("/user/login")
        security.formLogin().loginPage("/toLogin")
                .usernameParameter("username").passwordParameter("password")
                .loginProcessingUrl("/user/login");
        // 开启注销功能,源码http://localhost:8080/logout,并且注销成功后跳转到/的Controller
        security.logout().logoutSuccessUrl("/");
            // 版本不同问题,可能会出现注销失败,关闭csrf
            // security.csrf().disable();
        // 开启记住我功能:本质就是记住一个cookies,默认保存2周
        security.rememberMe().rememberMeParameter("remember");
    }

    // 用户认证:AuthenticationManagerBuilder
        // SpringSecurity5 以后默认需要新郑密码密码加密方式
    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception {
    
    
        // 内存中测试数据
        builder.inMemoryAuthentication()   // SpringSecurity5 以后默认需要新郑密码密码加密方式
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
                .and()
                .withUser("admin1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2", "vip3")
        ;
    }
}

4、登录注销和记住我

// 自带登录页面,http://localhost:8080/login
    // 定制登录页,loginPage("/toLogin")
    // 指定表单提交url:loginProcessingUrl("/user/login")
security.formLogin().loginPage("/toLogin")
        .usernameParameter("username").passwordParameter("password")
        .loginProcessingUrl("/user/login");

// 开启注销功能,源码http://localhost:8080/logout,并且注销成功后跳转到/的Controller
security.logout().logoutSuccessUrl("/");

// 开启记住我功能:本质就是记住一个cookies,默认保存2周
security.rememberMe().rememberMeParameter("remember");

5、前端权限验证

<!--security整合thymeleaf,便于前端整合
            Springboot2.1.X以上需要springSecurity5的版本
            但是xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"后缀还是4依然可以使用
-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

6、前端

<!--登录注销-->
<div class="right menu">
    <!--如果未登录,就消失登录按钮-->
    <div sec:authorize="!isAuthenticated()">
        <a class="item" th:href="@{/toLogin}">
            <i class="address card icon"></i> 登录
        </a>
    </div>
    <!--如果登录,就显示用户名和注销-->
    <div sec:authorize="isAuthenticated()">
        <a class="item" th:href="@{/logout}">
            <!--从授权那里获取name-->
            用户名:<span sec:authentication="name"></span>
            <!-- 有bug不能使用,角色:<span sec:authentication="principal.getAuthorities()"></span>-->
        </a>
        <a class="item" th:href="@{/logout}">
            <i class="sign-out card icon"></i> 注销
        </a>
    </div>
</div>

十、整合Shiro

shiro官网:http://shiro.apache.org/
核心三大对象:用户Subject, 管理用户SecurityManager, 连接数据Realms

1、依赖

<!--前端交互整合-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
<!--整合Mybatis-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.22</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!-- shiro-spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
</dependency>
<!--thymeleaf-->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

2、Controller

@Controller
public class MyController {
    
    

    @RequestMapping({
    
    "/", "/index"})
    public String toIndex(Model model) {
    
    
        model.addAttribute("msg", "首页");
        return "index";
    }

    @RequestMapping("/user/add")
    public String addUser() {
    
    
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String updateUser() {
    
    
        return "user/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin() {
    
    
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
    
    
        // 获取用户
        Subject user = SecurityUtils.getSubject();
        // 封装参数,获取token
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 验证登录
        try {
    
    
            // 执行登录操作,跨类调用
            user.login(token);
            model.addAttribute("msg", "成功登录");
            return "index";
        } catch (UnknownAccountException uae) {
    
    
            model.addAttribute("msg", "用户名错误");
            return "login";
        } catch (IncorrectCredentialsException ice) {
    
    
            model.addAttribute("msg", "用户密码错误");
            return "login";
        }
    }

    @RequestMapping("/noAuth")
    @ResponseBody
    public String noAuth() {
    
    
        return "未授权,无法访问";
    }
}

3、ShiroConfig

@Configuration
public class ShiroConfig {
    
    

    // 3 获取ShiroBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(
            @Qualifier("getDefaultWebSecurityManager") WebSecurityManager securityManager
    ) {
    
    
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(securityManager);
        // 添加shiro的内置过滤器
        /*
            anon: 无需认证就可以登录
            authc:必须认证才能登录
            user: 必须拥有“记住我”这个功能
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色才能访问
         */
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        // 权限授权,访问url需要权限,支持通配符
        map.put("/user/add", "perms[user:add]");
        map.put("/user/update", "perms[user:update]");
        bean.setFilterChainDefinitionMap(map);
        // 设置登录url映射
        bean.setLoginUrl("/toLogin");
        // 设置未授权的请求
        bean.setUnauthorizedUrl("/noAuth");
        return bean;
    }

    // 2 获取安全管理器
    @Bean(name = "getDefaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(
            @Qualifier("userRealm") UserRealm userRealm) {
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 直接userRealm()传参也可以,这里演示Spring指定自动注入
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 1 创建realm对象,需要自定义另一个类
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
    
    
        return new UserRealm();
    }

    // 结合Mybatis,整合ShiroDialect进组件
    @Bean
    public ShiroDialect getShiroDialect(){
    
    
        return new ShiroDialect();
    }

}

4、userRelam

// 自定义realms,继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    
    
    @Autowired
    private UserServiceImpl userService;

    //  授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
        // 进入被拦截的url,就会进这个info
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 授权应该从数据库查出权限字段
        // info.addStringPermission("user:add");
        // 从 new SimpleAuthenticationInfo(queryUser, queryUser.getPwd(), "");传递过来第一个参数user最为subject
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        // 从数据库中获取验证权限
        info.addStringPermission(currentUser.getPerms());
        return info;
    }

    // 用户认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        // 模拟数据库中查出用户名、密码
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        User currentUser = userService.queryUserByName(userToken.getUsername());
        // 验证用户名
        if (currentUser == null) {
    
    
            // 用户名不正确,就抛出UnknownAccountException
            return null;
        }
        // 密码验证,shiro完成,不需要用户判断.直接返回
        return new SimpleAuthenticationInfo(currentUser, currentUser.getPwd(), "");
    }
}

十一、整合Swagger

1、依赖

 <!-- springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- springfox-swagger-ui -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

2、Controller

@RestController
public class HelloController {
    
    

    @RequestMapping(value = "/hello")
    public String hello() {
    
    
        return "hello";
    }
}

3、Config

@Configuration
@EnableSwagger2 // 开启Swagger2
public class MySwagger {
    
    

    // 配置SwaggerBean实例
    @Bean
    public Docket getDocker(Environment environment) {
    
    
        // 判断是否是生产环境的配置文件
        Profiles isDevPro = Profiles.of("dev");
        boolean flag = environment.acceptsProfiles(isDevPro);

        return new Docket(DocumentationType.SWAGGER_2)
                // 是否启用swagger,则浏览器不能访问
                // .enable(false)
                // 这里判断是dev生产环境才开启Swagger
                .enable(flag)
                .apiInfo(changeInfo())
                .select()
                // 扫描接口:,点进去看源码有很多种方式指定扫描方式
                .apis(RequestHandlerSelectors.basePackage("com.ssl.controller"))
                // 扫描路劲:过滤路径
                .paths(PathSelectors.ant("/hello"))
                .build();
    }

    // 更改默认info
    private ApiInfo changeInfo() {
    
    
        Contact author_contact = new Contact("laoSong", "http://123", "[email protected]");
        return new ApiInfo(
                "swagger文档学习",
                "学习swagger",
                "v1.0",
                "http://123",
                author_contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());

    }

}

测试运行:访问:http://localhost:8080/swagger-ui.html

4、指定swagger在生产环境中使用

方法:

  • 判断是不是生产环境的yml
  • 注入enable(flag)
server:
  port: 8080
# 选择使用哪些配置
spring:
  profiles:
    active: dev # 激活dev配置
---
server:
  port: 8081
spring:
  profiles: test
---
server:
  port: 8082
spring:
  profiles: dev

5、设置docket分组

配置多个不同名注入bean,供多人开发使用

@Bean
public Docket docket2() {
    
    
    return new Docket(DocumentationType.SWAGGER_2)
        .groupName("AA");
}

十二、异步任务,定时任务,邮件任务

1、异步

异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。

  • 编写方法,假装正在处理数据,使用线程设置一些延时,模拟同步等待的情况
  • 创建一个service包
  • 创建一个类AsyncService
@Service
public class AsyncService {
    
    

    //告诉Spring这是一个异步的方法
    @Async
    public void hello() {
    
    
        try{
    
    
           Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("正在处理异步请求");
    }
}

  • 编写controller包
  • 编写AsyncController类

我们去写一个Controller测试一下

@Controller
public class AsyncController {
    
    

    @Autowired
    private AsyncService asyncService;

    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
    
    
        return "hello";
    }

    // 开启异步后,先返回结果,在等待后台处理请求
    @RequestMapping("/sleep")
    @ResponseBody
    public String sleep() {
    
    
        // hello开启异步后,会创建另一个线程进行该操作
        asyncService.hello();
        return "hello";
    }
}

// 启动类开启异步注解功能
@EnableAsync
@SpringBootApplication
public class TestApplication {
    
    

    public static void main(String[] args) {
    
    

        SpringApplication.run(TestApplication.class, args);
    }

}

访问http://localhost:8080/hello进行测试,3秒后出现success,这是同步等待的情况。

2、定时任务

项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了两个接口。

  • TaskExecutor接口
  • TaskScheduler接口

两个注解:

  • @EnableScheduling
  • @Scheduled

创建一个ScheduledService

@Service
public class ScheduledService {
    
    
   
   //秒   分   时     日   月   周几
   //0 * * * * MON-FRI
   //注意cron表达式的用法;
   @Scheduled(cron = "0 * * * * 0-7")
   public void hello(){
    
    
       System.out.println("hello.....");
  }
}

这里写完定时任务之后,我们需要在主程序上增加@EnableScheduling 开启定时任务功能

@EnableAsync //开启异步注解功能
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class SpringbootTaskApplication {
    
    

   public static void main(String[] args) {
    
    
       SpringApplication.run(SpringbootTaskApplication.class, args);
  }

}

我们来详细了解下cron表达式;

http://www.bejson.com/othertools/cron/

3、邮件任务

  • 依赖
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
   <groupId>com.sun.mail</groupId>
   <artifactId>jakarta.mail</artifactId>
   <version>1.6.4</version>
   <scope>compile</scope>
</dependency>

配置文件:

[email protected]
spring.mail.password=你的qq授权码
spring.mail.host=smtp.qq.com
# qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true

获取授权码:在QQ邮箱中的设置->账户->开启pop3和smtp服务

测试

@Autowired
JavaMailSenderImpl mailSender;

@Test
public void contextLoads() {
    
    
   //邮件设置1:一个简单的邮件
   SimpleMailMessage message = new SimpleMailMessage();
   message.setSubject("通知-明天来狂神这听课");
   message.setText("今晚7:30开会");

   message.setTo("[email protected]");
   message.setFrom("[email protected]");
   mailSender.send(message);
}

@Test
public void contextLoads2() throws MessagingException {
    
    
   //邮件设置2:一个复杂的邮件
   MimeMessage mimeMessage = mailSender.createMimeMessage();
   MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

   helper.setSubject("通知-明天来狂神这听课");
   helper.setText("<b style='color:red'>今天 7:30来开会</b>",true);

   //发送附件
   helper.addAttachment("1.jpg",new File(""));
   helper.addAttachment("2.jpg",new File(""));

   helper.setTo("[email protected]");
   helper.setFrom("[email protected]");

   mailSender.send(mimeMessage);
}

十三、整合Dubbo+Zookeeper

1、RPC

两个核心模块:通讯,序列化。

2、Dubbo

官网:https://dubbo.apache.org/zh/

在这里插入图片描述
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。

服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

调用关系说明

l 服务容器负责启动,加载,运行服务提供者。

l 服务提供者在启动时,向注册中心注册自己提供的服务。

l 服务消费者在启动时,向注册中心订阅自己所需的服务。

l 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

l 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

l 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

3、zookeeper

下载地址:http://archive.apache.org/dist/zookeeper/

  • 运行/bin/zkServer.cmd ,初次运行会报错,没有zoo.cfg配置文件,可能遇到问题:闪退 !

解决方案:将conf文件夹下面的zoo_sample.cfg复制一份改名为zoo.cfg即可。编辑zkServer.cmd文件末尾添加pause 。这样运行出错就不会退出,会提示错误信息,方便找到原因。

  • 使用zkCli.cmd测试

  • ls /:列出zookeeper根下保存的所有节点

  • create –e /zhu123:创建一个kuangshen节点,值为123

  • get /zhu:获取/zhu节点的值

4、window下安装dubbo-admin

地址 :https://github.com/apache/dubbo-admin/tree/master

dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。

但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不过这个监控即使不装也不影响使用。

在项目目录下打包dubbo-admin

执行 dubbo-admin\target 下的dubbo-admin-0.0.1-SNAPSHOT.jar

java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

http://localhost:7001/ 用户名密码为:root-root;

5、SpringBoot + Dubbo + zookeeper

  • 依赖
<!-- Dubbo Spring Boot Starter -->
<dependency>
   <groupId>org.apache.dubbo</groupId>
   <artifactId>dubbo-spring-boot-starter</artifactId>
   <version>2.7.3</version>
</dependency> 
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
   <groupId>com.github.sgroschupf</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.1</version>
</dependency>

<!-- 引入zookeeper -->
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-framework</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</artifactId>
   <version>2.12.0</version>
</dependency>
<dependency>
   <groupId>org.apache.zookeeper</groupId>
   <artifactId>zookeeper</artifactId>
   <version>3.4.14</version>
   <!--排除这个slf4j-log4j12-->
   <exclusions>
       <exclusion>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
       </exclusion>
   </exclusions>
</dependency>
  • 启动zookeeper !

  • IDEA创建一个空项目;

  • 创建一个模块,实现服务提供者:provider-server , 选择web依赖即可

  • 项目创建完毕,我们写一个服务,比如卖票的服务;

编写接口

package com.kuang.provider.service;

public interface TicketService {
    
    
   public String getTicket();
}

编写实现类

package com.kuang.provider.service;

public class TicketServiceImpl implements TicketService {
    
    
   @Override
   public String getTicket() {
    
    
       return "《狂神说Java》";
  }
}
  • 创建一个模块,实现服务消费者:consumer-server , 选择web依赖即可
  • 项目创建完毕,我们写一个服务,比如用户的服务;

编写service

package com.zhu.consumer.service;

public class UserService {
    
    
   //我们需要去拿去注册中心的服务
}

服务提供者

  • 将服务提供者注册到注册中心,
  • 在springboot配置文件中配置dubbo相关属性!
#当前应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下服务
dubbo.scan.base-packages=com.zhu.provider.service
  • 在service的实现类中配置服务注解,发布服务!注意导包问题
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

@Service //将服务发布出去
@Component //放在容器中
public class TicketServiceImpl implements TicketService {
    
    
   @Override
   public String getTicket() {
    
    
       return "《狂神说Java》";
  }
}

逻辑理解 :应用启动起来,dubbo就会扫描指定的包下带有@component注解的服务,将它发布在指定的注册中心中!

服务消费者

  • 导入依赖,和之前的依赖一样;
    配置参数
#当前应用名字
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
  • 本来正常步骤是需要将服务提供者的接口打包,然后用pom文件导入,我们这里使用简单的方式,直接将服务的接口拿过来,路径必须保证正确,即和服务提供者相同;

完善消费者的服务类

package com.kuang.consumer.service;

import com.kuang.provider.service.TicketService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;

@Service //注入到容器中
public class UserService {
    
    

   @Reference //远程引用指定的服务,他会按照全类名进行匹配,看谁给注册中心注册了这个全类名
   TicketService ticketService;

   public void bugTicket(){
    
    
       String ticket = ticketService.getTicket();
       System.out.println("在注册中心买到"+ticket);
  }

}
  • 测试类编写;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConsumerServerApplicationTests {
    
    

   @Autowired
   UserService userService;

   @Test
   public void contextLoads() {
    
    

       userService.bugTicket();

  }

}

启动测试

  1. 开启zookeeper

  2. 打开dubbo-admin实现监控

  3. 开启服务者

  4. 消费者消费测试,

猜你喜欢

转载自blog.csdn.net/qq_43003203/article/details/122252625