IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot知识点总结-DX的笔记 -> 正文阅读

[Java知识库]SpringBoot知识点总结-DX的笔记

什么是SpringBoot?

  • Spring Boot 是由 Pivotal 团队提供的全新框架
  • 目的是用来简化新 Spring 应用的初始搭建以及开发过程
  • 该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置
  • 通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
  • Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。

特性

  • 约定大于配置
  • 创建独立的spring应用程序。
  • 嵌入的tomcat jetty 或者undertow 不用部署WAR文件。
  • 允许通过Maven来根据需要获取starter
  • 尽可能的使用自动配置spring
  • 提供生产就绪功能,如指标,健康检查和外部配置properties yaml yml
  • 开箱即用,没有代码生成,也无需 XML 配置,同时也可以修改默认值来满足特定的需求。

传统开发模式

  • 优点

    • 开发简单,集中式管理
    • 基本不会重复开发
    • 功能都在本地,没有分布式的管理和调用消耗
  • 缺点

    • 效率低:开发都在同一个项目改代码,相互等待,冲突不断
    • 维护难:代码功功能耦合在一起,新人不知道何从下手
    • 不灵活:构建时间长,任何小修改都要重构整个项目,耗时
    • 稳定性差:一个微小的问题,都可能导致整个应用挂掉
    • 扩展性不够:无法满足高并发下的业务需求
    • 对服务器的性能要求要统一,要高

微服务开发模式

  • 优点
    • 每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求
    • 微服务能够被小团队开发,这个小团队2-5人就可以完成了
    • 微服务是松耦合的,是有功能,有意义的服务,开发阶段或部署阶段都是独立的
    • 微服务可以使用不同的语言开发
    • 微服务能部署在中低端配置的服务器上
    • 很容易和第三方集成
    • 每个服务都有自己的存储能力,单独的库,也可以有统一的库
  • 缺点:
    • 微服务会带来过多的操作
    • 可能有双倍的努力
    • 分布式系统可能复杂难管理
    • 分布跟踪部署难
    • 当服务数量增加时,管理复杂度增加

SpringBoot的配置

配置文件

  • properties文件

    #第一种配置文件
    #mysql
    jdbc.mysql.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.mysql.url=jdbc:mysql://localhost:3306/crm?serverTimezone=UTC
    jdbc.mysql.username=root
    jdbc.mysql.password=root
    
    #druid
    druid.initialSize=10
    druid.minIdle=10
    druid.maxActive=50
    druid.maxWait=60000
    

    yml文件

    #第二种配置文件
    #myKey: myValue
    
    #yaml 层级式键值对配置
    #层级:通过换行符+空格,同一层级必须对其,父层与子层之间通常使用2个空格
    #level1:
    #  level21: aaa
    #  level2:
    #    level31: bb
    #    level32: cc
    
    #数据类型
    #简单值
    #字符串:可以不使用引号包裹,但是有特殊符号时需要使用引号包裹(例如:空格)
    name: 张三
    age: 10
    birthday: 1998/07/25
    gender: true
    obj: ~
    
    #数组(也认为是LIst集合)
    #行内
    array1: [spring, springmvc, mybatis]
    array2:
      - html
      - css
      - javascript
    
    #对象(也可认为是Map集合)
    student:
      name: 李四
      age: 22
      birth: 1999/10/17
      gender: false
      hobby: [football, games, swimming]
      address:
        city: 郑州市
        area: 金水区
        street: 民航路
    
    #主配置
    #激活环境配置profiles
    #spring:
    #  profiles:
    #    active: dev
    #server:
    #  port: 8080
    

读取配置文件

  • 本质是将配置类对象放到ioc容器中

  • 单个读取

    @Value("${name}")
    private String username;
    
  • 使用配置类读取

    • 第一种方式

      @Component + @ConfigurationProperties
      
    • 第二种方式

      @EnableConfigurationProperties + @ConfigurationProperties
      
  • 读取第三方的properties文件

    @Component
    @ConfigurationProperties(prefix = "jdbc.mysql")
    //加载第三方资源文件
    @PropertySource(value = "classpath:db.properties")
    
  • 读取原生的xml文件

  • demo

    /**
     * 读取配置文件数据
     */
    @RestController
    public class ReadConfigController {
    
        /**
         * 单个读取,不方便
         * 使用@Value注解+SpringEL来实现配置读取 
         */
        @Value("${name}")
        private String username;
        @Value("${age}")
        private Integer age;
        @Value("${birthday}")
        private Date birthday;
        @Value("${gender}")
        private Boolean gender;
        @Value("${student.name}")
        private String studentName;
        @GetMapping("/read")
        public String read(){
            return "姓名:" + username + " 年龄:" + age + " 生日:" + birthday + " 性别:" + gender + " 学生姓名:" + studentName;
        }
    
        /**
         * 使用配置类
         * 将配置类放到IOC容器,在这里自动注入
         */
        @Autowired
        private StudentProperties studentProperties;
        @GetMapping("/readProperties")
        public StudentProperties readProperties(){
            return studentProperties;
        }
    
        /**
         * 读取第三方的properties文件
         */
        @Autowired
        private MysqlProperties mysqlProperties;
        @GetMapping("/readProp")
        public MysqlProperties readProp(){
            return mysqlProperties;
        }
    
        /**
         * 读取原生的xml配置文件
         */
        @Autowired
        private Emp emp;
        @GetMapping("/readXml")
        public Emp readXml(){
            return emp;
        }
    }
    

    在配置类上加注解

    //加载原生的xml spring配置文件
    @ImportResource(locations = "classpath:applicationContext.xml")
    //开启配置文件读取,属性为要读取的配置类,可以批量读取配置类
    @EnableConfigurationProperties({StudentProperties.class, AddressProperties.class})
    @SpringBootApplication
    public class Springboot02ConfigApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Springboot02ConfigApplication.class, args);
        }
    }
    

    读取yaml文件的配置类

    /**
     * 学生配置读取类,自动读取yml配置文件
     *
     * 成员变量名称必须与配置名称保持一致(驼峰与中划线)
     * 1.@Component + @ConfigurationProperties
     * 2.@EnableConfigurationProperties + @ConfigurationProperties
     *
     * 第二种方式的@EnableConfigurationProperties注解
     * 需要放到配置类上
     * 作用是:开启配置文件读取
     */
    @Data
    //@Component //配置bean
    @ConfigurationProperties(prefix = "student")  //读取配置文件,属性用来指定层级
    public class StudentProperties {
    
        private String name;
        private Integer age;
        private Date birth;
        private Boolean gender;
        private String[] hobby;
        private AddressProperties address;
    
        //get...  set...
    }
    
    // @Component
    @Data
    @ConfigurationProperties(prefix = "student.address")
    public class AddressProperties {
        private String city;
        private String area;
        private String street;
    }
    

    读取properties文件的配置类

    /**
     * 读取#第三方的priperties文件
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "jdbc.mysql")
    //加载第三方资源文件
    @PropertySource(value = "classpath:db.properties")
    public class MysqlProperties {
    
        private String driverClassName;
        private String url;
        private String username;
        private String password;
    }
    

    读取原生的applicationContext.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--使用原生的xml配置文件,此文件默认不读,需要在配置-->
        <bean id="dept" class="com.dx.bean.Dept">
            <property name="id" value="10"/>
            <property name="dname" value="研发部"/>
        </bean>
        <bean id="emp" class="com.dx.bean.Emp">
            <property name="id" value="1001"/>
            <property name="ename" value="汤姆"/>
            <property name="dept" ref="dept"/>
        </bean>
    </beans>
    
    @Data
    public class Emp {
        private Integer id;
        private String ename;
        private Dept dept;
    }
    
    @Data
    //开启链式调用
    @Accessors(chain = true)
    public class Dept {
        private Integer id;
        private String dname;
    }
    

profile配置文件

  • 三种工作环境配置文件:开发环境(dev)、生产环境(prod)、测试环境(test)

    #环境配置
    #需要激活:在application.yml中激活
    #命名规则:application-xxx.xml
    
    #开发环境配置
    server:
      #端口号
      port: 8001
      servlet:
        #根路径
        context-path: /web-dev
    
    #生产环境配置
    server:
      port: 8003
      servlet:
        context-path: /web-prod
    
    #测试环境
    server:
      port: 8002
      servlet:
        context-path: /web-test
    

    在测试的时候,需要修改pom的编译路径,确保把所有的配置文件都编译以后再测试

    <build>
        <!-- 将所有的配置文件都编译-->
        <resources>
            <resource>
                <directory>D:\workspace\SpringBoot-Code\02-spring-boot-config</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>application.yml</include>
                </includes>
            </resource>
        </resources>
    </build>
    

部署时配置文件

  • 外部配置文件

    在D盘放一个application.yml文件 端口指定为8009
    打包后使用命令行运行并且指定
    
    java -jar aaa.jar --spring.config.location=D:/application.yml
    
  • 命令修改配置文件

    可以使用命令行参数指定(文档一行写不下的时候,不要回车)
    
    java -jar aaa.jar --server.port=8888 --server.servlet.context-path=/bjpowernode
    

配置优先级

  • 后加载的会覆盖先加载的

  • 最后加载的优先级最高

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

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

  • 配置文件可以放的位置和优先级
    classpath:/ --优先级4

    classpath:/config/ --优先级3

    file:./ --优先级2

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

日志logback

配置日志级别

  • @RestController
    //注解式日志:动态为该类声明一个静态Logger成员变量
    //@Slf4j
    public class LogController {
    
        /**
         * 使用日志:
         * 在当前类中声明一个成员变量,类型为Logger类型
         */
        private Logger log = LoggerFactory.getLogger(LogController.class);
    
        @GetMapping("/getLog")
        public String getLog(){
    
            /**
             * 在各个日志框架都有级别
             * 错误error,警告warn,信息info,调试debug,底层trace
             *
             * springboot默认级别为info
             * 可以在application.yml配置文件中配置
             *
             * 例如:当级别为info,日志输入仅会输入info以及以上级别日志
             */
            //程序出现异常
            log.error("error级别的日志");
            //程序有可能出现问题,提醒或警告
            log.warn("warn级别的日志");
            //信息:记录用户操作
            log.info("info级别的日志");
            //调试信息:开发时使用
            log.debug("debug级别的日志");
            //程序底层信息
            log.trace("trace级别的日志");
            //热部署添加内容
            log.info("无需重启自动生效...");
            return "正在记录日志";
        }
    }
    
  • 全局配置 局部配置

    #配置日志
    logging:
      level:
        #使用root配置全局的日志级别
        root: info
        #局部日志级别,两种写法
    #    com.dx.controller: trace
        com:
          dx:
            controller: trace
    

