一 SpringBoot简介

SpringBoot是Spring框架的一个新子项目 用于创建Spring4.0项目 它的开发始于2013年 2014年4月发布1.0.0版本 它可以自动配置Spring的各种组件 并不依赖代码生成和XML配置文件 SpringBoot也提供了对于常见场景的推荐组件配置 SpringBoot可以大大提升使用Spring框架时的开发效率 使用SpringBoot可以轻松的创建独立运行的程序 非常容易构建独立的服务组件 是实现分布式架构 微服务架构利器

二 SpringBoot优点

1. 轻松创建独立的Spring应用程序

2. 内嵌Tomcat Jetty等web容器 不需要部署WAR文件

3. 提供一系列的Starter 来简化的Maven配置 不需要添加很多依赖

4. 开箱即用 尽可能自动配置Spring

三 SpringBoot初体验

1. 新建一个Maven项目 注意不需要webapp文件夹及子文件夹和web.xml

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hy.springboot</groupId>
<artifactId>springboot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent> <!-- 定义依赖版本号 -->
<properties>
<mysql-connector-java.version>8.0.11</mysql-connector-java.version>
<druid.version>1.1.10</druid.version>
<mybatis-spring-boot-starter.version>2.1.3</mybatis-spring-boot-starter.version>
<pagehelper-spring-boot-starter.version>1.3.0</pagehelper-spring-boot-starter.version>
</properties> <!-- 管理jar版本号 -->
<dependencyManagement>
<dependencies>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</dependencyManagement> <dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
</dependencies> <build>
<!-- 允许mybatis的mapper.java和mapper.xml在同一目录 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources> <!-- 打包名称 -->
<finalName>one</finalName>
<plugins>
<!-- 编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- spring boot -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.hy.springboot.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

sql

-- 用户表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户id',
username VARCHAR(32) COMMENT '用户名',
money DOUBLE COMMENT '用户余额'
); INSERT INTO user VALUES (1, '曹操', 8000);
INSERT INTO user VALUES (2, '孙权', 8000);
INSERT INTO user VALUES (3, '刘备', 8000);
INSERT INTO user VALUES (4, '诸葛亮', 5000);
INSERT INTO user VALUES (5, '司马懿', 5000);
INSERT INTO user VALUES (6, '张飞', 0);
INSERT INTO user VALUES (7, '关羽', 0);
INSERT INTO user VALUES (8, '马超', 1000);
INSERT INTO user VALUES (9, '黄忠', 1000);
INSERT INTO user VALUES (10, '赵云', 3000);

2. 创建配置文件 resources/application.yml

spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_hy?characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
max-active: 20
min-idle: 5
servlet:
multipart:
max-file-size: 5Mb
max-request-size: 10MB
mybatis:
type-aliases-package: com.hy.springboot.model
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
server:
port: 8081
servlet:
context-path: /

3. 创建实体类 com.hy.springboot.model.User com.hy.springboot.model.PageWrapper com.hy.springboot.model.HttpError

public class User implements Serializable {

    private Integer id;
private String username;
private Double money; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public Double getMoney() {
return money;
} public void setMoney(Double money) {
this.money = money;
} @Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", money=" + money +
'}';
}
}
public class PageWrapper implements Serializable {

    private Long total;
private List list; public PageWrapper() {} public PageWrapper(Long total, List list) {
this.total = total;
this.list = list;
} public Long getTotal() {
return total;
} public void setTotal(Long total) {
this.total = total;
} public List getList() {
return list;
} public void setList(List list) {
this.list = list;
}
}
public class HttpError extends Exception {

    private int code;
private String message; public HttpError() {} public HttpError(int code, String message) {
this.code = code;
this.message = message;
} public int getCode() {
return code;
} public void setCode(int code) {
this.code = code;
} @Override
public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
} @Override
public String toString() {
return "HttpError{" +
"code=" + code +
", message='" + message + '\'' +
'}';
}
}

4. 自定义文件上传实现类 com.hy.springboot.resolver.PostAndPutMultipartResolver 解决客户端(android ios...) PUT提交文件表单异常

public class PostAndPutMultipartResolver extends StandardServletMultipartResolver {

