[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYpzy66l-1604016342362)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1559143761089.png)]
springboot整合 web功能
整合 servlet(方式一)
导 jar 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
通过注解来完成 servlet注册
@WebServlet(name="indexServlet",urlPatterns = "/firstServlet")
public class firstServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service 方法执行了。。");
}
}
编写启动类 扫描servlet
SpringBootApplication
@ServletComponentScan//在springboot启动时扫描@webservlet注解,并将该类实例化
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
}
整合 servlet(方式二)
通 过方法来完成 servlet 注册
public class SecondServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("第二个servlet");
}
}
编写启动类 扫描servlet
@SpringBootApplication
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
@Bean //加入IOC容器
public ServletRegistrationBean getServletBean(){
ServletRegistrationBean<SecondServlet> secondServlet = new ServletRegistrationBean(new SecondServlet());
secondServlet.addUrlMappings("/secondServlet");
return secondServlet;
}
}
整合 filter(方式一)
通过注解来完成 filter注册
//@WebFilter(name="firstFilter",urlPatterns = {"*.do", "*.jsp"})//拦截以.do .jsp结尾的资源
@WebFilter(filterName="firstFilter",urlPatterns = {"/firstServlet"})//只拦截firstServlet
public class FirstFilter implements Filter {//表示浏览器输入locallhost/firstServlet会被拦截
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入filter");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("放行了...");
}
}
编写启动类 扫描 filter
@SpringBootApplication
@ServletComponentScan
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
}
整合 filter(方式二)
过方法来完成 filter 注册
public class SecondFilter implements javax.servlet.Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入filter");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("放行了...");
}
}
编写启动类 扫描 filter
@SpringBootApplication
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
@Bean //加入IOC容器
public FilterRegistrationBean getFilterBean(){
FilterRegistrationBean bean = new FilterRegistrationBean(new SecondFilter());
//bean.addUrlPatterns(new String[]{".do", "*.jsp"});
bean.addUrlPatterns("/secondServlet");
return bean;
}
}
整合 listener (方式一)
通过注解完成 注册
@WebListener
public class FirstListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听器已启动");
}
}
@SpringBootApplication
@ServletComponentScan
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iJqKTHxl-1604016342365)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557645194034.png)]
整合 listener (方式二)
public class SecondListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("第二个监听器已启动");
}
}
@SpringBootApplication
public class SpringbootStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootStartApplication.class, args);
}
@Bean //注册 加入IOC容器
public ServletListenerRegistrationBean<SecondListener> getListenerBean(){
ServletListenerRegistrationBean<SecondListener> listener = new ServletListenerRegistrationBean<>(new SecondListener());
return listener;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFwqLOyW-1604016342366)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557645416044.png)]
访问静态资源
方式一:默认 static 目录
spring 官方建议静态资源(图片、html…)统一放入 resource/static 文件夹下,览器输入 http://localhost:8080/aa.jpg或者http://localhost:8080/index.html 都可以访问到资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uMSJCSH4-1604016342371)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557647406483.png)]
方式二:ServletContext根目录( webapp 目录)
名字必须叫 webapp, 为了让新建的 webapp 可以建 Jsp等资源,先要把它变成 web 目录,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KDZ9THmF-1604016342374)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557648842063.png)]
接着为了使得能够访问 webapp下的资源,还须做如下设置,配置完重启即可访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GaWKsuFB-1604016342377)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557648932147.png)]
在 springboot 实现文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<form action="upload/uploadfile" method="post" enctype="multipart/form-data">
文件:<input type="file" name="multipartFile"><br>
<input type="submit" value="开始上传">
</form>
</body>
</html>
//@Controller
@RestController // 表示这个类下所有的方法的返回值为 json 格式
public class FileUploadController {
//multipartFile 必须跟页面 file 表单 name 相同
@RequestMapping("upload/uploadfile")
public @ResponseBody Map<String,String> uploadFile(MultipartFile multipartFile, HttpServletRequest request){
Map<String,String> map = new HashMap<>();
try {
String path = request.getSession().getServletContext().getRealPath("/upload");
File file = new File(path);
if (!file.exists()){
file.mkdirs();
}
String fileName = multipartFile.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid+"_"+fileName;
multipartFile.transferTo(new File(file,fileName));
map.put("msg","OK");
return map;
} catch (Exception e) {
e.printStackTrace();
map.put("msg","failed");
}
return map;
}
}
可以在 application.proterties 配置上传文件的限制大小
#spring.http.multipart.max-file-size=200MB 已过时
#spring.http.multipart.max-request-size=200MB 已过时
spring.servlet.multipart.max-file-size=300MB
spring.servlet.multipart.max-request-size=300MB
SpringBoot 修改访问请求后缀
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* -设置url后缀模式匹配规则
* -该设置匹配所有的后缀
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(true) //设置是否是后缀模式匹配,即:/test.*
.setUseTrailingSlashMatch(true); //设置是否自动后缀路径模式匹配,即:/test/
}
/**
* -该设置指定匹配后缀*.do;
* @param dispatcherServlet servlet调度器
* @return ServletRegistrationBean
*/
@Bean
public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean servletServletRegistrationBean = new ServletRegistrationBean(dispatcherServlet);
servletServletRegistrationBean.addUrlMappings("*.do");//指定.do后缀,可替换其他后缀
return servletServletRegistrationBean;
}
}
@RestController
public class HelloController {
@GetMapping("/hello.do")//这里的.do写不写都行
@ResponseBody
public String hello(){
return "dddddd";
}
}
SpringBoot使用拦截器处理日志
拦截器定义
@Slf4j //有了这个注解 就不用定义 log 对象了
public class MyInterceptor implements HandlerInterceptor {
//private final Logger log = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("preHandle execute....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("postHandle execute....");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("afterCompletion execute....");
}
}
拦截器配置
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
application.yaml设置日志级别
logging:
level:
com.lhg: debug #注意这里是个 map
springboot 整合视图层技术
springboot 整合 jsp
导入整合 jsp 所需包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
代码
@Controller
public class IndexController {
@RequestMapping("testJsp")
public String testJsp(Model model){
//模拟查询数据库
List<Account> lists = new ArrayList<>();
lists.add(new Account(1, 200, "张三"));
lists.add(new Account(2, 250, "李四"));
model.addAttribute("lists",lists);
return "list";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table border="solder 1px blue" align="center" width="50%">
<tr>
<th>ID</th>
<th>姓名</th>
<th>账户金额</th>
</tr>
<c:forEach items="${lists}" var="account">
<tr align="center">
<td>${account.id}</td>
<td>${account.name}</td>
<td>${account.money}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
springboot 整合 freemarker
添加 freemarker 坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
freemarker 配置
#设定 ftl 文件路径
spring.freemarker.template-loader-path=classpath:/templates
#关闭缓存及时刷新
spring.freemarker.cache=false
#设定 templates的编码
spring.freemarker.charset=utf-8
# 是否检查 templates 路径是否存在
spring.freemarker.check-template-location=false
spring.freemarker.content-type=text/html
# 设定模板的后缀
spring.freemarker.suffix=.ftl
编写试图
springboot 要求模板形式的视图层技术的文件必须要放到 src/main/resource/templates 目录下
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index</title>
</head>
<body>
<a href="testFtl">查询所有</a>
</body>
</html>
@Controller
public class IndexController {
@RequestMapping("testFtl")
public String testJsp(Model model) {
//模拟查询数据库
List<Account> lists = new ArrayList<>();
lists.add(new Account(1, 200, "张三"));
lists.add(new Account(2, 250, "李四"));
model.addAttribute("lists", lists);
return "list";
}
}
<body>
<table border="solder 1px blue" align="center" width="50%">
<tr>
<th>ID</th>
<th>姓名</th>
<th>账户金额</th>
</tr>
<#list lists as account>
<tr align="center">
<td>${account.id}</td>
<td>${account.name}</td>
<td>${account.money}</td>
</tr>
</#list>
</table>
</body>
注意部署项目运行时默认会找 index.ftl,不需要在地址栏敲 http://localhost:8080/index.ftl,而且也没有效果,直接http://localhost:8080/就行
springboot 整合 thymeleaf
导 thymeleaf 包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
创建存放视图的目录
和 freemarker 一样,必须放在 src/main/resource/templates 目录下,该目录是安全的,只有服务器级别的请求才能访问到
thymeleaf 基本使用
thymeleaf 通过它特定 语法对 html 的标记做渲染,现罗列如下常见用法:
变量输出和字符串操作:thymeleaf 语法要求 th:xx="" 这里必须要带双引号
<body>
<span th:text="hello,刘和广"></span><br>
<!--变量输出 model.addAttribute("msg","thymeleaf language"); -->
<span th:text="${msg}"></span>
<input type="text" th:value="${msg}">
<!--判断字符串是否为空-->
<!--Thymeleaf 内置对象 注意语法:
1,调用内置对象一定要用#
2,大部分的内置对象都以 s 结尾 strings、numbers、dates.
3, API 跟 java 很多都是一样的。。 -->
<span th:text="${#strings.isEmpty(msg)}"></span><br>
<!--msg中是否包含 9-->
<span th:text="${#strings.contains(msg,'9')}"></span><br>
<!--msg中是否以 XXX 开头-->
<span th:text="${#strings.startsWith(msg,'Thy')}"></span><br>
<!--msg中是否以 XXX 结尾-->
<span th:text="${#strings.endsWith(msg,'Thy')}"></span><br>
<!--msg长度-->
<span th:text="${#strings.length(msg)}"></span><br>
<!--msg 指定字符索引位置,从零开始,没有返回 -1 -->
<span th:text="${#strings.indexOf(msg,'a')}"></span><br>
<span th:text="${#strings.substring(msg,3)}"></span><br>
<span th:text="${#strings.toUpperCase(msg)}"></span><br>
<span th:text="${#strings.toLowerCase(msg)}"></span><br>
</body>
日期操作
<!-- model.addAttribute("date",new Date());-->
<!--格式化日期,默认以浏览器默认语言为格式化标准-->
<span th:text="${#dates.format(date)}"></span><br>
<!--指定格式化日期-->
<span th:text="${#dates.format(date,'yyyy-MM-dd')}"></span><br>
<span th:text="${#dates.year(date)}"></span><br>
<span th:text="${#dates.month(date)}"></span><br>
<span th:text="${#dates.day(date)}"></span><br>
条件判断
<!--model.addAttribute("sex","男");-->
<span th:if="${sex} == '男' ">
性别:男
</span>
<span th:if="${sex} == '女' ">
性别:女
</span>
<!--model.addAttribute("id","2");-->
<div th:switch="${id}">
<span th:case="1">ID 为1</span>
<span th:case="2">ID 为2</span>
<span th:case="3">ID 为3</span>
</div>
迭代遍历 List 集合
<!--model.addAttribute("lists", lists);-->
<table border="solder 1px blue" align="center" width="50%">
<tr>
<th>ID</th>
<th>姓名</th>
<th>账户金额</th>
</tr>
<tr th:each="account : ${lists}">
<td th:text="${account.id}"></td>
<td th:text="${account.name}"></td>
<td th:text="${account.money}"></td>
</tr>
</table>
迭代遍历过程中的状态变量
<tr th:each="account,var : ${lists}">
<td th:text="${account.id}"></td>
<td th:text="${account.name}"></td>
<td th:text="${account.money}"></td>
<td th:text="${var.index}"></td>
<td th:text="${var.count}"></td>
<td th:text="${var.size}"></td>
<td th:text="${var.even}"></td>
<td th:text="${var.odd}"></td>
<td th:text="${var.first}"></td>
<td th:text="${var.last}"></td>
</tr>
<!--状态变量属性
1,index:当前迭代器的索引 从 0 开始
2,count:当前迭代对象的计数 从 1 开始
3,size:被迭代对象的长度
4,even/odd:布尔值,当前循环是否是偶数/奇数 从 0 开始
5,first:布尔值,当前循环的是否是第一条,如果是返回 true 否则返回 false
6,last:布尔值,当前循环的是否是最后一条,如果是则返回 true 否则返回 false-->
迭代遍历 Map
<!--model.addAttribute("map", map);-->
<table border="solder 1px blue" align="center" width="50%">
<tr>
<th>key</th>
<th>ID</th>
<th>姓名</th>
<th>账户金额</th>
</tr>
<tr th:each="map : ${map}">
<td th:text="${map.key}"></td>
<td th:text="${map.value.id}"></td>
<td th:text="${map.value.name}"></td>
<td th:text="${map.value.money}"></td>
</tr>
</table>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-04MAfCFy-1604016342379)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557671428833.png)]
域对象操作
@RequestMapping("scope")
public String testMap(HttpServletRequest request, Model model){
request.setAttribute("req","request的数据");
request.getSession().setAttribute("ses","sesson的数据");
request.getSession().getServletContext().setAttribute("appli","applicatiod的数据");
return "list";
}
<!--固定用法,注意大小写敏感o-->
request:<span th:text="${#httpServletRequest.getAttribute('req')}"></span><br>
session:<span th:text="${session.ses}"></span><br>
application:<span th:text="${application.appli}"></span><br>
URL 表达式
<a th:href="@{http://www.baidu.com}">绝对路径</a><br/>
<a th:href="@{/show}">相对路径</a>
<a th:href="@{`/project2/resourcename}">相对于服务器的根</a>
<a th:href="@{/show(id=1,name=zhagnsan)}">相对路径-传参</a>
<a th:href="@{/path/{id}/showRestFul(id=1,name=zhagnsan,age=18)}"> 相 对 路 径 - 传 参 -restful</a>
@RequestMapping("show")
public String testShow(Integer id, String name){
System.out.println(id+"--"+name);
return "list";
}
@RequestMapping("path/{id}/showRestFul")//id会绑定在地址栏,name和age作为参数xxx?name=xx&aeg=xx
public String testRestFul(@PathVariable Integer id, String name, int age){
System.out.println(id+"--"+name);
System.out.println(age);
return "list";
}
springboot 整合 mybatis
导入相关 包
spring-boot-starter-web
坐标里面包含了spring 和 springmvc的 jar 包,但是不包含 mybatis 的 jar,springboot官方没有给mybatis加启动器 ,https://github.com/mybatis/spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependency>
application.yaml 文件
server:
port: 8088
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.62.134:3306/ssm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
type: org.springframework.jdbc.datasource.DriverManagerDataSource
mybatis:
type-aliases-package: com.lhg.domain
configuration:
map-underscore-to-camel-case: true
index 首页 及其它页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<a th:href="@{/account/findAll}">查询所有账户</a><br>
<a th:href="@{/account/saveAccount}">新增账户</a><br>
<a th:href="@{/account/transfer}">转账事务</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>list</title>
</head>
<body>
<!--model.addAttribute("lists", lists);-->
<table border="solder 1px blue" align="center" width="50%">
<tr>
<th>ID</th>
<th>姓名</th>
<th>账户金额</th>
<th>操作</th>
</tr>
<tr th:each="account : ${lists}" align="center">
<td th:text="${account.id}"></td>
<td th:text="${account.name}"></td>
<td th:text="${account.money}"></td>
<td >
<a th:href="@{/account/findAccountById(id=${account.id})}">更新账户</a>
<a th:href="@{/account/delAccount(id=${account.id})}">删除账户</a>
</td>
</tr>
</table>
</body>
</html>
th:field:thymeleaf 语法,能做数据回显,th:value 只能把值注入value 属性当中
<body>
<div>更新账户</div>
<form th:action="@{/account/update}" method="post">
<input type="hidden" name="id" th:field="${account.id}" >
账户名字:<input type="text" name="name" th:field="${account.name}"><br>
账户金额:<input type="text" name="money" th:field="${account.money}"><br>
<input type="submit" value="提交">
</form>
</body>
<body>
<div>添加新账户账户</div>
<form th:action="@{/account/addAccount}" method="post">
账户:<input type="text" name="name"><br>
金额:<input type="text" name="money"><br>
<input type="submit" value="提交">
</form>
</body>
表现层及业务层代码
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/{page}")
public String showPage(@PathVariable String page){
System.out.println("page:"+page);
return page;
}
@RequestMapping("findAll")
public String findAll(Model model){
List<Account> lists = accountService.findAll();
model.addAttribute("lists",lists);
return "showAccount";
}
@RequestMapping("transfer")
public String transfer(){
try {
accountService.transfer("a","b",500);
} catch (Exception e){
e.printStackTrace();
return "failed";
}
return "success";
}
@RequestMapping("findAccountById")
public String findAccountById(Integer id,Model model){
try {
Account account = accountService.findById(id);
model.addAttribute("account",account);
} catch (Exception e){
e.printStackTrace();
return "failed";
}
return "findAccount";
}
@RequestMapping("update")
public @ResponseBody Map<String,String> update(Account account){
Map<String,String> map = new HashMap<>();
int i = accountService.updateAccount(account);
if (i>0){
map.put("msg","OK");
return map;
}else {
map.put("msg","failed");
return map;
}
}
@RequestMapping("delAccount")
public String delAccount(Integer id){
accountService.delAccount(id);
return "redirect:/account/findAll";
}
@RequestMapping("addAccount")
public String addAccount(Account account){
try {
System.out.println(account);
accountService.saveAccount(account);
} catch (Exception e){
e.printStackTrace();
return "failed";
}
return "success";
}
}
@Service
@Transactional
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account findById(Integer id) {
return accountMapper.findById(id);
}
@Override
public List<Account> findAll() {
return accountMapper.findAll().isEmpty()?null: accountMapper.findAll();
}
@Override
public int updateAccount(Account account) {
return accountMapper.updateAccount(account);
}
@Override
public void transfer(String sourceName, String targetName, int money) {
System.out.println("start transfer...");
//根据名称查询转入转出账户
Account source = accountMapper.findByName(sourceName);
Account target = accountMapper.findByName(targetName);
//转出账户减钱 转入账户加钱
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
//更新转入转出账户
accountMapper.updateAccount(source);
int i = 1/0;
accountMapper.updateAccount(target);
}
@Override
public void saveAccount(Account account) {
accountMapper.saveAccount(account);
}
@Override
public void delAccount(Integer id) {
accountMapper.delAccount(id);
}
}
@SpringBootApplication
@MapperScan(basePackages = {"com.lhg.smb.mapper"})//在这里写了MapperScan就不用在每个pojo类上面写@Mapper注解了
public class SpringbootMybatisThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisThymeleafApplication.class, args);
}
}
注意:有@configuration注解标识的是一个配置文件,springboot会主动扫描到,如果项目中有这种文件,要看看是否对项目运行有影响,如果无用的文件就删掉
springboot 服务器端表单数据校验
前端代码
<body>
<div>添加新账户账户</div>
<form th:action="@{/account/addAccount}" method="post">
账户:<input type="text" name="name"><span style="color: red;" th:errors="${account.name}"></span><br>
金额:<input type="text" name="money"><span style="color: red" th:errors="${account.money}"></span><br>
<input type="submit" value="提交">
</form>
</body>
服务器端代码
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
/**
* @param account 当通过restful直接进入添加页面,${account.xxx}便取不到会报错,所以这里加个参数
*/
@RequestMapping("/{page}")
public String showPage(@PathVariable String page, Account account) {
return page;
}
@RequestMapping("addAccount")
public String addAccount(@Valid Account account, BindingResult bindingResult) {
if (bindingResult.hasErrors()){
return "saveAccount";
}
accountService.saveAccount(account);
return "success";
}
}
public class Account implements Serializable {
private int id;
@NotBlank(message="money不能为空")//message属性可以自定以错误消息
private Integer money;
@NotBlank
private String name;
}
如果不想用驼峰式命名,可以用 @ModelAttribute 注解自定义
public String showPage(@PathVariable String page, @ModelAttribute("aa") Account account) {...}
public String addAccount(@ModelAttribute("aa") @Valid Account account, BindingResult bindingResult) {...}
springboot 异常处理五种方式
自定义错误页面
SpringBoot 默认的处理异常的机制: SpringBoot 默认的已经提供了一套处理异常的机制。 一旦程序中出现了异常 SpringBoot 会像/error 的 url 发送请求。 springBoot 中提供了一个 叫 BasicExceptionController 来处理/error 请求,然后跳转到默认显示异常的页面来展示异常 信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBYrSW51-1604016342381)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557843312579.png)]
如 果 我 们 需 要 将 所 有 的 异 常 同 一 跳 转 到 自 定 义 的 错 误 页 面 , 需 要 再 templates 目录下创建 error.html 页面。注意:名称必须叫 error ,
<body>
这是我自定义的总的错误页面。。,exception 是内置的属性
<span th:text="${exception}"></span>
</body>
这样一来所有的异常都会跳转到这个error.html 页面,不太好…
@ExceptionHandle 注解处理异常
@RequestMapping("except")
public String exception(){
int i = 1/0;
return "index";
}
//与空指针有关的异常进这里,如果还有除这两个外的其它异常则进 error.html
@ExceptionHandler(value = {java.lang.NullPointerException.class})
public ModelAndView nullPointerException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("exception");
return mv;
}
//与算数有关的异常进这里,如果还有除这两个外的其它异常则进 error.html
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView arithmeticException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("exception");
return mv;
}
<body>
这是我自定义的总的错误页面。。,我操
<span th:text="${error}"></span>
</body>
这种方式的问题就是如果代码中有很多种类异常,岂不是要写很多这种,而且这种方式只在当前 controller有效,其它 controller无效,所以…看下面的几种吧
@ControllerAdvice+@ExceptionHandler 注解处理异常
需要创建一个能够处理异常的全局异常类。在该类上需要添加@ControllerAdvice 注解,使之能够在多个controller 中共同使用,缺点就是还是要写很多方法,如果有多种异常的话
@ControllerAdvice
public class GlobalException {
//与空指针有关的异常进这里,如果还有除这两个外的其它异常则进 error.html
@ExceptionHandler(value = {java.lang.NullPointerException.class})
public ModelAndView nullPointerException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("exception");
return mv;
}
//与算数有关的异常进这里,如果还有除这两个外的其它异常则进 error.html
@ExceptionHandler(value = {java.lang.ArithmeticException.class})
public ModelAndView arithmeticException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("error",e.toString());
mv.setViewName("exception");
return mv;
}
}
配置 SimpleMappingExceptionResolver 处理异常
通过 SimpleMappingExceptionResolver 做全局异常处理,不过这种方式与前面第三种不同的是不会传递具体的异常信息(你在 html 页面不能通过类似 ${exception}取得具体的异常)
@Configuration
public class GlobalException {
/*
该方法必须要有返回值。返回值类型必须是: SimpleMappingExceptionResolver
*/
@Bean
public SimpleMappingExceptionResolver getMappingExceptionHandler(){
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
/** *
* 参数一:异常的类型,注意必须是异常类型的全名
** 参数二:视图名称
* */
properties.put("java.lang.NullPointerException","exception1");
properties.put("java.lang.ArithmeticException","exception2");
exceptionResolver.setExceptionMappings(properties);
return exceptionResolver;
}
}
自定义 HandlerExceptionResolver 类处理异常
这种相较于之前的几种,除了可以跳转到不同的视图还可以传递异常信息
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
//根据异常的类型不同跳转到不同的视图去
if (e instanceof ArithmeticException){
mv.setViewName("exception1");
}
if (e instanceof NullPointerException){
mv.setViewName("exception2");
}
//传递异常信息
mv.addObject("error",e.toString());
return mv;
}
}
<body>
这是我自定义的总的错误页面。。,我操,还全局的YYY
<span th:text="${error}"></span>
</body>
springboot 单元测试
/** SpringBoot 测试类
* @RunWith:启动器
* SpringJUnit4ClassRunner.class:让 junit 与 spring 环境进行整合
* @SpringBootTest(classes={App.class}) 1,当前类为 springBoot 的测试类
* @SpringBootTest(classes={App.class}) 2,加载 SpringBoot 启动类。启动 springBoot
* junit 与 spring 整合 @Contextconfiguartion("classpath:applicationContext.xml")
*/
@RunWith(SpringRunner.class)//SpringJUnit4ClassRunner.class也可以
@SpringBootTest(classes = {App.class})
public class SpringbootMybatisThymeleafApplicationTests {
@Autowired
private AccountService accountService;
@Test
public void test() {
List<Account> lists = accountService.findAll();
lists.forEach(account -> System.out.println(account));
}
}
springboot 热部署
使用 springloader 插件,这种对页面无能为力,还比较麻烦,总之不使用
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
DevTools 工具
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
对于 IDEA 开发还需作如下设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjjUAwdQ-1604016342383)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557888616455.png)]
ctrl+alt+shift+/---->registry
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuLp1Bce-1604016342385)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1557888687284.png)]
属性注入方式
spring 属性 java 注入方式
首先导入 jdbc 启动器、mysql驱动包, 下面是 db.properties 文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.62.134:3306/ssm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
@Configuration//标识这是一个配置文件
@PropertySource("classpath:db.properties")//加载外部属性文件
public class JdbcConfig {
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Bean(value = "dataSource")
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
return dataSource;
}
}
@RestController
public class HelloController {
@Autowired
private DataSource dataSource;//debug发现dataSource属性已经加载
@GetMapping("/hello")
@ResponseBody
public String hello(){
return "hello!";
}
}
springboot 属性 java 注入方式 一
首先 db.properties 重命名 application.properties,springboot默认会去读取application.properties,并导入 lombok 包
@ConfigurationProperties(prefix = "jdbc") //加上前缀
@Data //set/get方法
public class JdbcProperties {
private String driverClassName;
private String username;
private String password;
private String url;
}
//标识这是一个配置文件
@Configuration
//EnableConfigurationProperties:启用配置属性
//或者可以在 JdbcProperties 类上使用 @Component 注解,效果一样
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcConfig {
@Bean(value = "dataSource")
public DataSource createDataSource(JdbcProperties prop){//你也用可以 @Autowire 在属性上
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(prop.getDriverClassName());
dataSource.setUsername(prop.getUsername());
dataSource.setPassword(prop.getPassword());
dataSource.setUrl(prop.getUrl());
return dataSource;
}
}
springboot 属性 java 注入方式二
只保留 application.properties 文件
//标识这是一个配置文件
@Configuration
public class JdbcConfig {
//当springboot扫描到Bean注解时,发现还有个 ConfigurationProperties 注解,
//它就会检查这个实例对象的 setXXX方法,然后根据前缀从配置文件中读取并设值
@Bean(value = "dataSource")
@ConfigurationProperties(prefix = "jdbc") //加上前缀
public DataSource createDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
return dataSource;
}
}
yaml 配置文件
properties 形式的配置比较冗余,现在主流使用 yaml 配置文件,注意 : 号后面必须要有空格
jdbc:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.62.134:3306/ssm?characterEncoding=utf-8
username: root
password: root
什么是 springboot
SpringBoot 是一个快速开发框架,能够帮助我们快速整合第三方框架,完全采用注解化,简化 XML 配置,内置嵌入 http服务器(tomcat和jetty),最终以 Java 应用程序执行,SpringBoot 项目没有 web.xml
为什么使用 springboot
传统的 ssm 或者 ssh 最大缺点:开发效率低, Jar 冲突,配置多,springboot底层帮你实现版本统一(maven继承原理)
1、能快速整合第三方框架
2、简化 XML 配置,完全采用注解化,内置 http 服务器(tomcat和jetty)
3、最终是以 Java 应用程序执行
SpringBoot 整合 SpringData JPA
导入 jar 包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
application.properties 配置
# db config
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.62.134:3306/ssm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
# jpa config
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
SpringData JPA 提供的几种接口
Repository
方法名称命名查询方式
public interface AccountRepository2 extends Repository<Account,Integer> {
//方法的名称必须要遵循驼峰式规则,方法名构成:findBy+属性名字(首字母大写)+查询条件
List<Account> findByName(String name);//省略了Equals/Is
List<Account> findByIdAndName(Integer id, String name);
//根据用户姓名做 Like 处理
List<Account> findByNameLike(String name);
//:查询名称为王五,并且他的年龄大于等于 22 岁
List<Account> findByNameAndAgeGreaterThanEqual(String name, Integer age);
}
query 注解查询方式
public interface AccountRepository3 extends Repository<Account,Integer> {
//方法的名称必须要遵循驼峰式规则,方法名构成:findBy+属性名字(首字母大写)+查询条件,注意Account 大写
@Query(value=" from Account where name=:name")
List<Account> queryByNameUseHQL(String name);
//nativeQuery 值为 true 本地化 sql 查询,account 要小写
@Query(value = "select * from account where id=?1 and name = ?2", nativeQuery = true)
List<Account> queryByNameUseSQL(Integer id, String name);
@Query(value = "update Account set name=?1 where id=?2")
@Modifying //需要执行一个更新操作
int updateAccountById(String name, Integer id);
//JPQL:通过 Hibernate 的 HQL 演变过来的。他和 HQL 语法及其相似
@Query(" from Account where username like ?")
List<Account> queryUserByLikeNameUse JPQL(String name);
@Query("from Account where Name = ? and age >= ?")
List<Account> queryUserByNameAndAge(String name,Integer age);
}
注意使用 update 更新操作时,由于是和 @test 一起使用,会默认回滚,所以得像下面这么写
@Test
@Transactional
@Rollback(false) //取消自动回滚
public void test2(){
int i = accountRepository3.updateAccountById("林红川",11);
System.out.println(i);
}
CrudRepository
继承自 Repository 接口,主要是完成一些增删改查的操作
public interface AccountRepository4 extends CrudRepository<Account, Integer> {
}
@Test
public void test2(){
Account account = new Account();
account.setId(14);
account.setName("李克强");
account.setMoney(666);
//增加和更新都是save方法
Account res = accountRepository4.save(account);
Iterable<Account> all = accountRepository4.findAll();
accountRepository4.delete(14);//根据 id 删除
System.out.println(res);
}
PagingAndSortingRepository
继承自 CrudRepository 接口,提供了分页和排序的操作,该接口只提供两个方法 findAll(Sort sort) 和 findAll(Pageable pageable)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-anAzz0jX-1604016342387)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1558406973412.png)]
public interface AccountRepository5 extends PagingAndSortingRepository<Account, Integer> {
}
@Service
public class AccountServiceImpl {
@Autowired
private AccountRepository5 accountRepository5;
public Page pageAccounts(int pageNum, int pageSize){
Pageable pageable = PageRequest.of(pageNum, pageSize);//new PageRequest已过时
Page<Account> accountPage = accountRepository5.findAll(pageable);
return accountPage;
}
}
当然你也可以查询过程进行排序, SpringBoot2.0 后 api 有多处修改
@Service
public class AccountServiceImpl {
@Autowired
private AccountRepository5 accountRepository5;
public Page pageAccounts(int pageNum, int pageSize){
//Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");//单个属性降序
//Sort sort = new Sort(Sort.Direction.DESC, "id","money"); //多个属性降序排序
Sort.Order order1 = new Sort.Order(Sort.Direction.DESC,"id");//id降序
Sort.Order order2 = new Sort.Order(Sort.Direction.ASC,"money");//money升序
Sort sort = Sort.by(order1,order2);
Pageable pageable = PageRequest.of(pageNum, pageSize, sort);
Page<Account> accountPage = accountRepository5.findAll(pageable);
return accountPage;
}
}
@Test
public void test2(){
int pageNum = 4;//从0 开始
int pageSize = 3;
Page page = accountServiceImpl.pageAccounts(pageNum-1, pageSize);//如果不减一,查询的是第五页,没有数据
System.out.println("当前页数:"+(page.getNumber()+1));
System.out.println("页大小:"+page.getSize());
System.out.println("总记录数:"+page.getTotalElements());
System.out.println("总页数:"+page.getTotalPages());
System.out.println("查询出的记录数:"+page.getNumberOfElements());
System.out.println("查询数据结果集:");
List pageContent = page.getContent();
pageContent.forEach(account-> System.out.println(account));
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9VLUlfh-1604016342389)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1558417696920.png)]
JpaRepository
在实际开发中尽量用这个接口,因为是它有其它接口的所有方法,直接使用即可
@Service
public class AccountServiceImpl {
@Autowired
private AccountRepository5 accountRepository5;
public void deleteBatch(Iterable<Integer> ids){
List<Account> accountList = accountRepository5.findAllById(ids);
accountRepository5.deleteInBatch(accountList);
}
}
@Test
public void test3(){
List<Integer> ids = new ArrayList<>();
ids.add(16);
ids.add(17);
accountServiceImpl.deleteBatch(ids);
}
JPASpecificationExecutor
该接口主要提供了多条件查询的支持,并且可以在查询中添加分页与排序。注意:JpaSpecificationExecutor是单独存在,完全独立。
public interface AccountRepository5 extends JpaRepository<Account, Integer>, JpaSpecificationExecutor<Account> {
}
比如查询条件是 and 的多条件写法
@Test
public void test4() {
/**
* root:查询对象的属性的封装
*criteriaQuery:封装了我们要执行的查询中的各个部分信息 select from order
* criteriaBuilder:查询条件的构造器,定义不同的查询条件
*/
Specification<Account> spec = new Specification<Account>() {
@Override
public Predicate toPredicate(Root<Account> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<>();
list.add(criteriaBuilder.equal(root.get("id"), 1));
list.add(criteriaBuilder.equal(root.get("name"), "a"));
Predicate[] predicates = new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(predicates));
}
};
List<Account> accountList = accountRepository5.findAll(spec);
accountList.forEach(account -> System.out.println(account));
}
}
对于上面这段代码可以有如下简写
@Test
public void test4() {
Specification<Account> spec = (Specification<Account>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.and(criteriaBuilder.equal(root.get("id"), 1),criteriaBuilder.equal(root.get("name"), "a"));
List<Account> accountList = accountRepository5.findAll(spec);
accountList.forEach(account -> System.out.println(account));
}
如果查询条件变成了 or 则是下面
@Test
public void test4() {
Specification<Account> spec = (Specification<Account>) (root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.or(criteriaBuilder.equal(root.get("id"), 1),criteriaBuilder.equal(root.get("id"), 11));
List<Account> accountList = accountRepository5.findAll(spec);
accountList.forEach(account -> System.out.println(account));
}
如果查询条件有 or 又有 and 呢?
@Test
public void test4() {
//(name="a" and id=1) or id=13
Specification<Account> spec = (Specification<Account>) (root, cq, cb) ->
cb.or( cb.and(cb.equal(root.get("name"),"a"),cb.equal(root.get("id"),1)),
cb.equal(root.get("id"),13));
List<Account> accountList = accountRepository5.findAll(spec);
accountList.forEach(account -> System.out.println(account));
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ua18k78P-1604016342391)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1558428694407.png)]
SpringBoot 缓存技术
-
redis 介绍
Redis 是目前业界使用最广泛的内存数据存储,相比 memcached,Redis 支持更丰富的数据结构,例如hashes,lists,sets 等,同时支持数据持久化,除此之外,Redis 还提供一些类数据库的特性,比如事务、HA、主从库。可以说 Redis 兼具了缓存系统和数据库的一些特性,因此有着丰富的场景。
-
整合 redis
-
pom 所需 jar 包
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</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>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.7</version> </dependency> </dependencies>
-
配置文件
application.properties
# Redis 数据库索引(默认为0) spring.redis.database=2 # Redis 服务器地址 spring.redis.host=192.168.62.134 # Redis 服务端连接端口 spring.redis.port=6379 # Redis 连接密码(默认为空) spring.redis.password=123456 # 连接超时时间(毫秒) spring.redis.timeout=3000 spring.redis.jedis.pool.max-active=20 spring.redis.jedis.pool.max-idle=10 spring.redis.jedis.pool.min-idle=5
自定义序列化器代替 jdk 自带的
@Configuration public class MySerializConfig { @Bean(name = "redisTemplate") public RedisTemplate<Object, Object> creaeteRedisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); redisSerializer.setObjectMapper(objectMapper); //设置String<key,value>的序列化规则 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(redisSerializer); //设置hash<key,<k,v>>的序列化规则 redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(redisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; }
}
+ 测试 ```java @RunWith(SpringRunner.class) @SpringBootTest(classes = SpringbootRedisApplication.class) public class SpringbootRedisApplicationTests { //操作字符串 @Autowired private StringRedisTemplate stringRedisTemplate; //带泛型,操作对象 @Autowired private RedisTemplate redisTemplate; @Test public void setValue() { //存数据 stringRedisTemplate.opsForValue().set("age","18"); List<String> list=new ArrayList<String>(); list.add("a1"); list.add("a2"); list.add("a3"); stringRedisTemplate.opsForList().leftPushAll("lists", list); } @Test public void getValue() throws InterruptedException { //取数据 String lists = stringRedisTemplate.opsForList().leftPop("lists",1, TimeUnit.SECONDS); System.out.println(lists); } //存对象并用json格式 @Test public void test3(){ User users = new User(); users.setAge(23); users.setId(2); users.setName("李四sf"); this.redisTemplate.opsForValue().set("user2", users); } }
-
-
SpringBoot 使用 spring cache 缓存注解
-
Spring cache 缓存介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zsT3eXHG-1604016342392)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1559184996290.png)]
-
注解介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t1INjiWp-1604016342393)(C:\Users\HeGuang\AppData\Roaming\Typora\typora-user-images\1559185064043.png)]
-
-
添加 Spring Cache 步骤
-
开启注解缓存
@Configuration @EnableCaching//开启缓存注解 public class MySerializConfig { @Bean(name = "cacheManager") public CacheManager createCacheManage(RedisConnectionFactory redisConnectionFactory){ RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); //设置 CacheManage的序列化方式为 json 序列化 GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer); RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, cacheConfiguration); return cacheManager; } }
-
Pojo 类
@Data public class User implements Serializable { private Integer id; private String name; private Integer age; }
-
Controller 代码
@RestController //如果写了此注解,下面的 Cacheable、CachePut、CacheEvict..括号中不用再写 value,这里统一管理 //@CacheConfig(cacheNames = "user") public class RedisController { @Cacheable(value = "user")//从缓存数据库取数据 @GetMapping("user/{id}") public User getUser(@PathVariable Integer id){ User user = new User(); user.setId(id); user.setAge(new Random().nextInt(20)); user.setName("get user:"+id); System.out.println("模拟查询数据库:"+id); return user; } @CachePut(value = "user")//缓存数据库中的数据也会修改 @GetMapping("user/update/{id}") public User updateUser(@PathVariable Integer id){ User user = new User(); user.setId(id); user.setAge(new Random().nextInt(20)); user.setName("update user:"+id); System.out.println("模拟更新数据库:"+id); return user; } @CacheEvict(value = "user", key ="#id")//根据 Id 删,默认全删 @GetMapping("user/delete/{id}") public void deleteUser(@PathVariable Integer id){ System.out.println("模拟删除数据库:"+id); } }
-