自定义日志输出格式

  #自定义日志格式
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss SSS} %-5level [%thread] %logger -- %msg%n"

生成日志文件

  #日志在文件中的输出
  file:
    #日志文件名,在根目录下可以找到文件
    name: demo.log
    #指定日志文件路径,但是不能与那么同时配置,两个只能用一个
#    path: c:/log/

  #如果想既自定义文件名 又指定文件位置,可以导入logback.xml文件,如果使用logback.xml记得把这里的配置去掉,不然会冲突

logback.xml:可以直接使用,直接放到项目中就可以

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="d:/logback/" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="cc" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
			%d表示日期时间,
			%thread表示线程名,
			%-5level:级别从左显示5个字符宽度
			%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
			%msg:日志消息,
			%n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <appender name="ff" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称
        d:/logback/springboot.log
        -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!--
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!--
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!--
		logger主要用于存放日志对象,也可以定义日志类型、级别
		name:表示匹配的logger类型前缀,也就是包的前半部分
		level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
		additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
		false:表示只用当前logger的appender-ref,true:
		表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- 自定义的 logger -->
    <logger name="com.bjpowernode.controller" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>

    <!--
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
    -->
    <root level="info">
        <!--        控制台-->
        <appender-ref ref="cc" />
        <!--        滚动文件-->
        <appender-ref ref="ff" />
    </root>
</configuration>

工具

自定义配置提示

<!--实现自定义配置的提示功能-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

热部署

<!--热部署:更新代码或页面不需要重启服务,直接Build一下就可以-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

lombok

<!--简化工具
    作用:实现注解式get/set,构造方法,toString方法...注解式日志
    条件:1.依赖,2.IDEA插件
-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
//开启对象中set方法的链式调用
@Accessors(chain = true)
//链式调用
Student s3 = new Student()
    .setId(30)
    .setName("王五")
    .setAddress("深圳")
    .setScore(82.6);
System.out.println(s3);

注解式日志

//注解式日志:动态为该类声明一个静态Logger成员变量
@Slf4j
public class LogController {
    
	  //可以代替这条语句
//     private Logger log = LoggerFactory.getLogger(LogController.class);

    @GetMapping("/getLog")
    public String getLog(){
        log.error("error级别的日志");
        log.warn("warn级别的日志");
        log.info("info级别的日志");
        log.debug("debug级别的日志");
        log.trace("trace级别的日志");
        log.info("无需重启自动生效...");
        return "正在记录日志";
    }
}

处理静态资源

