Java中NIO的简单介绍
NIO基本介绍
- Java NIO(New IO) 也有人称之为Java non-blocking IO 是从Java1.4版本开始引入的一个新的IO API,可以代替标准的IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的,基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。NIO可以理解为非阻塞IO,传统的IO的read和write只能阻塞执行,线程在读写IO期间不能干其他事情,比如调用socket.read()时,如果服务器一直没有数据传输过来,线程就一直阻塞,而NIO中可以配置socket为非阻塞模式
- NIO有三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)
NIO和BIO的比较
- BIO以流的方式处理数据,而NIO以块的方式处理数据,块IO的效率比流IO高很多
- BIO是阻塞的,NIO则是非阻塞的
- BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道
NIO三大核心原理
Buffer缓冲区
- 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存,这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存,相比较直接对数组的操作,Buffer API更加容易操作和管理
Channel(通道)
- Java NIO的通道类似流,但又有些不同 : 既可以从通道中读取数据,又可以写数据到通道。但流的(input或output)读写通常是单向的。通道可以非阻塞读取和写入通道,通道可以支持读取或写入缓冲区,也支持异步地读写
Selector选择器
- Selector是一个Java NIO组件,可以能够检查一个或多个NIO通道,并确定哪些通道已经准备好进行读取或写入。这压根,一个单独的线程可以管理多个channel,从而管理多个网络连接
- 每个Channel都会对应一个Buffer
- 一个线程对应Selector,一个Selector对应多个Channel(连接)
- 程序切换到那个Channel是由事件决定的
- Selector会根据不同的事件,在各个通道上切换
- Buffer就是一个内存块,底层是一个数组
- 数据的读取写入是通过Buffer完成的,BIO中要么是输入流,或者是输出流,不能双向,但是NIO的Buffer时可以读也可以写。
- Channel负责传输,Buffer负责存取数据
缓冲区Buffer
- 一个用于特定基本数据类型的容器。由 Java。nio包定义的,所有缓冲区都是Buffer抽象类的子类。Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的
Buffer类及其子类
Buffer就像一个数组,可以保存多个相同类型的数据。根据数据类型不同,有以下Buffer常用子类:
- ByteBuffer
- CharBuffer
- ShortBuffer
- xxxBuffer(xxx代表八种基本数据类型)
上述Buffer类 他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已。都是通过如下方法获取一个Buffer对象:
//创建一个容量为capacity的xxxBuffer对象
static xxxBuffer allocate(int capacity);
|
|
|
IntBuffer buffer = IntBuffer.allocate(10);
Buffer中的重要概念
容量(capacity):创建后不能更改,且容量不能为负
限制(limit):表示缓冲区中可以操作数据的大小.缓冲区的限制不能为负,并且不能大于其容量.
写入模式,限制等于buffer的容量.读取模式下,limit等于写入的数据量
位置(position):下一个要读取或写入的数据的索引.缓冲区的位置不能为负,并且不能大于其限制
标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法 指定 Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position.
常用API测试
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION:
* @USER: 罗龙达
* @DATE: 2021/2/10 17:34
*/
public class apiTest {
public void print(Buffer buffer){
System.out.println("pos = " + buffer.position());
System.out.println("lim = " + buffer.limit());
System.out.println("cap = " + buffer.capacity());
}
@Test
public void test001(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
buffer.put("LongDa66".getBytes());
print(buffer);
//3. Buffer flip() --> 将缓冲区的界限设置为当前位置,并将当前位置设置为0 可读模式
System.out.println("--------调用flip()方法--------");
buffer.flip();
print(buffer);
//4. get数据的读取
System.out.println("--------缓冲区中读取数据--------");
byte b = buffer.get();
System.out.println("从缓冲区中读取 " + (char)b);
print(buffer);
}
@Test
public void test002(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
buffer.put("LongDa66".getBytes());
print(buffer);
//2. 清除缓冲区中的数据,调用clear方法后只是将pos移到了0.
System.out.println("-------调用clear()方法后--------");
buffer.clear();
byte b = buffer.get();
System.out.println("从缓冲区中读取 " + (char)b);
print(buffer);
}
@Test
public void test003(){
//1. 分配一个缓冲区,容量设置为10
ByteBuffer buffer = ByteBuffer.allocate(10);
print(buffer);
//2. put往缓冲区中添加数据
System.out.println("--------缓冲区添加数据--------");
System.out.println("向缓冲区添加 : LongDa66");
buffer.put("LongDa66".getBytes());
print(buffer);
buffer.flip();
//3. 从缓冲区中读取前4位
System.out.println("--------缓冲区读取前4位数据--------");
byte[] bytes = new byte[4];
buffer.get(bytes);
String s = new String(bytes);
System.out.println(s);
print(buffer);
System.out.println("-----接着用mark()标记后,读取的数据-----");
buffer.mark();
byte[] bytes2 = new byte[4];
buffer.get(bytes2);
String s2 = new String(bytes2);
System.out.println(s2);
print(buffer);
System.out.println("-----调用reset()回到标记位置-----");
buffer.reset();
print(buffer);
System.out.println("-----调用remaining,看看position和limit之间剩余元素个数-----");
print(buffer);
System.out.println("缓冲区剩余元素个数" + buffer.remaining());
}
}
直接缓冲区与非直接缓冲区
- 非直接缓冲区 : 通过allocate() 方法分配缓冲区,将缓冲区建立在JVM的内存中。
- 直接缓冲区 : 通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。
通道Channel
- 通道(Channel):表示IO源与目标打开的连接。Channel类似于传统的“流”.只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互
Channel与流的区别
- Channel可以同时进行读写,而流只能读或者写
- Channel可以实现异步读写数据
- Channel可以从缓冲读数据,也可以写数据到缓冲
Channel在NIO中是一个接口
Channel常用实现类
- FileChannel : 用于读取/写入/映射和操作文件的通道
- DatagramChannel : 通过UDP读写网络中的数据通道
- SocketChannel : 通过TCP读写网络中的数据
- ServerSocketChannel : 可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
FileChannel类
常用方法测试
- 写入文件
@Test
public void test004() {
try {
//1. 字节输出流通向目标文件
FileOutputStream fos = new FileOutputStream("D:\\data2.txt");
//2. 得到字节输出流对应的通道
FileChannel channel = fos.getChannel();
//3. 分配缓冲区
ByteBuffer bufer = ByteBuffer.allocate(1024);
bufer.put("hello,world".getBytes());
//4. 切换成写模式
bufer.flip();
channel.write(bufer);
//5. 关闭通道
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 读取文件
@Test
public void test005(){
try {
//定义一个文件字节输入流与源文件连通
FileInputStream fis = new FileInputStream("D:\\data.txt");
//得到文件字节输入流的文件通道
FileChannel channel = fis.getChannel();
//定义一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据到缓冲区
channel.read(buffer);
buffer.flip();
//读取缓冲区中的数据
String s = new String(buffer.array());
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
- 文件的复制测试
@Test
public void test006(){
File file = new File("D:\\data.txt");
try {
//得到字节输入/输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream("D:\\data3.txt");
//得到输入输出流的通道
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
while(true){
//清空缓冲区再写入数据
buffer.clear();
//判断文件是否结束
int flag = fisChannel.read(buffer);
if(flag == -1){
break;
}
//切换写模式,写入数据
buffer.flip();
fosChannel.write(buffer);
fisChannel.close();
fosChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
- 分散读取和聚集操作数据
@Test
public void test007(){
File file = new File("D:\\data.txt");
File file2 = new File("D:\\data3.txt");
try {
//字节输入输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
//定义多个缓冲区 --> 数据分散
ByteBuffer buffer1 = ByteBuffer.allocate(4);
ByteBuffer buffer2 = ByteBuffer.allocate(400);
ByteBuffer[] buffers = {buffer1,buffer2};
//从通道中读取数据分散到各个缓冲区
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//从通道中读取数据分散到各个缓冲区
fisChannel.read(buffers);
//从每个缓冲区中查询是否有数据读取到了
for (ByteBuffer buffer : buffers) {
buffer.flip();
System.out.println(new String(buffer.array(),0,buffer.remaining()));
}
//聚集操作缓冲区
fosChannel.write(buffers);
fisChannel.close();
fosChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- TransferFrom() & TransferTo()方法
@Test
public void test008(){
File file = new File("D:\\data.txt");
File file2 = new File("D:\\data3.txt");
try {
//字节输入输出流
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
//从通道中读取数据分散到各个缓冲区
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//复制数据
//从目标通道中复制原通道数据
// fosChannel.transferFrom(fisChannel,fisChannel.position(),fisChannel.size());
//把原通道数据复制到目标通道数据
fisChannel.transferTo(fisChannel.position(),fisChannel.position(),fosChannel);
fisChannel.close();
fosChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
NIO非阻塞式网络通信原理分析
Selector可以实现 : 一个I/O线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞I/O已连接一线程模型,架构的性能,弹性伸缩能力和可靠性都得到了极大的提升.
入门案例
- 服务器端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 目标 : NIO非阻塞通信下的入门案例 : 服务器端
* @USER: 罗龙达
* @DATE: 2021/2/11 0:33
*/
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("---------服务端启动-----------");
//获取通道 --> 接收客户端的连接请求
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//切换为非阻塞模式
ssChannel.configureBlocking(false);
//绑定连接的端口
ssChannel.bind(new InetSocketAddress(9999));
//获取选择器 Selector
Selector selector = Selector.open();
//将通道都注册到选择器上,并且开始指定监听接收事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//使用Selector轮询已经准备就绪的事件
while (selector.select() > 0){
//获取选择器中的所有注册的通道中已经准备就绪的事件
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
//遍历已经准备好的时间
while (it.hasNext()){
System.out.println("开始一轮事件处理");
//提取当前事件
SelectionKey sk = it.next();
//判断这个事件具体是什么
if(sk.isAcceptable()){
//接收事件准备就绪,直接获取当前接入的客户端通道
SocketChannel sChannel = ssChannel.accept();
//切换成非阻塞模式
sChannel.configureBlocking(false);
//将本客户端通道注册到选择器里 服务器端监听读事件
sChannel.register(selector,SelectionKey.OP_READ);
}
//读事件
else if(sk.isReadable()){
//获取当前选择器上的读就绪事件
SocketChannel sChannel = (SocketChannel) sk.channel();
//读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = 0;
while ((len = sChannel.read(buffer)) > 0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
//清除之前的数据
buffer.clear();
}
}
//处理完毕移除当前事件 防止重复监听
it.remove();
}
}
}
}
- 客户端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 目标: 客户端案例实现 - 基于NIO非阻塞通信
* @USER: 罗龙达
* @DATE: 2021/2/11 0:56
*/
public class Client {
public static void main(String[] args) throws IOException {
//获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9999));
//切换成非阻塞模式
socketChannel.configureBlocking(false);
//指定缓冲区大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
//发送数据给服务端
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("请说:");
String s = scanner.nextLine();
LocalDateTime timeNow = LocalDateTime.now();
buffer.put((timeNow + " 波妞 : " + s).getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
}
}
群聊案例
- 服务器端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION:
* @USER: 罗龙达
* @DATE: 2021/2/11 1:47
*/
public class Server {
//定义选择器,服务端通道,端口
private Selector selector;
private ServerSocketChannel ssChannel;
private static final int PORT = 9999;
//初始化
public Server(){
try {
//创建选择器
selector = Selector.open();
//获取通道
ssChannel = ServerSocketChannel.open();
//绑定客户端连接的端口
ssChannel.bind(new InetSocketAddress(PORT));
//设置非阻塞通信模式
ssChannel.configureBlocking(false);
//八通道注册到选择器上,并且开始指定接收事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 监听事件
*/
private void listen(){
try {
while(selector.select() > 0){
//获取选择器中所有注册通道的就绪事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
//开始遍历
while (iterator.hasNext()){
SelectionKey sk = iterator.next();
//判断事件的类型
if(sk.isAcceptable()){
//客户端接入请求
//获取当前客户端通道
SocketChannel socketChannel = ssChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}
else if(sk.isReadable()){
//处理这个客户端的消息,接收它然后实现转发逻辑
readClientData(sk);
}
iterator.remove();//处理完毕,移除当前事件
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 接受当前客户端通道的信息,转发给其他全部客户端通道
* @param sk
*/
private void readClientData(SelectionKey sk) {
SocketChannel socketChannel = null;
try{
//获取当前客户端通道
socketChannel = (SocketChannel) sk.channel();
//创建缓冲区对象开始接受客户端通道的数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = socketChannel.read(buffer);
if (count > 0){
buffer.flip();
//提取读取到的信息
String msg = new String(buffer.array(), 0, buffer.remaining());
System.out.println("接收到客户端消息 : " + msg);
sendMsgToAllClient(msg,socketChannel);
}
}catch (Exception e){
try {
System.out.println("有人离线了 : " + socketChannel.getRemoteAddress());
//当前客户端离线
sk.cancel();
socketChannel.close();
} catch (IOException ioException) {
}
}
}
/**
* 把当前客户端的消息数据都推送给当前全部在线注册的channel
* @param msg
* @param socketChannel
*/
private void sendMsgToAllClient(String msg, SocketChannel socketChannel) {
System.out.println("服务端开始转发消息, 当前处理的线程 : " + Thread.currentThread().getName());
for (SelectionKey key : selector.keys()) {
Channel channel =key.channel();
if(channel instanceof SocketChannel && socketChannel != channel){
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
try {
((SocketChannel)channel).write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//创建服务端对象
Server server = new Server();
//开始监听客户端的各种消息事件
server.listen();
}
}
- 客户端
/**
* @PROJECT_NAME: JAVA_Test
* @DESCRIPTION: 客户端代码逻辑实现
* @USER: 罗龙达
* @DATE: 2021/2/11 17:33
*/
public class Client {
private Selector selector;
private static int PORT = 9999;
private static SocketChannel socketChannel;
public Client(){
try {
//创建选择器
selector = Selector.open();
//连接服务端
socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",PORT));
//设置非阻塞通信模式
socketChannel.configureBlocking(false);
//八通道注册到选择器上,并且开始指定接收事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("当前客户端准备完成");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client();
//定义一个线程专门负责监听服务端发送过来的读消息事件
new Thread(client::readInfo).start();
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()){
String s = sc.nextLine();
Client.sendMsg(s);
}
}
private static void sendMsg(String s) {
try {
socketChannel.write(ByteBuffer.wrap(("波仔说:" + s).getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
private void readInfo() {
try{
while(selector.select() > 0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
if (key.isReadable()){
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array()).trim());
System.out.println("--------分割线-----------");
}
iterator.remove();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
AIO异步非阻塞IO
- Java AIO : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可,这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统会主动通知应用程序
可以理解为,read/write方法都是异步的,完成后会主动调用回调函数,在JDK1.7中,这部分内容被称作NIO 2
BIO,NIO,AIO三者比较
Java BIO :
同步并阻塞,服务器实现模式为一个链接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个链接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善.
Java NIO :
同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用轮询到连接有I/O请求时才启动一个线程进行处理
Java AIO :
异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理
使用场景分析
- BIO适用于连接数目较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,但程序直观简单易理解
- NIO方式适用于连接数目多且连接比较短的架构,比如聊天服务器,并发局限于应用中,编程比较复杂
- AIO方式适用于连接数目多且连接比较长的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂
Java中NIO的简单介绍的更多相关文章
- java中数据流的简单介绍
java中的I/O操作主要是基于数据流进行操作的,数据流表示了字符或者字节的流动序列. java.io是数据流操作的主要软件包 java.nio是对块传输进行的支持 数据流基本概念 “流是磁盘或其它外 ...
- java 中的多线程简单介绍
package com.zxf.demo; /* * 多线程的实现方式两种? * 一..实现 runnable 接口 * 2.重写run方法 Run():当一个线程启动后,就会自动执行该方法 * 3. ...
- 多线程(三) java中线程的简单使用
java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依 ...
- Java基础-JAVA中常见的数据结构介绍
Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...
- java反射机制的简单介绍
参考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先给出反射机制中常用的几个方法: Class.forName (& ...
- java基础---->java中nio的使用(一)
JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的.面向块的 I/O.今天我们就简单的学习一下nio的知识.我笑,便面如春花,定是能感动人的,任他是谁. nio的简 ...
- Java中NIO、BIO、AIO相关概念及应用场景
1.同步阻塞IO(JAVA BIO):同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时,服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通 ...
- Java泛型使用的简单介绍
目录 一. 泛型是什么 二. 使用泛型有什么好处 三. 泛型类 四. 泛型接口 五. 泛型方法 六. 限定类型变量 七. 泛型通配符 7.1 上界通配符 7.2 下界通配符 7.3 无限定通配符 八. ...
- 史上最全 Java 中各种锁的介绍
更多精彩原创内容请关注:JavaInterview,欢迎 star,支持鼓励以下作者,万分感谢. 锁的分类介绍 乐观锁与悲观锁 锁的一种宏观分类是乐观锁与悲观锁.乐观锁与悲观锁并不是特定的指哪个锁(J ...
随机推荐
- ACNet:用于图像超分的非对称卷积网络
编辑:Happy 首发:AIWalker Paper:https://arxiv.org/abs/2103.13634 Code:https://github.com/hellloxiaotian/A ...
- 北航OO第四单元作业总结(4.1~4.3)及课程总结
前言 在学习过JML规格描述语言之后,本单元进行了UML(Unified Modeling Language)的学习.和JML单纯用语言描述的形式不同,UML通过可视化的图形形式,对一系列有关类的元素 ...
- C语言-字符串函数的实现(一)之strlen
C语言中的字符串函数有如下这些 获取字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数 strncpy strncat strncmp ...
- SpringBoot 使用逆向工程 构建Mapper.xml Dao层(持久层) 实体类
逆向工程 注: 有数据库表即可 第一步为创建数据库表 (可选)使用PowerDesigner设计数据库表,物理模型构建 添加pom.xml 逆向工程生成代码插件 <!--plugin 逆向工程生 ...
- SpringBoot - yml写法
1 #区分大小写 2 server: 3 port: 8081 4 path: hello 5 6 #字面量:普通的值(数字,字符串,布尔): 7 #字符串:双引号 - 不转义 单引号 - 转义 8 ...
- 前端DDD总结与思考
软件开发架构演化与DDD起源 单体服务架构:大概10年前,我在武汉工作的时候,甲方客户购买我们的产品,一般都是连着设备一起购买,一套软件系统,一台惠普或者戴尔的企业级服务器,再加一个黑色的铁盒,销售部 ...
- HTTP 基础(特性、请求方法、状态码、字段)
1. HTTP 简介(含义.特性.缺点) 2. HTTP 报文 3. GET 和 POST 4. 状态码 5. HTTP 头字段 1. HTTP 简介 HTTP 的含义 HTTP (HyperText ...
- k8s 证书更新操作
kubernetes证书更新 版本:1.14.2,以下操作在3台master节点上操作 1.各个证书过期时间 /etc/kubernetes/pki/apiserver.crt #1年有效期 /etc ...
- 13- jmeter性能测试案例
配置原件 HTTP请求默认值 前置处理程序 定时器 取样器 后置处理器:正则表达式提取器 断言 监听器 性能测试流程 1.评估获取性能测试需求(访问量大,操作频繁) 2.确定性能测试目标 : 并发用户 ...
- 小图标文字对齐的终极解决方案demo
CSS代码: .icon { display: inline-block; width:20px; height:20px; background: url(delete.png) no-repeat ...