08_SpringBoot_整合PageHelper(掌握)

PageHelper插件

我们在正常的查询业务之中,只需要加上一行代码就可以实现分页的数据的封装处理

实现原理

PageHelper方法使用了静态的ThreadLocal参数,分页参数和线程是绑定的。内部流程是ThreadLocal中设置了分页参数(pageIndex,pageSize),之后在查询执行的时候,获取当线程中的分页参数,执行查询的时候通过拦截器在sql语句中添加分页参数,之后实现分页查询,查询结束后在 finally 语句中清除ThreadLocal中的查询参数

使用方法

1.调用PageHelper方法:PageHelper.startPage(pageNum, pageSize)

2. MyBatis 查询方法

注意:只要你可以保证在PageHelper方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为PageHelper在finally代码段中自动清除了ThreadLocal存储的对象。

MyBatis整合PageHelper插件,自行阅读即可

mybatis中pageHelper的配置使用_铛铛响的博客-CSDN博客

添加PageHelper启动器依赖

1. <dependency>
2.      <groupId>com.github.pagehelper</groupId>
3.      <artifactId>pagehelper-spring-boot-starter</artifactId>
4.      <version>1.2.12</version>
5.  </dependency>

EmpController

1. package com.msb.controller;
2.
3.  import com.msb.pojo.Emp;
4.  import com.msb.service.EmpService;
5.  import org.springframework.beans.factory.annotation.Autowired;
6.  import org.springframework.stereotype.Controller;
7.  import org.springframework.web.bind.annotation.PathVariable;
8.  import org.springframework.web.bind.annotation.RequestMapping;
9.  import org.springframework.web.bind.annotation.ResponseBody;
10.
11. import java.util.List;
12.
13. /**
14.  * @Author: Ma HaiYang
15.  * @Description: MircoMessage:Mark_7001
16.  */
17. @Controller
18. @RequestMapping("/emp")
19. public class EmpController {
20.
21.     @Autowired
22.     private EmpService empService;
23.
24.     @RequestMapping("/findByPage/{pageNum}/{pageSize}")
25.     @ResponseBody
26.     public List<Emp> findByPage(@PathVariable("pageNum") Integer pageNum,@PathVariable("pageSize") Integer pageSize){
27.         return empService.findByPage(pageNum,pageSize);
28.     }
29. }
30.

Service层代码编写

1. package com.msb.service.impl;
2.
3.  import com.github.pagehelper.Page;
4.  import com.github.pagehelper.PageHelper;
5.  import com.github.pagehelper.PageInfo;
6.  import com.msb.mapper.EmpMapper;
7.  import com.msb.pojo.Emp;
8.  import com.msb.service.EmpService;
9.  import org.springframework.beans.factory.annotation.Autowired;
10. import org.springframework.stereotype.Service;
11.
12. import java.util.List;
13.
14. /**
15.  * @Author: Ma HaiYang
16.  * @Description: MircoMessage:Mark_7001
17.  */
18. @Service
19. public class EmpServiceImpl implements EmpService {
20.     @Autowired
21.     private EmpMapper empMapper;
22.     @Override
23.     public List<Emp> findByPage(Integer pageNum, Integer pageSize) {
24.         Page<Emp> page = PageHelper.startPage(pageNum, pageSize);
25.         List<Emp> list =empMapper.findAll();
26.
27.         // 方式1
28.         System.out.println("当前页:"+page.getPageNum());
29.         System.out.println("总页数"+page.getPages());
30.         System.out.println("页大小:"+page.getPageSize());
31.         System.out.println("总记录数:"+page.getTotal());
32.         System.out.println("当前页数据"+page.getResult());
33.
34.         // 方式2
35.         PageInfo<Emp> pi =new PageInfo<>(list);
36.         System.out.println("当前页"+pi.getPageNum());
37.         System.out.println("总页数"+pi.getPages());
38.         System.out.println("页大小"+pi.getSize());
39.         System.out.println("总记录数"+pi.getTotal());
40.         System.out.println("当前页数据"+pi.getList());
41.
42.         return list;
43.     }
44. }

PageInfo对象和Page对象的区别

Page和PageInfo_程序员^晓洋的博客-CSDN博客_page和pageinfo

09_SpringBoot_整合Druid((掌握)

Druid是由阿里巴巴推出的数据库连接池。它结合了C3P0、DBCP、PROXOOL等数据库连接池的优点。之所以从众多数据库连接池中脱颖而出,还有一个重要的原因就是它包含控制台,很方便的帮助我们实现对于sql执行的监控。

添加依赖

<dependency>
2.              <groupId>com.alibaba</groupId>
3.              <artifactId>druid-spring-boot-starter</artifactId>
4.              <version>1.1.10</version>
5.          </dependency>

修改配置文件application.yml

1. spring:
2.    datasource:
3.      # 使用阿里的Druid连接池
4.      type: com.alibaba.druid.pool.DruidDataSource
5.      driver-class-name: com.mysql.cj.jdbc.Driver
6.      # 填写你数据库的url、登录名、密码和数据库名
7.      url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
8.      username: root
9.      password: root
10.   druid:
11.     # 连接池的配置信息
12.     # 初始化大小,最小,最大
13.     initial-size: 5
14.     min-idle: 5
15.     maxActive: 20
16.     # 配置获取连接等待超时的时间
17.     maxWait: 60000
18.     # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
19.     timeBetweenEvictionRunsMillis: 60000
20.     # 配置一个连接在池中最小生存的时间,单位是毫秒
21.     minEvictableIdleTimeMillis: 300000
22.     validationQuery: SELECT 1
23.     testWhileIdle: true
24.     testOnBorrow: false
25.     testOnReturn: false
26.     # 打开PSCache,并且指定每个连接上PSCache的大小
27.     poolPreparedStatements: true
28.     maxPoolPreparedStatementPerConnectionSize: 20
29.     # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
30.     filters: stat,wall,slf4j
31.     # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
32.     connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
33.     # 配置DruidStatFilter
34.     web-stat-filter:
35.       enabled: true
36.       url-pattern: "/*"
37.       exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
38.     # 配置DruidStatViewServlet
39.     stat-view-servlet:
40.       url-pattern: "/druid/*"
41.       # IP白名单(没有配置或者为空,则允许所有访问)
42.       allow: 127.0.0.1,192.168.8.109
43.       # IP黑名单 (存在共同时,deny优先于allow)
44.       deny: 192.168.1.188
45.       #  禁用HTML页面上的“Reset All”功能
46.       reset-enable: false
47.       # 登录名
48.       login-username: admin
49.       # 登录密码
50.       login-password: 123456

代码测试

访问监控页面

10_SpringBoot_整合JSP(了解)

添加依赖

<!--JSP依赖-->
2.          <dependency>
3.              <groupId>org.apache.tomcat.embed</groupId>
4.              <artifactId>tomcat-embed-jasper</artifactId>
5.              <scope>provided</scope>
6.          </dependency>

添加webapps目录并设置目录

设置工作目录,如果在IDEA中项目结构为聚合工程。那么在运行jsp是需要指定路径。如果项目结构为独立项目则不需要。

在 yml配置文件中配置视图解析器参数

在控制类中声明单元方法请求转发jsp资源

1. package com.msb.controller;
2.
3.  import org.springframework.stereotype.Controller;
4.  import org.springframework.web.bind.annotation.PathVariable;
5.  import org.springframework.web.bind.annotation.RequestMapping;
6.
7.  /**
8.   * @Author: Ma HaiYang
9.   * @Description: MircoMessage:Mark_7001
10.  */
11. @Controller
12. public class PageController {
13.     @RequestMapping("/{uri}")
14.     public String getPage(@PathVariable("uri") String uri){
15.         return uri;
16.     }
17. }

11_SpringBoot_整合FreeMarker(熟悉)

1FreeMarker的简介

FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据。

常用的java模板引擎还有哪些?

Jsp、Freemarker、Thymeleaf 、Velocity 等。

模板+数据模型=输出

freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件)

freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。 需要创建Spring Boot+Freemarker工程用于测试模板。

2FreeMarker初次使用

导入依赖

 <!--freeMaker依赖-->
2.          <dependency>
3.              <groupId>org.springframework.boot</groupId>
4.              <artifactId>spring-boot-starter-freemarker</artifactId>
5.          </dependency>

创建controller

 @RequestMapping("/freemarker")
2.      @Controller
3.      public class FreemarkerController {
4.          @RequestMapping("/show")
5.          public String freemarker(Map<String, Object> map){
6.              map.put("name","msb");
7.             //返回模板文件名称
8.              return "show";
9.          }
10.     }

通过查阅配置信息发现,默认前缀为 &apos;&apos; ,后缀为.ftlh,默认路径为templates

templates目录下创建模板文件

1.   <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.  </head>
7.  <body>
8.  this is showftlh
9.  <br/>
10. ${name}
11. <img src="img/a.jpg"></img>
12. </body>
13. </html>

3FreeMarker常用指令

A遍历List集合

1、注释,即<#‐‐和‐‐>,介于其之间的内容会被freemarker忽略

2、插值(Interpolation):即${..}部分,freemarker会用真实的值代替${..}

3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。

4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。

1.   @Controller
2.  public class FreemarkerController {
3.      @Autowired
4.      private EmpService empService;
5.
6.      @RequestMapping("/showEmp")
7.      public ModelAndView testList(){
8.          ModelAndView mv =new ModelAndView();
9.          List<Emp> list =empService.findAll();
10.         mv.addObject("empList", list);
11.         mv.setViewName("showEmp");
12.         return mv;
13.     }
14. }
15.

页面代码

1.   <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.      <style type="text/css">
7.          #empTable{
8.              width: 80%;
9.              border: 1px solid blue;
10.             margin: 0px auto;
11.         }
12.         #empTable th,td{
13.             border: 1px solid green;
14.             text-align: center;
15.         }
16.
17.     </style>
18. </head>
19. <body>
20.
21. <table id="empTable" cellpadding="0px" cellspacing="0px">
22.     <tr>
23.         <th>索引</th>
24.         <th>工号</th>
25.         <th>姓名</th>
26.         <th>岗位</th>
27.         <th>薪资</th>
28.         <th>部门号</th>
29.     </tr>
30.     <#list empList as emp>
31.         <tr>
32.             <td>${emp_index}</td>
33.             <td>${emp.empno}</td>
34.             <td>${emp.ename}</td>
35.             <td>${emp.job}</td>
36.             <td>${emp.sal}</td>
37.             <td>${emp.deptno}</td>
38.         </tr>
39.     </#list>
40.
41.
42. </table>
43.
44. </body>
45. </html>

说明: _index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始

B遍历Map数据

遍历输出指定内容

controller