    @Override
public boolean isMultipart(HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
return false;
}
}

5. 配置文件上传实现类 com.hy.springboot.config.MultipartResolverConfig

@Configuration
public class MultipartResolverConfig { @Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
return new PostAndPutMultipartResolver();
}
}

6. 创建拦截器 com.hy.springboot.interceptor.Interceptor

public class Interceptor implements HandlerInterceptor {

    // Controller执行前调用此方法
// 返回true继续执行 返回false中止执行
// 这里可加入登录校验 权限拦截等
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println(" ========== preHandle ========== ");
// 放行
return true;
} // Controller执行后 且未返回视图前 调用此方法
// 这里可在返回用户前对模型数据进行加工处理 比如这里加入公用信息以便页面显示
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {} // Controller执行后 且视图返回后 调用此方法
// 这里可得到执行Controller时的异常信息
// 这里可记录操作日志
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}

7. 配置拦截器 com.hy.springboot.config.InterceptorConfig

@Configuration
public class InterceptorConfig implements WebMvcConfigurer { @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor()).addPathPatterns("/**"); //拦截所有
}
}

8. 配置异常处理器 com.hy.springboot.exception.HttpErrorResolver

@ControllerAdvice
public class HttpErrorResolver { @ExceptionHandler(RuntimeException.class)
@ResponseBody
public HttpError resolveException(Exception e) {
HttpError error = e instanceof HttpError ? (HttpError) e : new HttpError(-1, "未知异常");
return error;
}
}

9. 配置数据源 com.hy.springboot.config.DruidConfig

@Configuration
public class DruidConfig { @Bean
@ConfigurationProperties(prefix = "spring.datasource") //将application.yml配置文件中前缀为spring.datasource的属性注入到DruidDataSource同名参数中
public DataSource dataSource() {
return new DruidDataSource();
}
}

10. 配置AOP com.hy.springboot.aop.LogAspect

@Component
@Aspect
public class LogAspect { /**
* 声明公共切入点
* expression(表达式): 切点表达式
* * com.hy.springboot.controller..*.*(..)
* * = 任意返回值
* com.hy.springboot.controller. = com.hy.springboot.controller包和所有子包
* .*.* = .任意类.任意方法名
* (..) = 任意参数
*/
@Pointcut("execution(* com.hy.springboot.controller..*.*(..))")
public void pointcut() {} /**
* 前置通知 在方法执行前执行 如果通知抛出异常 阻止方法运行
* @param joinPoint 连接点
*/
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("前置通知 JoinPoint = " + joinPoint.toString());
// 请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
System.out.println("url = " + url);
System.out.println("method = " + method);
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = enu.nextElement();
System.out.println(name + "=" + request.getParameter(name));
}
} /**
* 后置通知 方法正常返回后执行 可以获得方法返回值 如果方法中抛出异常 通知无法执行
* @param joinPoint 连接点
* @param ret 方法返回值
*/
@AfterReturning(value = "pointcut()", returning = "ret")
public void afterReturning(JoinPoint joinPoint, Object ret) {
System.out.println("后置通知 ret = " + ret + " JoinPoint = " + joinPoint.toString());
}
}

11. 配置启动器 com.hy.springboot.Application

@SpringBootApplication
@MapperScan("com.hy.springboot.mapper")
public class Application { public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

12. 创建mapper接口 com.hy.springboot.mapper.UserMapper

public interface UserMapper {

    List<User> selectUserList();

    Integer updateUserMoney(Map map);
}

13. 创建mapper映射文件 com.hy.springboot.mapper.UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hy.springboot.mapper.UserMapper">
<select id="selectUserList" resultType="User">
SELECT *
FROM user
</select> <update id="updateUserMoney" parameterType="HashMap">
UPDATE user
SET money = money + #{money}
WHERE id = #{id}
</update>
</mapper>

14. 创建service接口 com.hy.springboot.service.IUserService

public interface IUserService {

    List<User> selectUserList();

    PageWrapper selectUserList(Integer pageNum, Integer pageSize);

