【Java】各类知识汇总(2023.05.06更新)

知识点

非入侵式框架

该框架对被调用者的源代码进行非入侵约束。

使用该框架的应用程序源代码基本不需要修改,也不依赖于该框架的API。

有哪些

  1. AOP(面向切面编程):通过配置的方式对业务逻辑的各个部分进行拦截,并在这些拦截点上添加增强处理。代表框架有Spring AOP等。

  2. Plugin插件机制:通过插件可以对系统进行扩展,而不需要修改源代码。典型代表是Eclipse的插件机制。

  3. 拦截器:通过拦截器可以在某个方法或请求被执行前后进行干预,扩展原有功能。如Spring MVC的拦截器等。

  4. 事件监听机制:当系统中发生某些事件时,触发注册在该事件上的监听器方法。该机制可以在事件发生后进行一些扩展处理。如Spring的事件监听机制等。

  5. 高级别API:框架提供高级别的API给最终用户使用,而内部使用基础或低级别的API进行实现。外部使用时也不需要关注内部实现,起到解耦作用。

  6. 中间件:中间件处于不同系统或层之间,通过扩展中间件可以影响多个系统。典型代表有消息队列、流量控制等中间件。

  7. 代理模式:通过代理对象对原对象进行功能扩展,而客户端调用代理对象的接口,无需关注原对象细节。

  8. 模板方法模式:通过钩子方法让子类有扩展点,扩展原有逻辑功能。

  9. 外观模式:框架内部有多个子系统,外观对象提供简单接口给外部,而内部实现调用不同子系统的复杂逻辑。

非入侵式框架主要利用面向切面、插件、代理、外观等设计模式,或事件、拦截器、高级API抽象等机制,对系统进行扩展与增强,而不破坏原有代码结构与逻辑。使得系统具有良好的可扩展性和维护性。

方式实现

  1. 代理模式:框架通过代理对象调用实际对象的方法,然后附加一些额外的操作,实现框架的功能。这不需要修改实际对象的源代码。
  2. 动态代理:框架在运行期动态生成代理对象,代理对象实现实际对象的接口,调用实际对象的方法,在前后附加额外操作。这也不需要实际对象的任何改变。
  3. AOP:框架使用AOP编织器,在编译期,加载期或运行期给实际对象织入额外的操作。实际对象的源代码不需要任何改变。

非入侵式框架的优点

  1. 不需要修改已有系统的源代码,易于集成和升级。
  2. 降低耦合度,框架与应用程序解耦。
  3. 更容易重用,可以应用于更多系统。

所以,非入侵式框架可以最大限度地维护系统的原有结构,便于系统的扩展与升级,这是它的重要优势。许多主流框架都采用非入侵式设计,以更好地满足用户的需求并提高使用率。

相比之下,入侵式框架需要应用程序的源代码与框架产生依赖,直接修改或调用框架API,这会增加程序的耦合度,限制框架的重用性,增加升级的难度。

主要特征是

  1. 不需要修改应用程序的源代码
  2. 不会产生代码级别的依赖
  3. 可以使用代理模式、动态代理或AOP实现
  4. 易于重用和升级,降低耦合度
  5. 可以最大限度保留系统原有结构

Spring Security安全框架

Spring Security 是 Spring 家族中第一个应用的框架,它提供全面且可定制的安全服务。

它可以用于保护基于Spring的企业应用程序。

执行流程

【Java】各类知识汇总(2023.05.06更新)_第1张图片

Spring Security 的主要功能

  1. 认证(Authentication):验证用户身份,确认用户是谁。
  2. 授权(Authorization):验证用户是否有权限执行某个操作。
  3. 会话管理(Session Management):管理用户会话,用于多次请求。
  4. 防跨站请求伪造(CSRF):防止恶意站点伪造请求。
  5. 运行时权限管理:根据用户请求动态决定是否授权。
  6. 安全异常管理:处理授权失败或会话超时等异常。
  7. 集成各种认证方式:HTTP Basic、HTTP Digest、Form 表单、LDAP、CAS 等。
  8. 记住我:记住认证后的用户身份,下次自动登录。