1.   @Controller
2.  public class FreemarkerController {
3.      @Autowired
4.      private EmpService empService;
5.
6.      @RequestMapping("/showEmpMap")
7.      public ModelAndView testMap(){
8.          ModelAndView mv =new ModelAndView();
9.          List<Emp> list =empService.findAll();
10.         Map<String,Emp> empMap =new HashMap<>();
11.         for (Emp emp : list) {
12.             empMap.put(emp.getEmpno().toString(), emp);
13.         }
14.
15.         mv.addObject("empMap", empMap);
16.         mv.setViewName("showEmpMap");
17.         return mv;
18.     }

页面

1.   <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.      <style type="text/css">
7.          #empTable{
8.              width: 80%;
9.              border: 1px solid blue;
10.             margin: 0px auto;
11.         }
12.         #empTable th,td{
13.             border: 1px solid green;
14.             text-align: center;
15.         }
16.
17.     </style>
18. </head>
19. <body>
20.
21. 输出7521员工信息:<br/>
22. 工号:${empMap['7521'].empno}<br/>
23. 姓名:${empMap['7521'].ename}<br/>
24. 岗位:${empMap['7521'].job}<br/>
25. 薪资:${empMap['7521'].sal}<br/>
26. 部门号:${empMap['7521'].deptno}<br/>
27. <br/>
28. 遍历EmpMap
29.
30. <table id="empTable" cellpadding="0px" cellspacing="0px">
31.     <tr>
32.         <th>索引</th>
33.         <th>工号</th>
34.         <th>姓名</th>
35.         <th>岗位</th>
36.         <th>薪资</th>
37.         <th>部门号</th>
38.     </tr>
39.     <#list empMap?keys as k>
40.         <tr>
41.             <td>${k_index}</td>
42.             <td>${k}</td>
43.             <td>${empMap[k].ename}</td>
44.             <td>${empMap[k].job}</td>
45.             <td>${empMap[k].sal}</td>
46.             <td>${empMap[k].deptno}</td>
47.         </tr>
48.     </#list>
49.
50.
51. </table>
52.
53. </body>
54. </html>

FreeMarker在遍历map集合是,key必须是String

C  if指令

if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否 则跳过内容不再输出。

if中支持的运算符

a算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %

b逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误

c比较运算符有如下几个:

① =或者==:判断两个值是否相等.

② !=:判断两个值是否不等.

③ > 或者gt:判断左边值是否大于右边值

④ >=或者gte:判断左边值是否大于等于右边值

⑤ <或者lt:判断左边值是否小于右边值

⑥ <=或者lte:判断左边值是否小于等于右边值

注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>

如何判断空值

1、判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false 例:为防止stus为空报错可以加上判断如下

2、缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。例: ${name!&apos;&apos;}表示如果name为空显示空字符串。如果是嵌套对象则建议使用()括起来。

<#if empList??>
2.          <#list empList as emp>
3.              <tr <#if emp_index%2 ==0 > style="background-color: gray" </#if>>
4.                  <td>${emp_index}</td>
5.                  <td>${emp.empno}</td>
6.                  <td <#if emp.ename == 'KING'> style="color: aqua" </#if>>${emp.ename}</td>
7.                  <td>${emp.job}</td>
8.                  <td>${emp.mgr!'无'}</td>
9.                  <td <#if emp.sal gte 2000.0> style="color: red" </#if>>${emp.sal}</td>
10.                 <td>${emp.comm!'0'}</td>
11.                 <td>${emp.deptno}</td>
12.             </tr>
13.         </#list>
14.     </#if>

4 内置函数

内建函数语法格式: 变量+?+函数名称

1、内建函数获取某个集合的大小

${集合名?size}

2、内建函数日期格式化

显示年月日:         ${today?date}

显示时分秒:      ${today?time}

显示日期+时间:${today?datetime}

自定义格式化:   ${today?string("yyyy年MM月")}

3、内建函数将json字符串转成对象

1.   <#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
2.  <#assign data=text?eval />
3.  开户行:${data.bank} 账号:${data.account}

其中用到了 assign标签,assign的作用是定义一个变量。

页面

1.   员工人数:${empList?size}
2.  <table id="empTable" cellpadding="0px" cellspacing="0px">
3.      <tr>
4.          <th>索引</th>
5.          <th>工号</th>
6.          <th>姓名</th>
7.          <th>岗位</th>
8.          <th>上级</th>
9.          <th>入职日期a</th>
10.         <th>入职日期b</th>
11.         <th>入职日期c</th>
12.         <th>入职日期d</th>
13.         <th>薪资</th>
14.         <th>补助</th>
15.         <th>部门号</th>
16.     </tr>
17.     <#if empList??>
18.         <#list empList as emp>
19.             <tr <#if emp_index%2 ==0 > style="background-color: gray" </#if>>
20.                 <td>${emp_index}</td>
21.                 <td>${emp.empno}</td>
22.                 <td <#if emp.ename == 'KING'> style="color: aqua" </#if>>${emp.ename}</td>
23.                 <td>${emp.job}</td>
24.                 <td>${emp.mgr!'无'}</td>
25.                 <td>${emp.hiredate?date}</td>
26.                 <td>${emp.hiredate?time}</td>
27.                 <td>${emp.hiredate?datetime}</td>
28.                 <td>${emp.hiredate?string("yyyy年MM月dd日")}</td>
29.                 <td <#if emp.sal gte 2000.0> style="color: red" </#if>>${emp.sal}</td>
30.                 <td>${emp.comm!'0'}</td>
31.                 <td>${emp.deptno}</td>
32.             </tr>
33.         </#list>
34.     </#if>
35.
36. </table>

更多内置函数FreeMarker内置函数的用法_WO木子的博客-CSDN博客_freemarker lower_case

12_SpringBoot_整合Thymeleaf(掌握)

Thymeleaf的主要目标是将优雅的自然模板带到开发工作流程中,并将HTML在浏览器中正确显示,并且可以作为静态原型,让开发团队能更容易地协作。Thymeleaf能够处理HTML,XML,JavaScript,CSS甚至纯文本。

长期以来,jsp在视图领域有非常重要的地位,随着时间的变迁,出现了一位新的挑战者:Thymeleaf,Thymeleaf是原生的,不依赖于标签库.它能够在接受原始HTML的地方进行编辑和渲染.因为它没有与Servelet规范耦合,因此Thymeleaf模板能进入jsp所无法涉足的领域。

Thymeleaf在Spring Boot项目中放入到resources/templates中。这个文件夹中的内容是无法通过浏览器URL直接访问的(和WEB-INF效果一样),所有Thymeleaf页面必须先走控制器。

创建项目,准备配置文件及各层级代码

项目中添加依赖

1.   <dependencies>
2.          <dependency>
3.              <groupId>org.springframework.boot</groupId>
4.              <artifactId>spring-boot-starter-web</artifactId>
5.          </dependency>
6.
7.          <dependency>
8.              <groupId>org.springframework.boot</groupId>
9.              <artifactId>spring-boot-starter-test</artifactId>
10.             <scope>test</scope>
11.         </dependency>
12.
13.         <dependency>
14.             <groupId>org.mybatis.spring.boot</groupId>
15.             <artifactId>mybatis-spring-boot-starter</artifactId>
16.             <version>2.1.3</version>
17.         </dependency>
18.         <dependency>
19.             <groupId>mysql</groupId>
20.             <artifactId>mysql-connector-java</artifactId>
21.             <version>8.0.21</version>
22.         </dependency>
23.
24.         <dependency>
25.             <groupId>org.projectlombok</groupId>
26.             <artifactId>lombok</artifactId>
27.             <version>1.18.12</version>
28.             <scope>provided</scope>
29.         </dependency>
30.
31.
32.         <dependency>
33.             <groupId>com.alibaba</groupId>
34.             <artifactId>druid-spring-boot-starter</artifactId>
35.             <version>1.1.10</version>
36.         </dependency>
37.
38.
39.         <dependency>
40.             <groupId>com.github.pagehelper</groupId>
41.             <artifactId>pagehelper-spring-boot-starter</artifactId>
42.             <version>1.2.12</version>
43.         </dependency>
44.
45.         <dependency>
46.             <groupId>org.springframework.boot</groupId>
47.             <artifactId>spring-boot-starter-thymeleaf</artifactId>
48.             <version>2.4.5</version>
49.         </dependency>
50.
51.     </dependencies>

关于Thymeleaf默认配置

在resources下新建templates文件夹。新建index.html

新建控制器

1.   package com.msb.controller;
2.
3.  import org.springframework.stereotype.Controller;
4.  import org.springframework.web.bind.annotation.RequestMapping;
5.
6.  /**
7.   * @Author: Ma HaiYang
8.   * @Description: MircoMessage:Mark_7001
9.   */
10. @Controller
11. public class ThymeleafController {
12.     @RequestMapping("showIndex")
13.     public String showIndex(){
14.         return "index";
15.     }
16. }

13_SpringBoot_Thymeleaf基础语法(掌握)

Thymeleaf通过标准变量表达式完成数据的展示和处理

1 标准变量表达式必须依赖标签,不能独立使用

2 标准变量表达式一般在开始标签中,以 th开头

3 语法为: <tag th:***="${key}"   ></tag>

4 表达式中可以通过${}取出域中的值并放入标签的指定位置

5 ${}在这里不能单独使用,必须在 th:后面的双引号里使用

为了有提示,修改html页面中<html>标签为

1.   <html xmlns:th="http://www.thymeleaf.org" >

举例

1.   <input th:type="" th:text="${}" th:style="" th:id="" th:class=""  ... ... >

th:text属性

向HTML标签内部输出信息。

处理器 Controller

 @RequestMapping("showIndex")
2.      public String  showIndex(Map<String,Object> map){
3.          map.put("msg", "testMessage");
4.          return "index";
5.      }

页面代码

<!--向span双标签内部添加文本-->
2.      <span th:text="pageMessage"></span> <br/>
3.      <!--从域中根据参数名取出参数值放在双标签中-->
4.      <span th:text="${msg}"></span>      <br/>

th:value

表单元素,设置HTML标签中表单元素value属性时使用。

 <!--向input标签中的value属性赋值-->
2.      <input type="text"  th:value="pageMessage"/>
3.      <!--从域中根据参数名取出参数值 向input标签中的value属性赋值-->
4.      <input type="text"  th:value="${msg}"/>

th:if

1.   <span th:if="${name}!='张三'">会显示</span>

循环遍历.th:each

示例中u为迭代遍历。

th:each="u,i :${list}" 其中i表示迭代状态。

1,index:当前迭代器的索引 从0开始

2,count:当前迭代对象的计数 从1开始

3,size:被迭代对象的长度

4,even/odd:布尔值,当前循环是否是偶数/奇数 从0开始

5,first:布尔值,当前循环的是否是第一条,如果是返回true否则返回false

6,last:布尔值,当前循环的是否是最后一条,如果是则返回true否则返回false

controller代码

