入门
启动
第一个例子
编辑pom.xml
1 | <!-- springBoot的启动器 --> |
编写controller
1 |
|
编写启动类
1 | package com.bjsxt.app; |
打包
进入到项目目录下,终端运行
1 | mvn clean package -Dmaven.test.skip=true |
运行
1 | java -jar demo_springboot-0.0.1-SNAPSHOT.jar |
整合Web开发
整合Servlet
注解扫描注册
通过注解扫描完成Servlet组件的注册
编写Servlet
1 |
|
编写启动类
1 |
|
方法注册
通过方法完成Servlet组件的注册
编写Servlet
1 |
|
编写启动类
1 |
|
整合Filter
注解扫描注册
编写Filter
1 |
|
编写启动类
1 |
|
方法注册
编写Filter
1 |
|
编写启动类
1 |
|
整合Listener
注解扫描注册
编写Listener
1 | package com.bjsxt.listener; |
编写启动类
1 | package com.bjsxt; |
方法注册
编写Listener
1 | package com.bjsxt.listener; |
编写启动类
1 |
|
访问静态资源
从classpath/static的目录下读取
从ServletContext根目录下读取,即webapp目录
文件上传
编写Controller
1 |
|
编写启动类
1 | package com.bjsxt; |
编写文件上传表单
进入src/main/resources/static目录下,新增upload.html
1 |
|
设置上传文件大小的默认值
进入/src/main/resources目录,编辑application.properties文件
1 | spring.http.multipart.maxFileSize=200MB |
整合jsp
编辑pom.xml
1 | <!-- jdk1.7 --> |
创建application.properties
进入src/main/resources目录,新增编辑application.properties文件
1 | spring.mvc.view.prefix=/WEB-INF/jsp/ |
编写Controller
1 |
|
创建jsp
进入src/main/webapp/WEB-INF/jsp目录,新增userList.jsp
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" |
创建启动类
1 | package com.bjsxt; |
整合freemarker
编辑pom.xml
1 | <properties> |
创建 Controller
1 |
|
编写前端页面
springBoot要求模板形式的视图层技术的文件必须要放到src/main/resources目录下必
须要一个名称为templates
进入到src/main/resources/templates目录,新增userList.ftl文件
1 | <html> |
创建启动类
1 | package com.bjsxt; |
整合Thymeleaf
编辑pom.xml
1 | <properties> |
编辑前端页面
进入src/main/resources/templates,templates目录是安全的。意味着该目录下的内容是不允许外界直接访问的。
1 |
|
编辑Controller
1 | package com.bjsxt.controller; |
编辑启动类
1 | package com.bjsxt; |
坑
org.xml.sax.SAXParseException:元素类型 "meta" 必须由匹配的结束标记"</meta>"终止
解决
方式一
让html的标记按照严禁的语法编写
1 | <meta charset="UTF-8"/> |
方式二
thymeleaf.jar更新为3.0以上的版本
thymeleaf-layout-dialect.jar更新为2.0以上的版本
1 | <properties> |
Thymeleaf语法
变量输出与字符串操作
调用内置对象一定要用#
大部分的内置对象都以s结尾strings、numbers、dates
后台数据
1 | package com.bjsxt.controller; |
th:text
1 | <span th:text="Hello"></span> |
th:value
1 | <span th:text="${msg} "></span> |
判断字符串是否为空
1 | <span th:text="${#strings.isEmpty(msg)}"></span> |
判断字符串是否包含指定的子串
1 | <span th:text="${#strings.contains(msg,'9')}"></span> |
判断当前字符串是否以子串开头
1 | <span th:text="${#strings.startsWith(msg,'a')}"></span> |
判断当前字符串是否以子串结尾
1 | <span th:text="${#strings.endsWith(msg,'a')}"></span> |
返回字符串的长度
1 | <span th:text="${#strings.length(msg)}"></span> |
查找子串的位置
返回该子串的下标,如果没找到则返回-1
1 | <span th:text="${#strings.indexOf(msg,'h')}"></span> |
截取子串
1 | <span th:text="${#strings.substring(msg,13)}"></span> |
字符串转大小写
1 | <span th:text="${#strings.toUpperCase(msg)}"></span> |
日期格式化处理
后台数据
1 | package com.bjsxt.controller; |
默认格式日期
1 | <span th:text="${#dates.format(key)}"></span> |
按照自定义的格式做日期转换
1 | <span th:text="${#dates.format(key,'yyy/MM/dd')}"></span> |
取年
1 | <span th:text="${#dates.year(key)}"></span> |
取月
1 | <span th:text="${#dates.month(key)}"></span> |
取日
1 | <span th:text="${#dates.day(key)}"></span> |
条件判断
后台数据
1 | package com.bjsxt.controller; |
th:if
1 | <span th:if="${sex} == '男'"> |
th:switch
1 | <div th:switch="${id}"> |
迭代遍历
后台数据
1 | package com.bjsxt.controller; |
th:each迭代list
1 | <tr th:each="u : ${list}"> |
th:each状态变量
index当前迭代器的索引从0开始count当前迭代对象的计数从1开始size被迭代对象的长度even/odd布尔值,当前循环是否是偶数/奇数从0开始first布尔值,当前循环的是否是第一条,如果是返回true否则返回falselast布尔值,当前循环的是否是最后一条,如果是则返回true否则返回false
1 | <tr th:each="u,var : ${list}"> |
th:each迭代Map
1 | <tr th:each="maps : ${map}"> |
域对象操作
后台数据
1 | package com.bjsxt.controller; |
HttpServletRequest
1 | Request:<span th:text="${#httpServletRequest.getAttribute('req')}"></span><br/> |
HttpSession
1 | Session:<span th:text="${session.sess}"></span><br/> |
ServletContext
1 | Application:<span th:text="${application.app}"></span> |
URL表达式
基本语法@{}
后台数据
1 | package com.bjsxt.controller; |
绝对路径
1 | <a th:href="@{http://www.baidu.com}">绝对路径</a><br/> |
相对路径
1 | <a th:href="@{/show}">相对路径</a> |
整合MyBatis
编辑pom.xml
1 | <!-- Mybatis启动器 --> |
编辑application.properties
进入src/main/resources目录,编辑application.properties
1 | spring.datasource.driverClassName=com.mysql.jdbc.Driver |
数据库表设计
1 | CREATE TABLE `users` ( |
添加用户
创建实体类
1 | package com.bjsxt.pojo; |
创建mapper接口以及映射配置文件
1 | package com.bjsxt.mapper; |
1 |
|
创建业务层
所有方法开启事务@Transactional标注在类上
1 |
|
创建Controller
前端请求http://localhost:8080/users/input跳转到input.html页面
1 |
|
编写页面
使用thymeleaf模板,页面必须放在src/main/resources/templates目录下
添加用户页面input.html
1 |
|
添加成功页面ok.html
1 |
|
创建启动类
用户扫描MyBatis的Mapper接口,使用@MapperScan("com.bjsxt.mapper")注解
1 | package com.bjsxt; |
查询用户
修改mapper接口中以及映射配置文件
1 | List<Users> selectUsersAll(); |
1 | <select id="selectUsersAll" resultType="users"> |
修改业务层
添加查询方法
1 |
|
修改Controller
浏览器请求http://localhost:8080/findUserAll,跳转到showUsers.html
1 | /** |
创建页面
查询用户页面showUsers.html
1 |
|
更新用户
数据回显
更新用户之前的查询,将数据在页面中回显
修改mapper接口以及映射配置文件
1 | Users selectUsersById(Integer id); |
1 | <select id="selectUsersById" resultType="users"> |
修改业务层代码
1 |
|
修改Controller
浏览器请求http://localhost:8080/findUserById,跳转到updateUser.html页面,将数据用户数据回显
1 | /** |
编写页面
更新用户页面updateUser.html进行数据回显,使用th:field取得controller中的模型数据
1 |
|
更新
修改mapper接口以及映射配置文件
1 | void updateUser(Users users); |
1 | <update id="updateUser" parameterType="users"> |
修改业务层代码
1 |
|
修改Controller
更新用户页面updateUser.html点击提交,浏览器请求http://localhost:8080/editUser,跳转到修改成功页面ok.html
1 | /** |
删除用户
修改mapper接口以及映射配置文件
1 | void deleteUserById(Integer id); |
1 | <delete id="deleteUserById"> |
修改业务层
1 |
|
修改Controller
浏览器请求http://localhost:8080/delUser重定向请求/users/findUserAll,跳转到查询用户页面showUsers.html
1 | /** |
修改页面
修改查询用户页面showUsers.html,增加删除按钮链接
1 |
|
服务端表单数据校验
表单做数据校验
修改实体类
非空校验使用@NotBlank注解标注在属性上
1 | package com.bjsxt.pojo; |
修改Controller
浏览器请求http://localhost:8080/addUser,跳转到添加用户页面add.html,点击提交按钮浏览器请求http://localhost:8080/save,如果校验通过跳转到成功页面ok.html,校验不通过跳转到添加用户页面add.html
开启校验使用@Valid注解
封装了校验的结果BindingResult
1 |
|
修改页面
添加用户页面add.html,使用th:errors取得校验信息
1 |
|
坑
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name ‘users’ available as request attribute
描述
浏览器请求http://localhost:8080/addUser方法,该方法并没有传递Users对象,跳转到添加用户页面add.html取不到数据所以报错
解决
方式一
跳转页面注入对象
可以在浏览器请求http://localhost:8080/addUser方法中注入一个Uesrs对象
由于springmvc会将Uesrs对象放入到Model模型中传递,使用驼峰命名规则,参数的变量名需要与对象的名称相同,将首字母小写
修改Controller
1 |
|
方式二
设置参数名称可变
如果想为传递的对象更改名称,可以使用@ModelAttribute("aa")这表示当前传递的对象的key为aa
那么我们在页面中获取该对象的key也需要修改为aa
修改Controller
1 |
|
修改页面
添加用户页面add.html,${aa.name}需要和请求http://localhost:8080/addUser方法中设置的@ModelAttribute("aa")一样
1 |
|
其他校验规则
@NotBlank判断字符串是否为null或者是空串(去掉首尾空格)。@NotEmpty判断字符串是否null或者是空串。@Length判断字符的长度(最大或者最小)@Min判断数值最小值@Max判断数值最大值@Email判断邮箱是否合法
修改实体类
1 |
|
异常处理
SpringBoot中对于异常处理提供了五种处理方式
自定义错误页面
默认的处理异常的机制
一旦程序中出现了异常SpringBoot会向/error发送请求。在springBoot中提供了一个
叫BasicExceptionController类来处理/error请求,然后跳转到默认显示异常的页面来展示异常
信息
修改Controller
浏览器请求http://localhost:8080/show和http://localhost:8080/show2发生了异常,并不会跳转到index.html页面,而是会有默认异常页面error.html来显示异常信息
1 | package com.bjsxt.controller; |
效果

修改页面
如果我们需要将所有的异常同一跳转到自定义的错误页面需要在src/main/resources/templates目录下创建error.html页面,名称必须叫error.html
1 |
|
弊端
对所有异常都显示到异常页面error.html,不能处理对不同的异常跳转到不同的异常显示页面
@ExceptionHandle
修改Controller
使用@ExceptionHandler注解标注在方法上分别对不同的异常进行处理
使用ModelAndView可以指定异常信息及异常视图页面
1 |
|
修改页面
进入src/main/resources/templates目录下,创建空指针和算术异常页面
算术异常页面error1.html
1 |
|
空指针异常页面error2.html
1 |
|
效果

弊端
只有标记了@ExceptionHandle注解的Controller类才能处理跳转到自定义异常页面,而其它的Controller类中的方法则还是跳转到springboot默认机制的异常处理
@ControllerAdvice+@ExceptionHandler
修改Controller
全局异常控制类GlobalException.java,浏览器请求Controller,如果发生了异常,则会走全局异常控制类GlobalException.java,该类可以对不同的异常进行不同的处理(指定异常信息、视图页面)
该方式优于@ExceptionHandle注解处理异常
1 |
|
效果

SimpleMappingExceptionResolver
比起@ControllerAdvice+@ExceptionHandler处理异常,通过SimpleMappingExceptionResolver做全局异常处理,方式更优雅,但是有个缺点即在异常页面不能输出异常信息
修改Controller
1 |
|
效果
不能输出异常信息

弊端
不能在异常页面输出异常信息,SimpleMappingExceptionResolver类处理异常只能针对不同的异常映射不同的视图逻辑
自定义HandlerExceptionResolver
修改Controller
定义全局异常控制类必须要实现HandlerExceptionResolver接口,该种方式可以针对不同异常跳转不同的异常页面,并且在异常异页面可以输出异常信息,但是平常异常信息不会直接输出在异常页,避免异常数据被人窃取
推荐使用
1 |
|
单元测试
修改pom.xml
1 | <!-- 添加junit环境的jar包 --> |
创建dao
1 | package com.bjsxt.dao; |
创建service
1 | package com.bjsxt.service; |
创建启动类
1 | package com.bjsxt; |
整合Junit做单元测试
@RunWith启动器SpringJUnit4ClassRunner.class让junit与spring环境进行整合
@SpringBootTest(classes={App.class})说明当前类为springBoot的测试类加载SpringBoot启动类App.class
1 | package com.bjsxt.test; |
热部署
SpringLoader插件
创建controller
1 | package com.bjsxt.controller; |
创建页面
进入/src/main/resources/templates目录下,创建热部署前端测试页面
1 |
|
创建启动类
1 | package com.bjsxt; |
运行
方式一
修改pom.xml
1 | <!-- springloader插件 --> |
maven命令启动

弊端
这种方式的缺点是Springloader热部署程序是在系统后台以进程的形式来运行,需要手动关闭该进程
对Java代码做部署处理,但是对页面无能为力
方式二
在项目中直接使用jar包的方式
启动命令VM Options参数-javaagent:.\lib\springloaded-1.2.5.RELEASE.jar -noverify
DevTools工具
修改pom.xml
1 | <!-- DevTools的坐标 --> |
SpringLoader与DevTools的区别
SpringLoader在部署项目时使用的是热部署的方式
DevTools在部署项目时使用的是重新部署的方式
整合Ehcache
修改pom.xml
1 | <dependencies> |
创建Ehcache的配置文件
进入src/main/resources/目录下,创建ehcache.xml
1 | <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> |
修改application.properties文件
进入src/main/resources/目录下,修改application.properties
1 | spring.datasource.driverClassName=com.mysql.jdbc.Driver |
创建service
使用缓存策略@Cacheable(value="users")
1 | package com.bjsxt.service.impl; |
修改实体类
1 | package com.bjsxt.pojo; |
创建启动类
开启缓存使用@EnableCaching注解
1 | package com.bjsxt; |
测试
1 | package com.bjsxt.test; |
效果
后台输出执行了一次sql查询,第二条结果是从缓存中取出来的
1 | Hibernate: select users0_.id as id1_0_0_, users0_.address as address2_0_0_, users0_.age as age3_0_0_, users0_.name as name4_0_0_ from t_users users0_ where users0_.id=? |
@Cacheable
把方法的返回值添加到Ehcache中做缓存
Value属性:指定一个Ehcache配置文件中的缓存策略,如果么有给定value,则
表示使用默认的缓存策略
Key属性:给存储的值起个名称。在查询时如果有名称相同的,那么则知己从缓存中将
数据返回
修改service
1 | package com.bjsxt.service.impl; |
修改测试
1 | package com.bjsxt.test; |
效果
后台输出执行了一次sql查询,后面两次是从缓存取出来的
因为service层使用了缓存策略@Cacheable(value="users",key="#pageable.pageSize")且指定了key为分页的每页条数
在测试查询的时候虽然第三次查询重新new PageRequest(1, 2),但是size还是为2,所以还是从缓存中取
1 | Hibernate: select users0_.id as id1_0_, users0_.address as address2_0_, users0_.age as age3_0_, users0_.name as name4_0_ from t_users users0_ limit ? |
@CacheEvict
清除缓存,在对频繁查询的数据可以从缓存中取,然而一旦有新的数据写入数据库,必须重新刷新缓存,即清除缓存
修改service
1 |
|
修改测试
1 | package com.bjsxt.test; |
效果
后台输出第一次查询和第二次查询之间插入了一条新数据,第一次查询结果是5条,在插入数据方法saveUsers上开启了清除缓存,所以插入数据之后再一次查询的数据也就同步过来更新为6条
1 | Hibernate: select users0_.id as id1_0_, users0_.address as address2_0_, users0_.age as age3_0_, users0_.name as name4_0_ from t_users users0_ |
整合Redis
修改pom.xml
1 | <!-- Spring Data Redis的启动器 --> |
创建Redis配置类
1 | package com.bjsxt.config; |
创建测试
修改pom.xml
添加测试启动依赖坐标
1 | <!-- Test的启动器 --> |
测试
1 | package com.bjsxt.test; |
提取redis的配置信息
修改application.properties
进入src/main/resource/目录,修改application.properties
1 | spring.redis.pool.max-idle=10 |
修改redis配置类
将前缀相同的内容创建一个实体使用@ConfigurationProperties注解
1 | package com.bjsxt.config; |
效果
1 | 2019-04-08 16:24:36.624 INFO 9340 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode! |
存储实体对象
创建实体
实体必须实现序列化接口
1 | package com.bjsxt.pojo; |
修改测试
保存实体前时必须重新设置序列化器
1 | package com.bjsxt.test; |
效果
1 | 2019-04-08 16:29:32.045 INFO 12720 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode! |
JdkSerializationRedisSerializer弊端
实体对象通过JdkSerializationRedisSerializer序列化后存到Redis中占用空间大
JSON格式存储实体对象
推荐使用
修改测试
重置序列化Jackson2JsonRedisSerializer
1 | package com.bjsxt.test; |
效果
1 | 2019-04-08 16:41:53.694 INFO 19280 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode! |