Spring Security 的主要组件

  1. SecurityContextHolder:用于存储认证信息的上下文对象,可以在任何地方访问。
  2. Authentication:代表一个认证请求或认证的用户
  3. UserDetails:描述用户信息的接口,包含用户名、密码、权限等信息。
  4. UserDetailsService:用来加载 UserDetails 的服务接口。
  5. GrantedAuthority:授权信息的抽象类,代表一个权限。
  6. AuthenticationManager:认证管理器,处理 Authentication 请求。
  7. AccessDecisionManager:访问决定管理器,决定是否授权。
  8. AuthenticationProvider:认证提供者,具体执行认证的接口。
  9. FilterSecurityInterceptor:安全拦截器,主要负责拦截用户请求并调用 AccessDecisionManager 来决定是否授权。
  10. ExceptionTranslationFilter:负责捕获 AccessDeniedException 和 AuthenticationException ,并将其翻译成对应的响应。

Redis

Redis 是一个高性能的键值数据库

是一个非关系型、开源、高性能、多功能和丰富特性的 NoSQL 数据库。

特性

  1. 非关系型:Redis 不支持表与表之间的关系,它的数据模式是 key-value 对。
  2. 开源:Redis 是完全开源免费的,遵循 BSD 协议。
  3. 高性能:Redis 能够提供非常高的读写性能,每秒可以处理超过10万次读写操作。
  4. 持久化:Redis 支持将内存中的数据持久化到磁盘中,重启的时候可以再将数据加载到内存中。
  5. 多功能:Redis 不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  6. 丰富的特性:Redis还支持数据的备份,持久化,内存管理,事务,秒杀等特性。

优点

  1. 性能高:读的速度是110000次/s,写的速度是81000次/s 。

  2. 丰富的数据类型:string,hash,list,set,sorted set,bitmaps,hyperloglogs。

  3. 持久化:将内存中的数据持久化到磁盘中,重启的时候可以再将数据加载到内存中。

使用场景

  • 缓存读取频繁的数据,如商品信息、文章内容等。

  • 缓存计算结果,如排行榜、好友推荐等。

  • 缓存用户登录信息,购物车信息等。

Redis 哨兵(Sentinel)

Redis 哨兵是Redis的高可用解决方案,具有如下功能:

  • 监控 redis 实例状态,主观下线失效实例。
  • 提供实例挂机时自动故障转移支持。
  • 回复故障转移后,自动将从节点设置为主节点的从节点。
  • 提供客户端查询功能,客户端可查询哨兵获得主从节点信息。

Redis 哨兵架构由若干个 Sentinel 实例和若干个 Redis 实例组成。其中一个 Redis 实例为主实例,其他实例为从实例,以主从复制方式工作。Sentinel 实例监听主从实例的运行状态,在主实例下线时,会在从实例中选举新的主实例,完成自动故障转移。

Redis 分片(Cluster)

Redis Cluster是Redis的分布式解决方案,具有如下功能:

  • 自动分片存储数据到不同的节点。
  • 节点自动发现和故障恢复
  • 支持多种节点角色:主节点、从节点、哨兵节点等。
  • 支持在线扩容节点。

Redis Cluster通过分配一定数量的槽(slot)来表示整个数据的范围,每个节点负责一定数量的槽。客户端将一个键转换为槽的编号,从而得到键所映射到的节点,实现分片效果。
通过在节点之间以主从关系或者分片映射的关系复制数据,从而实现高可用、可扩展的集群方案。

Redis雪崩,穿透,击穿

  1. 雪崩:大量的key对应的数据过期,导致redis一次性释放大量内存,造成短暂的server hang。

    解决方案:

    • 降低过期时间,分散过期时间
    • 使用惰性删除,过期键不立即删除,只是标记为过期,定期批量删除
    • 在高内存阈值时禁止删除操作
  2. 穿透:查询一个本不存在的key,导致每次请求都通过redis到达DB数据库,造成DB数据库负载过高过载。

    解决方案:

    • 针对查询为空的key也设置过期时间,避免 key一直不存在导致重复查询
    • 使用布隆过滤器提前判断 key 是否存在
    • 缓存 null 值,过期时间短
  3. 击穿:大量并发请求同一个key且该key缓存失效过期,大量请求直接到达数据库,造成数据库过载。

    解决方案:

    • 设置热点数据永不过期
    • 加互斥锁,同一时刻只允许一个线程访问
    • 使用加权随机算法,随机选择多个 candidate key,其中只有一个是真实的