配置静态资源路径

  • 默认位置

    # 默认静态资源路径:
    #   "classpath:/META-INF/resources/"
    #   "classpath:/resources/"
    #   "classpath:/static/"
    #   "classpath:/public/"
    
  • 配置自定义位置

  • 配置静态资源访问路径

    spring:
      web:
        resources:
          #设置当前项目静态资源目录
          #注意:配置自定义静态资源目录时,需要添加原有配置项,否则会覆盖原有配置
          #classpath表示在类路径下查找资源,file表示在操作系统的文件系统下查找资源 d:/
          static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/my/,file:${custom.upload}
      mvc:
        #设置当前项目静态资源的访问前缀
        static-path-pattern: /static/**
        
    #配置自定义本地的资源路径
    custom:
      upload: E:\文件\图片\壁纸
    

使用静态资源jar包

  • 导入bootstrap和jqury依赖

    <!--jquery-->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.6.0</version>
    </dependency>
    <!--bootstrap-->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>3.4.1</version>
    </dependency>
    
  • 使用demo

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/webjars/bootstrap/3.4.1/css/bootstrap.min.css">
    </head>
    <body>
      <div style="text-align: center">
          <h1>首页</h1>
          <div>
              <span id="hello">你好</span>
          </div>
          <div>
              <button type="button" class="btn btn-primary">按钮</button>
          </div>
          <div>
              <img src="/static/bbb.jpg" alt="" style="width: 300px; height: 200px">
          </div>
          <!--webjars:以jar包的方式引入静态资源,可以从maven配置依赖-->
          <script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script>
          <script type="text/javascript">
              $(function (){
                  var str = $('#hello').html();
                  alert(str);
                  $('#hello').append('springboot');
              })
          </script>
      </div>
    </body>
    </html>
    

使用模板引擎 Thymeleaf

配置Thymeleaf

  • 开发时关闭缓存、发布时打开缓存

    spring:   
      #Thymeleaf的配置
      thymeleaf:
        #页面缓存,开发时关闭缓存、发布时打开缓存
        cache: false
        #前后缀,使用默认值就可以
    #    prefix: classpath:/templates/
    #    suffix: .html
    

动态属性获取数据

  • 取值

    <!--
    要在头上添加这个,不然${msg}会报错
    xmlns:th="http://www.w3.org/1999/xhtml"-->
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    ......
    <!--
    通过动态属性来获取数据
    th:text 从域对象中获取数据,渲染到指定标签中,对特殊符号进行转码  会将传过来的标签直接当字符串输出
    th:utext 从域对象中获取数据,渲染到指定标签中,不处理特殊符号		传过来的标签会生效
    [[${msg}]]  加两个中括号,表示里面的内容是动态的
    -->
    <p th:text="${msg}"></p>
    <p th:text="${message}"></p>
    <p th:utext="${message}"></p>
    <p>[[${msg}]]</p>
        
    <!--表单取各种值-->
    <form action="/emp/edit" th:action="@{/emp/edit}" method="post">
        <div class="form-group">
            <label>员工编号</label>
            <!--取值方式一:${emp.id}-->
            <input type="text" name="id" class="form-control" th:value="${emp.id}">
        </div>
        <div class="form-group">
            <label>员工姓名</label>
            <!--取值方式二:*{ename}-->
            <input type="text" name="ename" class="form-control" th:value="*{ename}">
        </div>
        
        <div class="form-group">
            <label>入职日期</label>
            <!--取日期-->
            <input type="date" name="hiredate" class="form-control" th:value="${#dates.format(emp.hiredate, 'yyyy-MM-dd')}">
        </div>
        <div class="form-group">
            <label>员工性别</label>
            <!--select取值-->
            <select name="gender" class="form-control">
                <option value=""></option>
                <option value="1" th:selected="${emp.gender=='1'}">男性</option>
                <option value="2" th:selected="${emp.gender=='2'}">女性</option>
            </select>
        </div>
        <div class="form-group">
            <button type="button" class="btn btn-primary">提交</button>
        </div>
    </form>
    
  • 循环

    <tr th:each="emp : ${empList}">
        <td>[[${emp.id}]]</td>
        <td th:text="${emp.ename}"></td>
    </tr>
    
  • 判断

    <td>
        <span th:if="${emp.gender=='1'}">男性</span>
        <span th:if="${emp.gender=='2'}">女性</span>
    </td>
    <td th:switch="${emp.gender}">
        <span th:case="'1'">帅哥</span>
        <span th:case="'2'">靓女</span>
    </td>
    
  • 日期格式化

    //日期格式化
    //在成员变量上添加注解  @DateTimeFormat:接收参数时格式化		@JsonFormat:发送参数时格式化
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date hiredate;
    
    <!--动态页面中的日期格式化-->
    <td th:text="${#dates.format(emp.hiredate, 'yyyy-MM-dd')}"></td>
    
  • 自动根路径 @{...}

    #在application.yml文件中
    #修改项目的根路径
    server:
      servlet:
        context-path: /boot
    
    <!--
    静态路径:href="/emp/list"
    动态路径:th:href="@{/emp/list}"
    可以设置一个静态的和一个动态的,静态的留给前端用
    -->
    <p><a href="/emp/list" th:href="@{/emp/list}">员工列表</a></p>
    
  • 路径传参

    <td>
        <!--路径传参-->
        <a href="/emp/get/1" th:href="@{'/emp/get/' + ${emp.id}}" class="btn btn-warning btn-xs">编辑</a>&emsp;
        <!--路径拼接传参,传的是名值对-->
        <a href="#" th:href="@{/emp/remove(id=${emp.id},ename=${emp.ename},job=${emp.job})}" class="btn btn-danger btn-xs">删除</a>
    </td>
    
    <!--表单传参-->
    <form th:action="@{/auth/login}" method="post" style="width: 350px;margin: 10px auto;">
        <div style="color: red;">[[${message}]]</div>
        <div class="form-group">
            <label>用户名</label>
            <input type="text" name="username" class="form-control">
        </div>
        <div class="form-group">
            <label>密码</label>
            <input type="password" name="password" class="form-control">
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-primary">登录</button>
        </div>
    </form>
    

SpringMVC配置

/**
 * 自定义WebMvc配置类
 * 实现一个接口WebMvcConfigurer的配置类,相当于原生springmvc.xml配置文件
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 动态页面访问映射
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/page/login").setViewName("login");
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/page/index").setViewName("index");
    }

    /**
     * 注册拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                //配置拦截器对象
                .addInterceptor(new LoginInterceptor())
                //配置拦截器拦截路径
                .addPathPatterns("/**")
                //配置拦截器放行路径(白名单)
                .excludePathPatterns("/static/**", "/webjars/**", "/auth/login", "/page/login", "/");
    }

    /**
     * 对静态资源处理
     */
    // @Override
    // public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //     registry
    //             .addResourceHandler("/static2/**")
    //             .addResourceLocations("classpath:/my2/");
    // }

    /**
     * 添加自定义的数据格式转换器
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        // registry.addFormatter();
        registry.addConverter(new PointConverter());
    }

    /**
     * 前后端分离的项目:设置全局跨域处理
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    }
}

拦截器

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(loginUser == null){
            request.setAttribute("message", "没有登录");
            request.getRequestDispatcher("/page/login").forward(request, response);
            return false;
        }
        return true;
    }
}

类型转换器

/**
 * 自定义的类型转换器:
 * 作用:将String转换为自定义Point
 */
public class PointConverter implements Converter<String, Point> {
    @Override
    public Point convert(String source) {
        String[] ss = source.split(",");
        Point point = new Point(ss[0], ss[1]);
        return point;
    }
}
<p><a th:href="@{/getPoint(point='15,58')}">类型转换器案例</a></p>