    Boolean updateUserMoney(Map map1, Map map2);
}

15. 创建service实现类 com.hy.springboot.service.imp.UserService

@Service
@Transactional
public class UserService implements IUserService { @Autowired
private UserMapper userMapper; @Override
public List<User> selectUserList() {
return userMapper.selectUserList();
} @Override
public PageWrapper selectUserList(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> userList = userMapper.selectUserList();
PageInfo<User> pageInfo = new PageInfo<>(userList);
return new PageWrapper(pageInfo.getTotal(), userList);
} @Override
public Boolean updateUserMoney(Map map1, Map map2) {
Integer integer1 = userMapper.updateUserMoney(map1);
int i = 3 / 0; //模拟异常
Integer integer2 = userMapper.updateUserMoney(map2);
return 0 != integer1 && 0 != integer2;
}
}

16. 创建控制器 com.hy.springboot.controller.TestController

@RestController
@RequestMapping("/test")
public class TestController { @Autowired
private IUserService userService; @RequestMapping("/tm")
public Boolean tm() {
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 6);
map1.put("money", -1000.0);
Map<String, Object> map2 = new HashMap<>();
map2.put("id", 7);
map2.put("money", +1000.0);
return userService.updateUserMoney(map1, map2);
} @RequestMapping("/selectUserList")
public List<User> selectUserList() {
return userService.selectUserList();
} @GetMapping("/selectUserList/{page}/{size}")
public PageWrapper selectUserList(@PathVariable Integer page, @PathVariable Integer size) {
return userService.selectUserList(page, size);
} /** RESTful 只进行演示 没有做数据持久化 **/ @PostMapping("/insert")
public Map insert(String name) {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
return map;
} @PostMapping("/insertX")
public Map insertX(String name, MultipartFile file) throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("fileSize", file.getSize()); //建议使用文件服务器保存
return map;
} @DeleteMapping("/delete/{id}")
public Map delete(@PathVariable Integer id, String name) {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
return map;
} @PutMapping("/update/{id}")
public Map update(@PathVariable Integer id, String name) {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
return map;
} @PutMapping("/updateX/{id}")
public Map updateX(@PathVariable Integer id, String name, MultipartFile file) throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
map.put("fileSize", file.getSize()); //建议使用文件服务器保存
return map;
} @GetMapping("/select/{id}")
public Map select(@PathVariable Integer id, String name) {
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
return map;
}
}

启动项目 访问 http://localhost:8081/test/selectUserList

android客户端核心代码

public interface Api {

    @POST("/test/insert")
@FormUrlEncoded
Call<ResponseBody> insert(@Field("name") String name); @POST("/test/insertX")
@Multipart
Call<ResponseBody> insertX(@Part MultipartBody.Part name, @Part MultipartBody.Part file); @DELETE("/test/delete/{id}")
Call<ResponseBody> delete(@Path("id") Integer id, @Query("name") String name); @PUT("/test/update/{id}")
@FormUrlEncoded
Call<ResponseBody> update(@Path("id") Integer id, @Field("name") String name); @PUT("/test/updateX/{id}")
@Multipart
Call<ResponseBody> updateX(@Path("id") Integer id, @Part MultipartBody.Part name, @Part MultipartBody.Part file); @GET("/test/select/{id}")
Call<ResponseBody> select(@Path("id") Integer id, @Query("name") String name);
}
private void insert() {
Call<ResponseBody> call = mApi.insert("祎哥哥");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
} private void insertX() {
MultipartBody.Part name = MultipartBody.Part.createFormData("name", "祎哥哥");
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), card("text.txt"));
MultipartBody.Part file = MultipartBody.Part.createFormData("file", "text.txt", body);
Call<ResponseBody> call = mApi.insertX(name, file);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
} private void delete() {
Call<ResponseBody> call = mApi.delete(3, "黄祎");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
} private void update() {
Call<ResponseBody> call = mApi.update(3, "黄祎");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
} private void updateX() {
MultipartBody.Part name = MultipartBody.Part.createFormData("name", "黄祎");
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), card("text.txt"));
MultipartBody.Part file = MultipartBody.Part.createFormData("file", "text.txt", body);
Call<ResponseBody> call = mApi.updateX(3, name, file);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
} private void select() {
Call<ResponseBody> call = mApi.select(3, "黄祎");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {} @Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
}