提高数据库交互效率

使用 Redis 作为缓存数据库

可以将热点数据或大量读、少量写的数据存入 Redis,以减轻主数据库的压力。

Reads 从 Redis 读取,Writes 同步更新到 Redis 和主数据库。这种架构可以大大提高系统的性能和吞吐量。

使用 Redis 做分布式锁

分布式系统中,可以使用 Redis 的 SETNX(set if not exists) 命令实现分布式锁。这个可以有效的防止并发问题。

分布式锁执行流程

  1. 获取锁使用 SETNX 命令尝试获取锁。

    如果键 lock 不存在,则 SETNX 成功,返回 1 ,表示获取锁成功。如果键 lock 已存在,则返回 0 ,表示获取锁失败。

SETNX lock.redis 123
  1. 锁过期时间为了避免进程崩溃导致锁无法释放,可以给锁设置一个过期时间。如 1 秒:
SETNX lock.redis 123 EX 1 
  1. 释放锁使用 DEL 命令删除键 lock 来释放锁:
DEL lock.redis 
  1. 锁失效时自动释放

    因为我们为锁设置了过期时间,如果锁在过期时间内未被释放,它将自动过期并被 Redis 删除,相当于自动释放了锁

  2. 解决进程崩溃导致锁无法释放的问题

    我们可以设置一个信号量,在进程获取锁后,将信号量设置为 1。如果进程崩溃,信号量不会被清除,值仍为 1。其他进程在尝试获取锁时,首先检查信号量,如果值为 1,表示上一个持有锁的进程崩溃,此时调用 DEL 命令删除锁,然后再设置信号量为 0,获取锁。

    这个解决方案可以确保锁总是被正常释放,避免死锁。

    利用 Redis 的 SETNX 和 DEL 命令,我们可以很容易实现一个分布式锁,并解决掉分布式锁常见的死锁问题。这在分布式系统和微服务架构中是一个非常实用的技术

使用 Redis 队列消息

可以利用 Redis 的 LIST 类型实现一个消息队列。生产者使用 LPUSH/RPUSH 命令将消息推入队列,消费者使用 LPOP/RPOP 命令将消息从队列弹出。这种架构可以实现异步任务,提高系统效率。

发布订阅

Redis 发布订阅(pub/sub)功能可以实现服务端消息推送给客户端。

生产者作为发布者将消息推送到频道,消费者作为订阅者订阅相应的频道。

这种架构可以实现实时消息推送,满足一对多的通信需求。

Redis 事务

Redis 事务可以一次执行多个命令,这些命令要么全部成功,要么全部失败。这在并发环境下可以确保数据的一致性。事务可以确保批量操作数据的原子性。

分片集群

搭建Redis集群,实现分布式存储和负载均衡。

这样大量的数据和操作可以分配到不同的节点上,既提高了存储能力,也提高了并发处理能力。


Java Servlet技术

Servlet 是 Java EE 规范中定义的服务器端程序。Servlet 运行于服务器上,并由服务器调用。

Java Servlet执行流程(Gif)

Java Servlet执行流程(图文)

【Java】各类知识汇总(2023.05.06更新)_第2张图片

  1. 客户端发送请求给服务器。
  2. 服务器开始接受,先判断该请求的servlet实例是否存在,如果不存在先装载一个servlet类并创建实例。如果存在则直接调用该servlet的service方法,之后进行判断是调用doGet方法还是doPost方法。
  3. servlet创建实例后,调用init方法进行初始化。之后调用servce方法,判断是调用doGet方法还是doPost方法。
  4. 最后判断服务是否关闭,如果关闭则调用destroy方法。

Spring MVC

Spring MVC是Spring框架的一部分,是基于Java实现MVC的轻量级Web框架。