1.   @Controller
2.  public class ThymeleafController {
3.
4.      @Autowired
5.      private EmpService empService;
6.
7.      @RequestMapping("/showEmp")
8.      public String showEmp(Map<String, Object> map) {
9.          List<Emp> empList = empService.findAll();
10.         map.put("empList", empList);
11.         map.put("emp", empList.get(0));
12.         return "showEmp";
13.     }

页面模板代码

1.   <!DOCTYPE html>
2.  <html lang="en" xmlns:th="http://www.thymeleaf.org">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.      <style type="text/css">
7.          #empTable{
8.              width: 80%;
9.              border: 1px solid blue;
10.             margin: 0px auto;
11.         }
12.         #empTable th,td{
13.             border: 1px solid green;
14.             text-align: center;
15.         }
16.     </style>
17. </head>
18. <body>
19. 展示单个员工信息:
20. <span th:if="${emp}!=null">
21.     工号:<span th:text="${emp.empno}"></span><br/>
22.     姓名:<span th:text="${emp.ename}"></span><br/>
23.     职务:<span th:text="${emp.job}"></span><br/>
24.     上级:<span th:text="${emp.mgr}"></span><br/>
25.     入职日期:<span th:text="${emp.hiredate}"></span><br/>
26.     工资:<span th:text="${emp.sal}"></span><br/>
27.     补助:<span th:text="${emp.comm}"></span><br/>
28.     部门号:<span th:text="${emp.deptno}"></span><br/>
29. </span>
30. <hr/>
31. <span  th:if="${empList}!=null">
32.     <span  th:if="${empList.size()} ne 0">
33.         工号:<span th:text="${empList[0].empno}"></span><br/>
34.         姓名:<span th:text="${empList[0].ename}"></span><br/>
35.         职务:<span th:text="${empList[0].job}"></span><br/>
36.         上级:<span th:text="${empList[0].mgr}"></span><br/>
37.         入职日期:<span th:text="${empList[0].hiredate}"></span><br/>
38.         工资:<span th:text="${empList[0].sal}"></span><br/>
39.         补助:<span th:text="${empList[0].comm}"></span><br/>
40.         部门号:<span th:text="${empList[0].deptno}"></span><br/>
41.     </span>
42. </span>
43. <table  id="empTable" cellpadding="0px" cellspacing="0px">
44.     <tr>
45.         <th>索引</th>
46.         <th>序号</th>
47.         <th>总人数</th>
48.         <th>偶数索引?</th>
49.         <th>奇数索引?</th>
50.         <th>第一?</th>
51.         <th>最后?</th>
52.         <th>工号</th>
53.         <th>姓名</th>
54.         <th>职务</th>
55.         <th>上级</th>
56.         <th>入职日期</th>
57.         <th>工资</th>
58.         <th>补助</th>
59.         <th>部门号</th>
60.     </tr>
61.     <tr th:each="emp,i:${empList}">
62.         <td th:text="${i.index}"></td>
63.         <td th:text="${i.count}"></td>
64.         <td th:text="${i.size}"></td>
65.         <td th:text="${i.odd}"></td>
66.         <td th:text="${i.even}"></td>
67.         <td th:text="${i.first}"></td>
68.         <td th:text="${i.last}"></td>
69.         <td th:text="${emp.empno}"></td>
70.         <td th:text="${emp.ename}"></td>
71.         <td th:text="${emp.job}"></td>
72.         <td th:text="${emp.mgr}"></td>
73.         <td th:text="${emp.hiredate}"></td>
74.         <td th:text="${emp.sal}"></td>
75.         <td th:text="${emp.comm}"></td>
76.         <td th:text="${emp.deptno}"></td>
77.     </tr>
78.
79. </table>
80.
81. </body>
82. </html>

页面效果

标准变量表达式运算符支持

标准变量表达式支持的运算符

算数运算符

算术运算:+ , - , * , / , %
1.   <span th:text="1+1"></span>
2.  <span th:text="'1'+1"></span>
3.  <span th:text="${emp.empno}+1"></span>
4.  <span th:text="${emp.empno+1}"></span>

关系运算符

 gt:     great than(大于)>
2 ge:    great equal(大于等于)>=
3 eq:    equal(等于)==
4 lt:    less than(小于)<
5 le:    less equal(小于等于)<=
6 ne:    not equal(不等于)!=

逻辑运算符

&&  或 and:   表示并且

||  或  or  : 表示或者

1.   <div th:text="1>0 and 2<3"></div>
2.  <div th:text="1>0 and 2>3"></div>
3.  <div th:text="1>0 or 2<3"></div>
4.  <div th:text="1>0 or 2>3"></div>
5.  <hr/>
6.  <div th:text="${emp.sal ge 800}"></div>
7.  <div th:text="${emp.sal } ge 800"></div>
8.  <div th:text="${emp.sal ge 800} and ${emp.deptno eq 20}"></div>
9.  <div th:text="(${emp.sal }ge 800) or (${emp.deptno } ne 20)"></div>
10. <div th:text="${emp.sal ge 800 or emp.deptno ne 20 }"></div>

在早期的thymeleaf模板引擎框架中 逻辑运算符要写在${}的外边,目前我们2.4.5版本中,可以写在${}里面

三目运算符

1.   <tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">

对空值作出处理

 <tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
2.          <td th:text="${i.index}"></td>
3.          <td th:text="${i.count}"></td>
4.          <td th:text="${i.size}"></td>
5.          <td th:text="${i.odd}"></td>
6.          <td th:text="${i.even}"></td>
7.          <td th:text="${i.first}"></td>
8.          <td th:text="${i.last}"></td>
9.          <td th:text="${emp.empno}"></td>
10.         <td th:text="${emp.ename}"></td>
11.         <td th:text="${emp.job}"></td>
12.         <td th:text="${emp.mgr} eq null ?老板:${emp.mgr}"></td>
13.         <td th:text="${emp.hiredate}"></td>
14.         <td th:text="${emp.sal}"></td>
15.         <td th:text="${emp.comm} eq null ?0:${emp.comm}"></td>
16.         <td th:text="${emp.deptno}"></td>
17.     </tr>

th:href

设置href属性的。取值使用@{}取值

1.   <a th:href="@{/getParam(id=1,name='msb')}" >跳转</a>
2.  <!-- 获取作用域值-->
3.  <a th:href="@{/getParam(name=${stu.name},age=${stu.age})}">跳转二</a>

页面代码

 <tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
2.          <td th:text="${i.index}"></td>
3.          <td th:text="${i.count}"></td>
4.          <td th:text="${i.size}"></td>
5.          <td th:text="${i.odd}"></td>
6.          <td th:text="${i.even}"></td>
7.          <td th:text="${i.first}"></td>
8.          <td th:text="${i.last}"></td>
9.          <td th:text="${emp.empno}"></td>
10.         <td th:text="${emp.ename}"></td>
11.         <td th:text="${emp.job}"></td>
12.         <td th:text="${emp.mgr} eq null ?老板:${emp.mgr}"></td>
13.         <td th:text="${emp.hiredate}"></td>
14.         <td th:text="${emp.sal}"></td>
15.         <td th:text="${emp.comm} eq null ?0:${emp.comm}"></td>
16.         <td th:text="${emp.deptno}"></td>
17.         <td>
18.             <a th:href="@{/removeEmp(empno=${emp.empno},ename=${emp.ename})}">删除</a>
19.         </td>
20.     </tr>

后台controller处理器

1.   @Controller
2.  public class ThymeleafController {
3.
4.      @Autowired
5.      private EmpService empService;
6.
7.      @RequestMapping("/showAllEmp")
8.      public String showEmp(Map<String, Object> map) {
9.          List<Emp> empList = empService.findAll();
10.         map.put("empList", empList);
11.         map.put("emp", empList.get(0));
12.         return "showEmp";
13.     }
14.
15.     @RequestMapping("/removeEmp")
16.     public String removeEmp(Integer empno,String ename){
17.         boolean success =empService.removeEmp(empno,ename);
18.         return "redirect:showAllEmp";
19.     }
20. }

service和mapper 略

th:onclick

给元素绑定事件,单击事件并传递参数

写法1:仅仅支持数字和布尔类型参数的传递,字符串不支

1.   <a href="javascript:viod(0)"  th:onclick="'del('+${emp.empno}+')'">删除</a>

写法2:支持数字和文本类型的参数传递

1.   <a href="javascript:void(0)" th:onclick="delEmp([[${emp.empno}]],[[${emp.ename}]])">删除</a>

前端代码

1.   <table  id="empTable" cellpadding="0px" cellspacing="0px">
2.      <tr>
3.          <th>索引</th>
4.          <th>序号</th>
5.          <th>总人数</th>
6.          <th>偶数索引?</th>
7.          <th>奇数索引?</th>
8.          <th>第一?</th>
9.          <th>最后?</th>
10.         <th>工号</th>
11.         <th>姓名</th>
12.         <th>职务</th>
13.         <th>上级</th>
14.         <th>入职日期</th>
15.         <th>工资</th>
16.         <th>补助</th>
17.         <th>部门号</th>
18.         <th>操作</th>
19.     </tr>
20.     <tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
21.         <td th:text="${i.index}"></td>
22.         <td th:text="${i.count}"></td>
23.         <td th:text="${i.size}"></td>
24.         <td th:text="${i.odd}"></td>
25.         <td th:text="${i.even}"></td>
26.         <td th:text="${i.first}"></td>
27.         <td th:text="${i.last}"></td>
28.         <td th:text="${emp.empno}"></td>
29.         <td th:text="${emp.ename}"></td>
30.         <td th:text="${emp.job}"></td>
31.         <td th:text="${emp.mgr} eq null ?老板:${emp.mgr}"></td>
32.         <td th:text="${emp.hiredate}"></td>
33.         <td th:text="${emp.sal}"></td>
34.         <td th:text="${emp.comm} eq null ?0:${emp.comm}"></td>
35.         <td th:text="${emp.deptno}"></td>
36.         <td>
37.             <a href="javascript:void(0)" th:onclick="removeEmp([[${emp.empno}]],[[${emp.ename}]])">删除</a>
38.         </td>
39.     </tr>
40.
41. </table>
42. <script>
43.     function removeEmp(empno,ename){
44.         var resulet =confirm("确定要删除编号为"+empno+"的"+ename);
45.         if(resulet){
46.             window.location.href="removeEmp?empno="+empno+"&ename="+ename;
47.         }
48.     }
49.
50. </script>

14_SpringBoot_Thymeleaf内置对象(掌握)

Thymeleaf提供了一些内置对象,内置对象可直接在模板中使用。这些对象是以#引用的。

使用内置对象的语法

1引用内置对象需要使用#

2大部分内置对象的名称都以s结尾。如:strings、numbers、dates
3常见内置对象如下

#arrays:数组操作的工具;

#aggregates:操作数组或集合的工具;

#bools:判断boolean类型的工具;

#calendars:类似于#dates,但是是java.util.Calendar类的方法;

#ctx:上下文对象,可以从中获取所有的thymeleaf内置对象;

#dates:日期格式化内置对象,具体方法可以参照java.util.Date;

#numbers: 数字格式化;#strings:字符串格式化,具体方法可以参照String,如startsWith、contains等;

#objects:参照java.lang.Object;

#lists:列表操作的工具,参照java.util.List;

#sets:Set操作工具,参照java.util.Set;#maps:Map操作工具,参照java.util.Map;

#messages:操作消息的工具。

更多

这里我们着重学习 strings dates numbers和域对象

strings对象

dates对象

#numbers

#numbers.formatDecimal(numbwe,整数位,整数位千分位标识符,小数位,小数位表示符)

