该框架对被调用者的源代码进行非入侵约束。
使用该框架的应用程序源代码基本不需要修改,也不依赖于该框架的API。
AOP(面向切面编程):通过配置的方式对业务逻辑的各个部分进行拦截,并在这些拦截点上添加增强处理。代表框架有Spring AOP等。
Plugin插件机制:通过插件可以对系统进行扩展,而不需要修改源代码。典型代表是Eclipse的插件机制。
拦截器:通过拦截器可以在某个方法或请求被执行前后进行干预,扩展原有功能。如Spring MVC的拦截器等。
事件监听机制:当系统中发生某些事件时,触发注册在该事件上的监听器方法。该机制可以在事件发生后进行一些扩展处理。如Spring的事件监听机制等。
高级别API:框架提供高级别的API给最终用户使用,而内部使用基础或低级别的API进行实现。外部使用时也不需要关注内部实现,起到解耦作用。
中间件:中间件处于不同系统或层之间,通过扩展中间件可以影响多个系统。典型代表有消息队列、流量控制等中间件。
代理模式:通过代理对象对原对象进行功能扩展,而客户端调用代理对象的接口,无需关注原对象细节。
模板方法模式:通过钩子方法让子类有扩展点,扩展原有逻辑功能。
外观模式:框架内部有多个子系统,外观对象提供简单接口给外部,而内部实现调用不同子系统的复杂逻辑。
非入侵式框架主要利用面向切面、插件、代理、外观等设计模式,或事件、拦截器、高级API抽象等机制,对系统进行扩展与增强,而不破坏原有代码结构与逻辑。使得系统具有良好的可扩展性和维护性。
代理对象调
用实际对象的方法,然后附加一些额外的操作,实现框架的功能。这不需要修改实际对象的源代码。所以,非入侵式框架可以最大限度地维护系统的原有结构,便于系统的扩展与升级,这是它的重要优势。许多主流框架都采用非入侵式设计,以更好地满足用户的需求并提高使用率。
相比之下,入侵式框架需要应用程序的源代码与框架产生依赖,直接修改或调用框架API,这会增加程序的耦合度,限制框架的重用性,增加升级的难度。
Spring Security 是 Spring 家族中第一个应用的框架,它提供全面且可定制的安全服务。
它可以用于保护基于Spring的企业应用程序。
Redis 是一个高性能的键值数据库
是一个非关系型、开源、高性能、多功能和丰富特性的 NoSQL 数据库。
性能高:读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型:string,hash,list,set,sorted set,bitmaps,hyperloglogs。
持久化:将内存中的数据持久化到磁盘中,重启的时候可以再将数据加载到内存中。
缓存读取频繁的数据,如商品信息、文章内容等。
缓存计算结果,如排行榜、好友推荐等。
缓存用户登录信息,购物车信息等。
Redis 哨兵是Redis的高可用解决方案,具有如下功能:
Redis 哨兵架构由若干个 Sentinel 实例和若干个 Redis 实例组成。其中一个 Redis 实例为主实例,其他实例为从实例,以主从复制方式工作。Sentinel 实例监听主从实例的运行状态,在主实例下线时,会在从实例中选举新的主实例,完成自动故障转移。
Redis Cluster是Redis的分布式解决方案,具有如下功能:
Redis Cluster通过分配一定数量的槽(slot)来表示整个数据的范围,每个节点负责一定数量的槽。客户端将一个键转换为槽的编号,从而得到键所映射到的节点,实现分片效果。
通过在节点之间以主从关系或者分片映射的关系复制数据,从而实现高可用、可扩展的集群方案。
雪崩:大量的key对应的数据过期,导致redis一次性释放大量内存,造成短暂的server hang。
解决方案:
穿透:查询一个本不存在的key,导致每次请求都通过redis到达DB数据库,造成DB数据库负载过高过载。
解决方案:
击穿:大量并发请求同一个key且该key缓存失效过期,大量请求直接到达数据库,造成数据库过载。
解决方案:
可以将热点数据或大量读、少量写的数据存入 Redis,以减轻主数据库的压力。
Reads 从 Redis 读取,Writes 同步更新到 Redis 和主数据库。这种架构可以大大提高系统的性能和吞吐量。
分布式系统中,可以使用 Redis 的 SETNX(set if not exists) 命令实现分布式锁。这个可以有效的防止并发问题。
获取锁使用 SETNX 命令尝试获取锁。
如果键 lock 不存在,则 SETNX 成功,返回 1 ,表示获取锁成功。如果键 lock 已存在,则返回 0 ,表示获取锁失败。
SETNX lock.redis 123
SETNX lock.redis 123 EX 1
DEL lock.redis
锁失效时自动释放
因为我们为锁设置了过期时间,如果锁在过期时间内未被释放,它将自动过期并被 Redis 删除,相当于自动释放了锁
解决进程崩溃导致锁无法释放的问题
我们可以设置一个信号量,在进程获取锁后,将信号量设置为 1。如果进程崩溃,信号量不会被清除,值仍为 1。其他进程在尝试获取锁时,首先检查信号量,如果值为 1,表示上一个持有锁的进程崩溃,此时调用 DEL 命令删除锁,然后再设置信号量为 0,获取锁。
这个解决方案可以确保锁总是被正常释放,避免死锁。
利用 Redis 的 SETNX 和 DEL 命令,我们可以很容易实现一个分布式锁,并解决掉分布式锁常见的死锁问题。这在分布式系统和微服务架构中是一个非常实用的技术
可以利用 Redis 的 LIST 类型实现一个消息队列。生产者使用 LPUSH/RPUSH 命令将消息推入队列,消费者使用 LPOP/RPOP 命令将消息从队列弹出。这种架构可以实现异步任务,提高系统效率。
Redis 发布订阅(pub/sub)功能可以实现服务端消息推送给客户端。
生产者作为发布者将消息推送到频道,消费者作为订阅者订阅相应的频道。
这种架构可以实现实时消息推送,满足一对多的通信需求。
Redis 事务可以一次执行多个命令,这些命令要么全部成功,要么全部失败。这在并发环境下可以确保数据的一致性。事务可以确保批量操作数据的原子性。
搭建Redis集群,实现分布式存储和负载均衡。
这样大量的数据和操作可以分配到不同的节点上,既提高了存储能力,也提高了并发处理能力。
Servlet 是 Java EE 规范中定义的服务器端程序。Servlet 运行于服务器上,并由服务器调用。
Spring MVC是Spring框架的一部分,是基于Java实现MVC的轻量级Web框架。
Spring MVC的主要组件如下:
一个简单的Spring MVC应用的流程如下:
相比于Struts2,Spring MVC的主要优点在于:
所以,Spring MVC通过将MVC分层架构的不同职责解耦,使得每层都可以自行扩展,或被替换,十分灵活。它简单易学,同时功能也非常强大,是目前最受欢迎的MVC框架之一。
接收客户端请求
生成动态内容
与数据库交互
维护会话状态
Servlet --> GenericServlet -->HttpServlet
我们通常继承 HttpServlet 来处理 HTTP 请求。
在 web.xml 中配置 Servlet,指定:
JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案。
它可以用于用户登录验证和信息交换。
Header.Payload.Signature
每个部分使用 . 分隔,然后使用 BASE64 进行编码,形成最终的 JWT 如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
服务器使用 JWT 登录验证的流程:
const token = jwt.sign({ username: user.username, admin: true }, SECRET_KEY);
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
}
Authorization: Bearer <token>
JWT 是一种非常简洁而强大的跨域身份验证方案。利用JWT,我们可以轻松实现用户登录和权限验证等功能。
Spring Cloud是Spring旗下的微服务开发工具,它提供了一系列框架来快速构建微服务系统。
Dubbo 是一个分布式服务框架,提供高性能和透明化的 RPC 远程服务调用方案。
@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"/>
<!-- 消费方应用配置 -->
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService"/>
public interface HelloService {
String sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "Hello " + name;
}
}
<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" />
<dubbo:application name="hello-service-consumer" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:reference id="helloService" interface="com.alibaba.HelloService" />
HelloService helloService = (HelloService)context.getBean("helloService");
String hello = helloService.sayHello("dubbo");
三级裂变分销是一种网络营销模式,是指参与者通过自己的努力发展下线,下线再发展下线,形成了三级或多级的网络结构。这种模式需要维护每个参与者的上下级关系,以及计算不同级别参与者的业绩报酬。
为实现这种模式,可以创建如下表:
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
其中:
一级用户: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表达式是JSP页面中的表达式语言,它可以输出数据、执行运算、调用方法等。EL表达式以${}作为标识。
举些例子:
${user.name}
${user.getName()}
${1+2}
${param.id}
EL表达式要点:
${user.address.city}
+ - * / ()
&& || !
${empty str}
${age > 18? "成年":"未成年"}
<%-- 访问变量 --%>
${var}
<%-- 运算 --%>
${5+10} ${varOne > 10}
<%-- 访问对象属性 --%>
${user.name}
<%-- 访问数组元素 --%>
${users[0].name}
<%-- 访问隐式对象 --%>
${pageContext.request.contextPath}
<%-- 调用内建方法 --%>
${fn:toUpperCase('hello')}
// 开启分页
@PageHelper
// 查询列表
List<User> list = userMapper.selectAll();
// 得到分页相关数据
PageInfo<User> page = new PageInfo<>(list);
// 页面相关数据
model.addAttribute("page", page);
Page<User> page = userRepository.findAll(PageRequest.of(pageNum, pageSize));
所以,EL表达式和分页插件是JSP和Spring Boot开发时经常会使用到的技术,理解和掌握它们会对我们有很大帮助。
我们的项目给用户设计了四张表:用户表,分销商表,分销商等级表,佣金表
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;
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;
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;
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;