入门
启动
第一个例子
编辑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
否则返回false
last
布尔值,当前循环的是否是最后一条,如果是则返回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! |