web中的三大组件

  • servlet、filter、listener

  • 默认不支持,需要在配置类中配置bean

  • 配置三大组件

    @Configuration
    public class ServletConfig {
    
        /**
         * 注册servlet
         */
        @Bean
        public ServletRegistrationBean servletRegistrationBean(){
            ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
            //设置Servlet对象
            servletRegistrationBean.setServlet(new DemoServlet());
            //设置路径映射
            servletRegistrationBean.addUrlMappings("/demo");
            //设置初始化参数
            //单个
            servletRegistrationBean.addInitParameter("msg", "hello");
            //批量
            // servletRegistrationBean.setInitParameters();
            return servletRegistrationBean;
        }
    
        /**
         * 注册Filter
         */
        @Bean
        public FilterRegistrationBean filterRegistrationBean(){
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
            filterRegistrationBean.setFilter(new DemoFilter());
            filterRegistrationBean.addUrlPatterns("/*");
            // filterRegistrationBean.addInitParameter();
            return filterRegistrationBean;
        }
    
        /**
         * 注册Listener
         */
        @Bean
        public ServletListenerRegistrationBean servletListenerRegistrationBean(){
            ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
            servletListenerRegistrationBean.setListener(new DemoListener());
            return servletListenerRegistrationBean;
        }
    }
    

    DemoFilter.java

    @WebFilter("/*")
    public class DemoFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("DemoFilter执行了.........");
    
            //过滤器放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
    

    DemoListener.java

    @WebListener
    public class DemoListener implements ServletContextListener {
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("DemoListener执行了.....");
        }
    }
    

    DemoServlet.java

    @WebServlet("/demo")
    public class DemoServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("DemoServlet.........." + msg);
            resp.getWriter().write("ok");
        }
    
        private String msg;
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            msg = config.getInitParameter("msg");
        }
    }
    

全局异常处理器

  • public class CustomException extends RuntimeException{
    }
    
  • /**
     * 全局异常处理器
     * @RestControllerAdvice:全局异常处理器的注解
     */
    // @ControllerAdvice
    @RestControllerAdvice
    public class GlobalExceptionHander {
    
        @ExceptionHandler(CustomException.class)
        public Map<String, Object> customExceptionHandle(CustomException e){
            Map<String, Object> map = new HashMap<>();
            map.put("code", -2);
            map.put("message", "自定义异常发生");
            return map;
        }
    
        @ExceptionHandler(Exception.class)
        public Map<String, Object> exceptionHandle(Exception e){
            Map<String, Object> map = new HashMap<>();
            map.put("code", -1);
            map.put("message", "其他异常发生");
            return map;
        }
    }
    

全局日期格式化

  • 在SpringMVC使用@DateTimeFormat和@JsonFormat

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    private Date hiredate;
    
  • application.yml

    #全局输入日期格式化,局部@DateTimeFormat
    spring: 
      mvc: 
        format:
          date: yyyy-MM-dd HH:mm:ss
          
      #全局输出日期格式化,局部@JsonFormat
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    

配置文件上传

#配置文件上传
spring: 
  servlet:
    multipart:
      #单个文件大小限制
      max-file-size: 1MB
      #单次请求总文件大小限制
      max-request-size: 10MB

前后端分离

设置全局跨域处理

  • WebConfig.java配置文件

    /**
         * 前后端分离的项目:设置全局跨域处理
         * 浏览器的同源策略:
         * 在发送异步请求时,请求地址中 协议、服务器地址、服务器端口号必须保持一致
         * 如果不一致就违反浏览器同源策略,浏览器禁止访问,会接收到一个CORS错误
         *
         * 注意:协议http与https不同源
         *      地址localhost与127.0.0.1不同源
         *
         * 前后端分离的项目:设置全局跨域处理
         */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
            //跨域访问的路径(资源)
            .addMapping("/**")
            //设置允许跨域的服务器地址
            // .allowedOriginPatterns("http://localhost:63342")
            .allowedOriginPatterns("*")
            //设置允许跨域访问的请求方式
            // .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
            .allowedMethods("*")
            //设置允许跨域访问时的头部信息
            // .allowedHeaders()
            //设置暴露给请求服务器头部信息
            // .exposedHeaders()
            //设置跨域访问时是否允许携带凭证(cookie中JSESSIONID)
            .allowCredentials(true)
            //跨域访问时会先发送一个询问请求(询问当前请求是否允许跨域)
            //设置询问请求的发送周期
            .maxAge(3600);
    }
    
  • 后端资源

    @RestController
    public class UserController {
    
        @GetMapping("/user/list")
        public List<User> list(){
            User user1 = new User(1, "zhangsan", "123");
            User user2 = new User(2, "lisi", "123");
            User user3 = new User(3, "wangwu", "123");
            User user4 = new User(4, "zhaoliu", "123");
            return Arrays.asList(user1,user2,user3,user4);
        }
    }
    
  • 前端请求(另一个项目)

    $.ajax('http://localhost:8080/boot/user/list',{
        type:'get',
        success:function (res) {
            var str='';
            $.each(res,function () {
                str+='<tr>';
                    str+='<td>'+this.id+'</td>';
                    str+='<td>'+this.username+'</td>';
                    str+='<td>'+this.password+'</td>';
                    str+='</tr>';
            });
            $('#tab').html(str);
        }
    });
    

外部Tomcat

  • 外部Tomcat支持tomcat jsp,内部Tomcat不支持tomcat jsp

  • 新建项目的时候与SpringBoot相似,唯一不同是打包方式改为war

  • 自己配置TomCat

  • 将静态资源文件放到 webapp/WEB-IF/ 下面

  • 自动生成ServletInitializer.java文件

    /**
     * 在外部tomcat启动服务器时加载springboot程序
     * 如果把这个类去掉,不会启动springboot,也就无法使用@Controller等组件
     */
    public class ServletInitializer extends SpringBootServletInitializer {
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(SpringBoot05JspApplication.class);
        }
    }
    
  • 可以使用 jsp 文件

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <base href="${pageContext.request.contextPath}/">
        <title>Title</title>
    </head>
    <body>
        <div style="text-align: center">
            <h1>首页</h1>
            <p><a href="/hello">测试</a></p>
        </div>
    </body>
    </html>
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <base href="${pageContext.request.contextPath}/">
        <title>成功</title>
    </head>
    <body>
        <div style="text-align: center">
            <h1>成功</h1>
            <p>${message}</p>
        </div>
    </body>
    </html>
    
  • Controller

    @Controller
    public class HelloController {
    
        @GetMapping("/hello")
        public String hello(HttpServletRequest request){
            System.out.println("HelloController中的hello方法执行了....");
            request.setAttribute("message", "hello jsp");
            return "success";
        }
    }
    
  • application.yml

    #配置jsp页面的前缀与后缀
    spring:
      mvc:
        view:
          prefix: /WEB-INF/pages/
          suffix: .jsp
    

后端数据校验

  • 新建项目时除了选中工具和核心包外,还要选中IO中的Validation依赖

  • 自动引入依赖

    <!--后台数据校验-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    

数据校验

  • /**
     * hibernate-validator框架,后台数据校验,通过注解的方式来实现
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Accessors(chain = true)
    public class Student {
    
        /**
         * @NotNull 数据不能为null
         * @NotEmpty 数据不能为null,也不能为空字符串
         * @NotBlank 数据不能为null,也不能为空字符串,也不能由空白字符组成
         * @Size 校验字符串、数组、集合的长度
         * @Range 校验数字的范围
         * @Email 校验有效是否合法
         * @Pattern 通过正则表达式校验
         *
         * 注解通用配置项:message,校验失败时提示信息
         */
        @NotBlank(message = "姓名不能为空")
        @Size(message = "姓名长度为4~10个字符", min = 4, max = 10)
        private String name;
        @NotNull(message = "年龄不能为空")
        @Range(message = "年龄范围为0~100岁", min = 0, max = 100)
        private Integer age;
        @NotBlank(message = "邮箱不能为空")
        //xxx@xx.xx
        @Email(message = "邮箱格式不合法")
        //正则校验
        // @Pattern(message = "", regexp = "")
        private String email;
    }
    
  • 开启数据校验

    @RestController
    public class ValidationController {
    
        /**
         * 开启校验 @Validated
         *
         * 在方法的参数上标注@Validated表示开启后台数据校验,每个开启的数据校验数据之后,需要跟随一个BindingResult类型的参数
         * BindingResult对象用于接收校验失败的信息
         */
        @PostMapping("/save")
        public String save(@Validated Student student, BindingResult bindingResult){
            //判断校验是否通过
            if(bindingResult.hasErrors()){
                List<ObjectError> allErrors = bindingResult.getAllErrors();
                for (ObjectError error : allErrors) {
                    System.out.println(error.getDefaultMessage());
                }
            }else{
                System.out.println(student);
            }
            return "ok";
        }
    }
    

