springboot整合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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);
          }
      }
      
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值