Redis之多级缓存
Redis之多级缓存——亿级流量方案 序言 传统缓存问题: 传统缓存一般是请求到达Tomcat后,Tomcat先查询Redis缓存,如果未命中则查询数据库。存在下面问题: 请求要经过Tomcat服务器,Tomcat的性能成为整个并发缓存的瓶颈 如果Redis缓存失效,会对数据库产生冲击 多级缓存 充分利用各环节,分别添加缓存,减轻Tomcat的压力 客户端缓存->NGINX本地缓存->Redis->Tomcat->DB 其中Nginx缓存是业务Nginx,通常部署为集群,再由专门的Nginx做反向代理 JVM进程缓存 Caffeine技术 缓存驱逐策略: 基于容量、基于时间、基于引用 实现效果: 第一次查询走数据库,然后就会放入缓存,之后查询就走缓存 Lua语法 轻量小巧的脚本语言 数据类型 nil、boolean、number、string、function、table、type函数 变量 声明变量 12345local var_name = var_value#数组local arr = {'java','python','c++'}#maplocal map = {name = 'Jack',age = 2} 访问数组,下标从一开始arr[1] 访问Map, 12map['name']map.name 循环遍历 1234567local arr = {'Java','lua','python'}for index,value in ipairs(arr) do print(index,value) endfor key,value in pairs(map) do prinr(key,value) end 函数 1234function fun_name(para) --- return nil end 条件控制 123456if(布尔表达式) then --- else --- end 多级缓存 配置nginx OpenResty是基于Nginx的高性能Web平台 OpenResty使用流程类似于Nginx的使用 监听路径类似于Response Lua文件类似于Service层 请求参数处理,不同参数格式有相应解析代码 nginx查询Tomcat 小规律:服务器前三位地址相同,第四位换为1 nginx提供了内部API用以发送http请求 Tomcat集群的负载均衡 保证缓存一直生效,同一路径访问同一服务器 修改负载均衡算法由轮询改为Hash算法来直接匹配 添加Redis缓存 冷启动:服务器初启动时,没有缓存,此时查询会都导向数据库,带来较大的数据库压力 缓存预热:利用大数据统计热点数据,将热点数据存到缓存 实现nginx优先查询Redis缓存: OpenResty如何操作Redis? OpenResty提供了操作Redis的模块,大概模块如下: 封装函数,从Redis读取数据并返回 引入Redis模块,并初始化Redis模块 封装函数,释放Redis连接,其实是放入连接池 Nginx本地缓存 利用shared dict开启词典 修改查询逻辑,加入缓存业务 缓存同步 三种方式 设置有效期 同步双写,在修改数据的同时直接修改缓存 异步通知,改数据库时发事件通知,相关服务进行监听到通知后修改缓存 监听mysql,使用MQ/canal实现 canal基于mysql的主从模式,canal伪装为mysql的一个slave节点完成通知
分布式缓存——Redis高级篇
分布式缓存——Redis高级篇 单节点Redis问题 数据丢失问题:实现Redis数据持久化 并发能力问题:搭建主从集群,实现读写分离 故障恢复问题:Redis哨兵,实现健康检测和自动恢复 存储能力问题:搭建分片集群,利用插槽机制实现动态扩容 一、持久化 方式之一:RDB持久化 RDB全称RedisDatabaseBackup file(Redis数据备份文件) 有两个命令可以进行RDB持久化 save命令:由主进程执行RDB,会阻塞所有命令 bgsave命令:开子进程执行RDB,避免主进程受影响 默认Redis停机时会执行一次RDB (停机≠宕机) 可以通过修改redis.conf文件来配置相关的RDB参数 1234567save 900 1 #900秒内由1次修改,就会执行bgsave。参数可以进行修改#是否压缩,建议不开启,压缩也会消耗CPU,磁盘的话相对不值钱rdbcompression yes#RDB文件名称dbfilename dump.rdb#文件保存的路径目录dir ./ bgsave流程 bgsave开始时会fork主进程得到子进程(在fork过程中主进程会被阻塞),子进程共享主进程的内存数据。fork后主进程可以正常读写,子进程会读取内存数据并写入RDB文件。 如果只是这样可能会有脏数据的问题,就是说在子进程读的时候主进程可能更改了数据。 要理解这个问题,需要理解内存结构。 如图是我参考课程使用visio绘制的fork框图,下面结合这个来解释一下fork的流程。 首先是关于linux的结构,在linux中,进程都是不能直接更改物理内存的,它在虚拟内存中,通过一个页表来维护关系,进程通过修改页表修改内存中的数据。 当使用bgsave的时候,首先会进行fork,会开启子进程,子进程复制主进程的页表,复制完后,为避免脏读的情况,会将数据A和数据B设为只读的共享内存,这样子进程在存储RDB时就不会被主进程干扰。 相应的主进程要更改数据,数据B会被拷贝为数据B的副本,主进程通过修改副本实现修改的功能。 因为内存进行了拷贝,所以极限情况下可能会占用两倍的内存,这也就是为什么使用Redis都要预留一定的内存空间。 RDB缺点: RDB间隔较长,两次RDB间由数据丢失风险 fork子进程、压缩写出RDB比较耗时 方式二:AOF持久化 AOF全程Append Only File(追加文件),可以看成命令日志文件 可以通过redis.conf配置文件开启和配置AOF 1234567#开启appendonly yesappendfilename "名字"#频率appendfsync always#每执行一次命令,立即写到AOFappendfsync everysec#写命令执行完先放入AOF缓冲区,每隔一秒将缓冲数据写到AOFappendfsync no#写命令执行完先放入AOF缓冲区,由操作系统决定什么时候将缓冲数据写到AOF AOF体积压缩 因为AOF记录的是每条命令,多条命令叠加下可能很多命令是无效的,所以体积会很大。 可以使用bgrewriteaof命令将AOF文件进行重写以减小体积 什么时候进行重写? 可以在redis.conf文件中配置 12auto-aof-rewrite-percentage 100#AOF文件增长超过多少百分比触发重写auto-aof-rewrite-min-size 64mb#AOF文件体积最小多大以上触发重写 RDB和AOF对比 各有优缺点,如果对数据安全性要求较高,在实际开发中往往会将两种方式结合起来使用 RDB AOF 持久化方式 定时对整个内存做快照 记录每一次执行的命令 数据完整性 不完整,两次备份之间会丢失 相对完整,取决于刷盘策略 文件大小 会有压缩,文件体积小 记录命令,文件体积大 宕机恢复速度 很快 慢 数据恢复优先级 低,因为数据完整性不如AOF 高,因为数据完整性高 系统资源占用 高,大量CPU和内存消耗 低,主要是磁盘IO资源,但AOF重写时会占用大量CPU和内存资源 使用场景 可以容忍数分钟的数据丢失,追求更快的启动速度 对数据安全性要求较高 二、主从集群 搭建主从架构 开启主从关系 1replicaof/slaveof(5.0之前) host...
Spring框架
Spring Framework Java开发要用的Spring家族的基础就是SpringFramework、SpringBoot、SpringCloud。这里笔记记录的就是SpringFramework技术,学习视频是黑马的SSM快速入门Spring-01-初识Spring_哔哩哔哩_bilibili 要注意的是,本内容仅是对Spring框架应用的学习以及架构的认识,更加深入的内容要在Spring源码的部分进一步学习。 Spring系统架构 IoC(Inversion of Control)控制翻转 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象的创建控制权由程序转移到外部,这个思想就是控制翻转。 IoC、Bean和DI: Spring提供了一个容器,称为IoC容器,用来充当Ioc思想中的“外部” IoC容器负责对象的创建、初始化的一系列工作,被创建或管理的对象在IoC容器中统称为Bean DI(依赖注入)在容器中建立起bean与bean之间的关系 我们这样做的目的是充分解耦,具体做法可以概括如下: 使用IoC容器管理bean(IoC) 在IoC容器中将有依赖关系的bean进行关系绑定(DI) bean管理 bean作用范围 bean的作用范围对应着scope属性,主要就是控制其是否为单例。 为什么bean默认为单例? IoC容器就适合管理单例的对象,这样才能有效地提高效率。 适合交给容器管理的bean 表现层对象 业务层对象 数据层对象 工具对象 不适合交给容器管理的bean 封装实体的域对象、 bean的实例化 构造方法(常用) 提供可访问的构造方法 配置 无参构造器如果不存在,会抛出BeanCreationException 静态工厂(了解) 实例工厂(了解) 使用FactoryBean实例化,其中有三个方法,用来得到Bean实例、设定Bean类型、设定是否为单例(实用) bean的生命周期 bean生命周期:bean从创建到销毁的整体过程 bean生命周期控制:在bean创建后到销毁前做的一些事情 生命周期控制 配置的方式 接口控制 bean的生命周期 初始化容器 创建对象(内存分配) 执行构造方法 执行属性注入(set操作) 执行bean初始化方法 使用bean 执行业务操作 关闭/销毁容器 执行bean销毁方法 bean销毁 容器关闭前触发bean的销毁 关闭容器方式: 手动关闭容器,ConfigurableApplicationContext接口close()操作 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机,ConfigurableApplicationContext接口registerShutdownHook()操作 总结 bean相关标签属性 123456789101112<bean id="bookDao" name="dao bookDaoImpl daoImpl" class="com.itheima.dao.impl.BookDaoImpl" scope="singleton" init-method="init" destroy-method="destory" autowire="byType" factory-method="getInstance" factory-bean="com.itheima.factory.BookDaoFactory" lazy-init="true" /> id: bean的Id name: bean别名 class: bean类型,静态工厂类,FactoryBean类 scope: 控制bean的实例数量 init-method: 生命周期初始化方法 destroy-method: 生命周期销毁方法 autowire: 自动装配类型,实际开发一般用注解,该属性一般不用 factory-method: bean工厂方法,应用于静态工厂或实例工厂 factory-bean: 实例工厂bean lazy-init: 控制bean延迟加载 依赖注入 手动装配 setter注入 引用类型使用ref配置 简单类型:使用property标签value属性进行配置 12<property name="bookDao"...
JavaSE-反射
学到Spring框架发现自己JavaSE的反射还没学,就滚过来学反射了。 黑马的这个课讲的真不错,比尚硅谷的那个要简练一些,很轻松就能听懂了。 反射 Reflection allows programmatic access to information about the fields, methods and constructors of loaded classes, and the use of reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions. 官方对反射的描述:反射允许以编程方式访问已加载类的字段、方法和构造函数的信息,并在安全限制范围内使用反射字段、方法和构造函数来操作其底层对应项。 总之反射就是要获取某个类的Class文件,并解剖出它的成员变量、成员方法和构造方法。 反射第一步:获取class 要获取class,总共有三种方法 Class.forName("全类名") 类名.class 对象.getClass() 说明: 1中的全类名指的是包名+类名,一般可以直接复制粘贴 3的getClass方法是Object里的方法,所以所有对象都可以调用 三种方法虽然都可以获取class,但是对应的阶段不同 1对应的是源代码阶段 2对应的是加载到内存的阶段,一般作参数传递 3对应的是运行时阶段,只有这个类的对象创建时才能使用 反射第二步:获得并使用构造方法、属性、成员方法 Java中有一个重要的思想就是万事万物皆对象,相应的,构造方法、属性和成员方法也有其对应的类 属性(Field类)、成员方法(Method类)、构造方法(Constructor) 1.获得并使用构造方法 Class类中的方法: 1234Constructor<?>[] getConstrutors();Constructor<?>[] getDeclaredConstrutors();Constructor<?> getConstrucor(参数列表(注意传参数对应的class对象));Constructor<?> getDeclaredConstrucor(参数列表(注意传参数对应的class对象)); 使用Constructor类的方法,获得修饰符、名字、形参、对象创建 123public int getModifiers();//获得权限修饰符getName();getParameters();//获取参数列表 (IDEA底层的代码提示其实就是用的反射) 关于modifiers() 变量和类型 常量字段 值 public static final int ABSTRACT 1024 public static final int FINAL 16 public static final int INTERFACE 512 public static final int NATIVE 256 public static final int PRIVATE 2 public static final int PROTECTED 4 public static final int PUBLIC 1 public static final int STATIC 8 public static final int STRICT 2048 public static final int SYNCHRONIZED 32 public static final int TRANSIENT 128 public static final int VOLATILE 64 使用暴力反射进行对象的创建: 对于私有构造器的使用,注意使用setAccessible(true)跳过权限验证 123456Class clazz = Class.forName("User");Constructor cons = clazz.getDeclaredConstructor(String.class);System.out.println(cons.getModifiers());cons.setAccessible(true);//设定权限验证User user = (User)...
SpringMVC之登录校验
SpringMVC之登录校验 登录功能的实现比较简单,只需要根据输入的用户名和密码,或者其他登录验证形式,到数据库查询是否有这条数据就可以了。 但实际上这是一个伪登录的功能。因为HTTP协议是一种无状态的协议,所以在后续的访问中无法记录登录状态。也就是登录校验功能没有实现,下边来详细讲解一下该功能的实现。 graph LR 浏览器-->|请求|拦截器 拦截器-->|响应|浏览器 拦截器-->login 拦截器-->Controller接口 login-->|存|登录标记 登录标记-->|取|拦截器 上图展示了登录验证这一业务的实现流程 实现登录标记:会话技术 实现统一拦截:过滤器(filter)、拦截器(interceptor) 下边介绍这些技术 会话技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应 会话跟踪:一种维护浏览器状态的方法,服务器需要多次请求是否来自于同一浏览器,以便在同一次会话的多次请求之间能够共享数据 Cookie 客户端会话跟踪技术 服务端在响应的响应头里携带Set-Cookie:name=value 之后浏览器在请求时请求头里携带Cookie:name=value 优点: HTTP协议中支持的技术,浏览器自动进行 缺点: 在移动端无法使用Cookie 不安全,用户可以自己禁用Cookie Cookie不能跨域 session 服务器会话跟踪技术,是基于Cookie实现的 缺点 服务器集群环境下无法使用session cookie的缺点:因为底层就是用cookie实现的 令牌技术 优点: 支持PC端和移动端,因为现在不需要把令牌存储在Cookie中,其他的存储空间也是可以的 解决集群环境下的认证问题 减轻服务器端的存储压力 后边在redis还会详细讲解其实现 JWT令牌:(令牌的一种形式) JSON Web Token(https://jwt.io) 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全传输信息。由于数字签名的存在,这些信息是可靠的。 JWT令牌的组成: 第一部分:Header(头),记录令牌类型、签名算法等。 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 第三部分:Signature(数字签名),防止Token被篡改,确保安全性。融入header、payload,并加入指定秘钥,通过指定签名算法计算而来。 介绍一下Base64编码:是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。 JWT校验: JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的 如果JWT令牌解析时报错,则说明JWT令牌被篡改或失效了,令牌非法 JWT校验需要使用统一拦截的技术,下面将会讲解统一拦截的实现方式 统一拦截 过滤器 过滤器(Filter)是JavaWeb三大组件(Filter、Servlet、Listener)之一。 过滤器可以把对资源的请求拦截下来,从而实现一些特殊功能 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等 Filter快速入门:(并不重要) 定义:实现Filter接口 配置: 在实现类上加上@WebFilter(urlPatterns=“/*”)表示拦截路径 Filter并不是SpringBoot的组件,所以需要在启动类上加上@ServletComponentScan 过滤器的执行流程 graph LR 浏览器-->|请求|Filter subgraph...
Redis
Redis基础部分 介绍 Redis诞生于2009年,全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。 特征: 键值(key-value)型,value支持多种不同数据结构,功能丰富 单线程,每个命令具备原子性 低延迟,速度快(基于内存、IO多路复用、良好的编码) 支持数据持久化 支持主从集群、分片集群 支持多语言客户端 redis安装 关于redis,这里推荐装linux版的。 所以建议先学习安装linux和它的常用vi命令。这里做几点建议: win10及以上可以直接使用自带的wsl2来安装linux子系统,具体教程b站上有。我使用的是Ubuntu,并且没有什么问题 redis的安装和测试可以参考这个教程如何在 Ubuntu 20.04 上安装和配置 Redis-阿里云开发者社区 redis常用的命令 命令不用死记,建议去redis官网查询命令 又或者,你也可以在命令行中使用help [command]来查询,例如: 下边总结常用的命令 KEYS:查看符合模版的所有key,不建议在生产环境设备上使用,因为效率过低 DEL:删除一个指定的key EXISTS:判断key是否存在 EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除 TTL:查看一个key的剩余有效期 String类型 String类型,也就是字符串类型,是Redis中最简单的存储类型 其value是字符串,不过根据字符串的格式不同,又可以分为3类:string、int、float String的常见命令 SET:添加或者修改已经存在的一个String类型的键值对 GET:根据key获取String类型的value MSET:批量添加多个String类型的value MGET:根据多个key获取多个String类型的value INCR:让一个整型的key自增1 INCRBY:让一个整型的key自增并指定步长 INCRYBYFLOAT:让一个浮点类型的数字自增并指定步长 SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行 SETEX:添加一个String类型的键值对,并指定有效值 Key的层级格式: Redis的key允许有多个单词形成层级结构,多个单词之间用‘:’隔开,这样就会形成层级结构。 Hash类型 Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。 Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD: Hash的常见命令: HSET key field value:添加或修改hash类型key的field的值 HGET key field:获取一个hash类型key的field值 HMSET:批量添加多个hash类型key的field值 HMGET:获取获取多个hash类型key的field值 HGETALL:获取一个hash类型的key中的所有的field和value HKEYS:获取一个hash类型的key中的所有field HVALS:获取一个hash类型的key中的所有value HINCRBY:让一个hash类型key的字段值自增并指定步长 HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行 List类型 Redis中的List类型与Java中的LinkedList基本一致 List的常见命令: LPUSH key element…:想列表左侧插入一个或多个元素 LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil RPUSH key element…:向列表右侧插入一个或多个元素 RPOP key:移除并返回列表右侧的第一个元素 LRANGE key start end:返回一段角标范围内的所有元素 BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil 如何利用List结构模拟一个栈? 入口和出口在同一边 如何利用List结构模拟一个队列? 入口和出口在不同边 如何利用List结构模拟一个阻塞队列? 入口和出口在不同边 出队时采用BLPOP或BRPOP Set类型 Redis的Set结构和Java中的HashSet类似,可以看做是一个value为null的HashMap。 Set的常见指令: SADD key member …:向Set中添加一个或多个元素 SREM key member …:移除set中的指定元素 SCARD key:返回set中元素的个数 SISMEMBER key...
Mybatis入门
修改历史: 2025-06-28 修改了部分错误,优化了文档结构 2025-07-01 修复了代码块sql语句无法高亮的问题 Mybatis入门 0.序 Mybatis是持久层(也就是三层架构中的Dao层)的一款框架,简化了JDBC的开发。内容来自视频->Day08-14. Mybatis-入门-快速入门程序_哔哩哔哩_bilibili。 1.入门 快速入门 开启一个模版程序,实现使用Mybatis查询所有用户数据 准备工作(创建springboot文件,定义实际类User,数据库表user) 添加mybatis相关依赖,配置mybatis(数据库连接信息) 编写SQL语句(注解/XML) 具体操作流程如下: 创建springboot文件:与springboot入门中的创建方法基本一致,注意要勾选Mybatis Framework和MySQL Driver依赖。 定义实际类:实际类要与数据库中user的字段保证一致,常见的几种类型对应:int->Integer,varchar->String,tinyint->Short,主要使用其包装类。同时声明相应的getset方法、构造器、toString方法。 在properties配置文件中加入以下内容,格式比较固定,url要根据相应的数据库名称确定,密码设定为自己的密码 1234spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/mybatisspring.datasource.username=rootspring.datasource.password=1234 编写SQL语句(注解/XML) 123456@Mapper//在运行时,MyBatis会扫描这个接口,并为其生成一个实现类对象(代理对象),并交给IOC管理public interface UserMapper { //查询 @Select("select * from user") public List<User> list();} 单元测试 123456789101112@SpringBootTest//springboot整合单元测试的注解class SpringbootMybatisQuickstartApplicationTests { @Autowired private UserMapper userMapper; @Test public void testListUser() { List<User> userList = userMapper.list(); for (User user : userList) { System.out.println(user); } }} 创建完成后的文件结构如图 配置SQL提示 在上边的SQL语句中,如果没有配置,那么就不会显示相应的提示,即使写错了也不会提醒,这对于开发是非常不利的。好在Idea中可以为其配置MySQL数据库连接。 选中SQL语句,右键,配置为MySQL语句 点击右侧边栏的数据库选项,点击加号,数据源选择MySQL,填写host、port、用户和密码、数据库 数据库连接池 数据库连接池是个容器,负责分配、管理数据库连接 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个 能够释放空闲时间超过最大空闲时间的连接,避免因为没有释放连接引起的数据库连接遗漏 好处:资源重用 提升系统响应速度...
SpringBoot入门
修改历史: 增加了Web入门的基础知识,完善了结构 SpringBoot入门 0.概述 本篇只是对springboot的基本使用的总结,学习视频->Day05-01. 请求响应-概述_哔哩哔哩_bilibili,使用postman进行调试。 1.Web入门 HTTP协议 概述 HTTP协议(HyperTextTransferProtocol),超文本传输协议,规定了浏览器和服务器之间数据传输的规则。 特点: TCP协议:面向连接,安全 基于请求-响应模型:一次请求对应一次响应 HTTP是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的 缺点:多次请求无法共享数据 优点:速度快 请求协议 请求数据包括三个部分: 请求行:请求数据第一行(请求方式,资源路径、协议) 请求头:第二行开始,格式key: value 请求体:POST请求,存放请求参数 两种请求方式对比: 请求方式-get:请求参数在请求行中,没有请求体。get请求大小是有限制的。 请求方式-post:请求参数在请求体中,post请求大小是没有限制的。 响应协议 响应数据包括三部分: 响应行:相应数据第一行(协议、状态码、描述) 响应头:key: value 响应体:存放响应数据 常见状态码: 状态码 描述 1xx 响应中 2xx 成功 3xx 重定向-重定向到其他地方;让客户端再发起一次请求以完成整个处理 4xx 客户端错误-处理发生错误,责任在客户端。如请求了不存在的资源、客户端未被授权、禁止访问 5xx 服务器错误-处理发生错误,责任在服务端。如程序抛出异常 目前主要了解:200 处理成功,404 请求资源不存在,500 服务器端异常 更多状态码见网站: 状态 | Status - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云 协议解析 服务器端要做的事情,一方面是要根据请求协议,解析浏览器发送的请求;另一方面是要根据响应协议,发送响应数据。 这种解析的工作较为固定,有不少Web服务器已经完成了这些工作,这样后端就只用关注业务逻辑的实现。常见的Web服务器有:Tomcat、IBM、jetty、WebLogic等 Tomcat服务器 具体内容暂时不用详细了解,SpringBoot已经内嵌了Tomcat服务器,所以不用专门下载。介绍在此省略 2.请求 此处请求的含义:后端接收来自浏览器端的请求指令,进行解析 简单参数 请求: GET指令:http://localhost:8080/simpleParam?name=miku&age=16 POST指令:http://localhost:8080/simpleParam请求体:name=Miku&age=16 Java代码: 原始方式: 12345678910//在原始的Web程序中,获取请求参数,需要通过HttpServletRequest对象手动获取@RequestMapping("/simpleParam")public String simpleRequest(HttpServletRequest request) { String name = request.getParameter("name");//需要与请求参数的key保持一致 String ageStr = request.getParameter("age");//需要与请求参数的key保持一致 int age = Integer.parseInt(ageStr); System.out.println("Name: " + name + ", Age: " + age); return "OK";}//繁琐,并且需要进行手动类型转换 SpringBoot方式: 简单参数,参数名与形参列表名相同,定义形参即可接收参数 12345@RequestMapping("/simpleParam")public String simpleRequest(int age,String name) { System.out.println(name + ":" + age); return...
JavaSE-网络编程
网络编程 软件架构: CS架构:客户端-服务器 BS架构:浏览器-服务器 不过现在的很多软件都是一种混合开发,就是将CS架构和BS架构融合起来。 网络编程三要素 如何准确定位一台或多态主机:使用IP地址 如何定位主机上的特定的应用:使用端口号 如何可靠、高效地进行数据传输:规范通信协议 IP地址 IP地址:网络中的一台计算机设备做唯一编号 IP地址分类: 分类方式一: IPv4:占用4个字节,表示为a.b.c.d的形式,以点分十进制表示 IPv6:占用16个字节 分类方式二: 公网地址(万维网使用)和私网地址(局域网使用)192.168.开头的就是私有地址 本地回路地址:127.0.0.1 域名:便捷地记录ip地址 www.baidu.com kuoz.top 域名使用中通过DNS解析成IP再进行访问的 端口号 端口号可以唯一标识主机中的进程 不同的进程分配不同的端口号 范围:0~65535 网络通信协议 OSI模型:过于理想化,没能实现 TCP/IP:分为应用层、传输层、网络层、物理+数据链路层 InetAddress类 InetAddress类的一个实例就代表一个具体的ip地址 实例化方式: InetAddress getByName(String host) InetAddress getLocalHost() TCP协议和UDP协议 计算机网络的重要重要指示,在此处简要了解。 TCP的Socket网络编程 网络上具有唯一标识的IP地址和端口号的组合,唯一可识别的标识符套接字(Socket) 1234567891011121314151617181920212223242526272829303132333435363738394041@Testpublic void client() throws IOException { //创建Socket实例 int port = 8888; InetAddress address = InetAddress.getByName("10.10.208.62"); Socket socket = new Socket(address, port); //通过Socket创建输出流 OutputStream os = socket.getOutputStream(); //发送数据 os.write("嗨嗨嗨, 服务器我来辣!".getBytes()); //关闭Socket及相关流 os.close(); socket.close();}@Testpublic void server() throws IOException { //创建serverSocket实例 int port = 8888; ServerSocket serverSocket = new ServerSocket(port); //接收来自于客户端的Socket Socket socket = serverSocket.accept(); System.out.println("客户端连接成功,等待接收数据..."); System.out.println("收到了来自"+socket.getInetAddress().getHostAddress()+"的连接"); //通过Socket创建输入流 InputStream is = socket.getInputStream(); //读写过程 byte[] buffer = new byte[5]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; while((len = is.read(buffer)) != -1) { //当byte太小时,这里中文会因为中间被截断而出乱码,通过baos来解决 //String message = new String(buffer, 0, len); //解决方法 baos.write(buffer, 0, len); } System.out.print(baos.toString()); System.out.print("\n接收完毕"); //关闭Socket和相关流 is.close(); ...
Maven
Maven 概述 Maven是一款管理和构建Java项目的工具。基于POM(项目对象模型)的概念,通过一小段描述来管理项目 Maven坐标,唯一标识和定位一个项目 groupId:组织 artifactId:模块名称 version:版本 仓库:用于存储资源,管理jar包 本地仓库:自己计算机上的一个目录 中央仓库:由Maven团队维护的全球唯一的仓库 远程仓库:一般由公司团队搭建的私有仓库 使用顺序:本地仓库>远程仓库>中央仓库 依赖管理 依赖:指当前项目运行所需的jar包 配置依赖(在IDEA中) 在pom.xml中编写标签 在标签中使用引入坐标 定义坐标的groupId,artifactId,version 点击刷新按钮,引入最新加入的坐标 12345678<dependencyies> <dependency> <groupId></groupId> <artifactId></artifactId> <version></version> </dependency> ...</dependencyies> 依赖传递 依赖具有传递性 直接依赖:在当前项目中通过依赖配置建立的关系 间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源 排除依赖:主动断开依赖的资源,被排除的资源无需指定版本 123456<exclusions> <exclusion> <groupId></groupId> <artifactId></artifactId> </exclusion></exclusions> 依赖范围 scope值 主程序 测试程序 打包(运行) 范例 compile(默认) Y Y Y log4j test - Y - junit provided Y Y - servlet-api runtime - Y Y jdbc驱动 可以通过下面的xml标签来设置依赖范围 1<scope>...</scope> 生命周期 每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。(运行后面的阶段,前面的阶段也会运行) 生命周期阶段(常见) 常见生命周期阶段 clean 移除上一次构建生成的文件 compile 编译项目源代码 test 使用合适的单元测试框架运行测试 package 将编译后的文件打包,如:jar、war等 install 安装项目到本地仓库