Spring MVC的主要组件如下:

  1. 前端控制器DispatcherServlet:接收请求,响应结果,请求转发。
  2. 处理器映射HandlerMapping:根据请求的URL确定使用哪个Controller处理请求。
  3. 处理器适配器HandlerAdapter:适配DispatcherServlet与具体的Controller。
  4. 视图解析器ViewResolver:根据逻辑视图名解析成真正的视图。
  5. 处理器或页面控制器Controller:处理请求和业务逻辑,返回逻辑视图名。
  6. 视图View:渲染输出结果。

一个简单的Spring MVC应用的流程如下:

  1. 客户端发送请求至DispatcherServlet。
  2. DispatcherServlet根据请求信息调用HandlerMapping得到处理请求的Controller。
  3. DispatcherServlet再根据HandlerAdapter调用实际的Controller。
  4. Controller执行逻辑并返回一个逻辑视图名给DispatcherServlet。
  5. DispatcherServlet根据该视图名由ViewResolver解析成真正的View。
  6. DispatcherServlet利用View渲染输出结果,响应给客户端。

相比于Struts2,Spring MVC的主要优点在于:

  1. 基于方法设计,更加简介;
  2. 高度可定制性,如支持自定义类型的转换器、格式化工具、验证器等;
  3. 容易与Spring的其他框架进行集成,如IOC容器、AOP等;
  4. 支持RESTful风格的URL请求;
  5. 强大的视图技术,例如JSP、Velocity、FreeMarker等;
  6. 简单灵活的过程。

所以,Spring MVC通过将MVC分层架构的不同职责解耦,使得每层都可以自行扩展,或被替换,十分灵活。它简单易学,同时功能也非常强大,是目前最受欢迎的MVC框架之一。

DispatcherServlet (SpringMVC的前端控制器)

【Java】各类知识汇总(2023.05.06更新)_第3张图片

Servlet 主要用于

  • 接收客户端请求

  • 生成动态内容

  • 与数据库交互

  • 维护会话状态

Servlet 生命周期

  1. 初始化:执行 init() 方法,只执行一次,用于加载资源
  2. 服务:执行 service() 方法,每次请求都会调用
  3. 销毁:执行 destroy() 方法,只执行一次,用于释放资源

Servlet 工作原理

  1. 客户端发送请求到服务器
  2. 服务器(TomCat)接收请求,并将其交给 Servlet 容器
  3. 容器找到请求 URL 对应的 Servlet,并创建其实例(如果不存在)
  4. 容器调用 Servlet 的 service() 方法,处理客户端请求
  5. 容器将 Servlet 的响应发送给客户端

Servlet 接口包含 5 个方法:

  • init():初始化,创建 Servlet 时调用
  • service():对客户端请求作出响应
  • destroy():销毁,Servlet 被销毁前调用
  • getServletConfig():获取 Servlet 配置
  • getServletInfo():获取 Servlet 信息

Servlet 继承体系

Servlet --> GenericServlet -->HttpServlet

我们通常继承 HttpServlet 来处理 HTTP 请求。

Servlet 配置

在 web.xml 中配置 Servlet,指定:

  • Servlet 名称
  • Servlet 类
  • 请求映射 URL
  • 初始化参数等

JWT验证登录

JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案。

它可以用于用户登录验证和信息交换。

JWT 的工作流程是(图文)

【Java】各类知识汇总(2023.05.06更新)_第4张图片

  1. 客户端使用用户名和密码请求登录
  2. 服务器验证用户名和密码成功后,生成一个 JWT,返回给客户端
  3. 客户端存储 JWT,并在每次请求时携带该 JWT
  4. 服务器验证JWT的合法性和有效性,如果成功则返回数据(图中4 5 过程)

JWT 包含三部分

  • Header:头部,包含签名算法(如HMAC SHA256)和token类型(如JWT)
  • Payload:载荷,包含声明(claim)如iss(发行者),exp(过期时间),sub(面向对象)等
  • Signature:签名,是前两部分使用header中的签名算法计算出来的结果所以 JWT 的组成如下:
