Springboot Spring Boot 是一个快速开发框架,可以迅速搭建出一套基于Spring框架体系的应用,是Spring Cloud的基础。
Spring Boot开启了各种自动装配,从而简化代码开发,不需要编写各种配置文件,只需要引入相关依赖就可以迅速搭建一个应用
特点
1、不需要web.xml
2、不需要springmvc.xml
3、不需要tomcat,SpringBoot内嵌了一个tomcat
4、不需要配置JSON解析,支持REST架构
5、个性化配置非常简单
Spring Boot 2.x要求必须基于Spring 5.x,要求java版本必须是8以上
Spring Boot的使用 第一种方法是新建Maven工程然后在pom.xml中继承父项目
导入启动器Spring Boot的启动器实际上就是一个依赖。这个依赖中包含了整个这个技术的相关jar包,还包含了这个技术的自动配置,以前绝大多数XML配置都不需要配置了。当然了,启动器中自动配置无法实现所有内容的自动配置,在使用Spring Boot时还需要进行少量的配置(这个配置不是在xml中了,而是在properties或yml中即可)。如果是Spring自己封装的启动器的artifact id名字满足:spring-boot-starter-xxxx,如果是第三方公司提供的启动满足:xxxx-spring-boot-starter。以后每次使用Spring Boot整合其他技术时首先需要考虑导入启动器。
1 2 3 4 5 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.12.RELEASE</version > </parent >
1、创建Handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.bitzh.springboot.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/hello") public class HelloHandler { @GetMapping("/index") public String index () { return "Hello Spring Boot" ; } }
2、创建启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.bitzh.springboot;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
启动类必须覆盖所有与业务相关的类:启动类所在的包必须是业务类所在包的同包或者父包。如果没有覆盖,业务类就不会自动装配到IoC容器中。
Spring Boot配置文件 自定义banner
Properties
1 2 3 4 5 6 7 8 9 10 11 12 spring.application.name =springboot server.port =8181 server.servlet.context-path =/springboot server.servlet.session.cookie.max-age =100 server.servlet.session.timeout =100 server.tomcat.uri-encoding =utf-8
YAML
YAML是不同于Properties的另外一种文件格式,同样可以用来写配置文件,SpringBoot默认支持Yaml格式。
YAML格式的优点在于编写简单,结构清晰,利用缩进的形式来表示层级关系。
相比于Properties,YAML可以进一步简化配置文件的编写,更加方便。
1 2 3 4 5 6 7 8 9 10 server: port: 8181 servlet: context-path: /springboot session: cookie: max-age: 100 timeout: 100 tomcat: uri-encoding: utf-8
需要注意的是YAML格式书写非常严格,属性名和属性值之间必须至少有一个空格
如果Properties和YAML同时存在,Properties优先级更高
配置文件除了可以放在resources路径下之外,还有四个地方可以放置
这四个地方的优先级是
1、根路径下config里面的yaml
2、根路径下的yaml
3、resources路径下的config中的yaml
4、resources路径下的yaml
可以直接在Handler中读取yaml文件中的数据,比如我们在业务方法中向客户端返回当前服务端的端口信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.bitzh.springboot.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/hello") public class HelloHandler { @Value("${server.port}") private String port; @GetMapping("/index") public String index () { return "当前输入的端口" +this .port; } }
@Value注解同样适用于properties文件
SpringBoot项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 -- 项目名 --src --main --java java代码 --resources --public 公共资源。所有共享的内容。对外公开的内容。(访问的时候不需要写public这个路径,直接写里面的文件名+后缀就可以访问到了) --static静态资源。图片、js、css。不会被服务器解析。(访问的时候不需要写static这个路径,直接写里面的文件名+后缀就可以访问到了) --js -- jquery.js 访问:http://ip:port/js/jquery.js 注意:该目录是SpringBoot可以直接识别的目录,会将其中的 静态资源编译到web项目中,并放到tomcat中使用。静态资源的 访问路径中无需声明static 例如:localhost:8080/a.png --templates FreeMarker thymeleaf 页面所在目录。 --webapp 只有当页面使用jsp时才有。(已经不推荐用jsp了,所以默认没有) --WEB-INF
Springboot_整合MyBatis 1、导入依赖pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.0</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.33</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency >
2、编写mybatis的配置文件
appliction.yml中添加如下配置
1 2 3 mybatis: mapper-locations: classpath:com.bitzh.mapper/*.xml type-aliases-package: com.bitzh.pojo
1 mapper-locations: classpath:com.bitzh.mapper/*.xml mapper映射文件包扫描
1 type-aliases-package 实体类别名包扫描
3、编写Controlelr层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.bitzh.controller;import com.bitzh.pojo.User;import com.bitzh.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/findAll") @ResponseBody public List<User> findAll () { return userService.findAll(); } }
4、UserService.java
1 2 3 4 public interface UserService { public List<User> findAll () ; }
5、UserServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.bitzh.service.impl;import com.bitzh.mapper.UserMapper;import com.bitzh.pojo.User;import com.bitzh.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> findAll () { return userMapper.selectAll(); } }
6、UserMapper.xml
1 2 3 4 5 6 7 8 9 10 <?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.bitzh.mapper.UserMapper" > <select id ="selectAll" resultType ="user" > select * from t_user </select > </mapper >
7、启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.bitzh;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ComponentScan;@SpringBootApplication @ComponentScan("com.bitzh.mapper") public class Springboot02Application { public static void main (String[] args) { SpringApplication.run(Springboot02Application.class, args); } }
注意@ComponentScan(“com.bitzh.mapper”)这里要写扫描的mapper路径,或者在mapper的接口里面添加@Mapper注解否则扫描不到,但是有时候@Mapper注解也是不起作用,看情况使用(两个不能一起用)
其次在springboot中,mapper的映射文件的名字可以不一致,实际上是根据xml中的namespace来进行映射的,所以文件名不一致也没事。
Springboot_整合logback Springboot默认使用logback组件作为日志管理,logback是由log4j创世人设计的一个开源日志组件,不需要导入依赖,默认已经在starter里面了
使用步骤
1、在resources里面新建logback.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <?xml version="1.0" encoding="UTF-8" ?> <configuration > <property name ="LOG_HOME" value ="${catalina.base}/logs/" /> <appender name ="Stdout" class ="ch.qos.logback.core.ConsoleAppender" > <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 ="RollingFile" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <FileNamePattern > ${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern > <MaxHistory > 30</MaxHistory > </rollingPolicy > <layout class ="ch.qos.logback.classic.PatternLayout" > <pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n </pattern > </layout > <triggeringPolicy class ="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy" > <MaxFileSize > 10MB</MaxFileSize > </triggeringPolicy > </appender > <root level ="info" > <appender-ref ref ="Stdout" /> <appender-ref ref ="RollingFile" /> </root > <logger name ="com.msb.mapper" level ="DEBUG" > </logger > </configuration >
Springboot_整合PageHelper PageHelper插件 我们在正常的查询业务之中,只需要加上一行代码就可以实现分页的数据的封装处理
1、先完成基础的查询所有员工的业务(这里就不演示了太简单了)
2、添加pageHelper的启动器依赖
1 2 3 4 5 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper-spring-boot-starter</artifactId > <version > 1.2.12</version > </dependency >
3、controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.bitzh.controller;import com.bitzh.pojo.Emp;import com.bitzh.service.EmpService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controller @RequestMapping("/emp") public class EmpController { @Autowired private EmpService empService; @ResponseBody @RequestMapping("/findAll") public List<Emp> findAll () { return empService.findAll(); } @ResponseBody @RequestMapping("/findByPage/{pageNum}/{pageSize}") public List<Emp> findByPage (@PathVariable("pageNum") Integer pageNum,@PathVariable("pageSize") Integer PageSize) { return empService.findByPage(pageNum,PageSize); } }
4、Service层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.bitzh.service.impl;import com.bitzh.mapper.EmpMapper;import com.bitzh.pojo.Emp;import com.bitzh.service.EmpService;import com.github.pagehelper.Page;import com.github.pagehelper.PageHelper;import com.github.pagehelper.PageInfo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service public class EmpServiceImpl implements EmpService { @Autowired private EmpMapper empMapper; @Override public List<Emp> findAll () { return empMapper.findAll(); } @Override public List<Emp> findByPage (Integer pageNum, Integer pageSize) { Page<Object> page = PageHelper.startPage(pageNum, pageSize); List<Emp> list = empMapper.findAll(); System.out.println("当前页:" +page.getPageNum()); System.out.println("总页数" +page.getPages()); System.out.println("页大小:" +page.getPageSize()); System.out.println("总记录数:" +page.getTotal()); System.out.println("当前页数据" +page.getResult()); PageInfo<Emp> pi = new PageInfo <>(list); System.out.println("当前页" +pi.getPageNum()); System.out.println("总页数" +pi.getPages()); System.out.println("页大小" +pi.getSize()); System.out.println("总记录数" +pi.getTotal()); System.out.println("当前页数据" +pi.getList()); return list; } }
PageInfo对象和Page对象的区别
1 https://blog.csdn.net/weixin_43760541/article/details/107155386
Springboot_整合Druid Druid是由阿里巴巴推出的数据库连接池。它结合了C3P0、DBCP、PROXOOL等数据库连接池的优点。之所以从众多数据库连接池中脱颖而出,还有一个重要的原因就是它包含控制台,很方便的帮助我们实现对于sql执行的监控。
1、添加依赖
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency >
2、修改配置文件application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: root druid: initial-size: 5 min-idle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 filters: stat,wall,slf4j connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 web-stat-filter: enabled: true url-pattern: "/*" exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" stat-view-servlet: url-pattern: "/druid/*" allow: 127.0 .0 .1 ,192.168.8.109 deny: 192.168 .1 .188 reset-enable: false login-username: admin login-password: 123456
Springboot整合JSP(了解一下就好) 因为JSP技术基本上已经淘汰了
1、添加依赖
1 2 3 4 5 6 <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-jasper</artifactId > <scope > provided</scope > </dependency >
2、添加webapps目录并设置目录
3、设置工作目录,如果在IDEA中项目结构为聚合工程。那么在运行jsp是需要指定路径。如果项目结构为独立项目则不需要。
4、在 yml配置文件中配置视图解析器参数
后面跟springmvc类似
springboot整合FreeMarker(jsp的替代技术) FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
常用的java模板引擎还有哪些? Jsp、Freemarker、Thymeleaf 、Velocity 等。
模板+数据模型=输出 freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件) freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。 需要创建Spring Boot+Freemarker工程用于测试模板。
使用步骤
1、配置依赖pom.xml
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-freemarker</artifactId > </dependency >
阅读官方文档知道
通过查阅配置信息发现,默认前缀为 ‘’ ,后缀为.ftlh,默认路径为templates
2、在show.ftlh中测试一下
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > this is ${msg}Page </body > </html >
3、controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.bitzh.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;@Controller public class FreemarkerController { @ResponseBody @RequestMapping("/show") public ModelAndView freemarker (ModelAndView modelAndView) { modelAndView.setViewName("show" ); modelAndView.addObject("msg" , "freemarker" ); return modelAndView; } }
测试成功
FreeMarker 常用指令 遍历List集合 1、创建controller利用之前的userService方法
1 2 3 4 5 6 7 8 9 10 11 12 @Autowired private EmpService empService; @RequestMapping("/showEmp") public ModelAndView testList () { ModelAndView modelAndView = new ModelAndView (); List<Emp> empList = empService.findAll(); modelAndView.addObject("empList" , empList); modelAndView.setViewName("showEmp" ); return modelAndView; }
2、创建showEmp.flth
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } </style > </head > <body > <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 工号</th > <th > 姓名</th > <th > 岗位</th > <th > 薪资</th > <th > 部门号</th > </tr > </table > </body > </html >
3、在showEmp.flth中加入数据
1、注释,即
,介于其之间的内容会被freemarker忽略 2、插值(Interpolation):即
1 ${..}部分,freemarker会用真实的值代替${..}
3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。 4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } </style > </head > <body > <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 工号</th > <th > 姓名</th > <th > 岗位</th > <th > 薪资</th > <th > 部门号</th > </tr > <#--这里就类似于for(Emp emp : empList)--> <#list empList as emp> <tr > <td > ${emp_index}</td > <td > ${emp.empno}</td > <td > ${emp.ename}</td > <td > ${emp.job}</td > <td > ${emp.sal}</td > <td > ${emp.deptno}</td > </tr > </#list> </table > </body > </html >
说明:_ index:得到循环的下标,使用方法是在stu后边加”_index”,它的值是从0开始
遍历Map集合 1、创建Controlelr层
FreeMarker在遍历map集合是,key必须是String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Autowired private EmpService empService; @RequestMapping("/showEmpMap") public ModelAndView testMap () { ModelAndView modelAndView = new ModelAndView (); List<Emp> empList = empService.findAll(); Map<String,Emp> empMap = new HashMap <>(); for (Emp emp : empList) { empMap.put(emp.getEmpno().toString(), emp); } modelAndView.addObject("empMap" , empMap); modelAndView.setViewName("showEmpMap" ); return modelAndView; }
2、创建showEmpMap.ftlh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } </style > </head > <body > <#--查看Map集合中的一个元素--> 查看7521员工信息 工号:${empMap['7521'].empno}<br /> 姓名:${empMap['7521'].ename}<br /> 岗位:${empMap['7521'].job}<br /> 薪资:${empMap['7521'].sal}<br /> 部门号:${empMap['7521'].deptno}<br /> <hr /> <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 工号</th > <th > 姓名</th > <th > 岗位</th > <th > 薪资</th > <th > 部门号</th > </tr > <#list empMap?keys as k> <tr > <#--这个keys也是个集合--> <td > ${k_index}</td > <td > ${k}</td > <td > ${empMap[k].ename}</td > <td > ${empMap[k].job}</td > <td > ${empMap[k].sal}</td > <td > ${empMap[k].deptno}</td > </tr > </#list> </table > </body > </html >
if指令 如果在设置ModelAndView的时候,list集合为空的话会报错,这时候特别不友好,不展示就好了,所以就需要判断空值
if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否 则跳过内容不再输出。
如何判断空值 1、判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false 例:为防止stus为空报错可以加上判断如下 2、缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。例: ${name!’’}表示如果name为空显示空字符串。如果是嵌套对象则建议使用()括起来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } </style > </head > <body > <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 工号</th > <th > 姓名</th > <th > 岗位</th > <th > 薪资</th > <th > 上级</th > <th > 部门号</th > </tr > <#if empList??> <#--这里就类似于for(Emp emp : empList)--> <#list empList as emp> <tr > <td > ${emp_index}</td > <td > ${emp.empno}</td > <td > ${emp.ename}</td > <td > ${emp.job}</td > <td > ${emp.sal}</td > <td > ${emp.mgr!'没有'}</td > <td > ${emp.deptno}</td > </tr > </#list> </#if> </table > </body > </html >
if中支持的运算符 a算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % b逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误 c比较运算符有如下几个: ① =或者==:判断两个值是否相等. ② !=:判断两个值是否不等. ③ > 或者gt:判断左边值是否大于右边值 ④ >=或者gte:判断左边值是否大于等于右边值 ⑤ <或者lt:判断左边值是否小于右边值 ⑥ <=或者lte:判断左边值是否小于等于右边值 注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,”x”,”x “,”X”是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <#if empList??> <#list empList as emp> <tr <#if emp_index %2 ==0 > style="background-color: gray" </#if>> <td > ${emp_index}</td > <td > ${emp.empno}</td > <td <#if emp.ename == 'KING' > style="color: aqua" </#if>>${emp.ename}</td > <td > ${emp.job}</td > <td > ${emp.mgr!'无'}</td > <td <#if emp.sal gte 2000.0 > style="color: red" </#if>>${emp.sal}</td > <td > ${emp.comm!'0'}</td > <td > ${emp.deptno}</td > </tr > </#list> </#if>
内置函数 内建函数语法格式: 变量+?+函数名称
1、内建函数获取某个集合的大小 ${集合名?size}
2、内建函数日期格式化 显示年月日:
显示时分秒:
显示日期+时间:
自定义格式化:
1 ${today?string("yyyy年MM月")}
3、内建函数将json字符串转成对象
1 2 3 <#assign text="{'bank':'工商银行','account':'10101920201920212'}" /> <#assign data=text?eval /> 开户行:${data.bank} 账号:${data.account}
其中用到了 assign标签,assign的作用是定义一个变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 员工人数:${empList?size} <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 工号</th > <th > 姓名</th > <th > 岗位</th > <th > 上级</th > <th > 入职日期a</th > <th > 入职日期b</th > <th > 入职日期c</th > <th > 入职日期d</th > <th > 薪资</th > <th > 补助</th > <th > 部门号</th > </tr > <#if empList??> <#list empList as emp> <tr <#if emp_index %2 ==0 > style="background-color: gray" </#if>> <td > ${emp_index}</td > <td > ${emp.empno}</td > <td <#if emp.ename == 'KING' > style="color: aqua" </#if>>${emp.ename}</td > <td > ${emp.job}</td > <td > ${emp.mgr!'无'}</td > <td > ${emp.hiredate?date}</td > <td > ${emp.hiredate?time}</td > <td > ${emp.hiredate?datetime}</td > <td > ${emp.hiredate?string("yyyy年MM月dd日")}</td > <td <#if emp.sal gte 2000.0 > style="color: red" </#if>>${emp.sal}</td > <td > ${emp.comm!'0'}</td > <td > ${emp.deptno}</td > </tr > </#list> </#if> </table >
内置函数
1 https://blog.csdn.net/chami_/article/details/51992044
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页面必须先走控制器。
关于Thymeleaf默认配置
1、添加Thymeleaf依赖
1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > <version > 2.4.5</version > </dependency >
2、写个html和controller测试
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > this is index page </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.bitzh.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class ThymeleafController { @RequestMapping("/index") public String showIndex () { return "index" ; } }
Thymeleaf基础语法 Thymeleaf标准变量表达式 Thymeleaf通过标准变量表达式完成数据的展示和处理
1 标准变量表达式必须依赖标签,不能独立使用 2 标准变量表达式一般在开始标签中,以 th开头 3 语法为:
1 <tag th:***="${key}" ></tag>
4 表达式中可以通过
取出域中的值并放入标签的指定位置 5 ${}在这里不能单独使用,必须在 th:后面的双引号里使用
为了有提示,修改html页面中
标签为
1 <html xmlns:th ="http://www.thymeleaf.org" >
1、th:text属性 (向html双标签内部输出信息)
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > this is index page<br /> <span th:text ="pageMessage" > </span > <br /> <span th:text ="${msg}" > </span > </body > </html >
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.bitzh.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import java.util.Map;@Controller public class ThymeleafController { @RequestMapping("/index") public String showIndex (Map<String, Object> map) { map.put("msg" , "testMessage" ); return "index" ; } }
2、th:value
表单元素,设置HTML标签中表单元素value属性时使用
1 2 <input th:value ="pageMessage" /> <input th:value ="${msg}" />
3、th:if
因为如果emp为空的话会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > 展示单个员工信息<br /> <span th:if ="${emp}!=null" > 工号:<span th:text ="${emp.getEmpno()}" > </span > <br /> 姓名:<span th:text ="${emp.getEname()}" > </span > <br /> 职务:<span th:text ="${emp.getJob()}" > </span > <br /> 上级:<span th:text ="${emp.getMgr()}" > </span > <br /> 入职日期:<span th:text ="${emp.getHiredate()}" > </span > <br /> 工资:<span th:text ="${emp.getSal()}" > </span > <br /> 补助:<span th:text ="${emp.getComm()}" > </span > <br /> 部门号:<span th:text ="${emp.getDeptno()}" > </span > <br /> </span > </body > </html >
但是即使这样如果写成emp[0].getEmpno会出现越界报错,如果是这样该怎么解决呢?再嵌套一层th:if然后用emp.size() != 0来进行判断。
1 2 3 4 5 6 7 8 9 10 11 12 <span th:if ="${empList}!=null" > <span th:if ="${empList.size()} ne 0" > 工号:<span th:text ="${empList[0].empno}" > </span > <br /> 姓名:<span th:text ="${empList[0].ename}" > </span > <br /> 职务:<span th:text ="${empList[0].job}" > </span > <br /> 上级:<span th:text ="${empList[0].mgr}" > </span > <br /> 入职日期:<span th:text ="${empList[0].hiredate}" > </span > <br /> 工资:<span th:text ="${empList[0].sal}" > </span > <br /> 补助:<span th:text ="${empList[0].comm}" > </span > <br /> 部门号:<span th:text ="${empList[0].deptno}" > </span > <br /> </span > </span >
4、th:each标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } </style > </head > <body > 展示单个员工信息: <span th:if ="${emp}!=null" > 工号:<span th:text ="${emp.empno}" > </span > <br /> 姓名:<span th:text ="${emp.ename}" > </span > <br /> 职务:<span th:text ="${emp.job}" > </span > <br /> 上级:<span th:text ="${emp.mgr}" > </span > <br /> 入职日期:<span th:text ="${emp.hiredate}" > </span > <br /> 工资:<span th:text ="${emp.sal}" > </span > <br /> 补助:<span th:text ="${emp.comm}" > </span > <br /> 部门号:<span th:text ="${emp.deptno}" > </span > <br /> </span > <hr /> <span th:if ="${empList}!=null" > <span th:if ="${empList.size()} ne 0" > 工号:<span th:text ="${empList[0].empno}" > </span > <br /> 姓名:<span th:text ="${empList[0].ename}" > </span > <br /> 职务:<span th:text ="${empList[0].job}" > </span > <br /> 上级:<span th:text ="${empList[0].mgr}" > </span > <br /> 入职日期:<span th:text ="${empList[0].hiredate}" > </span > <br /> 工资:<span th:text ="${empList[0].sal}" > </span > <br /> 补助:<span th:text ="${empList[0].comm}" > </span > <br /> 部门号:<span th:text ="${empList[0].deptno}" > </span > <br /> </span > </span > <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 序号</th > <th > 总人数</th > <th > 偶数索引?</th > <th > 奇数索引?</th > <th > 第一?</th > <th > 最后?</th > <th > 工号</th > <th > 姓名</th > <th > 职务</th > <th > 上级</th > <th > 入职日期</th > <th > 工资</th > <th > 补助</th > <th > 部门号</th > </tr > <tr th:each ="emp,i:${empList}" > <td th:text ="${i.index}" > </td > <td th:text ="${i.count}" > </td > <td th:text ="${i.size}" > </td > <td th:text ="${i.odd}" > </td > <td th:text ="${i.even}" > </td > <td th:text ="${i.first}" > </td > <td th:text ="${i.last}" > </td > <td th:text ="${emp.empno}" > </td > <td th:text ="${emp.ename}" > </td > <td th:text ="${emp.job}" > </td > <td th:text ="${emp.mgr}" > </td > <td th:text ="${emp.hiredate}" > </td > <td th:text ="${emp.sal}" > </td > <td th:text ="${emp.comm}" > </td > <td th:text ="${emp.deptno}" > </td > </tr > </table > </body > </html >
5、标准变量表达式运算符支持
标准变量表达式支持的运算符
算数运算符
1 2 3 4 <span th:text ="1+1" > </span > <span th:text ="'1'+1" > </span > <span th:text ="${emp.empno}+1" > </span > <span th:text ="${emp.empno+1}" > </span >
关系运算符
1 2 3 4 5 6 1 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 2 3 4 5 6 7 8 9 10 <div th:text ="1>0 and 2<3" > </div > <div th:text ="1>0 and 2>3" > </div > <div th:text ="1>0 or 2<3" > </div > <div th:text ="1>0 or 2>3" > </div > <hr /> <div th:text ="${emp.sal ge 800}" > </div > <div th:text ="${emp.sal } ge 800" > </div > <div th:text ="${emp.sal ge 800} and ${emp.deptno eq 20}" > </div > <div th:text ="(${emp.sal }ge 800) or (${emp.deptno } ne 20)" > </div > <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" >
如果是偶数列就用a样式否则用b样式
对空值作出处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <tr th:each ="emp,i:${empList}" th:class ="${i.odd}?a:b" > <td th:text ="${i.index}" > </td > <td th:text ="${i.count}" > </td > <td th:text ="${i.size}" > </td > <td th:text ="${i.odd}" > </td > <td th:text ="${i.even}" > </td > <td th:text ="${i.first}" > </td > <td th:text ="${i.last}" > </td > <td th:text ="${emp.empno}" > </td > <td th:text ="${emp.ename}" > </td > <td th:text ="${emp.job}" > </td > <td th:text ="${emp.mgr} eq null ?老板:${emp.mgr}" > </td > <td th:text ="${emp.hiredate}" > </td > <td th:text ="${emp.sal}" > </td > <td th:text ="${emp.comm} eq null ?0:${emp.comm}" > </td > <td th:text ="${emp.deptno}" > </td > </tr >
6、th:href
设置href属性的。取值使用@{}取跳转路径值,小括号里面写的是获取参数
1 2 3 4 5 6 <td > <a th:href ="@{/removeEmp(empno=7369,ename='SMITH')}" > 删除</a > </td > <td > <a th:href ="@{/removeEmp(empno=${emp.empno},ename=emp.ename)}" > 删除</a > </td >
controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RequestMapping("/showEmp") public String showEmp (Map<String, Object> map) { List<Emp> empList = empService.findAll(); map.put("empList" , empList); map.put("emp" , empList.get(0 )); return "showEmp" ; } @RequestMapping("/removeEmp") public String removeEmp (Integer empno,String ename) { System.out.println("remove" ); return "redirect:showEmp" ; }
7、th:onclick
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 序号</th > <th > 总人数</th > <th > 偶数索引?</th > <th > 奇数索引?</th > <th > 第一?</th > <th > 最后?</th > <th > 工号</th > <th > 姓名</th > <th > 职务</th > <th > 上级</th > <th > 入职日期</th > <th > 工资</th > <th > 补助</th > <th > 部门号</th > <th > 操作</th > </tr > <tr th:each ="emp,i:${empList}" th:class ="${i.odd}?a:b" > <td th:text ="${i.index}" > </td > <td th:text ="${i.count}" > </td > <td th:text ="${i.size}" > </td > <td th:text ="${i.odd}" > </td > <td th:text ="${i.even}" > </td > <td th:text ="${i.first}" > </td > <td th:text ="${i.last}" > </td > <td th:text ="${emp.empno}" > </td > <td th:text ="${emp.ename}" > </td > <td th:text ="${emp.job}" > </td > <td th:text ="${emp.mgr} eq null ?老板:${emp.mgr}" > </td > <td th:text ="${emp.hiredate}" > </td > <td th:text ="${emp.sal}" > </td > <td th:text ="${emp.comm} eq null ?0:${emp.comm}" > </td > <td th:text ="${emp.deptno}" > </td > <td > <a href ="javascript:void(0)" th:onclick ="removeEmp([[${emp.empno}]],[[${emp.ename}]])" > 删除</a > </td > </tr > </table > <script > function removeEmp (empno,ename ){ var resulet =confirm ("确定要删除编号为" +empno+"的" +ename); if (resulet){ window .location .href ="removeEmp?empno=" +empno+"&ename=" +ename; } } </script >
在th:onclick中要传参的话,参数用两个[]扩住就好了
Thymeleaf内置对象 Thymeleaf提供了一些内置对象,内置对象可直接在模板中使用。这些对象是以#引用的。 使用内置对象的语法 引用内置对象需要使用# 大部分内置对象的名称都以s结尾。如:strings、numbers、dates 常见内置对象如下
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对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #numbers #numbers.formatDecimal(numbwe,整数位,整数位千分位标识符,小数位,小数位表示符) ${#numbers.formatDecimal(num,1,'COMMA',2,'POINT')} 显示:99,999,999.99 1:表示整数位至少一位,不足以0补齐,如:num = 0.00, ${#numbers.formatDecimal(num,0,'COMMA',2,'POINT')}则显示 .00 ${#numbers.formatDecimal(num,1,'COMMA',2,'POINT')}则显示 0.00 COMMA:',' POINT:‘.’
域对象
更多内置 对象:
1 https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#number-literals
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 @RequestMapping("showIndex") public String showIndex (Map<String,Object> map, HttpServletRequest req, HttpSession session) { req.setAttribute("msg" , "requestMessage" ); session.setAttribute("msg" , "sessionMessage" ); req.getServletContext().setAttribute("msg" , "applicationMessage" ); List<Emp> empList = empService.findAll(); map.put("empList" , empList); return "index" ; }
页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > <style type ="text/css" > #empTable { width : 80% ; border : 1px solid blue; margin : 0px auto; } #empTable th ,td { border : 1px solid green; text-align : center; } .a { background-color : antiquewhite; } .b { background-color : gray; } </style > </head > <body > <table id ="empTable" cellpadding ="0px" cellspacing ="0px" > <tr > <th > 索引</th > <th > 序号</th > <th > 总人数</th > <th > 偶数索引?</th > <th > 奇数索引?</th > <th > 第一?</th > <th > 最后?</th > <th > 工号</th > <th > 姓名</th > <th > 职务</th > <th > 上级</th > <th > 入职日期</th > <th > 入职年</th > <th > 入职月</th > <th > 入职日</th > <th > 工资</th > <th > 补助</th > <th > 部门号</th > <th > 操作</th > </tr > <tr th:each ="emp,i:${empList}" th:class ="${i.odd}?a:b" > <td th:text ="${i.index}" > </td > <td th:text ="${i.count}" > </td > <td th:text ="${i.size}" > </td > <td th:text ="${i.odd}" > </td > <td th:text ="${i.even}" > </td > <td th:text ="${i.first}" > </td > <td th:text ="${i.last}" > </td > <td th:text ="${emp.empno}" > </td > <td th:text ="${emp.ename}" > </td > <td th:text ="${emp.job}" > </td > <td th:text ="${#strings.isEmpty(emp.mgr)}?老板:${emp.mgr}" > </td > <td th:text ="${#dates.format(emp.hiredate,'yyyy-MM-dd HH:mm:ss')}" > </td > <td th:text ="${#dates.year(emp.hiredate)}" > </td > <td th:text ="${#dates.month(emp.hiredate)}" > </td > <td th:text ="${#dates.day(emp.hiredate)}" > </td > <td th:text ="${#numbers.formatDecimal(emp.sal,7,'COMMA',2,'POINT')}" > </td > <td th:text ="${#strings.isEmpty(emp.comm)}?0:${#numbers.formatDecimal(emp.sal,7,'COMMA',2,'POINT')}" > </td > <td th:text ="${emp.deptno}" > </td > <td > <a href ="javascript:void(0)" th:onclick ="removeEmp([[${emp.empno}]],[[${emp.ename}]])" > 删除</a > </td > </tr > </table > <script > function removeEmp (empno,ename ){ var resulet =confirm ("确定要删除编号为" +empno+"的" +ename); if (resulet){ window .location .href ="removeEmp?empno=" +empno+"&ename=" +ename; } } </script > <hr /> request:<br /> <span th:text ="${#httpServletRequest.getAttribute('msg')}" > </span > <br /> <span th:text ="${#request.getAttribute('msg')}" > </span > <br /> <span th:text ="${msg}" > </span > <br /> session:<br /> <span th:text ="${#httpSession.getAttribute('msg')}" > </span > <br /> <span th:text ="${#session.getAttribute('msg')}" > </span > <br /> <span th:text ="${session.msg}" > </span > <br /> application:<br /> <span th:text ="${#servletContext.getAttribute('msg')}" > </span > <br /> <span th:text ="${application.msg}" > </span > <br /> </body > </html >
模板引擎总结 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: 前后端分离,最多,未来趋势
springboot_开发者工具 使用开发者工具包不需要重启。监听内容改变。
使用步骤
1、导入依赖
1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <version > 2.4.5</version > <optional > true</optional > </dependency >
2、修改idea自动编译
2、修改Reigstry
Ctrl+Shift+Alt+/ 点击弹出框中Registry…
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服务器中运行即可。
jar和war区别就是jar可以直接在浏览器运行,war需要再tomgcat中运行
使用步骤
1、导入springboot打包插件
1 2 3 4 5 6 7 8 9 10 11 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <fork > true</fork > </configuration > </plugin > </plugins > </build >
将项目导出成jar包并运行 项目类型为jar
使用maven package指令打包即可
打成包后,可以通过dos java -jar指令直接启动运行
将项目导出war包并运行 项目打包成war之后,要放在一个Tomcat上运行
如果我们当前的maven项目本身就是war类型的项目,直接打包即可,但是如果我们当前的maven项目是jar类型的项目,我们需要将项目修改为war类型,修改项目的pom文件,使用packaging标签设置值为war.并且需要在项目中创建webapp文件夹,并设置为资源文件夹。
排除项目中自带的所有的Tomcat插件和jsp servlet 依赖,因为这里要将项目放到一个Tomcat上运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <exclusions > <exclusion > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > <scope > provided</scope > </dependency >
SpringBoot的启动类继承SpringBootServletInitializer,并重写configure
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootApplication public class MyApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure (SpringApplicationBuilder application) { return application.sources(MyApplication.class); } public static void main (String[] args) { SpringApplication.run(MyApplication.class,args); } }
使用install命令打包项目,并将war包放到tomcat下的webapps下,启动tomcat即可。
Springboot异常处理 默认情况,Spring Boot项目错误页面如下。当项目实际上线,如果给用户显示这个页面就不是很友好。当系统出现异常时应该给用户显示更加友好的错误页面。
设置具体的状态码页面 在templates/下新建error文件夹,在error中新建:状态码.html的页面。例如当出现500时显示的页面为500.html
使用x进行模糊匹配 当出现5开头状态码的错误时,显示页面可以命名为5xx.html 当出现50开头状态码的错误时,显示页面可以命名为50x.html
统一错误显示页面 在templates下新建error.html。如果项目中不存在具体状态码的页面或没有使用x成功匹配的页面时,显示error.html作为错误显示页面。
优先级就是有精确的就精确,没有精确的就模糊再到统一的。
SpringMVC异常简介 系统中异常包括两类:预期异常(检查型异常)和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。 系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理,如下图
异常处理具体实现
1使用@ExceptionHandler注解处理异常
缺点:只能处理当前Controller中的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Controller public class ControllerDemo1 { @RequestMapping("test1.action") public String test1 () { int i = 1 /0 ; return "success" ; } @RequestMapping("test2.action") public String test2 () { String s = null ; System.out.println(s.length()); return "success" ; } @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} ) public ModelAndView handelException () { ModelAndView mv = new ModelAndView (); mv.setViewName("error1" ); return mv; } }
2使用:@ControllerAdvice+@ExceptionHandler
此处优先级低于局部异常处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.msb.exceptionhandler;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.servlet.ModelAndView;@ControllerAdvice public class GloableExceptionHandler1 { @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} ) public ModelAndView handelException () { ModelAndView mv = new ModelAndView (); mv.setViewName("error1" ); return mv; } }
3使用:SimpleMappingExceptionResolver
xml配置 配置类配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class GloableException2 { @Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver () { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver (); Properties prop = new Properties (); prop.put("java.lang.NullPointerException" ,"error1" ); prop.put("java.lang.ArithmeticException" ,"error2" ); resolver.setExceptionMappings(prop); return resolver; } }
4自定义的HandlerExceptionResolver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration public class GloableException3 implements HandlerExceptionResolver { @Override public ModelAndView resolveException (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv = new ModelAndView (); if (e instanceof NullPointerException){ mv.setViewName("error1" ); } if (e instanceof ArithmeticException){ mv.setViewName("error2" ); } mv.addObject("msg" ,e); return mv; }}
Spring Boot中Bean管理 Spring Boot 由于没有XML文件,所以所有的Bean管理都放入在一个配置类中实现。 配置类就是类上具有@Configuration的类。这个类就相当于之前的applicationContext.xml
1 新建配置类 com.bitzh.config.MyConfig , 规范都是放入到config文件夹中。 注意:配置类要有@Configuration,方法要有@Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Configuration public class MyConfig { @Bean protected User getUser () { User user = new User (); user.setId(1L ); user.setName("张三" ); return user; } @Bean("user2") protected User getUser2 () { User user = new User (); user.setId(2L ); user.setName("李四" ); return user; } }
如果Spring容器中存在同类型的Bean通过Bean的名称获取到Bean对象。或结合@Qualifier使用
1 2 3 4 5 6 7 8 9 10 11 @SpringBootTest public class TestGetBean { @Autowired @Qualifier("user2") private User user; @Test public void testGetUser () { System.out.println(user); } }
在配置类的方法中通过方法参数让Spring容器把对象注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Bean("user1") public User getUser () { User user = new User (); user.setId(2L ); user.setName("李四" ); return user; } @Bean public People getPeople (User user1) { People p = new People (); p.setUser(user1); return p; }
springboot_单元测试 在src/main/test里面新建com.bitzh.项目上下文
注意:
测试类不能叫做Test,会和注解同名
测试方法必须是public
测试方法返回值必须是void
测试方法必须没有参数
1 2 3 4 5 6 7 8 9 10 @SpringBootTest(classes = Springboot03Application.class) class Springboot03AppliactionTests { @Autowired private EmpService empService; @Test public void testFindAll () { List<Emp> list = empService.findAll(); list.forEach(System.out::println); } }
Springboot_拦截器 新建拦截器类。 注意:不要忘记类上注解@Component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.bitzh.intercepter;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Component public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("执行拦截器" ); return true ; } }
配置拦截器 注意:类上有注解@Configuration。此类相当于SpringMVC配置文件。 addPathPattern(): 拦截哪些URL。 /** 拦截全部 excludePathPatterns(): 不拦截哪些URL。当和addPathPattern()冲突时,excludePathPatterns()生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.bitzh.intercepter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class MyInterceptorConfig implements WebMvcConfigurer { @Autowired private MyInterceptor myInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(myInterceptor).addPathPatterns("/**" ).excludePathPatterns("/login" ); } }
springboot_注解 springboot默认已经帮助我们整合好了SpringMVC,同时也给我们默认配置了DispathcerServlet 和编码过滤器,同时也给我们配置好了WEB项目开发的常见组件 查看容器中的所有组件
@SpringBootApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @SpringBootApplication public class Springboot04Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args); String[] names = context.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } } }
@Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 /** * MyConfig配置类本身也是一个spring容器中的bean * proxyBeanMethods=true 属性,给MyConfig对象产生一个代理对象 * 通过代理对象控制反复调用MyConfig里面的方法返回的是容器中的一个单实例 * 如果proxyBeanMethods=false 那么我们拿到的MyConfig对象就不是一个代理对象 * 那么这个时候反复调用MyConfig中的方法返回的就是多实例 * * proxyBeanMethods=false 称之为Lite模式 特点启动快 * proxyBeanMethods=true 称之为Full模式 特点依赖spring容器控制bean单例 * */ @Configuration(proxyBeanMethods = true) public class MyConfig { /*<bean id = "user1" class ="com.msb.pojo.User">... ...</bean>*/ @Bean // 向容器中添加一个Bean,以方法名作为Bean的id,返回值类型作为组件的类型 public User user1(){ return new User("zhangsan", 10); } /*<bean id = "user2" class ="com.msb.pojo.User">... ...</bean>*/ @Bean("user2") // 向容器中添加一个Bean,手动指定Bean的name属性,返回值类型作为组件的类型 public User getUser(){ return new User("lisi", 11); } }
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootApplication(scanBasePackages = "com.bitzh") public class Springboot04Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args); System.out.println(context.getBean("user1" )); System.out.println(context.getBean("user2" )); User usera = context.getBean(MyConfig.class).getUser(); User userb = context.getBean(MyConfig.class).getUser(); System.out.println(usera==userb); } }
@Import
1 2 3 4 5 6 7 8 9 10 @Import({User.class}) @Configuration(proxyBeanMethods = true) public class MyConfig {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @SpringBootApplication(scanBasePackages = "com.msb") public class Springboot04Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args); User bean = context.getBean(User.class); System.out.println(bean); String[] beanNamesForType = context.getBeanNamesForType(User.class); for (String s : beanNamesForType) { System.out.println(s); } } }
@Conditional 条件装配
满足Conditional指定的条件,则进行组件注入 @Conditional下还有很多子注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class MyConfig { @ConditionalOnProperty( name = "aaa.b",havingValue = "y") @Bean public User user1 () { return new User ("zhangsan" , 10 ); } }
这样User1就注入成功了
@ImportResource
原生配置文件引入,允许我们自己定义xml配置文件,在文件中配置bean resources目录下准备一个xml配置文件
配置类中加载该配置文件
1 2 3 4 5 import org.springframework.context.annotation.*;@Configuration @ImportResource("classpath:beans.xml") public class MyConfig {}
从容器中获取beans.xml中配置的bean
1 2 3 4 5 6 7 8 @SpringBootApplication public class Springboot04Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args); System.out.println(context.getBean("userx" )); } }
@ConfigurationProperties
读取application.properties配置文件中的内容,读取进入bean
1 2 3 4 5 6 7 8 @ConfigurationProperties( prefix = "user") @Component public class User { private String uname; private int age; }
配置类代码
1 2 3 4 5 6 7 8 @Configuration @EnableConfigurationProperties(User.class) public class MyConfig {}
测试代码
1 2 3 4 5 6 7 8 @SpringBootApplication public class Springboot04Application { public static void main (String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springboot04Application.class, args); System.out.println(context.getBean(User.class)); } }
springboot_静态资源 静态资源默认存放位置
resources 目录下,static/public目录是我们的静态资源目录,直接访问该目录下的资源的映射路径不需要携带/public或者/static,直接访问即可
请求进来,先去看Controller中有没有对应的资源,如果有则,执行controller资源,如果没有,就交给静态资源处理器,静态资源处理器也没有找到,则返回404
静态资源访问前缀 默认无前缀,如果想指定静态资源前缀,可以 通过spring.mvc.static-path-pattern配置
请求路径上通过 项目上下文路径+静态资源前缀+静态资源名的方式访问
指定静态资源位置
静态资源默认存放的路径为static content from a directory called /static (or /public or /resources or /META-INF/resources)
可以通过
static-locations 指定专门的静态资源路径
springboot还支持静态资源webjars 的处理方式,就是将静态资源打成jar导入
1 https://www.webjars.org/
项目中导入jQuery依赖
1 2 3 4 5 <dependency > <groupId > org.webjars</groupId > <artifactId > jquery</artifactId > <version > 3.6.0</version > </dependency >
导入后,自动映射
1 http://localhost:8080/springboot-static-content/webjars/jquery/3.6.0/jquery.js
欢迎页功能
方式1 支持静态资源欢迎页, index.html 注意此时不能配置静态资源前缀,否则不生效 静态资源位置
方式2 通过controller ,定义 / 和/index映射 路径
1 2 3 4 @RequestMapping(path={"/","/index"}) public String welcome () { return "welcome" ; }
导入thymeleaf
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > <version > 2.4.5</version > </dependency >
定义Favicon
static目录下准备一个favicon.ico的一个图片即可
浏览器默认访问favicon.ico的路径为 协议://ip:端口号/favicon.ico,所以这里不要设置项目的上下文路径
拦截器静态资源放行
定义一个登陆页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <link type ="text/css" href ="css/mycss.css" > <script src ="js/myjs.js" > </script > </head > <body > <img src ="img/logo.png" > <form action ="login" > <input type ="text" name ="username" > <input type ="submit" > </form > </body > </html >
定义一个登陆controller
1 2 3 4 5 6 7 8 9 10 11 @Controller public class LoginController { @RequestMapping("/login") public String login (String username, HttpServletRequest request) { if (null != username&&!"" .equals(username)){ request.getSession().setAttribute("username" , username); return "main" ; } return "redirect:/login.html" ; } }
定义一个登陆拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object username = request.getSession().getAttribute("username" ); if (null != username){ return true ; } response.sendRedirect("login.html" ); return false ; } }
配置拦截器
1 2 3 4 5 6 7 8 9 10 @Configuration public class MyInterceptorRegist implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor).addPathPatterns("/**" ).excludePathPatterns("/login" ,"/login.html" ,"/css/**" ,"/js/**" ,"/img/**" ,"/font/**" ); } }
springboot_文件上传 创建项目:略
启动文件上传服务器:
1、新建个tomcat作为文件上传服务器,
设置远程服务器端口号
webapps下创建一个upload目录
2、导入依赖
1 2 3 4 5 <dependency > <groupId > com.sun.jersey</groupId > <artifactId > jersey-client</artifactId > <version > 1.19</version > </dependency >
3、页面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 <html > <head > <meta charset ="UTF-8" > <title > Title</title > <style > .progress { width : 200px ; height : 10px ; border : 1px solid #ccc ; border-radius : 10px ; margin : 10px 0px ; overflow : hidden; } .progress > div { width : 0px ; height : 100% ; background-color : yellowgreen; transition : all .3s ease; } </style > <script type ="text/javascript" src ="js/jquery.min.js" > </script > <script type ="text/javascript" > $(function ( ){ $("#uploadFile" ).click (function ( ){ var photoFile =$("#photo" )[0 ].files [0 ] if (photoFile==undefined ){ alert ("您还未选中文件" ) return ; } var formData =new FormData (); formData.append ("headPhoto" ,photoFile) $.ajax ({ type :"post" , data :formData, url :"file/upload" , processData :false , contentType :false , success :function (result ){ alert (result.message ) $("#headImg" ).attr ("src" ,result.newFileName ); }, xhr : function ( ) { var xhr = new XMLHttpRequest (); xhr.upload .addEventListener ('progress' , function (e ) { var progressRate = (e.loaded / e.total ) * 100 + '%' ; $('.progress > div' ).css ('width' , progressRate); }) return xhr; } }) }) }) </script > </head > <body > <form action ="addPlayer" method ="get" > <p > 账号<input type ="text" name ="name" > </p > <p > 密码<input type ="text" name ="password" > </p > <p > 昵称<input type ="text" name ="nickname" > </p > <p > 头像: <br /> <input id ="photo" type ="file" > <br /> <img id ="headImg" style ="width: 200px;height: 200px" alt ="你还未上传图片" > <br /> <div class ="progress" > <div > </div > </div > <a id ="uploadFile" href ="javascript:void(0)" > 立即上传</a > </p > <p > <input type ="submit" value ="注册" > </p > </form > </body > </html >
5、Controller代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Controller @RequestMapping("/file") public class FileController { private final static String FILESERVER="http://127.0.0.1:8090/upload/" ; @RequestMapping("/upload") @ResponseBody public Map<String,String> upload (MultipartFile headPhoto, HttpServletRequest req) throws IOException { Map<String,String> map=new HashMap <>(); String realPath = req.getServletContext().getRealPath("/upload" ); File dir = new File (realPath); if (!dir.exists()){ dir.mkdirs(); } String originalFilename = headPhoto.getOriginalFilename(); String uuid = UUID.randomUUID().toString(); String extendsName = originalFilename.substring(originalFilename.lastIndexOf("." )); String newFileName=uuid.concat(extendsName); Client client=Client.create(); WebResource resource = client.resource(FILESERVER + newFileName); resource.put(String.class, headPhoto.getBytes()); map.put("message" , "上传成功" ); map.put("newFileName" , FILESERVER+newFileName); map.put("filetype" , headPhoto.getContentType()); return map; } }
yml中配置文件大小限制
1 2 3 4 5 spring: servlet: multipart: max-file-size: 10MB max-request-size: 100MB
多文件同步上传处理方式
页面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <form action ="file/upload" method ="post" enctype ="multipart/form-data" > <p > 账号<input type ="text" name ="name" > </p > <p > 密码<input type ="text" name ="password" > </p > <p > 昵称<input type ="text" name ="nickname" > </p > <p > 头像: <br /> <input id ="photo" name ="photo" type ="file" > <input id ="photos" name ="photos" type ="file" multiple > <br /> <img id ="headImg" style ="width: 200px;height: 200px" alt ="你还未上传图片" > <br /> <div class ="progress" > <div > </div > </div > <a id ="uploadFile" href ="javascript:void(0)" > 立即上传</a > </p > <p > <input type ="submit" value ="注册" > </p > </form >
后台接收的处理单元参数处理
1 2 3 4 5 public Map<String,String> upload (String name, String password, String nickname, @RequestPart("photo") MultipartFile photo, @RequestPart("photos") MultipartFile[] photos, HttpServletRequest req)
springboot_Mybatis-plus 使用步骤
1、导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.21</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.10</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.2</version > </dependency >
自动配置的内容 MyBatis PlusAutoConfiguration配置类,MyBatisPlusProperties配置项前缀 mybatis-plus: * 就是对mybatis-plus的参数的设置
SQLSessionFactory已经配置好 mapperlocations 自动配置好的,默认值是classpath:/mapper/**/ .xml 意为任意包路径下所有的mapper包下的xml文件
@Mapper建议替换成MapperScan
在yaml中配置mybatis-plus
1 2 mybatis-plus: type-aliases-package: com.bitzh.pojo
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @AllArgsConstructor @NoArgsConstructor @Data @TableName("dept") public class Dept implements Serializable { @TableField("deptno") private Integer deptno; private String dname; private String loc; }
mapper(这里在启动类上加了@MapperScan)
1 2 3 4 public interface DeptMapper extends BaseMapper <Dept> { }
service 接口
1 2 3 4 public interface DeptService extends IService <Dept> {}
实现类
1 2 3 @Service public class DeptServiceImpl extends ServiceImpl <DeptMapper, Dept> implements DeptService {}
在测试类中测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @SpringBootTest class SpringbootMybatisplusApplicationTests { @Autowired DeptService deptService; @Test public void testFindAll () { List<Dept> list = deptService.list(); for (Dept dept : list) { System.out.println(dept); } } @Test public void testQueryWrapper () { QueryWrapper<Dept> queryWrapper=new QueryWrapper <>(); List<Dept> list = deptService.list(queryWrapper); for (Dept dept : list) { System.out.println(dept); } } @Test public void testQueryWrapper2 () { QueryWrapper<Dept> queryWrapper=new QueryWrapper <>(); queryWrapper.eq("deptno" , 20 ); Dept dept = deptService.getOne(queryWrapper); System.out.println(dept); } @Test public void testAdd () { boolean save = deptService.save(new Dept (null , "aaa" , "bbb" )); System.out.println(save); } @Test public void testUpdate () { Dept dept = new Dept (); dept.setDname("xxx" ); dept.setLoc("yyy" ); QueryWrapper<Dept> queryWrapper=new QueryWrapper <>(); queryWrapper.eq("deptno" , 41 ); boolean update = deptService.update(dept, queryWrapper); System.out.println(update); } @Test public void testRemove () { QueryWrapper<Dept> queryWrapper=new QueryWrapper <>(); queryWrapper.eq("deptno" , 41 ); boolean remove = deptService.remove(queryWrapper); System.out.println(remove); } }
分页插件的使用 配置分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor (); PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor (); paginationInnerInterceptor.setDbType(DbType.MYSQL); mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor); return mybatisPlusInterceptor; } }
测试分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void testPage () { QueryWrapper<Dept> queryWrapper=new QueryWrapper <>(); Page<Dept> page = deptService.page(new Page <>(1 , 2 ), queryWrapper); List<Dept> list = page.getRecords(); list.forEach(System.out::println); System.out.println("总页数:" +page.getPages()); System.out.println("总记录数:" +page.getTotal()); System.out.println("当前页:" +page.getCurrent()); System.out.println("页大小:" +page.getSize()); }
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @SpringBootTest @DisplayName("Junit5测试类") class SpringbootMybatisplusApplicationTests2 { @Autowired private DeptMapper deptMapper; @BeforeEach public void testForeach () { System.out.println("beforeach" ); } @AfterEach public void testAftereach () { System.out.println("aferEach" ); } @BeforeAll public static void beforeAll () { System.out.println("beforall" ); } @AfterAll public static void aferAll () { System.out.println("afterAll" ); } @RepeatedTest(3) @Timeout(value = 10000,unit = TimeUnit.MILLISECONDS) @DisplayName("Junit测试方法1") @Test public void test1 () { System.out.println("a" ); System.out.println(deptMapper); } @Disabled @DisplayName("Junit测试方法2") @Test public void test2 () { System.out.println("b" ); } }
断言机制 断定某件事情,一定会发生,如果没有发生,那就是出现了问题,所欲的测试运行结束后,会有一个详细的断言报告 用来对测试需要满足的条件进行验证,这些断言方法都是org.junit.jupiter.api.Assertions中的静态方法, 简单断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @SpringBootTest @DisplayName("Junit5断言测试类") class SpringbootMybatisplusApplicationTests3 { @DisplayName("简单断言1") @Test public void testAssertions1 () { int add = add(1 , 2 ); Assertions.assertEquals(6 ,add,"add结果计算错误" ); } public int add (int a,int b) { return a+b; } @DisplayName("简单断言2") @Test public void testAssertions2 () { String s = new String ("xxx" ); String s2=new String ("abc" ); Assertions.assertEquals(s,s2,"String对象不一样" ); } @DisplayName("组合断言") @Test public void testAssertAll () { Assertions.assertAll("AssertAll" , ()-> Assertions.assertTrue(true && false ), ()-> Assertions.assertEquals(1 ,2 )); } @DisplayName("异常断言") @Test public void testAssertException () { Assertions.assertThrows(ArithmeticException.class, ()->{ int i=1 /0 ;}, "没有抛出异常" ); } @DisplayName("超时断言") @Test public void testAssertTimeOut () { Assertions.assertTimeout(Duration.ofMillis(1000 ),()-> Thread.sleep(5000 )); } @DisplayName("快速失败") @Test public void testFail () { if (true ){ Assertions.fail("测试 失败" ); } } }
前置条件(assumptions假设) 类似于断言,不同在于,不满足断言回事方法测试失败,而不满足的前置条件会使得的是方法的执行中止,前置条件可以看成是测试方法执行的前提,当条件不满足时,就没有继续执行的必要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @SpringBootTest @DisplayName("Junit5测试前置条件") class SpringbootMybatisplusApplicationTests4 { @DisplayName("测试前提条件") @Test public void testAssumptions () { Assumptions.assumeTrue(false ,"结果不是true" ); System.out.println("后面的测试代码前提条件" ); } @DisplayName("简单断言1") @Test public void testAssertions1 () { int add = 10 ; Assertions.assertEquals(6 ,add,"add结果计算错误" ); System.out.println("后面的测试代码简单断言" ); } }
嵌套测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @DisplayName("嵌套测试") class SpringbootMybatisplusApplicationTests5 { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew () { new Stack <>(); assertNull(stack); } @Nested @DisplayName("when new") class WhenNew { @BeforeEach void createNewStack () { stack = new Stack <>(); } @Test @DisplayName("is empty") void isEmpty () { assertTrue(stack.isEmpty()); } @Test @DisplayName("throws EmptyStackException when popped") void throwsExceptionWhenPopped () { assertThrows(EmptyStackException.class, stack::pop); } @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked () { assertThrows(EmptyStackException.class, stack::peek); } @Nested @DisplayName("after pushing an element") class AfterPushing { String anElement = "an element" ; @BeforeEach void pushAnElement () { stack.push(anElement); } @Test @DisplayName("it is no longer empty") void isNotEmpty () { assertFalse(stack.isEmpty()); } @Test @DisplayName("returns the element when popped and is empty") void returnElementWhenPopped () { assertEquals(anElement, stack.pop()); assertTrue(stack.isEmpty()); } @Test @DisplayName("returns the element when peeked but remains not empty") void returnElementWhenPeeked () { assertEquals(anElement, stack.peek()); assertFalse(stack.isEmpty()); } } } }
参数化测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @DisplayName("参数化测试") class SpringbootMybatisplusApplicationTests6 { @ParameterizedTest @ValueSource(ints = { 1, 2, 3 }) void testWithValueSource (int argument) { System.out.println(argument); assertTrue(argument > 0 && argument < 4 ); } @ParameterizedTest @MethodSource("stringProvider") void testWithExplicitLocalMethodSource (String argument) { assertNotNull(argument); } static Stream<String> stringProvider () { return Stream.of("apple" , "banana" ); } }