配置文件校验

  • application.yml

    #用于校验配置文件
    user:
      username: zhangsan
      age: 20
      email: aa@aa.aa
    
  • 配置类

    @SpringBootApplication
    //使配置类的注解生效
    @EnableConfigurationProperties(UserProperties.class)
    public class Springboot06ValidationApplication {
        public static void main(String[] args) {
            SpringApplication.run(Springboot06ValidationApplication.class, args);
        }
    }
    
    @Data
    @ConfigurationProperties(prefix = "user")
    //添加批量读取配置文件的类上
    @Validated
    public class UserProperties {
    
        @NotBlank(message = "用户名不能为空")
        @Size(message = "用户名长度为6~16个字符", min = 6, max = 16)
        private String username;
        @NotNull(message = "年龄不能为空")
        @Range(message = "年龄范围为0~100岁", min = 0, max = 100)
        private Integer age;
        @NotBlank(message = "邮箱不能为空")
        @Email(message = "邮箱格式不合法")
        private String email;
    }
    

正则表达式

  • 可以参考JDK手册:java.util.regex / Pattern

  • 后端使用正则表达式

    public class ParttenTests {
    
        @Test
        public void run(){
            /**
             * 正则表达式,验证字符串是否符合指定的规则
             */
            //1.String类
            String str = "zhang#123";
            System.out.println(str.matches("[abc]"));
            System.out.println(str.matches("[^abc]"));
            System.out.println(str.matches("[a-zA-Z]"));
            System.out.println(str.matches("[0-9]"));
            System.out.println(str.matches("[a-zA-Z0-9]"));
            System.out.println(str.matches("\\S"));
            System.out.println(str.matches("\\w{5,10}"));
            System.out.println(str.matches(".{5,10}"));
            System.out.println(str.matches(".*"));
            System.out.println("14345678901".matches("^1[3|5|6|7|8|9]\\d{9}$"));
    
            //2.Pattern类
            boolean result = Pattern.matches("^[0-9]+$", "123");
            System.out.println(result);
    
            //3.Matcher类
            Pattern pattern = Pattern.compile("^[0-9]+$");
            Matcher matcher = pattern.matcher("123a");
            System.out.println(matcher.matches());
        }
    }
    
  • 前端使用正则表达式

    <script type="text/javascript">
      var regex = /^[0-9]+$/;
      var result = regex.test('123');
      alert(result);
    </script>
    
  • 使用注解

    //正则校验
    // @Pattern(message = "", regexp = "")
    private String email;
    

aop

  • 引入依赖

    <!--aop-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
  • Controller

    @RestController
    public class AopController {
    
        @GetMapping("/list")
        public String list(){
            System.out.println("AopController中的list方法....");
            return "ok";
        }
    }
    
  • AopAspect.java

    /**
     * 切面类
     */
    @Component
    @Aspect
    // @Order(1)	//设置切面的启动顺序
    public class AopAspect {
    
        //公共切点表达式,方式一
        public static final String POINT_CUT = "execution(* com.bjpowernode.controller.*.*(..))";
        //公共切点表达式,方式二
        @Pointcut("execution(* com.bjpowernode.controller.*.*(..))")
        public void exp(){}
        
        /**
         * 前置通知
         */
        @Before("execution(* com.bjpowernode.controller.*.*(..))")
        public void before(){
            System.out.println("切面的前置通知...");
        }
        /**
         * 后置通知
         */
        @After(POINT_CUT)
        public void after(){
            System.out.println("切面的后置通知...");
        }
        /**
         * 返回通知
         */
        @AfterReturning(value = "exp()", returning = "result")
        public void returning(Object result){
            System.out.println("切面的返回通知..." + result);
        }
    }
    

连接数据库

使用jdbc

  • 创建项目选择 jdbc api和Mysql Dirver 依赖

  • application.yml

    #使用jdbc
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/student
        username: root
        password: 111111
    
        #?切换数据源,需要配置才能生效
        type: com.alibaba.druid.pool.DruidDataSource
    
        #数据源配置
        initialSize: 5
        minIdle: 10
        maxActive: 50
        maxWait: 30000
    
  • entity层 略

  • dao

    public class DeptDaoImpl implements DeptDao {
    
        /**
         * 注入JdbcTemplate对象
         * JdbcTemplate是spring-jdbc中提供的工具类,用于操作数据库
         */
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public int insert(Dept dept) {
            String sql="insert into dept(dname,loc) values(?,?)";
            return jdbcTemplate.update(sql,dept.getDname(),dept.getLoc());
        }
    }
    
  • 测试

    @SpringBootTest
    class Springboot08JdbcApplicationTests {
    
        @Autowired
        private DataSource dataSource;
    
        //查看数据源配置信息
        @Test
        void contextLoads() {
            //自带数据源是: HikariDataSource
            System.out.println("数据源:"+dataSource);
        }
    
        /**
         * 使用jdbc完成查询
         */
        @Test
        void testJdbc() throws SQLException {
            Connection connection = dataSource.getConnection();
            String sql="select deptno,dname,loc from dept where deptno=?";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1,60);
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                int deptno = resultSet.getInt("deptno");
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");
                System.out.println("编号="+deptno+" 名字="+dname+" 地址="+loc);
            }
            resultSet.close();
            preparedStatement.close();
            connection.close();
        }
    }
    

使用数据库连接池Druid

自定义配置类

  • 引入依赖,版本号不能省略

    <!--德鲁伊数据源,自己写配置类-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    
  • 配置文件按application.yml

    #配置数据源
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/student
        username: root
        password: 111111
    
        #切换数据源,需要配置才能生效
        type: com.alibaba.druid.pool.DruidDataSource
    
        #数据源配置
        initialSize: 5
        minIdle: 10
        maxActive: 50
        maxWait: 30000
    
        #开启监控: 监控stat,SQL防火墙wall,日志slf4j,需要配置
        filters: stat,wall,slf4j
    
  • 自定义的配置类

    /**
     * 自定义德鲁伊数据源配置类
     */
    @Configuration
    public class DruidConfig {
    
        @Bean
        //使用注解使配置文件的数据源生效
        @ConfigurationProperties(prefix = "spring.datasource")
        public DruidDataSource dataSource(){
            DruidDataSource druidDataSource = new DruidDataSource();
            //可以在这配置,也可以使用配置文件配置
    //        druidDataSource.setInitialSize(10);
            return druidDataSource;
        }
    
        /**
         * sql监控的servlet
         */
        @Bean
        public ServletRegistrationBean servletRegistrationBean(){
            ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
            servletRegistrationBean.setServlet(new StatViewServlet());
            servletRegistrationBean.addUrlMappings("/druid/*");
            servletRegistrationBean.addInitParameter("loginUsername","admin");
            servletRegistrationBean.addInitParameter("loginPassword", "123");
            return servletRegistrationBean;
        }
    
        /**
         * sql监控过滤器
         */
        @Bean
        public FilterRegistrationBean filterRegistrationBean(){
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
            filterRegistrationBean.setFilter(new WebStatFilter());
            filterRegistrationBean.addUrlPatterns("/*");
            //排除路径
            filterRegistrationBean.addInitParameter("exclusions", "/druid/*,*.js,*.css,*.jpg,*.png");
            return filterRegistrationBean;
        }
    }
    