Header.Payload.Signature

每个部分使用 . 分隔,然后使用 BASE64 进行编码,形成最终的 JWT 如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

服务器使用 JWT 登录验证的流程:

  1. 客户端 POST 用户名和密码到登录接口
  2. 服务器验证信息,生成 JWT,如:
const token = jwt.sign({ username: user.username, admin: true }, SECRET_KEY);
  1. 服务器返回 JWT 给客户端:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
}
  1. 客户端在每次请求时,在 Header 中带上:
Authorization: Bearer <token>
  1. 服务器验证JWT的合法性和有效性,如果成功则处理请求。

JWT 是一种非常简洁而强大的跨域身份验证方案。利用JWT,我们可以轻松实现用户登录和权限验证等功能。

SpringCloud 常用组件

  1. Eureka:服务注册与发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
  2. Ribbon:客户端负载均衡器,有多种负载均衡算法可以选择,如轮询、随机等。
  3. Feign:服务调用工具,ရ性服务调用方式。
  4. Hystrix:服务容错保护工具,避免服务雪崩。
  5. Zuul:服务网关,提供智能路由、访问过滤等功能。
  6. Config:服务配置中心,实现配置文件在服务重启时不需要手动更新,配合消息总线实现热更新。
  7. Bus:消息总线,用于将配置中心和客户端连起来。
  8. Zipkin:链路追踪系统,用于追踪服务之间调用关系,用于监控分布式系统架构。
  9. Spring Cloud Alibaba:阿里云的Spring Cloud生态组件,包括服务注册与发现、配置中心、消息总线等。
  10. Gateway:新一代的API网关服务,比Zuul更加强大。
  11. OpenFeign: 声明式服务调用工具,比Feign更加强大。
  12. Sentinel:服务熔断工具,功能更加强大,可以实现限流、降级等。

微服务框架

Spring Cloud是Spring旗下的微服务开发工具,它提供了一系列框架来快速构建微服务系统。

包括

  1. Eureka:服务注册和发现组件,用来实现微服务实例的注册与发现。
  2. Ribbon:客户端负载均衡工具,用来实现微服务实例的负载均衡。
  3. Zuul:API网关,用来实现微服务的路由转发、访问控制等功能。
  4. Hystrix:断路器,用来实现微服务的故障隔离和容错。
  5. Config:配置中心,用于集中管理各个微服务的配置文件。
  6. Bus:消息总线,用于传播配置文件的变化或其他事件。
  7. DiscoveryClient:服务发现客户端,自动化配置Eureka Client。
  8. Feign:声明式的HTTP客户端,用来简化RESTful服务的消费。
  9. Zuul:API网关。
  10. Stream:用来构建消息驱动的微服务。
  11. Sleuth:用来实现微服务的分布式请求链路跟踪。

构建步骤

  1. 使用Eureka作为服务注册中心。各个微服务启动后自动注册到Eureka中。
  2. 使用Ribbon实现负载均衡,在服务消费方使用@LoadBalanced注解开启负载均衡。
  3. 使用Feign声明式调用其他微服务。
  4. 使用Zuul构建API网关。
  5. 使用Config构建配置中心。
  6. 使用Spring Boot开发每个微服务,并加上@EnableDiscoveryClient注解。
  7. 使用Spring Boot Actuator监控微服务。
  8. 使用Hystrix实现服务容错。
  9. 使用Stream或Bus构建消息驱动能力。
  10. 使用Sleuth实现分布式请求链路跟踪。

Dubbo安装和应用

Dubbo 是一个分布式服务框架,提供高性能和透明化的 RPC 远程服务调用方案。