${#numbers.formatDecimal(num,1,&apos;COMMA&apos;,2,&apos;POINT&apos;)}

显示:99,999,999.99

1:表示整数位至少一位,不足以0补齐,如:num = 0.00,

${#numbers.formatDecimal(num,0,&apos;COMMA&apos;,2,&apos;POINT&apos;)}则显示 .00

${#numbers.formatDecimal(num,1,&apos;COMMA&apos;,2,&apos;POINT&apos;)}则显示 0.00

COMMA:&apos;,&apos;

POINT:‘.’

域对象

更多内置 对象: Tutorial: Using Thymeleaf

controller

@RequestMapping("showIndex")
2.      public String  showIndex(Map<String,Object> map, HttpServletRequest req, HttpSession session){
3.          // 向request域放数据
4.          req.setAttribute("msg", "requestMessage");
5.          // 向session域放数据
6.          session.setAttribute("msg", "sessionMessage");
7.          // 向application域放数据
8.          req.getServletContext().setAttribute("msg", "applicationMessage");
9.
10.         // 对象List集合数据
11.         List<Emp> empList = empService.findAll();
12.         map.put("empList", empList);
13.         return "index";
14.     }

页面

1. <!DOCTYPE html>
2.  <html lang="en" xmlns:th="http://www.thymeleaf.org">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.      <style type="text/css">
7.          #empTable{
8.              width: 80%;
9.              border: 1px solid blue;
10.             margin: 0px auto;
11.         }
12.         #empTable th,td{
13.             border: 1px solid green;
14.             text-align: center;
15.         }
16.         .a{
17.             background-color: antiquewhite;
18.         }
19.         .b{
20.             background-color: gray;
21.         }
22.     </style>
23. </head>
24. <body>
25. <table  id="empTable" cellpadding="0px" cellspacing="0px">
26.     <tr>
27.         <th>索引</th>
28.         <th>序号</th>
29.         <th>总人数</th>
30.         <th>偶数索引?</th>
31.         <th>奇数索引?</th>
32.         <th>第一?</th>
33.         <th>最后?</th>
34.         <th>工号</th>
35.         <th>姓名</th>
36.         <th>职务</th>
37.         <th>上级</th>
38.         <th>入职日期</th>
39.         <th>入职年</th>
40.         <th>入职月</th>
41.         <th>入职日</th>
42.         <th>工资</th>
43.         <th>补助</th>
44.         <th>部门号</th>
45.         <th>操作</th>
46.     </tr>
47.     <tr th:each="emp,i:${empList}" th:class="${i.odd}?a:b">
48.         <td th:text="${i.index}"></td>
49.         <td th:text="${i.count}"></td>
50.         <td th:text="${i.size}"></td>
51.         <td th:text="${i.odd}"></td>
52.         <td th:text="${i.even}"></td>
53.         <td th:text="${i.first}"></td>
54.         <td th:text="${i.last}"></td>
55.         <td th:text="${emp.empno}"></td>
56.         <td th:text="${emp.ename}"></td>
57.         <td th:text="${emp.job}"></td>
58.         <td th:text="${#strings.isEmpty(emp.mgr)}?老板:${emp.mgr}"></td>
59.         <td th:text="${#dates.format(emp.hiredate,'yyyy-MM-dd HH:mm:ss')}"></td>
60.         <td th:text="${#dates.year(emp.hiredate)}"></td>
61.         <td th:text="${#dates.month(emp.hiredate)}"></td>
62.         <td th:text="${#dates.day(emp.hiredate)}"></td>
63.         <td th:text="${#numbers.formatDecimal(emp.sal,7,'COMMA',2,'POINT')}"></td>
64.         <td th:text="${#strings.isEmpty(emp.comm)}?0:${#numbers.formatDecimal(emp.sal,7,'COMMA',2,'POINT')}"></td>
65.         <td th:text="${emp.deptno}"></td>
66.         <td>
67.             <a href="javascript:void(0)" th:onclick="removeEmp([[${emp.empno}]],[[${emp.ename}]])">删除</a>
68.         </td>
69.     </tr>
70.
71. </table>
72. <script>
73.     function removeEmp(empno,ename){
74.         var resulet =confirm("确定要删除编号为"+empno+"的"+ename);
75.         if(resulet){
76.             window.location.href="removeEmp?empno="+empno+"&ename="+ename;
77.         }
78.     }
79.
80. </script>
81.
82. <hr/>
83. request:<br/>
84. <span th:text="${#httpServletRequest.getAttribute('msg')}"></span><br/>
85. <span th:text="${#request.getAttribute('msg')}"></span><br/>
86. <span th:text="${msg}"></span><br/>
87.
88. session:<br/>
89. <span th:text="${#httpSession.getAttribute('msg')}"></span><br/>
90. <span th:text="${#session.getAttribute('msg')}"></span><br/>
91. <span th:text="${session.msg}"></span><br/>
92.
93. application:<br/>
94. <span th:text="${#servletContext.getAttribute('msg')}"></span><br/>
95. <span th:text="${application.msg}"></span><br/>
96.
97. </body>
98. </html>

15_SpringBoot_模板引擎总结(了解)

jsp

优点:

1、功能强大,可以写java代码

2、支持jsp标签(jsp tag)

3、支持表达式语言(el)

4、官方标准,用户群广,丰富的第三方jsp标签库

缺点:

性能问题。不支持前后端分离

freemarker

FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java 等。

目前企业中:主要用Freemarker做静态页面或是页面展示

优点:

1、不能编写java代码,可以实现严格的mvc分离

2、性能非常不错

3、对jsp标签支持良好

4、内置大量常用功能,使用非常方便

5、宏定义(类似jsp标签)非常方便

6、使用表达式语言

缺点:

1、不是官方标准

2、用户群体和第三方标签库没有jsp多

Thymeleaf

Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。

Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。Thymeleaf的可扩展性也非常棒。你可以使用它定义自己的模板属性集合,这样就可以计算自定义表达式并使用自定义逻辑。这意味着Thymeleaf还可以作为模板引擎框架。

优点:静态html嵌入标签属性,浏览器可以直接打开模板文件,便于前后端联调。springboot官方推荐方案。

缺点:模板必须符合xml规范

VUE: 前后端分离,最多,未来趋势

16_SpringBoot_开发者工具(了解)

使用开发者工具包不需要重启。监听内容改变。

1导入依赖

<dependency>
2.              <groupId>org.springframework.boot</groupId>
3.              <artifactId>spring-boot-devtools</artifactId>
4.              <version>2.4.5</version>
5.              <optional>true</optional>
6.          </dependency>

1修改idea自动编译

2修改Reigstry

Ctrl+Shift+Alt+/ 点击弹出框中Registry...

17_SpringBoot_项目打包部署(熟练)

SpringBoot项目可以是jar类型的maven项目,也可以是一个war类型的maven项目,取决于我们要不要整合jsp使用。但是不管是哪种项目类型,已经不是我们传统意义上的项目结构了

在本地使用SpringBoot的启动器即可访问我们开发的项目。如果我们将项目功能开发完成后,需要使用SpringBoot的打包功能来将项目进行打包。

SpringBoot项目打包在linux服务器中运行:

①jar类型项目会打成jar包:

jar类型项目使用SpringBoot打包插件打包时,会在打成的jar中内置一个tomcat的jar。所以我们可以使用jdk直接运行该jar项目可,jar项目中有一个功能,将功能代码放到其内置的tomcat中运行。我们直接使用浏览器访问即可。

②war类型项目会打成war包:

在打包时需要将内置的tomcat插件排除,配置servlet的依赖。将war正常的放到tomcat服务器中运行即可。

导入springboot打包插件

1.   <build>
2.      <plugins>
3.          <plugin>
4.              <groupId>org.springframework.boot</groupId>
5.              <artifactId>spring-boot-maven-plugin</artifactId>
6.              <configuration>
7.                  <fork>true</fork>
8.              </configuration>
9.          </plugin>
10.     </plugins>
11. </build>

将项目导出成jar包并运行

项目类型为jar

使用maven  package指令打包即可

打成包后,可以通过dos  java -jar指令直接启动运行

将项目导出war包并运行

项目打包成war之后,要放在一个Tomcat上运行

如果我们当前的maven项目本身就是war类型的项目,直接打包即可,但是如果我们当前的maven项目是jar类型的项目,我们需要将项目修改为war类型,修改项目的pom文件,使用packaging标签设置值为war.并且需要在项目中创建webapp文件夹,并设置为资源文件夹。

webapp文件夹可加可不加

排除项目中自带的所有的Tomcat插件和jsp servlet 依赖,因为这里要将项目放到一个Tomcat上运行

1.   <!--配置SpringBoot的web启动器-->
2.  <dependency>
3.      <groupId>org.springframework.boot</groupId>
4.      <artifactId>spring-boot-starter-web</artifactId>
5.      <!--排除web启动中自动依赖的tomcat插件-->
6.      <exclusions>
7.          <exclusion>
8.              <groupId>org.springframework.boot</groupId>
9.              <artifactId>spring-boot-starter-tomcat</artifactId>
10.         </exclusion>
11.     </exclusions>
12. </dependency>
13. <!--
14.     手动依赖tomcat插件,但是表明项目打包时该依赖不会被打进去,目的主要是保证开发阶段本地SpringBoot
15.     项目可以正常运行
16. -->
17. <dependency>
18.     <groupId>org.springframework.boot</groupId>
19.     <artifactId>spring-boot-starter-tomcat</artifactId>
20.     <!--打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。
21.         相当于compile,但是打包阶段做了exclude操作-->
22.     <scope>provided</scope>
23. </dependency>
24.

SpringBoot的启动类继承SpringBootServletInitializer,并重写configure

1.   @SpringBootApplication
2.  public class MyApplication extends SpringBootServletInitializer {
3.      //重写配置方法
4.      @Override
5.      protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
6.          return application.sources(MyApplication.class);
7.      }
8.      public static void main(String[] args) {
9.          //启动SpringBoot
10.         SpringApplication.run(MyApplication.class,args);
11.     }
12. }
13.

使用install命令打包项目,并将war包放到tomcat下的webapps下,启动tomcat即可。

如果我们使用的是tomcat7则需要将javax.el-api-3.0.0.jar包放到tomcat下        的lib目录中。

18_SpringBoot_异常处理(掌握)

默认情况,Spring Boot项目错误页面如下。当项目实际上线,如果给用户显示这个页面就不是很友好。当系统出现异常时应该给用户显示更加友好的错误页面。

1设置具体的状态码页面

在templates/下新建error文件夹,在error中新建:状态码.html的页面。例如当出现500时显示的页面为500.html

2使用x进行模糊匹配

当出现5开头状态码的错误时,显示页面可以命名为5xx.html

当出现50开头状态码的错误时,显示页面可以命名为50x.html

3统一错误显示页面

在templates下新建error.html。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html作为错误显示页面。

SpringMVC异常简介

系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理,如下图

异常处理具体实现

1使用@ExceptionHandler注解处理异常
缺点:只能处理当前Controller中的异常。

1.   @Controller
2.  public class ControllerDemo1 {
3.      @RequestMapping("test1.action")
4.      public String test1(){
5.          int i = 1/0;
6.          return "success";
7.      }
8.      @RequestMapping("test2.action")
9.      public String test2(){
10.         String s =null;
11.         System.out.println(s.length());
12.         return "success";
13.     }
14.     @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
15.     public ModelAndView handelException(){
16.         ModelAndView mv =new ModelAndView();
17.         mv.setViewName("error1");
18.         return mv;
19.     }
20. }

2使用:@ControllerAdvice+@ExceptionHandler

此处优先级低于局部异常处理器

1.   package com.msb.exceptionhandler;
2.
3.  import org.springframework.web.bind.annotation.ControllerAdvice;
4.  import org.springframework.web.bind.annotation.ExceptionHandler;
5.  import org.springframework.web.servlet.ModelAndView;
6.
7.  /**
8.   * @Author: Ma HaiYang
9.   * @Description: MircoMessage:Mark_7001
10.  */
11. @ControllerAdvice
12. public class GloableExceptionHandler1 {
13.     @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
14.     public ModelAndView handelException(){
15.         ModelAndView mv =new ModelAndView();
16.         mv.setViewName("error1");
17.         return mv;
18.     }
19. }
20.

3使用:SimpleMappingExceptionResolver

xml配置

配置类配置

1.   /**
2.
3.   * 全局异常
4.
5.   */
6.
7.    @Configuration
8.
9.    public class GloableException2 {
10.
11.     @Bean
12.
13.     public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
14.
15.         SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
16.
17.         Properties prop = new Properties();
18.
19.         prop.put("java.lang.NullPointerException","error1");
20.
21.         prop.put("java.lang.ArithmeticException","error2");
22.
23.         resolver.setExceptionMappings(prop);
24.
25.         return resolver;
26.
27.     }
28.
29. }

4自定义的HandlerExceptionResolver

1.   /**
2.
3.   * 全局异常
4.
5.   * HandlerExceptionResolve
6.
7.   */
8.
9.    @Configuration
10.
11.   public class GloableException3 implements HandlerExceptionResolver {
12.
13.     @Override
14.
15.     public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
16.
17.         ModelAndView mv = new ModelAndView();
18.
19.         if(e instanceof NullPointerException){
20.
21.                 mv.setViewName("error1");
22.
23.         }
24.
25.         if(e instanceof ArithmeticException){
26.
27.                 mv.setViewName("error2");
28.
29.         }
30.
31.         mv.addObject("msg",e);
32.
33.         return mv;
34.
35.     }}

