day01 HttpServlet中的service方法 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long errMsg; if (method.equals("GET" )) { errMsg = this .getLastModified(req); if (errMsg == -1L ) { this .doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since" ); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L ; } if (ifModifiedSince < errMsg / 1000L * 1000L ) { this .maybeSetLastModified(resp, errMsg); this .doGet(req, resp); } else { resp.setStatus(304 ); } } } else if (method.equals("HEAD" )) { errMsg = this .getLastModified(req); this .maybeSetLastModified(resp, errMsg); this .doHead(req, resp); } else if (method.equals("POST" )) { this .doPost(req, resp); } else if (method.equals("PUT" )) { this .doPut(req, resp); } else if (method.equals("DELETE" )) { this .doDelete(req, resp); } else if (method.equals("OPTIONS" )) { this .doOptions(req, resp); } else if (method.equals("TRACE" )) { this .doTrace(req, resp); } else { String errMsg1 = lStrings.getString("http.method_not_implemented" ); Object[] errArgs = new Object []{method}; errMsg1 = MessageFormat.format(errMsg1, errArgs); resp.sendError(501 , errMsg1); } } public void service (ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch (ClassCastException var6) { throw new ServletException ("non-HTTP request or response" ); } this .service(request, response); }
tips : HEAD: 与GET类似,但服务器在响应中只返回HTTP头部。 Options: 允许客户端查看服务器支持的HTTP方法。 Trace: 回显服务器接收到的请求。
java后端中的响应式编程 响应式编程的基石-响应流
响应流必须无阻塞
响应流必须是一个数据流
必须可以异步执行
处理背压(生产者可以感受到消费者反馈的消费压力,并根据压力进行动态调整生产速率)
Publiher 由于响应流的特点,我们不能再返回一个简单的POJO对象来表示结果了。必须返回一个类似Java中的Future的概念,在有结果可用时通知消费者进行消费响应。
Reactive Stream规范中这种被定义为Publisher<T>
,Publisher<T>
是一个可以提供0-N个序列元素的提供者,并根据其订阅者Subscriber<? super T>
的需求推送元素。一个Publisher<T>
可以支持多个订阅者,并可以根据订阅者的逻辑进行推送序列元素。下面这个Excel计算就能说明一些Publisher<T>
的特点。
A1-A9就可以看做Publisher<T>
及其提供的元素序列。A10-A13
分别是求和函数SUM(A1:A9)
、平均函数AVERAGE(A1:A9)
、最大值函数MAX(A1:A9)
、最小值函数MIN(A1:A9)
,可以看作订阅者Subscriber
。
网关的GlobalExcetionHandler
tips : ServerWebExchange是Spring WebFlux框架中用于表示HTTP请求和响应的接口。ServerWebExchange接口提供了一组方法来访问HTTP请求和响应的相关信息,包括请求头、请求体、响应头和响应体等。它还提供了一些方法来操作请求和响应,例如设置或获取请求头、请求体、响应头和响应体等。
OKHttpClient OKHttp是一个高效的HTTP客户端,具有以下默认特性:
支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
连接池减少请求延时
透明的GZIP压缩减少响应数据的大小
缓存响应内容,避免一些完全重复的请求
配置的例子:
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 @Configuration public class RestTemplateConfig { @Resource private HttpConfig httpConfig; @ConditionalOnMissingBean(RestTemplate.class) @Bean public RestTemplate restTemplate () { return new RestTemplate (getClientHttpRequestFactory()); } private ClientHttpRequestFactory getClientHttpRequestFactory () { OkHttpClient okHttpClient = new OkHttpClient .Builder() .connectTimeout(httpConfig.getConnTimeout(), TimeUnit.SECONDS) .writeTimeout(httpConfig.getWriteTimeout(), TimeUnit.SECONDS) .readTimeout(httpConfig.getReadTimeout(), TimeUnit.SECONDS) .sslSocketFactory(SSLSocketConfig.getSSLSocketFactory(), SSLSocketConfig.getX509TrustManager()) .hostnameVerifier(SSLSocketConfig.getHostnameVerifier()) .build(); return new OkHttp3ClientHttpRequestFactory (okHttpClient); } }
day02 MySQL相关查询语句 1 2 3 4 desc table_name; ALTER TABLE your_table_nameMODIFY COLUMN id INT NOT NULL FIRST ;
实体类映射工具MapStruct
tips : MapStruct的注解也是Mapper,此时导入的包是org.mapstruct.Mapper
,而非Mybatis的包。
在后端开发中一般会将实体类经过转换后(DO->RespVO(DTO))才返回给前端,前端提交过来的对象也需要经过转换Entity实体才做存储;通常使用的BeanUtils.copyProperties方法也比较粗暴,不仅效率低下(使用反射)而且仅映射相同名的属性,多数情况下还需要手动编写对应的转换方法实现。
插件MapStruct以接口方法结合注解优雅实现对象转换,MapStruct生成器生成代码以更贴近原生的Setter、Getter方法处理属性映射更为高效。
简单用例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Mapper public interface UserConvert { UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); @Mapping(source = "name", target = "userName") @Mapping(target = "username", ignore = true) @Mapping(target = "createdAt", expression = "java(java.time.LocalDateTime.now())") UserVO toVO (User entity) ; } @Test public void contextLoads () { User user = new User (0 , "Tester" , 1 , "上海市徐汇区" ); UserVO userVO = UserConvert.INSTANCE.toVO(user); }
linux开机自启动jar包的流程 1 2 3 4 5 6 crontab -e @reboot sleep 100 && /home/dmx/dmxStartup.sh
day03 Redis相关知识 引入Lua脚本的目的
保证原子性 在 Redis 中,所有的 Lua 脚本是作为一个原子操作执行的。换句话说,在脚本执行期间,Redis 不会处理其他客户端的命令。
减少网络延迟 Redis 是一个基于客户端-服务器的系统,每个命令都需要客户端与服务器之间进行一次通信。如果一个复杂的操作需要多个命令,频繁的网络通信会导致延迟。通过 Lua 脚本,可以将多个命令合并到一个脚本中发送给服务器,减少网络往返次数,从而提高性能。
执行复杂逻辑和操作灵活性 Lua 脚本提供了一种在 Redis 服务器端编写复杂逻辑的方法,允许在执行 Redis 命令时加入条件判断、循环等控制结构。
在SpringBoot项目中使用Lua脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static { UNLOCK_SCRIPT = new DefaultRedisScript <>(); UNLOCK_SCRIPT.setLocation(new ClassPathResource ("unlock.lua" )); UNLOCK_SCRIPT.setResultType(Long.class); } public void unLock () { stringRedisTemplate.execute(UNLOCK_SCRIPT , Collections.singletonList(KET_PREFIX+name) ,ID_PREFIX+Thread.currentThread().getId()); }
Redis的key分类形式
day04
tips : @TableId注解默认是自增的
和下列配置有关:
1 2 3 4 5 6 7 8 9 10 11 12 mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: NONE logic-delete-value: 1 logic-not-delete-value: 0 type-aliases-package: ${travel.info.base-package}.dal.dataobject
day05
tips: MybatisX快速生成接口、实体类以及xml的操作
在后端项目中,先建库还是先建表
如果你的项目规模较大、数据库设计较为复杂,可以先设计 MySQL 数据表,确保数据结构清晰、稳定。
如果你正在开发一个相对灵活或敏捷的项目,且使用了 ORM 工具,则可以先创建实体类,通过代码生成数据库表。 对于很多现代 Java 后端项目,开发者往往选择先创建实体类,然后通过 ORM 工具自动生成数据库表的方式,灵活应对业务变化,并且可以减少维护工作。
根据实体类自动建表的方案(JPA) 在连接数据库的时候会自动创建,(其实感觉写完实体类,让chatgpt帮你写下sql建表也行。)
1.导入JPA依赖与配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.16</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jpa</artifactId > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.1</version > </dependency >
JPA配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!--配置数据库连接与jpa的yml配置--> spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 password: root username: root jpa: database: mysql show-sql: true hibernate: ddl-auto: update dialect: org.hibernate.dialect.MySQL5InnoDBDialect
2.创建实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Data @Entity @Table(name = "demo_user") @TableName("demo_user") public class User { @TableId(type = IdType.AUTO) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "userid",columnDefinition = "int comment '主键ID'") private Integer userId; @Column(name = "name") private String name; @Column(name = "age") private Integer age; @Column(name = "email") private String email; @Column(name = "phone") private String phone; }
3. 注解属性
建表时候的要求
表名:见名知意,如user_name;
大小写:尽量都使用小写字母;
分隔符:尽量不使用空格和特殊字符,数据库名、表名和列名之间用下划线分隔;
字段名称:见名知意,尽量避免用不容易理解的缩写或拼音,统一字段,比如状态都用status
普通索引和联合索引用ix_前缀,唯一索引用ux_前缀。
字段类型:根据业务尽量选占用存储小的字段类型
字段长度:varchar和char类型长度为字符长度,其余类型代表的都是字节长度。
字段个数:每张表不超过20个字段 ,复杂的拆成多张表。
NOT NULL:在定义字段时,尽量明确该字段是否Not Null。因为在innodb中,需要额外空间存储NULL值;其次NULL值可能会导致索引失效;最后NULL值只能用is null或者is not null判断,“==”判断永远返回False
外键:互联网系统中一般不设置外键。
时间字段:优先使用datetime字段 ,其保存时间范围更大。
索引:在经常查询的字段上建立索引,同时也考虑索引的数量和长度,避免影响性能,单表索引数量不超过5个
唯一索引:使用频率很高 ,一般在不怎么更新的字段上加唯一索引(比如部门、机构等),创建唯一索引时,相关字段不能出现NULL值 ,不然唯一索引会失效。
大字段:如用户评论等,不建议使用text、blob类型(定义VarChar类型比较合理),可以考虑拆表或者存储在其他地方。
唯一索引 唯一索引特点
唯一索引的值必须是唯一的,不允许重复。
唯一索引可以提高数据的访问速度,因为Mysql会对唯一索引进行优化。
唯一索引可以通过UNIQUE INDEX关键词在表中指定,也可以在创建表之后使用ALTER TABLE语句添加唯一索引。
唯一索引使用场景 唯一索引适合用于需要保证数据列唯一性的情况,例如用户表中的邮箱字段。唯一索引可以避免插入重复的数据,保证数据的一致性和准确性。