自带配置类

  • 引入依赖

    <!--自带启动器的德鲁伊,配置类不用写了-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    
  • 配置

    #使用自带启动类的德鲁伊
    spring:
      datasource:
        #切换数据源
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          #必要信息
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/student
          username: root
          password: 111111
          #连接池基本信息
          initial-size: 6
          min-idle: 7
          max-active: 15
          max-wait: 20000
          #sql监控
          filter: stat,wall,slf4j
          #serlvet
          stat-view-servlet:
            enabled: true
            url-pattern: /druid/*
            login-password: 111
            login-username: root
          #filter
          web-stat-filter:
            enabled: true
            url-pattern: /*
            exclusions: "/druid/*,*.js,*.css,*.jpg,*.png"
    

JdbcTemplate工具

  • 封装了jdbc的工具

  • DeptDaoImpl.java

    @Repository
    public class DeptDaoImpl implements DeptDao {
    
        /**
         * 注入JdbcTemplate对象
         * JdbcTemplate是spring-jdbc中提供的工具类,用于操作数据库
         */
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public int insert(Dept dept) {
            String sql="insert into dept(dname,loc) values(?,?)";
            return jdbcTemplate.update(sql,dept.getDname(),dept.getLoc());
        }
    
        @Override
        public List<Dept> select() {
            /**
             * queryForList() 批量查询返回数据封装在map集合中
             * query(sql, rowMapper) 批量查询返回数据封装在指定的实体对象中
             *
             * RowMapper 行映射器,将字段与实体中的属性对照
             * BeanPropertyRowMapper 是RowMapper接口的实现类对象,根据字段名称与实体类中属性名称实现自动映射
             */
            String sql = "select deptno, dname, loc from dept order by deptno desc";
            //手动映射
            // jdbcTemplate.query(sql, new RowMapper<Dept>(){
            //     @Override
            //     public Dept mapRow(ResultSet rs, int rowNum) throws SQLException {
            //         Dept dept = new Dept();
            //         dept.setDeptno(rs.getInt("deptno"));
            //         dept.setDname(rs.getString("dname"));
            //         dept.setLoc(rs.getString("loc"));
            //         return dept;
            //     }
            // });
            RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
            List<Dept> list = jdbcTemplate.query(sql, rowMapper);
            return list;
        }
    
        @Override
        public Dept selectById(Integer deptno) {
            String sql = "select deptno, dname, loc from dept where deptno=?";
            /**
             * queryForObject(String sql, RowMapper rowMapper) 返回结果为单行多列
             * queryForObject(String sql, Class<T> requiredType) 返回结果为单行单列
             */
            // int i = jdbcTemplate.queryForObject(sql, Integer.class);
            RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
            Dept dept = jdbcTemplate.queryForObject(sql, rowMapper, deptno);
            return dept;
        }
    }
    
  • 测试类

    @SpringBootTest
    class Springboot08JdbcApplicationTests {
    
        @Autowired
        private DataSource dataSource;
    
        @Test
        void contextLoads() {
            //自带数据源是: HikariDataSource
            //查看数据源配置信息
            System.out.println("数据源:"+dataSource);
            if (dataSource instanceof DruidDataSource){
                DruidDataSource druidDataSource=(DruidDataSource) dataSource;
                System.out.println(druidDataSource.getInitialSize());
                System.out.println(druidDataSource.getMinIdle());
                System.out.println(druidDataSource.getMaxActive());
                System.out.println(druidDataSource.getMaxWait());
            }
        }
    
        @Autowired
        DeptDao deptDao;
        @Test
        void testInsert(){
            Dept dept = new Dept();
            dept.setDname("采购部");
            dept.setLoc("深圳");
            int result = deptDao.insert(dept);
            System.out.println(result);
        }
    
        @Test
        void testSelect(){
            List<Dept> list = deptDao.select();
            for (Dept dept : list) {
                System.out.println(dept);
            }
        }
    
        @Test
        void testSelectById(){
            Dept dept = deptDao.selectById(48);
            System.out.println(dept);
        }
    }
    