19_SpringBoot_单元测试支持(了解)

在src/main/test里面新建com.msb.项目上下文

注意:

1. 测试类不能叫做Test,会和注解同名

2. 测试方法必须是public

3. 测试方法返回值必须是void

4. 测试方法必须没有参数

1.   package com.msb;
2.
3.  import com.msb.pojo.Emp;
4.  import com.msb.service.EmpService;
5.  import org.junit.jupiter.api.Test;
6.  import org.springframework.beans.factory.annotation.Autowired;
7.  import org.springframework.boot.test.context.SpringBootTest;
8.
9.  import java.util.List;
10.
11. @SpringBootTest(classes = Springboot03Application.class)
12. class Springboot03AppliactionTests {
13.     @Autowired
14.     private EmpService empService;
15.     @Test
16.     public void testFindAll() {
17.         List<Emp> list = empService.findAll();
18.         list.forEach(System.out::println);
19.     }
20.
21. }
22.

20_SpringBoot_bean管理(了解)

Spring Boot中Bean管理

Spring Boot 由于没有XML文件,所以所有的Bean管理都放入在一个配置类中实现。

配置类就是类上具有@Configuration的类。这个类就相当于之前的applicationContext.xml

1 新建配置类

com.msb.config.MyConfig , 规范都是放入到config文件夹中。

注意:配置类要有@Configuration,方法要有@Bean

1.   @Configuration
2.  public class MyConfig {
3.      //访问权限修饰符没有强制要求,一般是protected
4.      //返回值就是注入到Spring容器中实例类型。
5.      // 方法名没有强制要求,相当于<bean >中id属性。
6.      @Bean
7.      protected User getUser(){
8.          User user = new User();
9.          user.setId(1L);
10.         user.setName("张三");
11.         return user;
12.     }
13.     //自定义bean名称
14.     @Bean("user2")
15.     protected  User getUser2(){
16.         User user = new User();
17.         user.setId(2L);
18.         user.setName("李四");
19.         return user;
20.     }
21. }

如果Spring容器中存在同类型的Bean通过Bean的名称获取到Bean对象。或结合@Qualifier使用

1.   @SpringBootTest
2.  public class TestGetBean {
3.      @Autowired
4.      @Qualifier("user2")
5.      private User user;
6.      @Test
7.      public void testGetUser(){
8.          System.out.println(user);
9.      }
10. }
11.

在配置类的方法中通过方法参数让Spring容器把对象注入。

1.   //自定义bean名称
2.  @Bean("user1")
3.  public  User getUser(){
4.      User user = new User();
5.      user.setId(2L);
6.      user.setName("李四");
7.      return user;
8.  }
9.
10. @Bean
11. //可以直接从方法参数中取到。
12. public People getPeople(User user1){
13.     People p = new People();
14.     p.setUser(user1);
15.     return p;
16. }
17.
18.

22_SpringBoot其他拓展

01_SpringBoot_注解拓展

springboot默认已经帮助我们整合好了SpringMVC,同时也给我们默认配置了DispathcerServlet 和编码过滤器,同时也给我们配置好了WEB项目开发的常见组件

查看容器中的所有组件

@SpringBootApplication

1.   /*
2.  * 默认扫描启动类所在包下的所有层级的子包
3.  * 可以通过scanBasePackages属性指定扫描路径
4.  * SpringBootApplication是一个合成注解,可以拆分为以下三个注解
5.  *   @SpringBootConfiguration
6.  *   @EnableAutoConfiguration
7.  *   @ComponentScan(basePackages = "com.msb")
8.  *
9.  *
10. * */
11. @SpringBootApplication
12. public class Springboot04Application {
13.     public static void main(String[] args) {
14.         //返回一个spring容器
15.         ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args);
16.         // 查看所有组件的名
17.         String[] names = context.getBeanDefinitionNames();
18.         for (String name : names) {
19.             System.out.println(name);
20.         }
21.     }
22. }

@Configuration

1.   package com.msb.config;
2.
3.  import com.msb.pojo.User;
4.  import org.springframework.context.annotation.Bean;
5.  import org.springframework.context.annotation.Configuration;
6.
7.  /**
8.   * @Author: Ma HaiYang
9.   * @Description: MircoMessage:Mark_7001
10.  * MyConfig配置类本身也是一个spring容器中的bean
11.  * proxyBeanMethods=true 属性,给MyConfig对象产生一个代理对象
12.  * 通过代理对象控制反复调用MyConfig里面的方法返回的是容器中的一个单实例
13.  * 如果proxyBeanMethods=false 那么我们拿到的MyConfig对象就不是一个代理对象
14.  * 那么这个时候反复调用MyConfig中的方法返回的就是多实例
15.  *
16.  * proxyBeanMethods=false 称之为Lite模式  特点启动快
17.  * proxyBeanMethods=true  称之为Full模式  特点依赖spring容器控制bean单例
18.  *
19.  */
20. @Configuration(proxyBeanMethods = true)
21. public class MyConfig {
22.
23.     /*<bean id = "user1" class ="com.msb.pojo.User">... ...</bean>*/
24.     @Bean // 向容器中添加一个Bean,以方法名作为Bean的id,返回值类型作为组件的类型
25.     public User user1(){
26.         return new User("zhangsan", 10);
27.     }
28.
29.     /*<bean id = "user2" class ="com.msb.pojo.User">... ...</bean>*/
30.     @Bean("user2") // 向容器中添加一个Bean,手动指定Bean的name属性,返回值类型作为组件的类型
31.     public User getUser(){
32.         return new User("lisi", 11);
33.     }
34. }

测试代码

1.   package com.msb;
2.
3.  import com.msb.config.MyConfig;
4.  import com.msb.pojo.User;
5.  import org.springframework.boot.SpringApplication;
6.  import org.springframework.boot.SpringBootConfiguration;
7.  import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8.  import org.springframework.boot.autoconfigure.SpringBootApplication;
9.  import org.springframework.context.ConfigurableApplicationContext;
10. import org.springframework.context.annotation.ComponentScan;
11.
12.
13. @SpringBootApplication(scanBasePackages = "com.msb")
14. public class Springboot04Application {
15.     public static void main(String[] args) {
16.         //返回一个spring容器
17.         ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args);
18.
19.
20.         System.out.println(context.getBean("user1"));
21.         System.out.println(context.getBean("user2"));
22.         User usera = context.getBean(MyConfig.class).getUser();
23.         User userb = context.getBean(MyConfig.class).getUser();
24.         System.out.println(usera==userb);
25.     }
26. }

@Import

1.   package com.msb.config;
2.
3.  import com.msb.pojo.User;
4.  import org.springframework.context.annotation.Bean;
5.  import org.springframework.context.annotation.Configuration;
6.  import org.springframework.context.annotation.Import;
7.
8.
9.  /*
10. * @Import({User.class}) 在容器中自动创建Bean的注解
11. * 通过传入字节码,默认调用bean的无参构造器,向容器中存放一个Bean
12. * 默认组件的名字就是类的全路径名
13. * @Import只要放到可以被扫描到的类之上就可以,不必非得是配置类或者Controller
14. * */
15. @Import({User.class})
16. @Configuration(proxyBeanMethods = true)
17. public class MyConfig {
18.
19. }
1.   @SpringBootApplication(scanBasePackages = "com.msb")
2.  public class Springboot04Application {
3.      public static void main(String[] args) {
4.          //启动SpringBoot, 返回一个spring容器
5.          ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args);
6.          // 根据类型获取Bean
7.          User bean = context.getBean(User.class);
8.          System.out.println(bean);
9.          // 获取属性User类的所有bean的name
10.         String[] beanNamesForType = context.getBeanNamesForType(User.class);
11.         for (String s : beanNamesForType) {
12.             System.out.println(s);
13.         }
14.     }
15. }

@Conditional 条件装配

满足Conditional指定的条件,则进行组件注入

@Conditional下还有很多子注解

1.   package com.msb.config;
2.
3.  import com.msb.pojo.Emp;
4.  import com.msb.pojo.User;
5.  import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
6.  import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
7.  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
8.  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9.  import org.springframework.context.annotation.Bean;
10. import org.springframework.context.annotation.Conditional;
11. import org.springframework.context.annotation.Configuration;
12. import org.springframework.context.annotation.Import;
13.
14.
15. @Configuration
16. /*
17.  * 当配置中存在一个aaa.b的配置并且值为x的时候,当前配置中所有的配置才会生效
18.  * */
19. //@ConditionalOnProperty( name = "aaa.b",havingValue = "x")
20. public class MyConfig {
21.     /*
22.     * 当配置中存在一个aaa.b的配置并且值为x的时候,容器才会初始化user1
23.     * */
24.     @ConditionalOnProperty( name = "aaa.b",havingValue = "x")
25.     @Bean
26.     public User user1(){
27.         return new User("zhangsan", 10);
28.     }
29. }

@ImportResource

原生配置文件引入,允许我们自己定义xml配置文件,在文件中配置bean

resources目录下准备一个xml配置文件

配置类中加载该配置文件

1.   package com.msb.config;
2.
3.  import org.springframework.context.annotation.*;
4.
5.  @Configuration
6.  @ImportResource("classpath:beans.xml")
7.  public class MyConfig {
8.  }
9.

从容器中获取beans.xml中配置的bean

1.   @SpringBootApplication
2.  public class Springboot04Application {
3.      public static void main(String[] args) {
4.          //启动SpringBoot, 返回一个spring容器
5.          ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args);
6.          System.out.println(context.getBean("userx"));
7.
8.      }
9.  }

@ConfigurationProperties

读取application.properties配置文件中的内容,读取进入bean

1.   /*prefix前缀,为配置文件中对应的前缀
2.  * 通过前缀找到对应的配置信息后,在根据属性名去注入匹配的数据*/
3.  @ConfigurationProperties( prefix = "user")
4.  @Component
5.  public class User {
6.      private String uname;
7.      private int age;
8.  }

配置类代码

1.   package com.msb.config;
2.
3.  import com.msb.pojo.User;
4.  import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5.  import org.springframework.boot.context.properties.EnableConfigurationProperties;
6.  import org.springframework.context.annotation.Configuration;
7.
8.  /**
9.   * @Author: Ma HaiYang
10.  * @Description: MircoMessage:Mark_7001
11.  */
12. @Configuration
13. /*开启了User的属性自动配置功能并把User自动注册到容器中
14. * 这个时候,我们的User上就不用加@Component注解了
15. * 适用于Bean来自于第三方JAR场景
16. * */
17. @EnableConfigurationProperties(User.class)
18. public class MyConfig {
19. }

测试代码

1.   package com.msb;
2.
3.  import com.msb.pojo.User;
4.  import org.springframework.boot.SpringApplication;
5.  import org.springframework.boot.autoconfigure.SpringBootApplication;
6.  import org.springframework.context.ConfigurableApplicationContext;
7.
8.  @SpringBootApplication
9.  public class Springboot04Application {
10.     public static void main(String[] args) {
11.         //启动SpringBoot, 返回一个spring容器
12.         ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args);
13.         System.out.println(context.getBean(User.class));
14.
15.     }
16. }

02_SpringBoot_静态资源

静态资源默认存放位置

resources 目录下,static/public目录是我们的静态资源目录,直接访问该目录下的资源的映射路径不需要携带/public或者/static,直接访问即可

如果controller的映射路径和静态资源路径冲突,会发生什么事情呢?