SpringBoot一统江湖的更多相关文章

  1. 13、Cocos2dx 3.0游戏开发找小三之3.0中的Director :郝萌主,一统江湖

    重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27706967 游戏中的基本元素 在曾经文章中.我们具 ...

  2. 13、Cocos2dx 3.0三,找一个小游戏开发3.0中间Director :郝梦主,一统江湖

    重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/27706967 游戏中的基本元素 在曾经文章中,我们具 ...

  3. 一统江湖的大前端(1)——PPT制作库impress.js

    <一统江湖的大前端>系列是自己的学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有点O ...

  4. 一统江湖的大前端(2)—— Mock.js + Node.js 如何与后端潇洒分手

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  5. 一统江湖的大前端(3) DOClever——你的postman有点low

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  6. 一统江湖的大前端(4)shell.js——穿上马甲我照样认识你

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  7. 一统江湖的大前端(5)editorconfig + eslint——你的代码里藏着你的优雅

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  8. 一统江湖的大前端(6)commander.js + inquirer.js——懒,才是第一生产力

    <一统江湖的大前端>系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新.如果你对前端的理解还是写写页面绑绑事件,那你真的是有 ...

  9. 一统江湖的大前端(7)React.js-从开发者到工程师

    目录 一. 前端打怪升级指南 1.1 我应该从哪个框架开始学? 1.2 一次转职 1.3 二次转职 1.4 转职-其他 二. 为什么你应该学习React 2.1 技术栈的延伸 2.2 组件化开发 2. ...

随机推荐

  1. python 复制与粘贴处理笔记

    在python中用有一个模块可以用来处理剪切板复制的内容,pyperclip模块 pyperclip模块有copy()和paste()函数,分别用于向计算机的剪贴板发送文本,或从它接受文本. pype ...

  2. 图解JAVA容器核心类库

    JAVA容器详解 类继承结构图 HashMap 1. 对象的HashCode是用来在散列存储结构中确定对象的存储地址的.   2. 如果两个对象的HashCode相同,即在数组中的地址相同.而数组的元 ...

  3. WPF新手快速入门系列 2.绑定

    [概要] 上一章讲了布局,按照市面上的书籍每一本讲的顺序都不一样,本系列是希望大家能快速上手去应对工作需要,所以本章就直接开始讲绑定. 如有学习过程中想交流学习.疑惑解答可以来此QQ群交流:58074 ...

  4. Python Selenium 之数据驱动测试的实现

    数据驱动模式的测试好处相比普通模式的测试就显而易见了吧!使用数据驱动的模式,可以根据业务分解测试数据,只需定义变量,使用外部或者自定义的数据使其参数化,从而避免了使用之前测试脚本中固定的数据.可以将测 ...

  5. Labview学习之路(十三)数组函数

    本文章介绍一下函数选版中的数组函数,一眼看懂没什么特殊地方的就不说了 (一)数组大小 返回I32类型的数或者数组. 什么是I32类型?就是32位有符号整型, 有符号什么意思?就是在32位数据中最高做符 ...

  6. 在Mac上打开多个Unity实例

    alias koa_unity="open -n /Applications/Unity\ 5.2.2/Unity.app" alias rob_unity="open ...

  7. Leetcode 974 和可被K整除的子数组

    题目: 解法 //前缀和算法+hash表 class Solution { public: int subarraysDivByK(vector<int>& A, int K) { ...

  8. java之5分钟插入千万条数据

    虽说不一定5分钟就插入完毕,因为取决去所插入的字段,如果字段过多会稍微慢点,但不至于太慢.10分钟内基本能看到结果. 之前我尝试用多线程来实现数据插入(百万条数据),半个多小时才二十多万条数据. 线程 ...

  9. Spring Boot 整合Redis 实现缓存

      本文提纲 一.缓存的应用场景 二.更新缓存的策略 三.运行 springboot-mybatis-redis 工程案例 四.springboot-mybatis-redis 工程代码配置详解   ...

  10. 搭建MQTT学习平台

    关于MQTT协议的介绍就不赘述了,网上资料非常多,直接入正题. MQTT协议的实现非常多,我选择了一个非常轻量级的开源项目来学习MQTT协议——mosquitto,官网:www.mosquitto.o ...