SpringBoot开发实用篇
热部署
手动启动热部署
项目做完某一个功能测试的时候,修改完功能可以马上见到效果
服务器发现程序有新的改动,服务器实现内部的重启
实现热部署,必须得在spring容器里添加配置
在pom文件中添加开发者工具并使用idea自带的重新构建项目完成热部署激活热备份(Ctrl+F9)
实际是idea重新启动了服务器
1 2 3 4 5 6
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
|
自动启动热部署
打开idea的自动构建项目 idea失去焦点五秒之后自动重新构建项目
新版idea移除这个功能
关于热部署
- 重启(Restart):自定义开发代码,包含类,页面,配置文件等,加载位置restart类加载器
- 重载(Reload):jar包,加载位置base类加载器
热部署仅仅加载当前开发者自定义开发的资源,不加载jar资源
热部署范围配置
- 默认不触发重启的目录列表
- /META-INF/maven
- /META-INF/resources
- /resources
- /static
- /public
- /templates
在springboot的配置文件中找到开发者工具,控制热部署范围配置
开发环境下有效
1 2 3 4
| devtools: restart: exclude: static/**,public/**,config/application.yml
|
关闭热部署
yml文件配置关闭
1 2 3 4 5 6
| devtools: restart: exclude: static/**,public/**,config/application.yml enabled: false
|
属性加载优先级覆盖关闭
在springboot启动入口类中设置关闭热部署
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication public class SSMPApplication {
public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled","false"); SpringApplication.run(SSMPApplication.class, args);
}
}
|
配置高级
属性绑定
configuration
三种属性绑定
读取单一属性
当要读取yml配置文件中country的值
1 2 3 4 5 6 7 8 9
| country: china province: beijing city: beijing area: haiding
user: name: ma age: 16
|
使用注解**@Value**获取
1 2 3 4 5
| @Value("${country}") private String country1;
@Value("${user.name}") private String name1;
|
像是是多级属性则一级一级读取
读取数组中数据
1 2 3 4
| likes: - game - music - sleep
|
同样是**@Value**获取 只不过是数组形式获取
1 2
| @Value("${likes[1]}") private String likes;
|
引用数据
1 2 3 4 5
| baseDir: c:\win10
tempDir: ${baseDir}\temp
tempDir2: "${baseDir}\temp \t1 \t2"
|
读取yml全部属性数据
1 2 3
| @Autowired private Environment env;
|
获取数据
1 2
| System.out.println(env.getProperty("user.name")); System.out.println(env.getProperty("likes[1]"));
|
将一组数据封装为对象
配置数据
在yml中配置数据
1 2 3 4
| servers: idAddress: 192.168.0.1 port: 2345 timeout: -1
|
定义实体类接收数据生成对象
1 2 3 4 5 6 7 8 9 10 11
|
@ConfigurationProperties(prefix = "servers") /@Component @Data public class ServerConfig { private String idAddress; private int port; private long timeout; }
|
接收数据
- 在springboot启动入口测试 需要使用getBean来获得数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @SpringBootApplication @EnableConfigurationProperties(ServerConfig.class) public class Springboot10ConfigurationApplication {
@Bean @ConfigurationProperties(prefix = "datasource") public DruidDataSource druidDataSource(){ DruidDataSource ds = new DruidDataSource(); return ds; } public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(Springboot10ConfigurationApplication.class,args); ServerConfig bean = ctx.getBean(ServerConfig.class);
System.out.println(bean); DruidDataSource ds = ctx.getBean(DruidDataSource.class); System.out.println(ds.getDriverClassName()); }
}
|
- 在congtroller层使用注解**@Autowired**来获得数据
1 2 3 4 5 6 7 8 9
| @Autowired private ServerConfig serverConfig;
@GetMapping public String test(){ System.out.println(serverfig) }
|
第三方bean绑定
@ConfigurationProperties不仅适用于自定义的bean属性还适用于第三方bean绑定
1 2 3 4 5 6 7
| @Bean @ConfigurationProperties(prefix = "datasource") public DruidDataSource druidDataSource(){ DruidDataSource ds = new DruidDataSource(); return ds; } System.out.println(ds.getDriverClassName());
|
1 2
| datasource: driverClassName: com.mysql.jdbc.DriverMa
|
@ConfigurationProperties和@EnableConfigurationProperties
松散绑定
- @ConfigurationProperties绑定属性支持绑定属性名宽松绑定
- @Value 不支持宽松绑定
- 绑定前缀命名命名规则——>建议统一小写命名
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @ConfigurationProperties(prefix = "servers") @Component @Data public class ServerConfig { private String idAddress; private int port; private long timeout; @DurationUnit(ChronoUnit.HOURS) private Duration serverTimeOut; @DataSizeUnit(DataUnit.MEGABYTES) private DataSize dataSize; }
|
1 2 3 4 5 6 7 8 9 10 11
| servers: idAddress: 192.168.0.1
port: 2345 timeout: -1 serverTimeOut: 3 dataSize: 10
|
开启bean数据校验
- 添加JSR303规范坐标与hibernate校验框架坐标
1 2 3 4 5 6 7 8 9 10 11 12 13
|
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> </dependency>
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
|
- 开启Bean校验功能 使用@Validated注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @ConfigurationProperties(prefix = "servers") @Component @Data
@Validated public class ServerConfig { private String idAddress; @Max(value = 8888,message = "最大值不超过8888") @Min(value = 202,message = "最小值不低于202") private int port; private long timeout; @DurationUnit(ChronoUnit.HOURS) private Duration serverTimeOut; @DataSizeUnit(DataUnit.MEGABYTES) private DataSize dataSize; }
|
进制转换规则
yaml文件中对于数字的定义支持进制书写格式,如需使用字符串请使用引号明确标注
测试
加载测试环境专用属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@SpringBootTest(properties = {"test.prop=vtb1"},args = {"--test.prop=vtb2"}) public class PropertiesAndArgsTest {
@Value("${test.prop}") private String msg; @Test void testProperties(){ System.out.println(msg);
} }
|
引用外部资源
- @Import(MsgConfig.class)可以导入bean
- 但是如果需要引用的类写了@Configuration 则springboot自动装配
1 2 3 4 5 6 7 8 9
| @Configuration public class MsgConfig {
@Bean public String msg(){ return "bean msg"; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootTest @Import(MsgConfig.class) public class ConfigurationTest {
@Autowired private String msg; @Test void Configuration(){ System.out.println(msg); }
}
|
测试web层
模拟web环境
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 使用默认端口号进行访问
模拟端口
1 2 3 4 5 6 7 8
| @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) public class WebTest {
@Test void webTest(){
} }
|
web环境模拟请求测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc public class WebTest {
@Test void webTest(@Autowired MockMvc mvc)throws Exception{ MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books"); mvc.perform(builder);
} }
|
虚拟请求响应状态匹配
- 如果与预期值匹配相等则正常运行
- 如果不相等则报出详细错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test void webTest(@Autowired MockMvc mvc)throws Exception{ MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books"); ResultActions actions = mvc.perform(builder); StatusResultMatchers status = MockMvcResultMatchers.status(); ResultMatcher ok = status.isOk(); actions.andExpect(ok); }
|
测试响应体内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test void testBody(@Autowired MockMvc mvc)throws Exception{ MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/1books"); ResultActions actions = mvc.perform(builder);
ContentResultMatchers content = MockMvcResultMatchers.content(); ResultMatcher result = content.string("springboot"); actions.andExpect(result); } }
|
测试响应体内容(JSON)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test void testBodyJson(@Autowired MockMvc mvc)throws Exception{ MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/1books"); ResultActions actions = mvc.perform(builder);
ContentResultMatchers content = MockMvcResultMatchers.content(); ResultMatcher result = content.json("id1\n" + "namespringboot\n" + "typemath\n" + "descriptiongood book"); actions.andExpect(result); }
|
测试响应头
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test void testContentType(@Autowired MockMvc mvc)throws Exception{ MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/1books"); ResultActions actions = mvc.perform(builder);
HeaderResultMatchers header = MockMvcResultMatchers.header(); ResultMatcher string = header.string("Content-Type", "application/json"); actions.andExpect(string); }
|
数据层测试事务回滚
- 在测试数据的时候,在测试层添加**@Transactional** springboot会自动执行事务回滚,springboot知道是测试数据不将其写入数据库以免变成脏数据
- 如果需要数据插入到数据库加上注解**@Rollback(false)**,默认为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @SpringBootTest @Transactional @Rollback public class DaoTest { @Autowired private BookService bookService;
@Test void DaoTest(){ Book book = new Book();
book.setName("springboot"); book.setType("math"); book.setDescription("good book"); bookService.save(book); } }
|
测试用例设置随机值
- 在多环境配置中的测试环境添加测试随机值
- 通过创建实体封装为对象来接收
- 使用**@Autowired**注入bean来接收数据
- 设置范围没有规定使用哪种分隔符,()不唯一,可以使用各种符号分割,但不要使用 - 号
1 2 3 4 5 6
| testcase: book: id: ${random.int(10)} name: ${random.value} uuid: ${random.uuid} publishTime: ${random.long}
|
1 2 3 4 5 6 7 8 9
| @Data @Component @ConfigurationProperties(prefix = "testcase.book") public class BookCase { private int id; private String name; private String uuid; private long publishTime; }
|
1 2 3 4 5 6 7 8 9 10 11
| public class PropertiesAndArgsTest { @Autowired private BookCase bookCase; @Test void testProperties(){ System.out.println(bookCase);
} }
|
数据层解决方案SQL
现有数据层解决方案技术选型
Druid+MyBatis-Plus+MySQL
- 数据源:DruidDataSource
- 持久化技术:MyBatis-Plus/MyBatis
- 数据库:MySQL
数据源内嵌配置
SpringBoot提供了三种内嵌的数据源对象共开发者选择
- HikariCP (springboot默认的配置):默认内置数据源对象
- Tomcat提供DataSource:HikariCP 不可用的情况下,且在web环境下,将使用tomcat服务器配置的数据源对象
- Commons DBCP:HikariCP 不可用,tomcat数据源也不可用,将使用dbcp数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: nian0209 datasource: url: jdbc:mysql://localhost:3306/test hikari: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: nian0209
|
内置持久化解决方案-JdbcTemplate
在不使用MyBatis/MyBatis-Plus的时候使用内置的JdbcTemlpate
1 2 3 4 5 6 7 8 9 10 11 12
| spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: nian0209 jdbc: template: fetch-size: -1 max-rows: 500 query-timeout: -1
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @SpringBootTest public class JdbcTemplateTest {
@Autowired private JdbcTemplate jdbcTemplate; @Test void testJdbc(){ String sql = "select * from tbl_book where id =1"; List<Book> qurey = jdbcTemplate.query(sql, new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs,int rowNum)throws SQLException{ Book temp = new Book(); temp.setId(rs.getInt("id")); temp.setName(rs.getString("name")); temp.setType(rs.getString("Type")); temp.setDescription(rs.getString("description")); return temp; } }); System.out.println(qurey); } @Test void save(){ String sql = "Insert into tbl_book values(null,'1','2''3')"; jdbcTemplate.update(sql); } }
|
内嵌数据库
- SpringBoot提供了三种内嵌数据库共开发者选择,提高开发测试效率
三种数据库均是Java语言编写,可以在springboot环境中运行
NoSQL解决方案
Redis
- Redis是一款key-values存储结构的内存级NoSQL数据库
Redis基本安装(windows)
Redis基本操作
第一次启动redis
- 首先文件夹下cmd窗口运行 redis-server.exe redis.windows.conf 运行服务器加指定配置
- 第一次运行会出现bug 运行出错
- 所以得再另开一个cmd窗口 运行客户程序redis-cli.exe,输入shutdown 再exit退出即可
- 再重新运行 redis-server.exe redis.windows.conf
- 服务器启动
- 清除数据
SpirngBoot整合Redis
1.导入Redis坐标
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
|
2.在springboot配置文件中加入配置(可省略)
1 2 3 4
| spring: redis: host: localhost port: 6379
|
3.注入redisTemplate对象 操作接口对象操作
Redis 两种注入方法
客户端:RedisTemplate以对象作为key和value,内部对数据进行序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @SpringBootTest class Springboot12RedisApplicationTests {
@Autowired private RedisTemplate redisTemplate;
@Test void set() { ValueOperations ops = redisTemplate.opsForValue(); HashOperations oph = redisTemplate.opsForHash(); ops.set("age",41); oph.put("info","a","aa"); }
@Test void get(){ ValueOperations ops = redisTemplate.opsForValue(); HashOperations oph = redisTemplate.opsForHash(); oph.get("info","a"); Object age = ops.get("age"); System.out.println(age); System.out.println(oph); }
}
|
客户端StringRedisTemplate(常用)以字符串作为key和value,与Redis客户端操作等效
1 2 3 4 5 6 7 8 9 10 11 12
| @Test void sets(@Autowired StringRedisTemplate stringRedisTemplate){ ValueOperations ops = stringRedisTemplate.opsForValue(); ops.set("testKey","TestValue"); }
@Test void gets(@Autowired StringRedisTemplate stringRedisTemplate){ ValueOperations<String, String> ops = stringRedisTemplate.opsForValue(); String testKey = ops.get("testKey"); System.out.println(testKey); }
|
jedis客户端
1.导入jedis坐标
1 2 3 4
| <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
|
2.设置client-type
1 2 3 4 5
| spring: redis: host: localhost port: 6379 client-type: jedis
|
客户端:lettcus(默认)与jedis区别
jedis连接Redis服务器是直连模式,当多线程模式下使用jedis会存在线程安全问题,解决方案可以通过配置连接池使每个连接专 用,这样整体性能就大受影响。
lettcus基于Netty框架进行与Redis服务器连接,底层设计中采用StatefulRedisConnection。 StatefulRedisConnection自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。当然lettcus也支持多连接实例一起工作
MangoDB
支持结构化的数据存储并且访问速度很快
Mongodb基本安装
Mongodb基本操作
超级详细MongoDB 的基本操作(理论+举例)_11.28.的博客-CSDN博客
SpirngBoot整合MongDB
1.导入对应坐标
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
|
2.配置mongodb数据库地址
1 2 3 4
| spring: data: mongodb: uri: mongodb://localhost/user
|
3.使用MongoTemplate接口客户端读写测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @SpringBootTest class Springboot13MongdbApplicationTests {
@Autowired private MongoTemplate mongoTemplate;
@Test void contextLoads() { Book book = new Book(); book.setId(1); book.setName("springboot"); book.setType("math"); book.setDescription("good book"); mongoTemplate.save(book); } @Test void find(){ List<Book> all = mongoTemplate.findAll(Book.class); System.out.println(all); }
}
|
Elasticesearch(ES)
- Elasticesearch是一个分布式全文搜索引擎
索引操作
添加ik分词器
将ik分词器解衣后存放在es目录下的plugins里
创建索引并指定规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| { "mappings":{ "properties":{ "id":{ "type":"keyword" }, "name":{ "type":"text", "analyzer":"ik_max_word", "copy_to":"all" }, "type":{ "type":"keyword" }, "decription":{ "type":"text", "analyzer":"ik_max_word", "copy_to":"all" }, "all":{ "type":"text", "analyzer":"ik_max_word" } } } }
|
创建文档
PUT http://localhost:9200/books/_doc
查询文档
删除文档
修改文档
springboot整合ES
- 因为视频中的方法以被springboot弃用,所以使用新的技术 在高版本的springboot中已经整合了High level client 可以使用注入bean来返回客户端
Spring Boot整合Elasticsearch,最新最全教程_Cloud-Future的博客-CSDN博客_springboot整合elasticsearch
SpringBoot 整合 high-level-client_张凯生的博客-CSDN博客
整合第三方技术
缓存
缓存是一种介于数据永久存储介质与属于应用之间的数据临时存储介质
使用缓存可以有效的减少低速数据读取过程的次数(例如:磁盘IO),提高系统性能
缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提高临时的数据存储空间
模拟缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private HashMap<Integer,Book> cache = new HashMap<>();
@Override public Book getById(Integer id) { Book book = cache.get(id); if (book==null){ Book queryBook = bookDao.selectById(id); cache.put(id,queryBook); return queryBook; } return cache.get(id); }
|
Springboot提高了缓存技术,方便缓存使用
Spring cache 注解详解_子沐月的博客-CSDN博客
史上最全的Spring Boot Cache使用与整合_我俗人的博客-CSDN博客_springboot 使用cache
启用缓存
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
|
- 在springboot启动程序入口设置启用缓存 注解 @EnableCaching
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication
@EnableCaching public class Springboot15CacheApplication {
public static void main(String[] args) { SpringApplication.run(Springboot15CacheApplication.class, args); }
}
|
在需要使用缓存的地方设置开启缓存功能 @Cacheable
在方法执行前spring先查看缓存中有没有数据,如果有数据,则直接返回数据,如果没有数据,调用方法并将方法返回值放入缓存
基于内存开启的缓存,当服务器停掉之后,缓存的数据也没有了
1 2 3 4 5 6
| @Override
@Cacheable(value = "cacheSpace",key ="#id") public Book getById(Integer id) { return bookDao.selectById(id); }
|
- @CachePut 将方法的返回值放入缓存中
- @CacheEvict 将一条或者多条数据从缓存中删除
Springboot 提供的缓存技术除了默认的缓存方案,还可以对其他缓存技术进行整合,统一接口,方便缓存的开发与管理
- Generic
- JCache
- Ehcache
- Hazelacst
- infinispan
- Coushbase
- Redis
- Caffenine
- Simple(默认)
- memcached(市面上,常用的缓存技术)
缓存使用案例—手机验证码
需求
- 输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)
- 输入手机号和验证码验证结果
需求分析
- 提供controller,传入手机号,业务层通过手机号计算出独有的六位验证码数据,存入缓存后返回此数据
- 提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入的验证码进行比对
springboot整合ehcache缓存
导入坐标依赖
- 注意springboot的版本稳定为2.5.x
- 添加ehcache配置文件
- 在springboot的配置文件中配置ehcache
1 2 3 4
| <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="D:\ehcache" />
<defaultCache eternal="false" diskPersistent="false" maxElementsInMemory="1000" overflowToDisk="false" timeToIdleSeconds="60" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU" /> <cache name="smsCode" eternal="false" diskPersistent="false" maxElementsInMemory="1000" overflowToDisk="false" timeToIdleSeconds="10" timeToLiveSeconds="10" memoryStoreEvictionPolicy="LRU" />
</ehcache>
|
1 2 3 4 5 6
| spring: cache: type: ehcache ehcache: config: classpath:ehcache.xml
|
转换缓存方案为Redis
springboot中切换缓存方案为Redis
- 导入依赖
- 在springboot的配置文件中配置Redis相关内容
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| spring: redis: port: 6379 host: localhost cache: type: redis redis: cache-null-values: false use-key-prefix: false key-prefix: aa time-to-live: 10s
|
memchaed 需要重新配置 window下安装有问题
memcached 安装及集成(转) - Jsonring - 博客园 (cnblogs.com)
缓存供应商变更jetcache
加入坐标依赖
1 2 3 4 5
| <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.6.2</version> </dependency>
|
本地缓存也远程缓存共存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| jetcache: local: default: type: linkedhashmap keyConvertor: fastjson remote: default: type: redis host: localhost port: 6379 poolConfig: maxTotal: 50 sms: type: redis host: localhost port: 6379 poolConfig: maxTotal: 50
|
开启使用jetCache 注解:**@EnableCreateCacheAnnotation**
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication
@EnableCreateCacheAnnotation public class Springboot16JetcacheApplication {
public static void main(String[] args) { SpringApplication.run(Springboot16JetcacheApplication.class, args); }
}
|
声明缓存对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Service public class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils;
@CreateCache(name="jetCache",expire = 3600,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL) private Cache<String,String> jetCache;
@Override public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); jetCache.put(tele,code); return code; }
@Override public boolean checkCode(SMSCode smsCode) {
String code = jetCache.get(smsCode.getTele()); return smsCode.getCode().equals(code); } }
|
启用方法注解
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootApplication
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "ithima") public class Springboot16JetcacheApplication {
public static void main(String[] args) { SpringApplication.run(Springboot16JetcacheApplication.class, args); }
}
|
基本操作
- 缓存对象必须保障可序列话
- 设置编码
- 设置增删查改的缓存操作
1 2 3 4 5 6 7 8 9
| import lombok.Data; import java.io.Serializable; @Data public class Book implements Serializable { private Integer id; private String type; private String name; private String description; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| jetcache: statIntervaMinutes: 10 local: default: type: linkedhashmap keyConvertor: fastjson remote: default: type: redis host: localhost port: 6379 keyConvertor: fastjson valueEncode: java valueDecode: java poolConfig: maxTotal: 50
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Service public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {
@Autowired private BookDao bookDao; @Override @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE) @CacheRefresh(refresh = 10,timeUnit = TimeUnit.SECONDS) public Book getById(Integer id) { return bookDao.selectById(id); } @CacheUpdate(name="book_",key="#book.id",value="#book") @Override public boolean update(Book book) { return bookDao.updateById(book)>1; } @CacheInvalidate(name = "book_",key="#id") @Override public boolean delete(Integer id) { return bookDao.deleteById(id)>1; } }
|
缓存供应商变更:j2cache
- j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身并不提供缓存功能
- 基于ebcache+redis进行整合
添加坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-core</artifactId> <version>2.8.5-release</version> </dependency>
<dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-spring-boot2-starter</artifactId> <version>2.8.0-release</version> </dependency>
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
|
在springboot配置文件中设置j2cache的配置文件
1 2
| j2cache: config-location: j2cache.properties
|
配置一级缓存和二级缓存以及一级缓存数据到二级缓存的发送方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
j2cache.L1.provider_class = ehcache ehcache.configXml = ehcache.xml
j2cache-l2-open = false
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider j2cache.L2.config_section = redis redis.hosts = localhost:6379
j2cache.broadcast =net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
redis.mode = single
|
设置缓存对象,操作数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Service public class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils;
@Autowired private CacheChannel cacheChannel;
@Override public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); cacheChannel.set("sms",tele,code); return code; }
@Override public boolean checkCode(SMSCode smsCode) {
String code = cacheChannel.get("sms",smsCode.getTele()).asString(); return smsCode.getCode().equals(code); } }
|
任务
定时任务是企业级应用中的常见操作
市面上流行的定时任务技术
Springboot 整合Quartz
- 相关概念
- 工作(Job):用于定义具体执行的工作
- 工作明细(JobDetail):用于描述定时工作相关的信息
- 触发器(Trigger):用于描述工作的规则,通常使用cron表达定义调度规则
- 调度器(Scheduler):描述工作明细与触发器的对应关系
导入对应坐标
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
|
定义具体要执行的任务
1 2 3 4 5 6 7 8
| public class MyQuartz extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("Quartz job is running...."); } }
|
定义工作明细与触发器,并绑定对应关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class QuartzConfig {
@Bean public JobDetail printJobDetail(){ return JobBuilder.newJob(MyQuartz.class).storeDurably().build(); }
@Bean public Trigger printTrigger(){ ScheduleBuilder<? extends Trigger> schedBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?"); return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(schedBuilder).build(); }
}
|
spring task
设置开启定时任务功能开关
- 在springboot启动类入口使用注解 @EnableScheduling
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication
@EnableScheduling public class springboot18TaskApplication {
public static void main(String[] args) { SpringApplication.run(springboot18TaskApplication.class, args); }
}
|
设置定时执行的任务
1 2 3 4 5 6 7 8 9
| @Configuration public class MyBean {
@Scheduled(cron = "0/3 * * * * ?") public void print(){ System.out.println(Thread.currentThread().getName() + "spring task is running....."); } }
|
1 2 3 4
| spring: task: execution: thread-name-prefix: spring_task_
|
springboot 整合JavaMail
发送简单邮件
- SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
- POP3(Post Office Protocol -Version3):用于接收电子邮件的标准协议
- IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
导入对应Javamail坐标
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
|
配置smtp协议POP3协议
1 2 3 4 5 6
| spring: mail: host: smtp.qq.com username: 2976908***@qq.com password: xxuchqhavybxde**
|
设置邮件客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Service public class SendMailServiceImpl implements SendMailService {
private String from = "2976908***@qq.com"; private String to = "129577****@qq.com"; private String subject = "测试邮件"; private String context = "测试邮件正文内容";
@Autowired private JavaMailSender javaMailSender;
@Override public void SendMail() {
SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(from); message.setTo(to); message.setSubject(subject); message.setText(context); javaMailSender.send(message); } }
|
发送复杂邮件
- 使用MimeMessageHelper对象
- 发送html 需要开启 helper.setText(context,true);
- 发送附件MimeMessageHelper(message,true);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Service public class SendMailServiceImpl implements SendMailService {
private String from = "2976908894@qq.com"; private String to = "1295773125@qq.com"; private String subject = "测试邮件"; private String context = "<a href='https://lulyqqqq.github.io/'>博客</a>";
@Autowired private JavaMailSender javaMailSender;
@Override public void SendMail() { try { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message,true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(context,true); File f1 = new File("src/main/resources/111.jpg"); helper.addAttachment("图片.jpg",f1); javaMailSender.send(message); } catch (MessagingException e) { e.printStackTrace(); } } }
|
消息
- 同步消息
- 异步消息
- 企业级应用中广泛使用的三种异步消息传递技术
- JMS(Java message Service):一个规范,等同于JDBC规范,提供了与消息服务相关的API接口 规范消息相关的API
- JSM消息模型
- peer-2-peer:点对点模型,消息发送到一个队列中,队列保存消息。队列的消息只能被一个消费者消费,或超时
- publish-subscribe:发布订阅模型,消息可以被多个消费者消费,生产者和消费者完全独立,不需要感知对方的存在
- AMQP(advanced message queuing protocol):一种协议(高级消息协议,也是消息代理规范)规范了网络交换的数据格式,兼容JMS 规范消息传递的格式
- 优点:兼容跨平台性,服务器供应商,生产者,消费者可以使用不同的语言实现
- AMQP消息模型
- direct exchange
- fanout exchange
- topic exchange
- headers exchange
- system exchange
- AMQP消息种类: byte[] 字节数组类型
- AMQP实现:RabbitMQ,StormMQ,RocketMQ
- MQIT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一
- Kafka:一种高吞吐量的分布式发布订阅消息系统,提供实时消息功能
消息队列
- RabbitMQ
- StormMQ
- RocketMQ
- Kafka
Springboot 整合ActiveMQ
加入activemq坐标
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency>
|
配置activemq
1 2 3 4 5 6 7 8 9 10 11 12
| server: port: 8080 spring: activemq: broker-url: tcp://localhost:61616 jms: pub-sub-domain: true template: default-destination: itheima
|
使用JmsMessagingTemplate对象进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Service public class MessageServiceActivemqImpl implements MessageService {
@Autowired private JmsMessagingTemplate jmsMessagingTemplate; @Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列:id = "+id); jmsMessagingTemplate.convertAndSend("order.queue.id",id);
}
@Override public String doMessage() { String id = jmsMessagingTemplate.receiveAndConvert("order.queue.id",String.class); System.out.println("已完成短信发送业务id = "+id); return id; } }
|
定义listener 自动处理消息队列
- @JmsListener(destination = “order.queue.id”) @JmsListener注解指定自动解决哪里位置的消息
- @SendTo 自动处理完后, 将方法的返回值 发送到指定的其他队列中
1 2 3 4 5 6 7 8 9 10 11
| @Component public class MessageListener {
@JmsListener(destination = "order.queue.id") @SendTo("order.queue.other.id") public String receive(String id){ System.out.println("已完成短信发送业务id = "+id); return "new:" + id; } }
|
RabbitMQ
- RabbitMQ基于Erlang语编写,需要安装Erlang
- Erlang
- 环境变量配置
安装RabbitMQ
启动RabbitMQ
开启RabbitMQ可视化界面
- 使用插件形式
- 打开插件:rabbitmq-plugins.bat enable rabbitmq_management
- 使用localhost:15672访问可视化界面
- 登录账号和密码:guest
springboot整合Rabbitmq 直连交换机模式
配置顺序基本相同
导入坐标
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
|
配置rabbitmq
1 2 3 4
| spring: rabbitmq: host: localhost port: 5672
|
定义消息队列 交换机 绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Configuration public class RabbitConfigDirect {
@Bean public Queue directQueue() { return new Queue("direct_queue"); }
@Bean public DirectExchange directExchange(){ return new DirectExchange("directExchange"); }
@Bean public Binding bindingDirect(){ return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct"); } }
|
生产与消费消息(direct)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Service public class MessageRabbitmqDirectImpl implements MessageService {
@Autowired private AmqpTemplate amqpTemplate;
@Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列(rabbitmq):id = "+id); amqpTemplate.convertAndSend("directExchange","direct",id); }
@Override public String doMessage() { return null; } }
|
使用消息监听器对消息队列进行监听
- 当有两个或多个监听器监听一个消息队列时,两个监听器是轮换监听
- 安装字符串排序监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Component public class MessageListener {
@RabbitListener(queues = "direct_queue") public void receive(String id){ System.out.println("已完成短信发送业务(rabbitmq direct)id = "+id); } }
|
springboot整合Rabbitmq 主题交换机模式
定义消息队列
可以采用模糊匹配的方式匹配消息队列进行监听器处理
生产与消费消息(topic)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class MessageRabbitmqTopicImpl implements MessageService {
@Autowired private AmqpTemplate amqpTemplate; @Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列(rabbitmq topic):id = "+id); amqpTemplate.convertAndSend("topicExchange","topic",id); }
@Override public String doMessage() { return null; } }
|
消息监听器监听消息队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Component public class MessageListener {
@RabbitListener(queues = "topic_queue") public void receive(String id){ System.out.println("已完成短信发送业务(rabbitmq topic)id = "+id); } @RabbitListener(queues = "topic_queue2") public void receive1(String id){ System.out.println("已完成短信发送业务(rabbitmq topic)id = "+id); } }
|
重点
当发消息队列的时候(routingKey)起名遵循了两个消息队列的(routingKey)的模糊条件时,设置了监听器的消息队列匹配成功之后,均执行监听器操作
1 2 3 4 5
| @Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列(rabbitmq topic):id = "+id); amqpTemplate.convertAndSend("topicExchange","topic.id.orders",id); }
|
1 2 3 4 5 6 7 8 9
| @Bean public Binding bindingTopic(){ return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.orders"); }
@Bean public Binding bindingTopic2(){ return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.id.*"); }
|
RocketMQ
安装:解压缩
- 默认服务端口:9876
- 环境变量配置:ROCKETMQ_HOME
- PATH
- NAMESRV_ADDR(建议):127.0.0.1:9876
启动命名服务器和broker
- 配置环境变量完成后直接双击文件
- 命名服务器:mqnamesrv.cmd
- broker服务器:mqbroker.cmd
- 启动服务器
测试服务器
recketmq自带测试程序 需要使用测试的话,在包路径下找对应的测试类
- 使用tools org.apache.rocketmq.example.quickstart.Producer (产生若干条消息) 如果服务器有问题,则无法产生
- 使用tools org.apache.rocketmq.example.quickstart.Consumer 消费消息
springboot整合rocketmq
需要启动rocketmq服务器
导入坐标
1 2 3 4 5 6
| <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.1</version> </dependency>
|
配置rocketmq
1 2 3 4 5
| rocketmq: name-server: localhost:9876 producer: group: group_rocketmq
|
设置发送消息对象 RocketMQTemplate
生产消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Service public class MessageServiceRocketmqImpl implements MessageService {
@Autowired private RocketMQTemplate rocketMQTemplate;
@Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列(rocketmq):id = "+id);
SendCallback callback = new SendCallback() { @Override public void onSuccess(SendResult sendResult) { System.out.println("消息发送成功!"); }
@Override public void onException(Throwable throwable) { System.out.println("消息发送失败!"); } }; rocketMQTemplate.asyncSend("order_id",id,callback); } }
|
配置监听器 消费消息
1 2 3 4 5 6 7 8 9 10
| @Component
@RocketMQMessageListener(topic = "order_id",consumerGroup = "group_rocketmq") public class MessageListener implements RocketMQListener<String> { @Override public void onMessage(String id) { System.out.println("已完成短信发送业务(rocketmq) = "+id); } }
|
Kafka
安装解压缩:windows建议使用2.x之类的版本 3.x的版本存在bug
启动Kafka服务器
测试kafka服务器
- 需要自己创建topic来测试
- 创建topic: kafka-topics.bat –zookeeper 127.0.0.1:2181 –create –replication-factor 1 –partitions 1 –topic itheima(最后一个参数设置topic名称)
- 查看topic
- kafka-topics.bat –zookeeper 127.0.0.1:2181 –list
- 测试
- 生产者功能测试
- kafka-console-producer.bat –broker-list localhost:9092 –topic itheima
- 消费者功能测试
- kafka-console-consumer.bat –bootstrap-server localhost:9092 –topic itheima –from-beginning
- 删除topic
- kafka-topics.bat –delete –zookeeper localhost:2181 –topic itheima
springboot整合kafka
导入坐标
1 2 3 4 5
| <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>
|
配置地址信息
1 2 3 4 5 6 7
| spring: kafka: bootstrap-servers: localhost:9092 consumer: group-id: order
|
生产消息
1 2 3 4 5 6 7 8 9 10 11 12
| @Service public class MessageServiceKafkaImpl implements MessageService {
@Autowired private KafkaTemplate<String,String> kafkaTemplate;
@Override public void SendMessage(String id) { System.out.println("待发送的短信已纳入处理队列(kafka):id = "+id); kafkaTemplate.send("itheima",id); } }
|
监听器消费消息
1 2 3 4 5 6 7 8
| @Component public class MessageListener {
@KafkaListener("itheima") public void onMessage(ConsumerRecord<String,String> record){ System.out.println("已完成短信发送业务id(kafka) = " + record.value); } }
|
监控
监控的意义
- 监控服务状态是否宕机
- 监控服务运行指标(内存,虚拟机,线程,请求等)
- 监控日志
- 管理服务(服务下线)
监控的实施方式
- 显示监控信息的服务器:用于获得服务信息,并显示对应的信息
- 运行的服务:启动时主动上报,告知监控服务器自己需要受到监控
可视化监控平台
- Spring Boot Admin,开源社区项目,用于管理和监控springboot应用程序。客户端注册到服务端,通过HTTP请求方式,服务端定期从客户端获取对应的信息,并提供ui界面展示对应信息
- 平台版本必须和springboot版本相一致
做成web形式,使用可视化界面查看 配置个web程序端口
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication
@EnableAdminServer public class Springboot21AdminServerApplication {
public static void main(String[] args) { SpringApplication.run(Springboot21AdminServerApplication.class, args); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <dependencies>
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.5.4</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <dependencies>
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.5.4</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring: boot: admin: client: url: http://localhost:8080 management: endpoint: health: show-details: always endpoints: web: exposure: include: "*"
|
监控原理 —–Actuator
- Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
- 端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
- 访问当前应用所有端点信息:/actuator 访问端点详细信息:/actuator/端点名称
端点暴露
./springboot开发实用篇/9
Info端点加载数据
1 2 3 4 5 6
| info: appName: @02@ version: @9.9@ company: Good Company author: XD
|
1 2 3 4 5 6 7 8 9
| @Component public class InfoConfig implements InfoContributor { @Override public void contribute(Info.Builder builder) { builder.withDetail("runTime",System.currentTimeMillis());
} }
|
health端点数据自定义
继承实现数据自定义
状态的设置,如果状态不是为up则会影响最终实例的状态
1 2 3 4 5 6 7 8 9 10 11 12
| @Component public class HealthConfig extends AbstractHealthIndicator { @Override protected void doHealthCheck(Health.Builder builder) throws Exception { builder.withDetail("runTime",System.currentTimeMillis()); Map infoMap = new HashMap(); infoMap.put("buildTime","2006"); builder.withDetails(infoMap); builder.up(); } }
|
性能指标自定义
执行了多少次结果会在可视化界面上展示
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private Counter counter;
public BookServiceImpl(MeterRegistry meterRegistry){
counter = meterRegistry.counter("用户操作付费次数:") }
@Override public boolean delete(Integer id) { counter.increment(); return bookDao.deleteById(id) > 0; }
|
自定义监控指标
自定义端点
通过http://localhost:8080/actuator/pay 访问
在可视化界面 Spring Boot Admin上可以查看
在使用了这个链接之后页面显示return回来的值
1 2 3 4 5 6 7 8 9 10 11 12
| @Component @Endpoint(id = "pay",enableByDefault = true) public class payEndPoint { @ReadOperation public Object getPay(){ Map payMap = new HashMap(); payMap.put("lever 1",100); payMap.put("lever 2",50); payMap.put("lever 3",10); return payMap; } }
|
总结:部分技术尚未掌握,大抵只是通过springboot整合其他技术,了解其他技术大概是怎么样使用,对各个模块有了更深层次的印象;可以加快速度学习,但是笔记总要回顾,需要不定时会看,巩固基础
开发实用篇告一段落