1.   package com.msb.controller;
2.
3.  import org.springframework.stereotype.Controller;
4.  import org.springframework.web.bind.annotation.RequestMapping;
5.  import org.springframework.web.bind.annotation.ResponseBody;
6.
7.  /**
8.   * @Author: Ma HaiYang
9.   * @Description: MircoMessage:Mark_7001
10.  */
11. @Controller
12. public class MyController {
13.     @RequestMapping("/logo.png")
14.     @ResponseBody
15.     public String logo(){
16.         return "controller logo";
17.     }
18. }
19.

请求进来,先去看Controller中有没有对应的资源,如果有则,执行controller资源,如果没有,就交给静态资源处理器,静态资源处理器也没有找到,则返回404

静态资源访问前缀

默认无前缀,如果想指定静态资源前缀,可以 通过spring.mvc.static-path-pattern配置

yml配置如下

请求路径上通过 项目上下文路径+静态资源前缀+静态资源名的方式访问

指定静态资源位置

静态资源默认存放的路径为static content from a directory called /static (or /public or /resources or /META-INF/resources)

可以通过

static-locations 指定专门的静态资源路径

springboot还支持静态资源webjars 的处理方式,就是将静态资源打成jar导入

WebJars - Web Libraries in Jars

项目中导入jQuery依赖

1.   <dependency>
2.      <groupId>org.webjars</groupId>
3.      <artifactId>jquery</artifactId>
4.      <version>3.6.0</version>
5.  </dependency>

导入后,自动映射

http://localhost:8080/springboot-static-content/res/webjars/jquery/3.6.0/jquery.js

或者

http://localhost:8080/springboot-static-content/webjars/jquery/3.6.0/jquery.js

欢迎页功能

方式1 支持静态资源欢迎页, index.html  注意此时不能配置静态资源前缀,否则不生效

静态资源位置

方式2 通过controller ,定义 / 和/index映射 路径

   @RequestMapping(path={"/","/index"})
2.      public String welcome(){
3.          return "welcome";
4.      }

导入thymeleaf

  <dependency>
2.              <groupId>org.springframework.boot</groupId>
3.              <artifactId>spring-boot-starter-thymeleaf</artifactId>
4.              <version>2.4.5</version>
5.          </dependency>

定义Favicon

static目录下准备一个favicon.ico的一个图片即可

浏览器默认访问favicon.ico的路径为  协议://ip:端口号/favicon.ico,所以这里不要设置项目的上下文路径

拦截器静态资源放行

定义一个登陆页面

1. <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.      <link type="text/css" href="css/mycss.css">
7.      <script src="js/myjs.js"></script>
8.  </head>
9.  <body>
10. <img src="img/logo.png">
11. <form action="login">
12.     <input type="text" name="username" >
13.     <input type="submit">
14. </form>
15.
16. </body>
17. </html>

定义一个登陆controller

1.   package com.msb.controller;
2.
3.  import org.springframework.stereotype.Controller;
4.  import org.springframework.web.bind.annotation.RequestMapping;
5.
6.  import javax.servlet.http.HttpServletRequest;
7.
8.  /**
9.   * @Author: Ma HaiYang
10.  * @Description: MircoMessage:Mark_7001
11.  */
12. @Controller
13. public class LoginController {
14.
15.     @RequestMapping("/login")
16.     public String login(String username, HttpServletRequest request){
17.         if(null != username&&!"".equals(username)){
18.             request.getSession().setAttribute("username", username);
19.             return "main";
20.         }
21.         return "redirect:/login.html";
22.     }
23. }

定义一个登陆拦截器

1.   package com.msb.interceptor;
2.
3.  import org.springframework.stereotype.Component;
4.  import org.springframework.web.servlet.HandlerInterceptor;
5.
6.  import javax.servlet.http.HttpServletRequest;
7.  import javax.servlet.http.HttpServletResponse;
8.
9.  /**
10.  * @Author: Ma HaiYang
11.  * @Description: MircoMessage:Mark_7001
12.  */
13. @Component
14. public class LoginInterceptor implements HandlerInterceptor {
15.     @Override
16.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
17.         Object username = request.getSession().getAttribute("username");
18.         // 如果登录过,那么就放行
19.         if(null != username){
20.             return true;
21.         }
22.         // 如果没登陆过,那么就回到登录页,重定向
23.         response.sendRedirect("login.html");
24.         return false;
25.     }
26. }
27.

配置拦截器

