• SpringSecurity权限管理系统实战—二、日志、接口文档等实现


    目录

    SpringSecurity权限管理系统实战—一、项目简介和开发环境准备
    SpringSecurity权限管理系统实战—二、日志、接口文档等实现
    SpringSecurity权限管理系统实战—三、主要页面及接口实现
    SpringSecurity权限管理系统实战—四、整合SpringSecurity(上)
    SpringSecurity权限管理系统实战—五、整合SpringSecurity(下)
    SpringSecurity权限管理系统实战—六、SpringSecurity整合jwt
    SpringSecurity权限管理系统实战—七、处理一些问题
    SpringSecurity权限管理系统实战—八、AOP 记录用户日志、异常日志
    SpringSecurity权限管理系统实战—九、数据权限的配置

    前言

    ​本篇文章的内容有点杂,搞得我都不知道怎么取标题了。

    ​上次我们已经搭建好了my-springsecurity-plus的基本环境,本次我们我们要实现功能有系统日志配置、配置swagger接口文档、配置druid连接池等

    一、Banner替换

    ​可以有些第一次接触到这个名词的小伙伴不清楚banner是什么,其实就是在运行springboot项目时控制台打印出的图案,就是下面这个东西。

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.3.1.RELEASE)
    
    

    ​这下是不是就熟悉了,其实SpringBoot支持自定义banner图案。只需要放在指定位置,SpringBoot会帮我们自动替换。Spring Boot 默认寻找 Banner 的顺序是:

    1. 依次在 Classpath 下找 文件 banner.gif , banner.jpg , 和 banner.png , 先找到谁就用谁。
    2. 继续 Classpath 下找 banner.txt
    3. 上面都没有找到的话, 用默认的 SpringBootBanner

    我们只需要在 src/main/resources 下新建一个 banner.txt,然后找一个在线生成banner的网站,例如patorjk,然后将生成的文本复制到banner.txt文件中。启动项目,查看控制台

    在这里插入图片描述

    是不是很炫酷,一个知名项目的banner是这样的

    ////////////////////////////////////////////////////////////////////
    //                          _ooOoo_                               //
    //                         o8888888o                              //
    //                         88" . "88                              //
    //                         (| ^_^ |)                              //
    //                         O\  =  /O                              //
    //                      ____/`---'\____                           //
    //                    .'  \\|     |//  `.                         //
    //                   /  \\|||  :  |||//  \                        //
    //                  /  _||||| -:- |||||-  \                       //
    //                  |   | \\\  -  /// |   |                       //
    //                  | \_|  ''\---/''  |   |                       //
    //                  \  .-\__  `-`  ___/-. /                       //
    //                ___`. .'  /--.--\  `. . ___                     //
    //              ."" '<  `.___\_<|>_/___.'  >'"".                  //
    //            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
    //            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
    //      ========`-.____`-.___\_____/___.-`____.-'========         //
    //                           `=---='                              //
    //      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
    //            佛祖保佑       永不宕机     永无BUG                    //
    ////////////////////////////////////////////////////////////////////
    
    

    二、日志

    ​在项目的开发中,日志是必不可少的一个记录事件的组件。应该很多不少刚入门的小伙伴对日志都是不怎么重视,对于我来说也是这样,即使现在我对日志也不是很重视,也没有养成记录日志的习惯。但其实日志在一个系统中尤为的重要,可以帮助快速定位bug,来保证服务的高可用。

    ​Spring Boot默认使用LogBack日志系统,如果不需要更改为其他日志系统如Log4j2等,则无需多余的配置,LogBack默认将日志打印到控制台上。

    ​而Spring Boot项目一般都会引用spring-boot-starter或者spring-boot-starter-web的依赖,这两个依赖中包含了spring-boot-starter-logging的依赖,所以我们如果不使用别的日志框架,无需修改依赖。

    ​如果我们要使用日志功能,只需要在相应类上加上@Slf4j(需要lambok插件)注解,在对应方法中log.indf(),log.error()等就可以输出日志。我们把HelloController改造成如下这样

    @Controller
    @Slf4j
    public class HelloController {
    
        @GetMapping(value = "/index")
        public String index(){
            log.info("测试");
            log.error("测试");
            return "index";
        }   
        @GetMapping(value = "/login")
        public String login(){
            return "login";
        }
        @GetMapping(value = "/console/console1")
        public String console1(){
            return "console/console1";
        }
    }
    

    ​重启项目,访问http://localhost:8080/index控制台会打印如下信息

    在这里插入图片描述

    ​那么如何把日志存贮到文件里呢?我们只要在application.yml中简单定义一下

    logging:
      file:
        path: src\main\resources\logger\ # logger文件夹需要提前生成
    

    ​启动项目,会在logger目录下生成一个spring.log文件,内容和控制台输出的一致。

    ​日志的输出格式支持自定义,但是自定义后在控制台输出的内容就不是彩色的了,当然也能定义成彩色的,还有日志文件生成的大小(总不能一直存在一个文件里吧,那不就无限大了)和存储时间等等,都可以自定义。我这里不详细介绍了,有兴趣的小伙伴可以自己了解。

    三、Swagger接口文档

    ​Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger让部署管理和使用功能强大的API变得非常简单。官方网站:http://swagger.io/。
    ​Swagger也可以用来测试接口(很多人会用postman,但是swagger可能用起来更简单一点)

    ​那么我们首先要在maven添加相关依赖

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

    ​ 这个我上一章给的依赖中有了,不要重复添加,这里只是为了说明。

    ​ 在启动类的那一层级中新建config包,在其中新建SwaggerConfig类

    @Configuration//表明这是一个配置类
    @EnableSwagger2//开启Swagger
    public class SwaggerConfig {
        @Bean
        public Docket webApiConfig(){
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("webApi")//组名称
                    .apiInfo(webApiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.codermy.myspringsecurityplus.controller"))//扫描的包
                    .paths(PathSelectors.any())
                    .build();
    
        }
        /**
         * 该套 API 说明,包含作者、简介、版本、等信息
         * @return
         */
        private ApiInfo webApiInfo(){
            return new ApiInfoBuilder()
                    .title("my-springsecurity-plus-API文档")
                    .description("本文档描述了my-springsecurity-plus接口定义")
                    .version("1.0")
                    .build();
        }
    
    }
    

    ​ 然后我们访问http://localhost:8080/swagger-ui.html

    在这里插入图片描述

    ​ 接口的名字也可以自定义,详细见Swagger 常用注解使用详解

    ​ 我们再改造一下HelloController

    @Controller
    @Slf4j
    @Api(tags = "前期测试后面会删")
    public class HelloController {
    
        @GetMapping(value = "/index")
        public String index(){
            return "index";
        }
    
        @GetMapping(value = "/login")
        public String login(){
            return "login";
        }
    
        @GetMapping(value = "/console/console1")
        @ApiOperation(value = "转发console1请求")
        public String console1(){
            return "console/console1";
        }
    }
    

    重启访问

    在这里插入图片描述

    四、主要界面接口

    接下来我们把用户管理,角色管理,和权限管理三个界面的的接口换成我们自己的。

    首先我们新建一个类来统一返回数据格式,新建utiils包,在其中新建Result类

    //统一返回结果的类
    @Data
    public class Result<T> implements Serializable {
    
        @ApiModelProperty(value = "是否成功")
        private Boolean success;
    
        @ApiModelProperty(value = "返回码")
        private Integer code;
    
        @ApiModelProperty(value = "返回消息")
        private String msg;
    
        @ApiModelProperty(value = "总数")
        private Integer count;
    
        @ApiModelProperty(value = "返回数据")
        private List<T> data = new ArrayList<T>();
    
        //把构造方法私有
        private Result() {}
    
        public static Result table_sucess() {
            Result r = new Result();
            r.setSuccess(true);
            r.setCode(ResultCode.TABLE_SUCCESS);
            r.setMsg("成功");
            return r;
        }
    
        //成功静态方法
        public static Result ok() {
            Result r = new Result();
            r.setSuccess(true);
            r.setCode(ResultCode.SUCCESS);
            r.setMsg("成功");
            return r;
        }
    
        //失败静态方法
        public static Result error() {
            Result r = new Result();
            r.setSuccess(false);
            r.setCode(ResultCode.ERROR);
            r.setMsg("失败");
            return r;
        }
        public Result success(Boolean success){
            this.setSuccess(success);
            return this;
        }
        public Result message(String message){
            this.setMsg(message);
            return this;
        }
        public Result code(Integer code){
            this.setCode(code);
            return this;
        }
    
        public Result data(List<T> list){
            this.data.addAll(list);
            return this;
        }
        public Result count(Integer count){
            this.count = count;
            return this;
        }
    }
    

    在新建一个ReslutCode接口来定义常用的状态码

    public interface ResultCode {
        /**
         * 请求t成功
         */
        public static Integer SUCCESS = 200;
        /**
         * 请求table成功
         */
        public static Integer TABLE_SUCCESS = 0;
        /**
         * 请求失败
         */
        public static Integer ERROR = 201;
    
        /**
         * 请求已经被接受
         */
        public static final Integer ACCEPTED = 202;
    
        /**
         * 操作已经执行成功,但是没有返回数据
         */
        public static final Integer NO_CONTENT = 204;
    
        /**
         * 资源已被移除
         */
        public static final Integer MOVED_PERM = 301;
    
        /**
         * 重定向
         */
        public static final Integer SEE_OTHER = 303;
    
        /**
         * 资源没有被修改
         */
        public static final Integer NOT_MODIFIED = 304;
    
        /**
         * 参数列表错误(缺少,格式不匹配)
         */
        public static final Integer BAD_REQUEST = 400;
    
        /**
         * 未授权
         */
        public static final Integer UNAUTHORIZED = 401;
    
        /**
         * 访问受限,授权过期
         */
        public static final Integer FORBIDDEN = 403;
    
        /**
         * 资源,服务未找到
         */
        public static final Integer NOT_FOUND = 404;
    
        /**
         * 不允许的http方法
         */
        public static final Integer BAD_METHOD = 405;
    
        /**
         * 资源冲突,或者资源被锁
         */
        public static final Integer CONFLICT = 409;
    
        /**
         * 不支持的数据,媒体类型
         */
        public static final Integer UNSUPPORTED_TYPE = 415;
    
        /**
         * 接口未实现
         */
        public static final Integer NOT_IMPLEMENTED = 501;
    }
    

    自定义异常处理(这里不过多解释,只简单实现直接贴代码

    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
        //指定处理什么异常
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public Result error(Exception e){
            e.printStackTrace();
            return Result.error().message("执行了全局异常");
        }
        //自定义异常
        @ExceptionHandler(MyException.class)
        @ResponseBody
        public Result error(MyException e){
            log.error(e.getMessage());
            e.printStackTrace();
            return Result.error().code(e.getCode()).message(e.getMsg());
        }
    }
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class MyException extends  RuntimeException {
        private  Integer code;//状态码
        private String  msg;//异常信息
    }
    

    新建PageTableRequest 分页工具类

    @Data
    public class PageTableRequest implements Serializable {
    
        private Integer page;//初始页
        private Integer limit;//一页几条数据
        private Integer offset;//页码
    
        public void countOffset(){
            if(null == this.page || null == this.limit){
                this.offset = 0;
                return;
            }
            this.offset = (this.page - 1) * limit;
        }
    
    }
    

    下面进入正题

    因为我这里用的是druid的连接池(之后介绍),我直接把application.yml贴出来

    server:
      port: 8080
    spring:
      profiles:
        active: dev
      application:
        name: my-springsecurity-plus
      datasource:
        driver:
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 后面时区不要忘了如果你是mysql8.0以上的版本
        url: jdbc:mysql://localhost:3306/my-springsecurity-plus?serverTimezone=Asia/Shanghai
        username: root
        password: 180430121
        type: com.alibaba.druid.pool.DruidDataSource #druid连接池之后会解释这里先复制
        druid:
          # 初始化配置
          initial-size: 3
          # 最小连接数
          min-idle: 3
          # 最大连接数
          max-active: 15
          # 获取连接超时时间
          max-wait: 5000
          # 连接有效性检测时间
          time-between-eviction-runs-millis: 90000
          # 最大空闲时间
          min-evictable-idle-time-millis: 1800000
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
          validation-query: select 1
          # 配置监控统计拦截的filters
          filters: stat
          web-stat-filter:
            url-pattern: /*
            exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
          #StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
          stat-view-servlet:
            enabled: true #是否启用StatViewServlet默认值true
            url-pattern: /druid/*
            reset-enable: true
            login-username: admin
            login-password: admin
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    
    # mybatis配置
    mybatis:
      type-aliases-package: com.codermy.myspringsecurityplus.entity
      mapper-locations: classpath:/mybatis-mappers/*
      configuration:
        map-underscore-to-camel-case: true
    
    logging:
      file:
        path: src\main\resources\logger\ # logger文件夹需要提前生成
    
    
    

    用户管理菜单接口,之前应该都创建好了相应的类,只拿这一个接口做例子,另外两个都一样

    MyUser实体类

    @Data
    @EqualsAndHashCode(callSuper = true)
    public class MyUser extends BaseEntity<Integer>{
        private static final long serialVersionUID = -6525908145032868837L;
        private String userName;
        private String password;
        private String nickName;
        private String phone;
        private String email;
        private Integer status;
        public interface Status {
            int LOCKED = 0;
            int VALID = 1;
        }
    }
    

    UserDao中新建两个方法,分页会用到

    @Mapper
    public interface UserDao {
        //分页返回所有用户
        @Select("SELECT * FROM my_user t ORDER BY t.id LIMIT #{startPosition}, #{limit}")
        List<MyUser> getAllUserByPage(@Param("startPosition")Integer startPosition,@Param("limit")Integer limit);
    
        //计算所有用户数量
        @Select("select count(*) from My_user")
        Long countAllUser();
    }
    

    UserService和UserServiceImlpl

    public interface UserService {
        Result<MyUser> getAllUsersByPage(Integer startPosition, Integer limit);
    }
    
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public Result<MyUser> getAllUsersByPage(Integer startPosition, Integer limit) {
    
            return Result.ok().count(userDao.countAllUser().intValue()).data(userDao.getAllUserByPage(startPosition,limit)).code(ResultCode.TABLE_SUCCESS);
        }
    }
    

    UserController

    @Controller
    @RequestMapping("/api/user")
    @Api(tags = "用户相关接口")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @GetMapping
        @ResponseBody
        @ApiOperation(value = "用户列表")
        public Result<MyUser> index(PageTableRequest pageTableRequest){
            pageTableRequest.countOffset();
            return userService.getAllUsersByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit());
        }
    }
    

    我们可以比较一下他需要的json(user.json,在admin/data/user.json)和我们返回的json格式

    在这里插入图片描述

    在这里插入图片描述

    他原先设置空值的可以不看,说明也用不着,然后在usr.html中把对应相同的数据,但是命名不一样的地方修改一下即可。

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    	<head>
    		<meta charset="utf-8">
    		<title></title>
    		<link rel="stylesheet" th:href="@{/PearAdmin/component/layui/css/layui.css}" />
    		<link rel="stylesheet" th:href="@{/PearAdmin/admin/css/pearCommon.css}"/>
    	</head>
    	<body class="pear-container">
    		<div class="layui-card">
    			<div class="layui-card-body">
    				<form class="layui-form" action="">
    					<div class="layui-form-item">
    						<label class="layui-form-label">用户名</label>
    						<div class="layui-input-inline">
    							<input type="text" name="nickName" placeholder="" class="layui-input">
    						</div>
    						<label class="layui-form-label">账号</label>
    						<div class="layui-input-inline">
    							<input type="text" name="userName" placeholder="" class="layui-input">
    						</div>
    						<label class="layui-form-label">地点</label>
    						<div class="layui-input-inline">
    							<select name="city" lay-verify="required">
    							        <option value=""></option>
    							        <option value="0">北京</option>
    							        <option value="1">上海</option>
    							        <option value="2">广州</option>
    							        <option value="3">深圳</option>
    							        <option value="4">杭州</option>
    							      </select>
    						</div>
    						<button class="pear-btn pear-btn-md pear-btn-primary" lay-submit lay-filter="user-query">
    							<i class="layui-icon layui-icon-search"></i>
    							查询
    						</button>
    						<button type="reset" class="pear-btn pear-btn-md">
    							<i class="layui-icon layui-icon-refresh"></i>
    							重置
    						</button>
    					</div>
    				</form>
    			</div>
    		</div>
    		<div class="layui-card">
    			<div class="layui-card-body">
    				<table id="user-table" lay-filter="user-table"></table>
    			</div>
    		</div>
    
    		<script type="text/html" id="user-toolbar">
    		    <button class="pear-btn pear-btn-primary pear-btn-md" lay-event="add">
    		        <i class="layui-icon layui-icon-add-1"></i>
    		        新增
    		    </button>
    		    <button class="pear-btn pear-btn-danger pear-btn-md" lay-event="batchRemove">
    		        <i class="layui-icon layui-icon-delete"></i>
    		        删除
    		    </button>
    		</script>
    		
    		<script type="text/html" id="user-bar">
    		    <button class="pear-btn pear-btn-primary pear-btn-sm" lay-event="edit"><i class="layui-icon layui-icon-edit"></i></button>
    		    <button class="pear-btn pear-btn-danger pear-btn-sm" lay-event="remove"><i class="layui-icon layui-icon-delete"></i></button>
    		</script>
    		
    		<script type="text/html" id="user-status">
    		    <input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="user-status" checked = "{{ d.status == 0 ? 'true' : 'false' }}">
    		</script>
    
    		
    		<script type="text/html" id="user-createTime">
    		    {{layui.util.toDateString(d.createTime, 'yyyy-MM-dd HH:mm:ss')}}
    		</script>
    
    		<script th:src="@{/PearAdmin/component/layui/layui.js}" charset="utf-8"></script>
    		<script>
    		    layui.use(['table','form','jquery'],function () {
    		        let table = layui.table;
    		        let form = layui.form;
    		        let $ = layui.jquery;
    		        let MODULE_PATH = "operate/";
                    //这里对应的field要和自己返回的json名称一致
    		        let cols = [
    		            [
    		                {type:'checkbox'},
    		                {title: '账号', field: 'userName', align:'center', 100},
    		                {title: '姓名', field: 'nickName', align:'center'},
    		                {title: '电话', field: 'phone', align:'center'},
    						{title: '邮箱', field: 'email', align:'center'},
    		                {title: '启用', field: 'status', align:'center', templet:'#user-status'},
    		                {title: '创建时间', field: 'createTime', align:'center',templet:'#user-createTime'},
    		                {title: '操作', toolbar: '#user-bar', align:'center', 130}
    		            ]
    		        ]
    		
    		        table.render({
    		            elem: '#user-table',
    		            url: '/api/user',//+++++++++++看这里  这里的url换成自己接口的url++++++++++++++
    		            page: true ,
    		            cols: cols ,
    		            skin: 'line',
    		            toolbar: '#user-toolbar',
    		            defaultToolbar: [{
    		                layEvent: 'refresh',
    		                icon: 'layui-icon-refresh',
    		            }, 'filter', 'print', 'exports']
    		        });
    		
    		        table.on('tool(user-table)', function(obj){
    		            if(obj.event === 'remove'){
    		                window.remove(obj);
    		            } else if(obj.event === 'edit'){
    		                window .edit(obj);
    		            }
    		        });
    		
    		        table.on('toolbar(user-table)', function(obj){
    		            if(obj.event === 'add'){
    		                window.add();
    		            } else if(obj.event === 'refresh'){
    		                window.refresh();
    		            } else if(obj.event === 'batchRemove'){
    		                window.batchRemove(obj);
    		            }
    		        });
    		
    		        form.on('submit(user-query)', function(data){
    		            table.reload('user-table',{where:data.field})
    		            return false;
    		        });
    		
    		        form.on('switch(user-status)', function(obj){
    		            layer.tips(this.value + ' ' + this.name + ':'+ obj.elem.checked, obj.othis);
    		        });
    		
    		        window.add = function(){
    		            layer.open({
    		                type: 2,
    		                title: '新增',
    		                shade: 0.1,
    		                area: ['500px', '400px'],
    		                content: MODULE_PATH + 'add.html'
    		            });
    		        }
    		        window.edit = function(obj){
    		            layer.open({
    		                type: 2,
    		                title: '修改',
    		                shade: 0.1,
    		                area: ['500px', '400px'],
    		                content: MODULE_PATH + 'edit.html'
    		            });
    		        }
    		
    		        window.remove = function(obj){
    		            layer.confirm('确定要删除该用户', {icon: 3, title:'提示'}, function(index){
    		                layer.close(index);
    		                let loading = layer.load();
    		                $.ajax({
    		                    url: MODULE_PATH+"remove/"+obj.data['id'],
    		                    dataType:'json',
    		                    type:'delete',
    		                    success:function(result){
    		                        layer.close(loading);
    		                        if(result.success){
    		                            layer.msg(result.msg,{icon:1,time:1000},function(){
    		                                obj.del();
    		                            });
    		                        }else{
    		                            layer.msg(result.msg,{icon:2,time:1000});
    		                        }
    		                    }
    		                })
    		            });
    		        }
    		
    		        window.batchRemove = function(obj){
    		            let data = table.checkStatus(obj.config.id).data;
    		            if(data.length === 0){
    		                layer.msg("未选中数据",{icon:3,time:1000});
    		                return false;
    		            }
    		            let ids = "";
    		            for(let i = 0;i<data.length;i++){
    		                ids += data[i].id+",";
    		            }
    		            ids = ids.substr(0,ids.length-1);
    		            layer.confirm('确定要删除这些用户', {icon: 3, title:'提示'}, function(index){
    		                layer.close(index);
    		                let loading = layer.load();
    		                $.ajax({
    		                    url: MODULE_PATH+"batchRemove/"+ids,
    		                    dataType:'json',
    		                    type:'delete',
    		                    success:function(result){
    		                        layer.close(loading);
    		                        if(result.success){
    		                            layer.msg(result.msg,{icon:1,time:1000},function(){
    		                                table.reload('user-table');
    		                            });
    		                        }else{
    		                            layer.msg(result.msg,{icon:2,time:1000});
    		                        }
    		                    }
    		                })
    		            });
    		        }
    		
    		        window.refresh = function(param){
    		            table.reload('user-table');
    		        }
    		    })
    		</script>
    	</body>
    </html>
    
    

    这样当我们再次点击用户管理时,访问的就是自己的接口了

    在这里插入图片描述

    原本自己看别人的教学博客时,是真的希望人家把所有的代码一字不差的贴上来。等到自己写的时候就觉得还是有道理的,代码太占篇幅了,还影响博客的观感。所以另外两个界面我就补贴代码了,大家仿照这个来就行。

    在这里插入图片描述
    放两张图片,让大家看一下改完的效果。

    在这里插入图片描述
    在这里插入图片描述

    数据库文件和同步代码在giteegithub中可以获取

    五、Druid连接池

    Druid是阿里开源的数据库连接池,作为后起之秀,性能比dbcp、c3p0更高,使用也越来越广泛。

    当然Druid不仅仅是一个连接池,还有很多其他的功能。

    druid的优点

    • 高性能。性能比dbcp、c3p0高很多。
    • 只要是jdbc支持的数据库,druid都支持,对数据库的支持性好。并且Druid针对oracle、mysql做了特别优化。
    • 提供监控功能。可以监控sql语句的执行时间、ResultSet持有时间、返回行数、更新行数、错误次数、错误堆栈等信息,来了解连接池、sql语句的工作情况,方便统计、分析SQL的执行性能

    如何使用??

    导入依赖,之前给的依赖中就有不用重复导入

    		<!--druid连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.21</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    

    application.yml中配置

    spring:
      profiles:
        active: dev
      application:
        name: my-springsecurity-plus
      datasource:
        driver:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/my-springsecurity-plus?serverTimezone=Asia/Shanghai
        username: root
        password: 180430121
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          # 初始化配置
          initial-size: 3
          # 最小连接数
          min-idle: 3
          # 最大连接数
          max-active: 15
          # 获取连接超时时间
          max-wait: 5000
          # 连接有效性检测时间
          time-between-eviction-runs-millis: 90000
          # 最大空闲时间
          min-evictable-idle-time-millis: 1800000
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
    
          validation-query: select 1
          # 配置监控统计拦截的filters
          filters: stat
          web-stat-filter:
            url-pattern: /*
            exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
          #StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
          stat-view-servlet:
            enabled: true #是否启用StatViewServlet默认值true
            url-pattern: /druid/*
            reset-enable: true
            login-username: admin #用户名
            login-password: admin #密码
    

    更详细的配置这里就不介绍了。
    然后重启项目访问http://localhost:8080/druid/login.html输入用户名密码就可以看到界面了。
    在这里插入图片描述
    呼,终于又写完一篇,写代码的时候真没感觉这么累,像我这种文笔差的经常写着写着就把自己写乱了。。。。。

  • 相关阅读:
    MYSQL: 什么是MYSQLD Service
    100 logging模块
    099 hashlib和hmac模块
    098 json和pickle模块
    097 sys模块
    096 os模块
    095 random模块
    094 datetime模块
    093 time模块
    092 模块基础实战之ATM和购物车系统分文件处理
  • 原文地址:https://www.cnblogs.com/codermy/p/13516369.html
Copyright © 2020-2023  润新知