Dubbo 的安装步骤

  1. 安装 Java 开发环境,Dubbo 支持 JDK 1.6 及以上版本。
  2. 下载 Dubbo,Dubbo 的发行版本有两种:- dubbo-admin-2.6.4.war:Dubbo 服务治理中心应用,用于管理和监控 Dubbo 服务。- dubbo-2.6.4.jar:Dubbo 的 Java 服务引擎,用于暴露和引用远程服务
  3. 部署 Dubbo 服务治理中心应用 dubbo-admin-2.6.4.war:
    1. 解压 war 包,并修改 WEB-INF/dubbo.properties 配置文件。
    2. 启动 Tomcat 并将 dubbo-admin-2.6.4 部署在 Tomcat 中。
    3. 访问 http://localhost:8080/dubbo-admin-2.6.4 登陆服务治理中心。
  4. 在服务提供者消费者中分别引入 dubbo-2.6.4.jar 依赖,并配置 dubbo.properties 配置文件。
  5. 在服务提供者中配置服务并暴露服务,如:
@Service 
public class DemoServiceImpl implements DemoService {
    ...
}

<!-- 服务提供方应用配置 -->
<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
  1. 在服务消费者中引用服务,如:
<!-- 消费方应用配置 -->
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService"/>
  1. 启动服务提供者和消费者,并在 dubbo-admin 中查看服务注册和调用情况。

Dubbo的调用(代码)

  1. 定义服务接口,比如:
public interface HelloService {
    String sayHello(String name);
}
  1. 服务提供者实现接口,比如:
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "Hello " + name;
    }
} 
  1. 在服务提供者引用Dubbo,发布服务:
 
<dubbo:application name="hello-service-provider" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" /> 
<dubbo:protocol name="dubbo" port="20880" /> 
<dubbo:service interface="com.alibaba.HelloService" ref="helloService" />

<bean id="helloService" class="com.alibaba.HelloServiceImpl" />  
  1. 服务消费者引用远程服务:
   
<dubbo:application name="hello-service-consumer"  />
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:reference id="helloService" interface="com.alibaba.HelloService" />  
  1. 服务消费者调用远程服务:
HelloService helloService = (HelloService)context.getBean("helloService");  
String hello = helloService.sayHello("dubbo");

Dubbo的调用过程(文字)

  1. 服务提供者启动,向注册中心注册服务。
  2. 服务消费者启动,从注册中心订阅服务。
  3. 服务消费者调用服务,Dubbo框架客户端按规则从多个提供者中路由调用一台。
  4. 提供者返回结果,Dubbo框架返回给消费者。

项目技术

三级裂变分销用户绑定sql

三级裂变分销是一种网络营销模式,是指参与者通过自己的努力发展下线,下线再发展下线,形成了三级或多级的网络结构。这种模式需要维护每个参与者的上下级关系,以及计算不同级别参与者的业绩报酬。

为实现这种模式,可以创建如下表:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(50) NOT NULL,
  `level` int(2) NOT NULL DEFAULT '1' COMMENT '用户级别:1 一级 | 2 二级 | 3 三级',
  `parent_id` int(11) DEFAULT NULL COMMENT '上级用户id', 
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

其中:

  1. id:用户唯一标识
  2. username/password:用户名和密码
  3. level:用户级别,1 为一级用户,2 为二级用户,3 为三级用户
  4. parent_id:上级用户id,除一级用户外,其他用户都有上级当一个用户注册后,需要将其级别和上级绑定:
  • 一级用户:level = 1,parent_id = NULL

  • 二级用户:level = 2,parent_id = 一级上级用户id

  • 三级用户:level = 3,parent_id = 二级上级用户id

    所以,当一个用户注册后,可以执行如下SQL进行级别和上级绑定:

-- 二级用户注册,绑定一级上级
INSERT INTO `user` (`username`, `password`, `level`, `parent_id`) 
VALUES ('user2', 'pwd2', 2, 1);

-- 三级用户注册,绑定二级上级   
INSERT INTO `user` (`username`, `password`, `level`, `parent_id`)
VALUES ('user3', 'pwd3', 3, 2);

EL表达式

EL表达式是JSP页面中的表达式语言,它可以输出数据、执行运算、调用方法等。EL表达式以${}作为标识。

举些例子:

  • 输出对象属性:${user.name}
  • 调用方法:${user.getName()}
  • 运算:${1+2}
  • 获取请求参数:
    • ${param.id}
    • Req+uest
    • Session
    • Application
    • Implicit Objects