整合MyBatis

  • 创建项目时,加上MyBatisFramwork依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    
  • 数据库连接池依赖和分页插件

    <!--德鲁伊数据源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--分页插件-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.4.2</version>
    </dependency>
    
  • 配置文件

    #配置数据源
    spring:
      jackson:
        date-format: yyyy-MM-dd
        time-zone: GMT+8
      mvc:
        format:
          date: yyyy-MM-dd
      datasource:
        #切换数据源
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          #必要信息
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/bjpowernode
          username: root
          password: root
          #连接池基本信息
          initial-size: 6
          min-idle: 7
          max-active: 15
          max-wait: 20000
          #sql监控
          filter: stat,wall,slf4j
          #serlvet
          stat-view-servlet:
            enabled: true
            url-pattern: /druid/*
            login-password: 111
            login-username: root
          #filter
          web-stat-filter:
            enabled: true
            url-pattern: /*
            exclusions: "/druid/*,*.js,*.css,*.jpg,*.png"
    
    #mybatis配置
    #1.SqlSessionFactory配置bean -- 在自动化配置中已实现
    #2.mapper代理对象配置bean -- 通过注解的方式来实现
    mybatis:
      #加载核心配置文件
      #  config-location: classpath:mybatis.xml
      #加载mapper文件
      mapper-locations: classpath:mapper/*.xml
      #  mapper-locations: classpath:mapper/**/*.xml
      #类型别名
      type-aliases-package: com.bjpowernode.entity
      #全局参数: 与加载核心配置文件项冲突
      configuration:
        #开启下划线转驼峰
        map-underscore-to-camel-case: true
        #日志输出
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      #插件:分页(单独配置)
    
    #分页组件pagehelper
    pagehelper:
      #自动识别数据库,应用数据库方言
      #mysql: select * from table_name limit offset,pageSize
      #oracle: select * from (select *,rownum myrow from (select * from table_name) where rownum <= 10) where myrow >= 5
      #  auto-dialect: true
      #合理化分页
      reasonable: true
    
  • @SpringBootApplication
    /**
     * 批量配置mapper代理
     * 需要指定dao接口所在包
     */
     @MapperScans({
             @MapperScan(basePackages = "com.bjpowernode.dao"),
             @MapperScan(basePackages = "com.bjpowernode.dao2")
     })
     @MapperScan(basePackages = "com.bjpowernode.dao")
    public class Springboot09MybatisApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(Springboot09MybatisApplication.class, args);
        }
    }
    
  • entity

    @Data
    public class Emp {
        private Integer empno;
        private String ename;
        private String job;
        private Integer mgr;
        private Date hiredate;
        private Double sal;
        private Double comm;
        private Integer deptno;
    }
    
  • dao

    /**
     * 使用注解来配置mapper代理对象
     *
     * @Mapper
     * 作用:为当前接口生成mapper代理对象,并将代理对象放入ioc容器
     *      类似于spring中单个接口代理对象的配置
     */
    @Mapper
    public interface EmpDao {
    
        List<Emp> select();
    
        Emp selectById(Integer id);
    
        int insert(Emp entity);
    
        int update(Emp entity);
    
        int delete(Integer id);
    }
    
  • mapper

    <?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.bjpowernode.dao.EmpDao">
    
        <select id="select" resultType="emp">
            select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp order by empno asc
        </select>
        <select id="selectById" parameterType="int" resultType="Emp">
            select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where empno=#{empno}
        </select>
        <insert id="insert" parameterType="com.bjpowernode.entity.Emp">
            insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
            values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
        </insert>
        <update id="update" parameterType="com.bjpowernode.entity.Emp">
            update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno}
            where empno=#{empno}
        </update>
        <delete id="delete" parameterType="int">
            delete from emp where empno=#{empno}
        </delete>
    </mapper>
    
  • service

    public interface EmpService {
    
        Map<String, Object> page(Integer pageNumber, Integer pageSize);
    
        Emp get(Integer id);
    
        boolean save(Emp entity);
    
        boolean update(Emp entity);
    
        boolean remove(Integer id);
    }
    
    
    @Service
    public class EmpServiceImpl implements EmpService {
    
        @Autowired
        private EmpDao empDao;
    
        @Override
        public Map<String, Object> page(Integer pageNumber, Integer pageSize) {
            PageHelper.startPage(pageNumber, pageSize);
            PageInfo<Emp> pageInfo = new PageInfo<>(empDao.select());
            Map<String, Object> pageMap = new HashMap<>();
            pageMap.put("list", pageInfo.getList());
            pageMap.put("total", pageInfo.getTotal());
            return pageMap;
        }
    
        @Override
        public Emp get(Integer id) {
            return empDao.selectById(id);
        }
    
        /**
         * springboot中事务管理
         * 使用注解的方式来实现
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean save(Emp entity) {
            return empDao.insert(entity) > 0;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean update(Emp entity) {
            return empDao.update(entity) > 0;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public boolean remove(Integer id) {
            return empDao.delete(id) > 0;
        }
    }
    
  • Controller

    @RestController
    @RequestMapping("/emp")
    public class EmpController {
    
        @Autowired
        private EmpService empService;
    
        @GetMapping("/page")
        public Map<String, Object> page(Integer pageNumber, Integer pageSize){
            Map<String, Object> pageMap = empService.page(pageNumber, pageSize);
            Map<String, Object> result = new HashMap<>();
            result.put("code", 200);
            result.put("message", "ok");
            result.put("data", pageMap);
            return result;
        }
    
        @GetMapping("/get/{id}")
        public Map<String, Object> get(@PathVariable("id") Integer id){
            Map<String, Object> result = new HashMap<>();
            result.put("code", 200);
            result.put("message", "ok");
            result.put("data", empService.get(id));
            return result;
        }
    
        @PostMapping("/save")
        public Map<String, Object> save(@RequestBody Emp emp){
            Map<String, Object> result = new HashMap<>();
            result.put("code", 200);
            result.put("message", "ok");
            result.put("data", empService.save(emp));
            return result;
        }
    
        @PutMapping("/edit")
        public Map<String, Object> edit(@RequestBody Emp emp){
            Map<String, Object> result = new HashMap<>();
            result.put("code", 200);
            result.put("message", "ok");
            result.put("data", empService.update(emp));
            return result;
        }
    
        @DeleteMapping("/remove/{id}")
        public Map<String, Object> remove(@PathVariable("id") Integer id){
            Map<String, Object> result = new HashMap<>();
            result.put("code", 200);
            result.put("message", "ok");
            result.put("data", empService.remove(id));
            return result;
        }
    }
    

Api接口文档 swagger

  • 引入依赖

    <!--swagger starter-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  • 配置文件

    #swagger自定义配置
    #测试访问文档页面:http://localhost:8080/swagger-ui/index.html
    swagger3:
      base-package: com.dx.controller
      name: xxx
      url: https://gitee.com/
      email: 1233453534@qq.com
      version: 1.0
      group-name: dx
      title: "标题 "
      description: "描述信息"
      terms-of-service-url: https://gitee.com/
      license: cxs
      license-url: https://gitee.com/
    
    #时间格式
    spring:
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
      mvc:
        format:
          date: yyyy-MM-dd HH:mm:ss
        #解决swagger3.0和springboot整合的时问题
        pathmatch:
          matching-strategy: ant_path_matcher
    
  • 读取配置的类

    @Data
    @ConfigurationProperties(prefix = "swagger3")
    public class SwaggerProperties {
    
        // 扫描的包
        // 给这个包下面的接口创建文档
        private String basePackage;
        // 作者姓名
        private String name;
        // 作者主页链接
        private String url;
        // 作者邮箱
        private String email;
        // 版本号
        private String version;
        // 分组名称
        private String groupName;
        // 文档标题
        private String title;
        //文档描述
        private String description;
        // 组织地址
        private String termsOfServiceUrl;
        // 许可证
        private String license;
        // 许可链接
        private String licenseUrl;
    }
    
  • 注入配置类,配置swagger

    @Configuration
    @EnableConfigurationProperties(SwaggerProperties.class)
    public class SwaggerConfig {
    
        @Autowired
        private SwaggerProperties swaggerProperties;
    
        /**
         * 配置swagger中的标题,描述,联系人,联系方法...
         */
        public ApiInfo getApiInfo(){
            //创建联系人对象
            Contact contact = new Contact(swaggerProperties.getName(), swaggerProperties.getUrl(), swaggerProperties.getEmail());
            return new ApiInfoBuilder()
                    .contact(contact)
                    .title(swaggerProperties.getTitle())
                    .description(swaggerProperties.getDescription())
                    .version(swaggerProperties.getVersion())
                    .license(swaggerProperties.getLicense())
                    .licenseUrl(swaggerProperties.getLicenseUrl())
                    .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
                    .build();
        }
    
        /**
         * swagger的配置,基础信息,设置接口路径等等
         * RequestHandlerSelectors.basePackage()通过指定基础包来生成文档
         * RequestHandlerSelectors.withMethodAnnotation()通过指定的方法上注解来实现生成文档
         */
        @Bean
        public Docket docket(){
            return new Docket(DocumentationType.OAS_30)
                    .apiInfo(getApiInfo())
                    .select()
                    //文档的生成的位置,两种方式
                    // .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
                    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                    .paths(PathSelectors.any())
                    .build();
        }
    }
    
  • 实体

    @Data
    @ApiModel("学生实体")
    public class Student {
    
        @ApiModelProperty("学生编号")
        private Integer id;
        @ApiModelProperty("学生姓名")
        private String name;
        @ApiModelProperty("学生住址")
        private String address;
        @ApiModelProperty("出生日期")
        private Date birthday;
    }
    
  • Controller

    • 配置对类的描述注解

    • 配置对方法的描述的注解

    • 对参数的注解

      @RestController
      @RequestMapping("/student")
      public class StudentController {
      
          @ApiOperation("分页+条件查询 学生信息")
          @ApiImplicitParams({
                  @ApiImplicitParam(name = "pageNumber", value = "当前页码", required = false, dataType = "Integer",defaultValue = "1", paramType = "query"),
                  @ApiImplicitParam(name = "pageSize", value = "每页条数", required = false, dataType = "Integer",defaultValue = "10", paramType = "query")
          })
          @ApiResponses({
                  @ApiResponse(code = 0, message = "成功"),
                  @ApiResponse(code = -1, message = "失败")
          })
          @GetMapping("/page")
          public Result page(@RequestParam(value = "pageNumber",defaultValue = "1") Integer pagerNumber,
                             @RequestParam(value = "pageSize",defaultValue = "5") Integer pageSize,
                             Student student) {
              System.out.println(pagerNumber);
              System.out.println(pageSize);
              System.out.println(student);
              return Result.success();
          }
      
          @GetMapping("/get/{id}")
          @ApiOperation("根据ID获取学生信息")
          @ApiImplicitParam(name="id",value = "学生编号",required = true, dataType="Integer", paramType = "path")
          public Result get(@PathVariable("id") Integer id) {
              System.out.println(id);
              return Result.success();
          }
      
          @PutMapping("/edit")
          @ApiOperation("编辑学生信息")
          public Result edit(@RequestBody Student student){
              System.out.println(student);
              return Result.success();
          }
      
          @DeleteMapping("/remove/{id}")
          @ApiOperation("根据编号删除学生信息")
          @ApiImplicitParam(name = "id", value = "学生编号", required = true, dataType = "Integer", paramType = "path")
          public Result remove(@PathVariable("id") Integer id){
              System.out.println(id);
              return Result.success();
          }
      }
      
  • 查看swagger页面

    #测试访问文档页面:http://localhost:8080/swagger-ui/index.html
    