1.   package com.msb.config;
2.
3.  import com.msb.interceptor.LoginInterceptor;
4.  import org.springframework.beans.factory.annotation.Autowired;
5.  import org.springframework.context.annotation.Configuration;
6.  import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
7.  import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
8.
9.  /**
10.  * @Author: Ma HaiYang
11.  * @Description: MircoMessage:Mark_7001
12.  */
13. @Configuration
14. public class MyInterceptorRegist implements WebMvcConfigurer {
15.     @Autowired
16.     private LoginInterceptor loginInterceptor;
17.     //配置拦截器的映射
18.     @Override
19.     public void addInterceptors(InterceptorRegistry registry) {
20.         registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login","/login.html","/css/**","/js/**","/img/**","/font/**");
21.     }
22. }

template下的main.html

1. <!DOCTYPE html>
2.  <html lang="en">
3.  <head>
4.      <meta charset="UTF-8">
5.      <title>Title</title>
6.  </head>
7.  <body>
8.
9.  this is main... ...
10.
11.
12. </body>
13. </html>

03_SpringBoot_文件上传

创建项目:略

启动文件上传服务器:略

导入依赖

1.   <dependency>
2.    <groupId>com.sun.jersey</groupId>
3.    <artifactId>jersey-client</artifactId>
4.    <version>1.19</version>
5.  </dependency>

页面代码

1. <html>
2.  <head>
3.      <meta charset="UTF-8">
4.      <title>Title</title>
5.      <style>
6.          .progress {
7.              width: 200px;
8.              height: 10px;
9.              border: 1px solid #ccc;
10.             border-radius: 10px;
11.             margin: 10px 0px;
12.             overflow: hidden;
13.         }
14.         /* 初始状态设置进度条宽度为0px */
15.         .progress > div {
16.             width: 0px;
17.             height: 100%;
18.             background-color: yellowgreen;
19.             transition: all .3s ease;
20.         }
21.     </style>
22.     <script type="text/javascript" src="js/jquery.min.js"></script>
23.     <script type="text/javascript">
24.         $(function(){
25.             $("#uploadFile").click(function(){
26.                 // 获取要上传的文件
27.                 var photoFile =$("#photo")[0].files[0]
28.                 if(photoFile==undefined){
29.                     alert("您还未选中文件")
30.                     return;
31.                 }
32.                 // 将文件装入FormData对象
33.                 var formData =new FormData();
34.                 formData.append("headPhoto",photoFile)
35.                 // ajax向后台发送文件
36.                 $.ajax({
37.                     type:"post",
38.                     data:formData,
39.                     url:"file/upload",
40.                     processData:false,
41.                     contentType:false,
42.                     success:function(result){
43.                         // 接收后台响应的信息
44.                         alert(result.message)
45.                         // 图片回显
46.                         $("#headImg").attr("src",result.newFileName);
47.                     },
48.                     xhr: function() {
49.                         var xhr = new XMLHttpRequest();
50.                         //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
51.                         xhr.upload.addEventListener('progress', function (e) {
52.                             //loaded代表上传了多少
53.                             //total代表总数为多少
54.                             var progressRate = (e.loaded / e.total) * 100 + '%';
55.                             //通过设置进度条的宽度达到效果
56.                             $('.progress > div').css('width', progressRate);
57.                         })
58.                         return xhr;
59.                     }
60.                 })
61.             })
62.         })
63.     </script>
64. </head>
65. <body>
66. <form action="addPlayer" method="get">
67.     <p>账号<input type="text" name="name"></p>
68.     <p>密码<input type="text" name="password"></p>
69.     <p>昵称<input type="text" name="nickname"></p>
70.     <p>头像:
71.         <br/>
72.         <input id="photo" type="file">
73.         <br/>
74.         <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
75.         <br/>
76.         <div class="progress">
77.             <div></div>
78.         </div>
79.         <a id="uploadFile" href="javascript:void(0)">立即上传</a>
80.     </p>
81.     <p><input type="submit" value="注册"></p>
82. </form>
83. </body>
84. </html>

Controller代码

1.   package com.msb.controller;
2.
3.  import com.sun.jersey.api.client.Client;
4.  import com.sun.jersey.api.client.WebResource;
5.  import org.springframework.stereotype.Controller;
6.  import org.springframework.web.bind.annotation.RequestMapping;
7.  import org.springframework.web.bind.annotation.ResponseBody;
8.  import org.springframework.web.multipart.MultipartFile;
9.
10. import javax.servlet.http.HttpServletRequest;
11. import java.io.File;
12. import java.io.IOException;
13. import java.util.HashMap;
14. import java.util.Map;
15. import java.util.UUID;
16.
17. /**
18.  * @Author: Ma HaiYang
19.  * @Description: MircoMessage:Mark_7001
20.  */
21. @Controller
22. @RequestMapping("/file")
23. public class FileController {
24.
25.     // 文件存储位置
26.     private final static String FILESERVER="http://127.0.0.1:8090/upload/";
27.
28.     @RequestMapping("/upload")
29.     @ResponseBody
30.     public Map<String,String> upload(MultipartFile headPhoto, HttpServletRequest req) throws IOException {
31.         Map<String,String> map=new HashMap<>();
32.
33.         // 指定文件存储目录为我们项目部署环境下的upload目录
34.         String realPath = req.getServletContext().getRealPath("/upload");
35.         File dir = new File(realPath);
36.         // 如果不存在则创建目录
37.         if(!dir.exists()){
38.             dir.mkdirs();
39.         }
40.         // 获取文件名
41.         String originalFilename = headPhoto.getOriginalFilename();
42.         // 避免文件名冲突,使用UUID替换文件名
43.         String uuid = UUID.randomUUID().toString();
44.         // 获取拓展名
45.         String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
46.         // 新的文件名
47.         String newFileName=uuid.concat(extendsName);
48.         // 创建 sun公司提供的jersey包中的client对象
49.         Client client=Client.create();
50.         WebResource resource = client.resource(FILESERVER + newFileName);
51.         //  文件保存到另一个服务器上去了
52.         resource.put(String.class, headPhoto.getBytes());
53.         // 上传成功之后,把文件的名字和文件的类型返回给浏览器
54.         map.put("message", "上传成功");
55.         map.put("newFileName", FILESERVER+newFileName);
56.         map.put("filetype", headPhoto.getContentType());
57.         return map;
58.     }
59. }

yml中配置文件大小限制

1.   spring:
2.    servlet:
3.      multipart:
4.        max-file-size: 10MB
5.        max-request-size: 100MB

多文件同步上传处理方式

页面代码

1.   <form action="file/upload" method="post" enctype="multipart/form-data">
2.      <p>账号<input type="text" name="name"></p>
3.      <p>密码<input type="text" name="password"></p>
4.      <p>昵称<input type="text" name="nickname"></p>
5.      <p>头像:
6.          <br/>
7.          <input id="photo" name="photo"  type="file">
8.          <input id="photos" name="photos"  type="file" multiple>
9.          <br/>
10.         <img id="headImg" style="width: 200px;height: 200px" alt="你还未上传图片">
11.         <br/>
12.     <div class="progress">
13.         <div></div>
14.     </div>
15.     <a id="uploadFile" href="javascript:void(0)">立即上传</a>
16.     </p>
17.     <p><input type="submit" value="注册"></p>
18. </form>

后台接收的处理单元参数处理

public Map<String,String> upload(String name,
2.                                       String password,
3.                                       String nickname,
4.                                       @RequestPart("photo") MultipartFile photo,
5.                                       @RequestPart("photos") MultipartFile[] photos, HttpServletRequest req)

04_SpringBoot_MyBatis-Plus

Redirect

MyBatis-plus是mybatis的增强工具,在MyBatis 上只做增强,不做改变,为简化开发,提高效率而生

MyBatis-plus和mybatis就像是魂斗罗中的两个兄弟

安装MyBatisX插件

创建项目

导入依赖

1.   <dependency>
2.              <groupId>org.springframework.boot</groupId>
3.              <artifactId>spring-boot-starter-web</artifactId>
4.          </dependency>
5.
6.          <dependency>
7.              <groupId>org.projectlombok</groupId>
8.              <artifactId>lombok</artifactId>
9.              <optional>true</optional>
10.         </dependency>
11.         <dependency>
12.             <groupId>org.springframework.boot</groupId>
13.             <artifactId>spring-boot-starter-test</artifactId>
14.             <scope>test</scope>
15.         </dependency>
16.
17.
18.         <dependency>
19.             <groupId>mysql</groupId>
20.             <artifactId>mysql-connector-java</artifactId>
21.             <version>8.0.21</version>
22.         </dependency>
23.
24.         <dependency>
25.             <groupId>com.alibaba</groupId>
26.             <artifactId>druid-spring-boot-starter</artifactId>
27.             <version>1.1.10</version>
28.         </dependency>
29.
30.         <dependency>
31.             <groupId>com.baomidou</groupId>
32.             <artifactId>mybatis-plus-boot-starter</artifactId>
33.             <version>3.4.2</version>
34.         </dependency>
35.

自动配置的内容

MyBatis PlusAutoConfiguration配置类,MyBatisPlusProperties配置项前缀 mybatis-plus: ***就是对mybatis-plus的参数的设置

SQLSessionFactory已经配置好

mapperlocations 自动配置好的,默认值是classpath*:/mapper/**/*.xml  意为任意包路径下所有的mapper包下的xml文件

@Mapper建议替换成MapperScan

配置mybatisplus

1.
2.  spring:
3.    datasource:
4.      type: com.alibaba.druid.pool.DruidDataSource
5.      driver-class-name: com.mysql.cj.jdbc.Driver
6.      url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
7.      username: root
8.      password: root
9.    druid:
10.     initial-size: 5
11.     min-idle: 5
12.     maxActive: 20
13.     maxWait: 60000
14.     timeBetweenEvictionRunsMillis: 60000
15.     minEvictableIdleTimeMillis: 300000
16.     validationQuery: SELECT 1
17.     testWhileIdle: true
18.     testOnBorrow: false
19.     testOnReturn: false
20.     poolPreparedStatements: true
21.     maxPoolPreparedStatementPerConnectionSize: 20
22.     filters: stat,wall,slf4j
23.     connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
24.     web-stat-filter:
25.       enabled: true
26.       url-pattern: "/*"
27.       exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
28.     stat-view-servlet:
29.       url-pattern: "/druid/*"
30.       allow: 127.0.0.1,192.168.8.109
31.       deny: 192.168.1.188
32.       reset-enable: false
33.       login-username: admin
34.       login-password: 123456
35.
36. mybatis-plus:
37.   type-aliases-package: com.msb.pojo

实体类

1.   @AllArgsConstructor
2.  @NoArgsConstructor
3.  @Data
4.  @TableName("dept")
5.  public class Dept implements Serializable {
6.      /*@TableField(exist = false)
7.      private String aaa;*/
8.      @TableField("deptno")
9.      private Integer deptno;
10.     private String dname;
11.     private String loc;
12. }

mapper

1.   public interface DeptMapper  extends BaseMapper<Dept> {
2.
3.  }

service

接口

1.   public interface DeptService extends IService<Dept> {
2.  }

实现类

1.   @Service
2.  public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements DeptService {
3.  }

测试代码CURD

1.   package com.msb;
2.
3.  import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4.  import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
5.  import com.msb.mapper.DeptMapper;
6.  import com.msb.pojo.Dept;
7.  import com.msb.service.DeptService;
8.  import lombok.AllArgsConstructor;
9.  import org.junit.jupiter.api.Test;
10. import org.springframework.beans.factory.annotation.Autowired;
11. import org.springframework.boot.test.context.SpringBootTest;
12.
13. import java.util.List;
14.
15. @SpringBootTest
16. class SpringbootMybatisplusApplicationTests {
17.
18.
19.     @Autowired
20.     DeptService deptService;
21.
22.
23.     @Test
24.     public void testFindAll(){
25.         List<Dept> list = deptService.list();
26.         for (Dept dept : list) {
27.             System.out.println(dept);
28.         }
29.     }
30.     // 查询集合
31.     @Test
32.     public void testQueryWrapper(){
33.         // 部门号》=20
34.         // QueryWrapper 作用就是在原本的SQL语句后面拼接where条件
35.         // selec * from where      delete from dept where  update dept set ...  where ....
36.         QueryWrapper<Dept> queryWrapper=new QueryWrapper<>();
37.         //queryWrapper.ge("deptno", 20).eq("dname", "ACCOUNTING").likeRight("dname", "A");
38.         //queryWrapper.likeRight("dname", "A");
39.         List<Dept> list = deptService.list(queryWrapper);
40.         for (Dept dept : list) {
41.             System.out.println(dept);
42.         }
43.
44.
45.     }
46.     // 查询单个
47.     @Test
48.     public void testQueryWrapper2(){
49.         QueryWrapper<Dept> queryWrapper=new QueryWrapper<>();
50.         queryWrapper.eq("deptno", 20);
51.         Dept dept = deptService.getOne(queryWrapper);
52.         System.out.println(dept);
53.
54.     }
55.
56.     // 增加
57.     @Test
58.     public void testAdd(){
59.         boolean save = deptService.save(new Dept(null, "aaa", "bbb"));
60.         System.out.println(save);
61.     }
62.
63.     // 修改
64.     @Test
65.     public void testUpdate(){
66.         // 要更新的数据
67.         Dept dept =new Dept();
68.         dept.setDname("xxx");
69.         dept.setLoc("yyy");
70.         // 更新的条件
71.         QueryWrapper<Dept> queryWrapper=new QueryWrapper<>();
72.         queryWrapper.eq("deptno", 41);
73.         boolean update = deptService.update(dept, queryWrapper);
74.         System.out.println(update);
75.     }
76.     // 删除
77.     @Test
78.     public void testRemove(){
79.         QueryWrapper<Dept> queryWrapper=new QueryWrapper<>();
80.         queryWrapper.eq("deptno", 41);
81.         boolean remove = deptService.remove(queryWrapper);
82.         System.out.println(remove);
83.     }
84.
85.
86.
87. }

分页插件的使用

配置分页插件

1.   package com.msb.config;
2.
3.  import com.baomidou.mybatisplus.annotation.DbType;
4.  import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
5.  import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
6.  import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
7.  import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
8.  import org.springframework.context.annotation.Bean;
9.  import org.springframework.context.annotation.Configuration;
10.
11. /**
12.  * @Author: Ma HaiYang
13.  * @Description: MircoMessage:Mark_7001
14.  */
15. @Configuration
16. public class MyBatisPlusConfig {
17.     @Bean
18.     public MybatisPlusInterceptor mybatisPlusInterceptor() {
19.         MybatisPlusInterceptor mybatisPlusInterceptor =new MybatisPlusInterceptor();
20.         PaginationInnerInterceptor paginationInnerInterceptor =new PaginationInnerInterceptor();
21.         // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
22.         //paginationInnerInterceptor.setOverflow(false);
23.         // 设置最大单页限制数量,默认 500 条,-1 不受限制
24.         //paginationInnerInterceptor.setMaxLimit(500L);
25.         // 设置数据库类型
26.         paginationInnerInterceptor.setDbType(DbType.MYSQL);
27.         mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
28.         return mybatisPlusInterceptor;
29.     }
30. }

测试分页插件

 @Test
2.      public void testPage(){
3.          // 当前页  页大小
4.          QueryWrapper<Dept> queryWrapper=new QueryWrapper<>();
5.          //queryWrapper.likeRight("dname", "A");
6.          Page<Dept> page = deptService.page(new Page<>(1, 2), queryWrapper);
7.          // 当前页数据  总页数  总记录数  当前页  页大小 ... ..
8.          List<Dept> list = page.getRecords();
9.          list.forEach(System.out::println);
10.         System.out.println("总页数:"+page.getPages());
11.         System.out.println("总记录数:"+page.getTotal());
12.         System.out.println("当前页:"+page.getCurrent());
13.         System.out.println("页大小:"+page.getSize());
14.     }

05_SpringBoot_Junit5

springboot 2.2.0开始引入Junit5作为单元测试的默认库

JUnit5和之前的版本有很大的不同,由单个子项目的几个不同模块组成

JUnit Platform ,是在JVM上启动测试框架的技术,不仅支持Junit自己的测试引擎,其他的测试引擎也可以

JUnit Jupiter,提供了Junit5的最新的编程模型,是Junit5 的核心,内部包含了一个测试引擎,用于在Junit Platform上运行

JUnit Vintager: 提供了兼容Junit4/3 的测试引擎

Junit5 = JUnit Platform+ JUnit Jupiter+JUnit Vintager

Junit支持Spring中的注解,测试起来比较方便, @Autowired @Transactional 等

1.   package com.msb;
2.
3.  import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4.  import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
5.  import com.msb.mapper.DeptMapper;
6.  import com.msb.pojo.Dept;
7.  import com.msb.service.DeptService;
8.  import org.junit.jupiter.api.*;
9.  import org.junit.jupiter.api.extension.ExtendWith;
10. import org.junit.platform.commons.annotation.Testable;
11. import org.springframework.beans.factory.annotation.Autowired;
12. import org.springframework.boot.test.context.SpringBootTest;
13. import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
14. import org.springframework.test.context.BootstrapWith;
15. import org.springframework.test.context.junit.jupiter.SpringExtension;
16.
17. import java.util.List;
18. import java.util.concurrent.TimeUnit;
19.
20. @SpringBootTest // 使用springboot的容器功能
21. /*@BootstrapWith(SpringBootTestContextBootstrapper.class)
22. @ExtendWith({SpringExtension.class})*/
23. @DisplayName("Junit5测试类")// 测试类描述
24. class SpringbootMybatisplusApplicationTests2 {
25.
26.     @Autowired
27.     private DeptMapper deptMapper;
28.
29.
30.     @BeforeEach
31.     public void testForeach(){
32.         System.out.println("beforeach");
33.     }
34.
35.     @AfterEach
36.     public void testAftereach(){
37.         System.out.println("aferEach");
38.     }
39.
40.     @BeforeAll
41.     public static void beforeAll(){
42.         System.out.println("beforall");
43.     }
44.
45.     @AfterAll
46.     public static void aferAll(){
47.         System.out.println("afterAll");
48.     }
49.     @RepeatedTest(3)// 重复测试3次
50.     @Timeout(value = 10000,unit = TimeUnit.MILLISECONDS)// 超时时间设置
51.     @DisplayName("Junit测试方法1")
52.     @Test
53.     public void test1(){
54.         System.out.println("a");
55.         System.out.println(deptMapper);
56.     }
57.
58.
59.     @Disabled// 设置不可用
60.     @DisplayName("Junit测试方法2") // 方法描述
61.     @Test
62.     public void test2(){
63.         System.out.println("b");
64.     }
65.
66.
67.
68. }

断言机制

断定某件事情,一定会发生,如果没有发生,那就是出现了问题,所欲的测试运行结束后,会有一个详细的断言报告

用来对测试需要满足的条件进行验证,这些断言方法都是org.junit.jupiter.api.Assertions中的静态方法,

简单断言

1.   package com.msb;
2.
3.
4.  import org.junit.jupiter.api.Assertions;
5.  import org.junit.jupiter.api.DisplayName;
6.  import org.junit.jupiter.api.Test;
7.  import org.springframework.boot.test.context.SpringBootTest;
8.
9.  import java.time.Duration;
10. import java.util.concurrent.TimeUnit;
11.
12. @SpringBootTest
13. @DisplayName("Junit5断言测试类")
14. class SpringbootMybatisplusApplicationTests3 {
15.
16.     @DisplayName("简单断言1")
17.     @Test
18.     public void testAssertions1(){
19.         int add = add(1, 2);
20.         Assertions.assertEquals(6,add,"add结果计算错误");
21.     }
22.     public int add(int a,int b){
23.         return a+b;
24.     }
25.
26.     @DisplayName("简单断言2")
27.     @Test
28.     public void testAssertions2(){
29.         String s =new String("xxx");
30.         String s2=new String("abc");
31.         Assertions.assertEquals(s,s2,"String对象不一样");
32.     }
33.
34.     // 组合断言
35.     @DisplayName("组合断言")
36.     @Test
37.     public void testAssertAll(){
38.         Assertions.assertAll("AssertAll",
39.                 ()-> Assertions.assertTrue(true&& false),
40.                 ()-> Assertions.assertEquals(1,2));
41.     }
42.
43.     // 异常断言 认为应该会出现异常
44.     @DisplayName("异常断言")
45.     @Test
46.     public void testAssertException(){
47.         Assertions.assertThrows(ArithmeticException.class, ()->{ int i=1/0;}, "没有抛出异常");
48.     }
49.
50.
51.
52.     // 超时断言 判断有没有超时
53.     @DisplayName("超时断言")
54.     @Test
55.     public void testAssertTimeOut(){
56.         Assertions.assertTimeout(Duration.ofMillis(1000),()-> Thread.sleep(5000));
57.     }
58.
59.     // 快速失败
60.     @DisplayName("快速失败")
61.     @Test
62.     public void testFail(){
63.         if(true){
64.             Assertions.fail("测试 失败");
65.         }
66.     }
67. }

前置条件(assumptions假设)

类似于断言,不同在于,不满足断言回事方法测试失败,而不满足的前置条件会使得的是方法的执行中止,前置条件可以看成是测试方法执行的前提,当条件不满足时,就没有继续执行的必要

1.   package com.msb;
2.
3.
4.  import org.junit.jupiter.api.Assertions;
5.  import org.junit.jupiter.api.Assumptions;
6.  import org.junit.jupiter.api.DisplayName;
7.  import org.junit.jupiter.api.Test;
8.  import org.springframework.boot.test.context.SpringBootTest;
9.
10. import java.time.Duration;
11.
12. @SpringBootTest
13. @DisplayName("Junit5测试前置条件")
14. class SpringbootMybatisplusApplicationTests4 {
15.
16.     @DisplayName("测试前提条件")
17.     @Test
18.     public void testAssumptions(){
19.         // 假设为true,才会执行
20.         Assumptions.assumeTrue(false,"结果不是true");
21.         System.out.println("后面的测试代码前提条件");
22.     }
23.
24.     @DisplayName("简单断言1")
25.     @Test
26.     public void testAssertions1(){
27.         int add =10;
28.         Assertions.assertEquals(6,add,"add结果计算错误");
29.         System.out.println("后面的测试代码简单断言");
30.     }
31.
32.
33. }
34.

嵌套测试

1.   package com.msb;
2.
3.
4.  import org.junit.jupiter.api.*;
5.  import static org.junit.jupiter.api.Assertions.*;
6.
7.  import java.util.EmptyStackException;
8.  import java.util.Stack;
9.
10.
11.
12. @DisplayName("嵌套测试")
13. class SpringbootMybatisplusApplicationTests5 {
14.
15.     Stack<Object> stack;
16.
17.     @Test
18.     @DisplayName("is instantiated with new Stack()")
19.     void isInstantiatedWithNew() {
20.         new Stack<>();
21.         // 外层的测试不能驱动内层的测试方法
22.         assertNull(stack);
23.     }
24.
25.     @Nested
26.     @DisplayName("when new")
27.     class WhenNew {
28.
29.         @BeforeEach
30.         void createNewStack() {
31.             stack = new Stack<>();
32.         }
33.
34.         @Test
35.         @DisplayName("is empty")
36.         void isEmpty() {
37.             assertTrue(stack.isEmpty());
38.         }
39.
40.         @Test
41.         @DisplayName("throws EmptyStackException when popped")
42.         void throwsExceptionWhenPopped() {
43.             assertThrows(EmptyStackException.class, stack::pop);
44.         }
45.
46.         @Test
47.         @DisplayName("throws EmptyStackException when peeked")
48.         void throwsExceptionWhenPeeked() {
49.             assertThrows(EmptyStackException.class, stack::peek);
50.         }
51.
52.         @Nested
53.         @DisplayName("after pushing an element")
54.         class AfterPushing {
55.
56.             String anElement = "an element";
57.
58.             @BeforeEach // 内层Test可以驱动外层的BeforeEach
59.             void pushAnElement() {
60.                 stack.push(anElement);
61.             }
62.
63.             @Test
64.             @DisplayName("it is no longer empty")
65.             void isNotEmpty() {
66.
67.                 assertFalse(stack.isEmpty());
68.             }
69.
70.             @Test
71.             @DisplayName("returns the element when popped and is empty")
72.             void returnElementWhenPopped() {
73.                 assertEquals(anElement, stack.pop());
74.                 assertTrue(stack.isEmpty());
75.             }
76.
77.             @Test
78.             @DisplayName("returns the element when peeked but remains not empty")
79.             void returnElementWhenPeeked() {
80.                 assertEquals(anElement, stack.peek());
81.                 assertFalse(stack.isEmpty());
82.             }
83.         }
84.     }
85. }

参数化测试

1.   package com.msb;
2.
3.
4.  import org.junit.jupiter.api.BeforeEach;
5.  import org.junit.jupiter.api.DisplayName;
6.  import org.junit.jupiter.api.Nested;
7.  import org.junit.jupiter.api.Test;
8.  import org.junit.jupiter.params.ParameterizedTest;
9.  import org.junit.jupiter.params.provider.MethodSource;
10. import org.junit.jupiter.params.provider.ValueSource;
11.
12. import java.util.EmptyStackException;
13. import java.util.Stack;
14. import java.util.stream.Stream;
15.
16. import static org.junit.jupiter.api.Assertions.*;
17.
18.
19. @DisplayName("参数化测试")
20. class SpringbootMybatisplusApplicationTests6 {
21.
22.
23.     @ParameterizedTest
24.     @ValueSource(ints = { 1, 2, 3 })
25.     void testWithValueSource(int argument) {
26.         System.out.println(argument);
27.         assertTrue(argument > 0 && argument < 4);
28.     }
29.
30.
31.
32.     @ParameterizedTest
33.     @MethodSource("stringProvider")
34.     void testWithExplicitLocalMethodSource(String argument) {
35.         assertNotNull(argument);
36.     }
37.
38.     static Stream<String> stringProvider() {
39.         return Stream.of("apple", "banana");
40.     }
41.
42.
43.
44.
45. }

骨灰级SSM 和spring boot大全(二)相关推荐

  1. spring boot / cloud (二) 规范响应格式以及统一异常处理

    spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...

  2. Spring Boot(二)应用实例

    Spring Boot(二) 一 . Spring Boot Web 应用实例 这是一个Spring Boot web应用程序示例,使用嵌入式Tomcat + JSP模板,并将包作为可执行文件WAR文 ...

  3. Spring Boot (二)集成Mybatis、Druid

    项目GitHub地址 : https://github.com/FrameReserve/TrainingBoot Spring Boot (二)集成Mybatis.Druid,标记地址: https ...

  4. 一步一步学Spring Boot(二)课程发布了~~~

    课程名称 <一步一步学Spring Boot(二)> 学习地址 CSDN学习地址: http://edu.csdn.net/lecturer/994 51CTO学习地址:http://ed ...

  5. 重拾后端之Spring Boot(二):MongoDB的无缝集成

    重拾后端之Spring Boot(一):REST API的搭建可以这样简单 重拾后端之Spring Boot(二):MongoDb的无缝集成 重拾后端之Spring Boot(三):找回熟悉的Cont ...

  6. Spring Boot教程(二十):Spring Boot使用String Task定时任务

    一.JAVA常见的几种定时任务比较 Timer:jdk自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让程序按照某一个频度执行, ...

  7. Java--SSH,SSM和Spring Boot框架区别优缺点

    一.SSH(Struts + Spring + Hibernate) 1.基本概念 SSH框架是JAVA EE中三种框架所集成,分别是Struts,Spring,Hibernate框架所组成 集成SS ...

  8. java 方式配置ssm,关于SSM以及Spring boot中对于Spring MVC配置的问题

    SSM中 Spring MVC配置 传统的web.xml配置 web.xml contextConfigLocation classpath*:applicationContext.xml org.s ...

  9. Spring Boot(二): 集成Mybatis

    上一篇讲述了什么是Spring Boot.如何创建Sping Boot项目以及如何通过配置修改端口号: 本篇将讲述Spring Boot与Mybatis的整合过程. 一.添加mybatis以及mysq ...

最新文章

  1. 菜鸟教程:SQL 通用数据类型
  2. mysql 打印_故障分析 | MySQL:5.6大事务show engine innodb status故障一例
  3. 为什么会有那么多的人选择Java?学Java到底好不好?
  4. 消息队列mysql redis那个好_Redis作为消息队列与RabbitMQ的比较
  5. java实现九九乘法表的输出
  6. 【转】QT布局QGridLayout QHBoxLayout QVBoxLayout简要分析!!
  7. icmp消息类型报告传输_ICMP消息的类型和ICMP消息格式
  8. iOS逆向之自动化重签名
  9. struts 1.x 原理
  10. Could not get unknown property 'packageForR' for task ':app:processDebugReso
  11. java定时器 多线程_Java多线程 定时器
  12. c语言图书管理系统简单代码,C语言图书管理系统代码
  13. mysql 建模工具 mac_MySQL Workbench for Mac 6.0 下载 - Mac上优秀的数据库建模工具 | 玩转苹果...
  14. 使用PMSM控制的puma560机械臂简单轨迹跟踪
  15. 飞翔的小鸟(FlyBird)游戏C语言编程(含撞柱子)
  16. springBoot做后台实现微信小程序图片上传和下载
  17. 三款『正则表达式』可视化工具
  18. En-Tan-Mo(ETM)项目周报(7.12-7.18)
  19. 023递归:这帮小兔崽子
  20. 宝塔解压文件,通过SSH命令解压缩.tar.gz、.gz、.zip文件的方法

热门文章

  1. 为什么即使调用EnableMenuItem菜单项后,菜单项还处于禁止状态
  2. Autojs Pro 9.3解除布局识别限制、解除打包后限制
  3. “驱动器中的磁盘未被格式化”错误的解决方法
  4. 10年鼻炎,一路的辛酸泪
  5. 解决SurfaceView预览Camera拉伸问题
  6. Aviator表达式应用
  7. 家居企业怎么做微信营销?
  8. b站看老九学习c++中的杂记
  9. 清华即将颁发特等奖学金 15位“神仙”打架看呆网友
  10. vim文字处理器(一)基本命令