EL表达式要点:

  1. 可以嵌套使用,如:${user.address.city}
  2. 支持各种运算:+ - * / ()
  3. 支持逻辑运算:&& || !
  4. 支持空判断: ${empty str}
  5. 支持三元运算: ${age > 18? "成年":"未成年"}
  6. 获取web开发常用的隐含对象
<%-- 访问变量 --%>
${var}  

<%-- 运算 --%> 
${5+10}   ${varOne > 10}

<%-- 访问对象属性 --%>
${user.name} 

<%-- 访问数组元素 --%> 
${users[0].name}

<%-- 访问隐式对象 --%>
${pageContext.request.contextPath}  

<%-- 调用内建方法 --%>
${fn:toUpperCase('hello')} 

分页查询

  1. PageHelper:Spring Boot中最常用的分页插件,只需要几个注解和PageInfo对象即可完成分页。
// 开启分页
@PageHelper

// 查询列表
List<User> list = userMapper.selectAll(); 

// 得到分页相关数据
PageInfo<User> page = new PageInfo<>(list);

// 页面相关数据
model.addAttribute("page", page);
  1. MyBatis分页插件:需要在MyBatis配置文件中配置插件,稍微麻烦一点,用的较少。3. JPA分页:Spring Data JPA自带分页支持,通过Pageable接口即可完成,较为简单。
Page<User> page = userRepository.findAll(PageRequest.of(pageNum, pageSize)); 

所以,EL表达式和分页插件是JSP和Spring Boot开发时经常会使用到的技术,理解和掌握它们会对我们有很大帮助。

三级分销用户表设计

我们的项目给用户设计了四张表:用户表,分销商表,分销商等级表,佣金表

  1. 用户表:存储用户的基本信息,如用户名、密码、姓名、手机号等。
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL COMMENT '用户名',
  `password` varchar(64) NOT NULL COMMENT '密码',
  `name` varchar(64) NOT NULL COMMENT '姓名',
  `mobile` varchar(11) NOT NULL COMMENT '手机号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 分销商表:存储用户的分销商信息,如是否是分销商、上级分销商id等。
CREATE TABLE `distributor` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `is_distributor` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否是分销商,0-不是,1-是',
  `upper_distributor_id` int(11) DEFAULT NULL COMMENT '上级分销商id',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `upper_distributor_id` (`upper_distributor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 分销商等级表:存储分销商的等级信息,可以设计多级,如一级分销商、二级分销商、三级分销商。
CREATE TABLE `distributor_level` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '等级名称',
  `commission_rate` decimal(4,2) NOT NULL COMMENT '佣金比例',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 佣金记录表:存储分销商的佣金记录,以便进行佣金结算。
CREATE TABLE `commission_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `distributor_id` int(11) NOT NULL COMMENT '分销商id',
  `amount` decimal(10,2) NOT NULL COMMENT '佣金金额',
  `create_time` datetime NOT NULL COMMENT '记录创建时间',
  PRIMARY KEY (`id`),
  KEY `distributor_id` (`distributor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

订单生成流程

  1. 用户发送添加订单的请求,前端将用户的各种信息(电话地址),选中的单个或多个sku商品封装addDto中
  2. 目前的addDto中信息不完全,给order赋值后,order还要继续补全
  3. 订单id手动赋值,避免在读写分离的数据库部署模式中,出现两条数据有相同的自增列Id
  4. 我用的是leaf来生成id,同时也要生成一个订单号,userId也需要赋值,订单状态赋予初始值,表明未支付状态
  5. 计算商品价格,order的信息补全后还需要为订单项补全信息,将order的id填入订单项表做关联
  6. 同时根据订单项中的skuId,商品数量调用减少库存的方法,再根据userId和skuId删除购物车中的对应数据
  7. 然后新增补全信息的订单,及订单项到相应的表中
  8. 最后把希望给用户呈现的信息(订单id,订单号,钱数等)赋值到addVo中,返回给前端
  9. 特别的,对于超时未支付的订单,采用延迟队列,订单生成后发到队列,30分钟后检查支付状态,如果未支付则删除订单退回库存

你可能感兴趣的:(java,java,开发语言,代理模式)