其他功能

spring boot 异步

  • 异步调用需要配置注解@Async

  • 启动类 开启异步调用

    //开启异步调用
    @EnableAsync
    
  • 案例:统计耗时

    @Service
    public class AsyncService {
    
        /**
         * 异步调用注解
         * 在处理请求的业务时,其中核心业务必须在同步处理中完成
         *                  辅助业务可以在异步处理中完成
         */
        @Async
        public void task1(){
            try {
                long start = System.currentTimeMillis();
                //模拟程序执行耗时
                Thread.sleep(1000);
                long end = System.currentTimeMillis();
                System.out.println("task1耗时:" + (end - start) + "毫秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Async
        public void task2(){
            try {
                long start = System.currentTimeMillis();
                //模拟程序执行耗时
                Thread.sleep(2000);
                long end = System.currentTimeMillis();
                System.out.println("task2耗时:" + (end - start) + "毫秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Async
        public void task3(){
            try {
                long start = System.currentTimeMillis();
                //模拟程序执行耗时
                Thread.sleep(3000);
                long end = System.currentTimeMillis();
                System.out.println("task3耗时:" + (end - start) + "毫秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    @RestController
    public class AsyncController {
    
        @Autowired
        private AsyncService asyncService;
    
        @GetMapping("/doAsync")
        public Map<String, Object> doAsync(){
            long start = System.currentTimeMillis();
            //调用service中的三个任务方法
            //同步:第一个方法完成之后才能调用第二方法...耗时:三个方法时间相加
            //异步:三个方法仅调用,不再登录方法执行完毕,即可向下执行
            asyncService.task1();
            asyncService.task2();
            asyncService.task3();
    
            Map<String, Object> map = new HashMap<>();
            long end = System.currentTimeMillis();
            map.put("code", 200);
            map.put("message", "调用方法成功,总耗时为" + (end-start) + "毫秒");
            return map;
        }
    }
    

定时任务

  • 在启动类上开启定时任务

    //开启定时任务
    @EnableScheduling
    
  • service 定时任务注解

  • 定时表达式

    @Service
    public class JobService {
        /**
         * @Scheduled
         * 定时任务注解
         * cron配置项,为定时表达式
         * 秒 分 时 日 月 周 年(可选)
         * *星号:表示每,每秒、每分、每时...
         * ?问好:只能在日和周两个位置出现,排除冲突
         * -中划线:表示一个范围
         * ,逗号:表示一个列表值,比如在星期中使用1,2,4
         */
        // @Scheduled(cron = "* * * ? * 1-5")
        @Scheduled(cron = "0/5 * * ? * 1-5")
        public void myJob(){
            System.out.println("定时任务...");
        }
    }
    

邮件

  • io中的 java mail … 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    
  • 开启邮箱pop3的服务,写配置文件

    spring:
      mail:
        #邮箱服务器地址     qq:smtp.qq.com    网易163:smtp.163.com
        host: smtp.qq.com
        #授权码,邮箱-》设置-》账户-》POP3/AMTP服务,开启服务后会获得授权码
        password: xxxxxceoibdaaaaa
        username: 11118422@qq.com
        default-encoding: UTF-8
    
  • 发送简单的内容

    @Autowired
    private JavaMailSender javaMailSender;
    
    /**
     * 发送基本的内容(纯文本)
     */
    @Test
    void testSend() {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        //发件人
        simpleMailMessage.setFrom("11112342@qq.com");
        //收件人
        simpleMailMessage.setTo("22212342@qq.com");
        //主题
        simpleMailMessage.setSubject("这是一个测试邮件20220812");
        //邮件内容
        simpleMailMessage.setText("测试内容2022-08-12");
        javaMailSender.send(simpleMailMessage);
    }
    
  • 发送邮件工具类

    • 添加文件
    • 添加附件
    /**
     * 测试发送复杂内容,例如图片和附件等
     */
    @Test
    void testSend2() throws MessagingException {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        // 创建一个邮件工具,可以发送附件
        MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true,"utf-8");
        mimeMessageHelper.setFrom("28718422@qq.com");
        mimeMessageHelper.setTo("28718422@qq.com");
        mimeMessage.setSubject("这是一个携带了图片和附件的邮件20220812");
        //拼接内容参数
        StringBuilder sb = new StringBuilder();
        sb.append("<html> <body> <h1 style='color:red'>springboot 测试邮件发送复杂格式o</h1>");
        sb.append("<p style='color:blue;font-size:16px'>哈哈哈</p>");
        sb.append("<p style='text-align:center'>居中</p>");
        sb.append("<img src='cid:picture'/> </body></html>");  //如果要插入图片src='cid:picture'
        //设置内容,可以被html解析
        mimeMessageHelper.setText(sb.toString(), true);
        // 从本地磁盘中读取到图片 站位到内容中去
        mimeMessageHelper.addInline("picture",new File("C:\\Users\\NINGMEI\\Desktop\\aaa\\ddd.jpg"));
        // 添加附件
        mimeMessageHelper.addAttachment("SpringBoot.doc",new File("D:\\course\\05-SpringBoot\\springboot\\document\\SpringBoot.doc"));
        javaMailSender.send(mimeMessage);
    }
    
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 00:56:31  更:2022-09-04 00:59:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 12:58:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码