diff --git a/.gitignore b/.gitignore
index 9c63d81..3540bb2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,10 @@ Spring-Boot/learnsb.iml
Spring/learnspring.iml
Spring-AOP/learnaop.iml
Spring-Netty/Spring-Netty.iml
+Spring-Netty/learnnetty.iml
Spring-Security/SpringSecurityDemo.iml
Spring-Security/springsecurity.iml
+rocketmqdemo/rocketmqdemo.iml
# target
JdkLearn/target
@@ -17,6 +19,7 @@ Spring-AOP/target
Spring-Boot/target
Spring-Netty/target
Spring-Security/target
+rocketmqdemo/target
# .DO_Store
.DS_Store
diff --git a/JdkLearn/pom.xml b/JdkLearn/pom.xml
index d196fc9..9fb74db 100644
--- a/JdkLearn/pom.xml
+++ b/JdkLearn/pom.xml
@@ -20,6 +20,18 @@
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+
+
+ io.netty
+ netty-all
+
+
org.springframework.boot
diff --git a/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java b/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java
new file mode 100644
index 0000000..aca54da
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/collection/LRUCache.java
@@ -0,0 +1,71 @@
+package com.learnjava.collection;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/6
+ */
+public class LRUCache implements Iterable {
+
+ private int MAX = 3;
+ private LinkedHashMap cache = new LinkedHashMap<>();
+
+ public void cache(K key, V value) {
+ if (cache.containsKey(key)) {
+ cache.remove(key);
+ } else if (cache.size() >= MAX) {
+ Iterator iterator = cache.keySet().iterator();
+ K first = iterator.next();
+ cache.remove(first);
+ }
+ cache.put(key, value);
+ }
+
+ public V getValue(K k) {
+ return cache.get(k);
+ }
+
+ @Override
+ public void forEach(Consumer super K> action) {
+ Iterable.super.forEach(action);
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return Iterable.super.spliterator();
+ }
+
+ @Override
+ public Iterator iterator() {
+ Iterator iterator = cache.keySet().iterator();
+ return new Iterator() {
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public K next() {
+ return iterator.next();
+ }
+ };
+ }
+
+ public static void main(String[] args) {
+ LRUCache cache = new LRUCache<>();
+ cache.cache("1", "1A");
+ cache.cache("2", "2A");
+ cache.cache("3", "3A");
+ cache.cache("1", "1A");
+
+ for (String next : cache) {
+ System.out.println(cache.getValue(next));
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java
new file mode 100644
index 0000000..0d04fb8
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/ReentrantLockDemo.java
@@ -0,0 +1,9 @@
+package com.learnjava.concurent;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/7
+ */
+public class ReentrantLockDemo {
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdDemo.java
new file mode 100644
index 0000000..aa00a30
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdDemo.java
@@ -0,0 +1,12 @@
+package com.learnjava.concurent;
+
+/**
+ * @author lhy
+ * @date 2021/7/27
+ */
+public class SnowIdDemo {
+ public static void main(String[] args) {
+ // 通过雪花秀算法获取分布式id
+ System.out.println(SnowIdUtils.uniqueLongHex());
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java
new file mode 100644
index 0000000..384d980
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/SnowIdUtils.java
@@ -0,0 +1,114 @@
+package com.learnjava.concurent;
+
+/**
+ * @author lhy
+ * @date 2021/7/27
+ */
+public class SnowIdUtils {
+ /**
+ * 私有的 静态内部类
+ */
+ private static class SnowFlake {
+
+ /**
+ * 内部类对象(单例模式)
+ */
+ private static final SnowIdUtils.SnowFlake SNOW_FLAKE = new SnowIdUtils.SnowFlake();
+ /**
+ * 起始的时间戳
+ */
+ private final long START_TIMESTAMP = 1609464335121L;
+ /**
+ * 序列号占用位数
+ */
+ private final long SEQUENCE_BIT = 12;
+ /**
+ * 机器标识占用位数
+ */
+ private final long MACHINE_BIT = 10;
+ /**
+ * 时间戳位移位数
+ */
+ private final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
+ /**
+ * 最大序列号 (4095)
+ */
+ private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
+ /**
+ * 最大机器编号 (1023)
+ */
+ private final long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT);
+ /**
+ * 生成id机器标识部分
+ */
+ private long machineIdPart;
+ /**
+ * 序列号
+ */
+ private long sequence = 0L;
+ /**
+ * 上一次时间戳
+ */
+ private long lastStamp = -1L;
+
+ /**
+ * 构造函数初始化机器编码
+ */
+ private SnowFlake() {
+// String ip = instance.getDockerIp().replace(".", "");
+ // 模拟获取机器节点ip
+ String ip = "127.0.0.1";
+ long localIp = Long.parseLong(ip.replace(".", ""));
+ machineIdPart = (localIp & MAX_MACHINE_ID) << SEQUENCE_BIT;
+ }
+ /**
+ * 获取雪花ID
+ */
+ public synchronized long nextId() {
+ long currentStamp = timeGen();
+ while (currentStamp < lastStamp) {
+ throw new RuntimeException(String.format("时钟已经回拨. Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
+ }
+ if (currentStamp == lastStamp) {
+ sequence = (sequence + 1) & MAX_SEQUENCE;
+ if (sequence == 0) {
+ currentStamp = getNextMill();
+ }
+ } else {
+ sequence = 0L;
+ }
+ lastStamp = currentStamp;
+ return (currentStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | machineIdPart | sequence;
+ }
+ /**
+ * 阻塞到下一个毫秒,直到获得新的时间戳
+ */
+ private long getNextMill() {
+ long mill = timeGen();
+ //
+ while (mill <= lastStamp) {
+ mill = timeGen();
+ }
+ return mill;
+ }
+ /**
+ * 返回以毫秒为单位的当前时间
+ */
+ protected long timeGen() {
+ return System.currentTimeMillis();
+ }
+ }
+
+ /**
+ * 获取long类型雪花ID
+ */
+ public static long uniqueLong() {
+ return SnowIdUtils.SnowFlake.SNOW_FLAKE.nextId();
+ }
+ /**
+ * 获取String类型雪花ID
+ */
+ public static String uniqueLongHex() {
+ return String.format("%016X", uniqueLong());
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java
new file mode 100644
index 0000000..4a89467
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/SynchronizeDemo.java
@@ -0,0 +1,9 @@
+package com.learnjava.concurent;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/7
+ */
+public class SynchronizeDemo {
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java
new file mode 100644
index 0000000..5d5b5fc
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/concurent/ThreadPoolExecutorDemo.java
@@ -0,0 +1,48 @@
+package com.learnjava.concurent;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/4/24
+ */
+public class ThreadPoolExecutorDemo {
+
+ public static void main(String[] args) {
+ testThreadPoolExecutorBinaryCalc();
+ }
+
+
+ /**
+ * 验证ThreadPoolExecutor中的二进制位运算操作
+ */
+ private static void testThreadPoolExecutorBinaryCalc() {
+// System.out.println(ctl.get());
+// System.out.println(Integer.toBinaryString(ctlOf(RUNNING, 0)));
+// System.out.println(Integer.toBinaryString(RUNNING));
+ // 修改线程状态-STOP
+ System.out.println(Integer.toBinaryString(~runStateOf(ctlOf(STOP, 10))));
+ // 修改线程状态-TERMINATED
+// System.out.println(runStateOf(3));
+// System.out.println(Integer.toBinaryString(~CAPACITY));
+ }
+
+ private static final int COUNT_BITS = Integer.SIZE - 3;
+
+ private static final int CAPACITY = (1 << COUNT_BITS) - 1;
+
+ private static final int RUNNING = -1 << COUNT_BITS;
+ private static final int SHUTDOWN = 0 << COUNT_BITS;
+ private static final int STOP = 1 << COUNT_BITS;
+ private static final int TIDYING = 2 << COUNT_BITS;
+ private static final int TERMINATED = 3 << COUNT_BITS;
+
+ private static AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
+
+ private static int runStateOf(int c) { return c & ~CAPACITY; }
+
+ private static int workerCountOf(int c) { return c & CAPACITY; }
+
+ private static int ctlOf(int rs, int wc) { return rs | wc; }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java b/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java
new file mode 100644
index 0000000..4226be5
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/io/bio/BIO.java
@@ -0,0 +1,47 @@
+package com.learnjava.io.bio;
+
+import java.io.InputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * @author lhy
+ *
+ * 在windows服务器下,可以使用telnet来合serversocket建立连接
+ */
+public class BIO {
+ public static void main(String[] args) throws Exception {
+ ServerSocket serverSocket = new ServerSocket(666);
+ System.out.println("Server started...");
+ while (true) {
+ System.out.println("socket accepting...");
+ Socket socket = serverSocket.accept();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ byte[] bytes = new byte[1024];
+ InputStream inputStream = socket.getInputStream();
+ while (true) {
+ System.out.println("reading...");
+ int read = inputStream.read(bytes);
+ if (read != -1) {
+ System.out.println(new String(bytes, 0, read));
+ } else {
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }).start();
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/Client.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java
similarity index 97%
rename from JdkLearn/src/main/java/com/learnjava/nettysource/Client.java
rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java
index 5514bee..f84b58e 100644
--- a/JdkLearn/src/main/java/com/learnjava/nettysource/Client.java
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Client.java
@@ -1,4 +1,4 @@
-package com.learnjava.nettysource;
+package com.learnjava.io.netty.bio;
import java.io.IOException;
import java.net.Socket;
diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java
similarity index 96%
rename from JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java
rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java
index d0ddba9..d2d6b88 100644
--- a/JdkLearn/src/main/java/com/learnjava/nettysource/ClientHandler.java
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ClientHandler.java
@@ -1,4 +1,4 @@
-package com.learnjava.nettysource;
+package com.learnjava.io.netty.bio;
import java.io.IOException;
import java.io.InputStream;
diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/Server.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java
similarity index 94%
rename from JdkLearn/src/main/java/com/learnjava/nettysource/Server.java
rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java
index 834f41b..af55558 100644
--- a/JdkLearn/src/main/java/com/learnjava/nettysource/Server.java
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/Server.java
@@ -1,4 +1,4 @@
-package com.learnjava.nettysource;
+package com.learnjava.io.netty.bio;
import java.io.IOException;
import java.net.ServerSocket;
@@ -30,7 +30,7 @@ public void run() {
}
public void doStart() {
- while (true) {
+ for (;;) {
try {
Socket client = serverSocket.accept();
new ClientHandler(client).start();
diff --git a/JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java
similarity index 76%
rename from JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java
rename to JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java
index 9348e09..bbb2762 100644
--- a/JdkLearn/src/main/java/com/learnjava/nettysource/ServerBoot.java
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/bio/ServerBoot.java
@@ -1,4 +1,4 @@
-package com.learnjava.nettysource;
+package com.learnjava.io.netty.bio;
/**
@@ -9,6 +9,6 @@ public class ServerBoot {
public static void main(String[] args) {
Server server = new Server(PORT);
-
+ server.start();
}
}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java
new file mode 100644
index 0000000..7c1d994
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/Server.java
@@ -0,0 +1,53 @@
+package com.learnjava.io.netty.demo01;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.*;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.AttributeKey;
+
+/**
+ * @author lhy
+ * @date 2021/5/21
+ */
+public class Server {
+
+ public static void main(String[] args) throws Exception {
+
+ /**
+ * EventLoopGroup:
+ * NioEventLoopGruop
+ * NioEventLoop
+ */
+
+ EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+ EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ ServerBootstrap bootstrap = new ServerBootstrap();
+ bootstrap.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childOption(ChannelOption.TCP_NODELAY, true)
+ .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
+ .handler(new ServerHandler())
+ .childHandler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) {
+// ch.pipeline().addLast(new AuthHandler());
+ //..
+
+ }
+ });
+
+ ChannelFuture future = bootstrap.bind(8888).sync();
+
+ future.channel().closeFuture().sync();
+ } finally {
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java
new file mode 100644
index 0000000..7123116
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/io/netty/demo01/ServerHandler.java
@@ -0,0 +1,74 @@
+package com.learnjava.io.netty.demo01;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+
+/**
+ * @author lhy
+ * @date 2021/5/22
+ */
+public class ServerHandler extends ChannelInboundHandlerAdapter {
+ public ServerHandler() {
+ super();
+ }
+
+ @Override
+ public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+ super.channelRegistered(ctx);
+ }
+
+ @Override
+ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
+ super.channelUnregistered(ctx);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ super.channelRead(ctx, msg);
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+ super.channelReadComplete(ctx);
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ super.userEventTriggered(ctx, evt);
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ super.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ super.exceptionCaught(ctx, cause);
+ }
+
+ @Override
+ public boolean isSharable() {
+ return super.isSharable();
+ }
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ super.handlerAdded(ctx);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ super.handlerRemoved(ctx);
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java b/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java
index 4097936..75cf208 100644
--- a/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java
+++ b/JdkLearn/src/main/java/com/learnjava/io/nio/MappedByteBufferTest.java
@@ -1,5 +1,8 @@
package com.learnjava.io.nio;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
@@ -17,13 +20,55 @@ public static void main(String[] args) throws Exception {
MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
- mappedByteBuffer.put(0, (byte)'H');
- mappedByteBuffer.put(3, (byte)'9');
+ mappedByteBuffer.put(0, (byte) 'H');
+ mappedByteBuffer.put(3, (byte) '9');
// IndexOutOfBoundsException
- mappedByteBuffer.put(5, (byte)'Y');
+ mappedByteBuffer.put(5, (byte) 'Y');
randomAccessFile.close();
System.out.println("change success");
}
+
+ /**
+ *
+ * @param from
+ * @param to
+ * @throws IOException
+ */
+ public static void mmap4zeroCopy(String from, String to) throws IOException {
+ FileChannel source = null;
+ FileChannel destination = null;
+ try {
+ source = new RandomAccessFile(from, "r").getChannel();
+ destination = new RandomAccessFile(to, "rw").getChannel();
+ MappedByteBuffer inMappedBuf =
+ source.map(FileChannel.MapMode.READ_ONLY, 0, source.size());
+ destination.write(inMappedBuf);
+ } finally {
+ if (source != null) {
+ source.close();
+ }
+ if (destination != null) {
+ destination.close();
+ }
+ }
+ }
+
+ public static void sendfile4zeroCopy(String from, String to) throws IOException{
+ FileChannel source = null;
+ FileChannel destination = null;
+ try {
+ source = new FileInputStream(from).getChannel();
+ destination = new FileOutputStream(to).getChannel();
+ source.transferTo(0, source.size(), destination);
+ } finally {
+ if (source != null) {
+ source.close();
+ }
+ if (destination != null) {
+ destination.close();
+ }
+ }
+ }
}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java
new file mode 100644
index 0000000..aab903b
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioClinet.java
@@ -0,0 +1,40 @@
+package com.learnjava.io.nio.demo01;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Date;
+import java.util.Scanner;
+
+/**
+ * @author lhy
+ * @date 2021/5/22
+ */
+public class NioClinet {
+
+ public static void main(String[] args) throws Exception {
+
+ // 获取通道
+ SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
+ // 切换至非阻塞模式
+ channel.configureBlocking(false);
+ // 分配缓冲区大小
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+
+ Scanner scan = new Scanner(System.in);
+
+ while (scan.hasNext()) {
+ String next = scan.next();
+ // 向缓冲区里写入数据
+ buffer.put( (new Date().toString() + "\n" + next).getBytes());
+ buffer.flip();
+
+ // 向通道里写入带有数据的缓冲区对象, 表示向服务器发送数据
+ channel.write( buffer);
+ buffer.clear();
+ }
+
+ // 关闭通道
+ channel.close();
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java
new file mode 100644
index 0000000..7ea41ec
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/io/nio/demo01/NioServer.java
@@ -0,0 +1,57 @@
+package com.learnjava.io.nio.demo01;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+/**
+ * @author lhy
+ * @date 2021/5/22
+ */
+public class NioServer {
+ public static void main(String[] args) throws Exception {
+ // 获取服务端通道
+ ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
+ // 切换为非阻塞模式
+ serverSocketChannel.configureBlocking(false);
+ // 绑定链接
+ Selector selector = Selector.open();
+ // 将通道注册在selector上,并绑定为读事件
+ serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
+ // 选择器轮训,阻塞
+ while (selector.select() > 0) {
+ Iterator it = selector.selectedKeys().iterator();
+
+ // 判断是否有事件进来
+ while (it.hasNext()) {
+ // 获取就绪的事件
+ SelectionKey selectionKey = it.next();
+
+ // 读事件
+ if (selectionKey.isAcceptable()) {
+ // 就绪的客户端连接事件
+ SocketChannel acceptChannel = serverSocketChannel.accept();
+ acceptChannel.configureBlocking(false);
+ acceptChannel.register(selector, SelectionKey.OP_READ);
+ } else if (selectionKey.isReadable()) {
+ // 读就绪事件
+ SocketChannel readAcceptChannel = serverSocketChannel.accept();
+ ByteBuffer allocateBuffer = ByteBuffer.allocate(1024);
+
+ int len = 0;
+ while ((len = readAcceptChannel.read(allocateBuffer)) > 0) {
+ allocateBuffer.flip();
+ System.out.println(new String(allocateBuffer.array(), 0, len));
+ allocateBuffer.clear();
+ }
+ }
+ }
+
+ // 取消选择键selectionKey
+ it.remove();
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java
new file mode 100644
index 0000000..1db2858
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaComparatorDemo.java
@@ -0,0 +1,165 @@
+package com.learnjava.lambda;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import sun.java2d.pipe.SpanShapeRenderer;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+public class LambdaComparatorDemo {
+
+ public static String[] arrays = {"Milan", "london", "San Francisco", "Tokyo", "New Delhi"};
+
+ public static List employees;
+
+ static {
+ Employee e1 = new Employee(1,23,"M","Rick","Beethovan", "2021-04-01");
+ Employee e2 = new Employee(2,13,"F","Martina","Hengis", "2021-04-02");
+ Employee e3 = new Employee(3,43,"M","Ricky","Martin","2021-04-09" );
+ Employee e4 = new Employee(4,26,"M","Jon","Lowman", "2021-04-10");
+ Employee e5 = new Employee(5,19,"F","Cristine","Maria", "2021-04-01");
+ Employee e6 = new Employee(6,15,"M","David","Feezor", "2021-04-06");
+ Employee e7 = new Employee(7,68,"F","Melissa","Roy", "2021-04-06");
+ Employee e8 = new Employee(8,79,"M","Alex","Gussin", "2021-04-08");
+ Employee e9 = new Employee(9,15,"F","Neetu","Singh", "2021-04-09");
+ Employee e10 = new Employee(10,45,"M","Naveen","Jain", "2021-04-10");
+ employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
+ }
+
+ public static void main(String[] args) {
+// test01();
+// test02();
+ test03();
+ }
+
+ /**
+ * List字符串排序
+ */
+ public static void test01() {
+ List cities = Arrays.asList(arrays);
+
+ System.out.println(cities);
+
+ // CASE_INSENSITIVE_ORDER 是一个排序器Comparator接口,意思是不区分大小写进行排序
+ cities.sort(String.CASE_INSENSITIVE_ORDER);
+ System.out.println(cities);
+
+ // 自然排序
+ cities.sort(Comparator.naturalOrder());
+ System.out.println(cities);
+
+ // 可以将排序器放在Stream管道流中
+ Stream.of(arrays)
+ .sorted(Comparator.naturalOrder())
+ .forEach(System.out::println);
+ }
+
+ /**
+ * 对整数数组进行排序
+ */
+ public static void test02() {
+ List numbers = Arrays.asList(6, 2, 4, 3, 1, 9);
+ System.out.println(numbers);
+
+ // 自然排序(升序)
+ numbers.sort(Comparator.naturalOrder());
+ System.out.println(numbers);
+
+ // 降序
+ numbers.sort(Comparator.reverseOrder());
+ System.out.println(numbers);
+ }
+
+ /**
+ * 对对象进行排序
+ */
+ public static void test03() {
+ // 根据employee的年龄进行自然排序
+ employees.sort(Comparator.comparing(Employee::getAge));
+ employees.forEach(System.out::println);
+
+ System.out.println();
+
+ // 根据employee的年龄进行降序排序
+ employees.sort(Comparator.comparing(Employee::getAge).reversed());
+ employees.forEach(System.out::println);
+
+ System.out.println();
+
+ // 先对性别排序,然后对年龄进行排序
+ employees.sort(
+ Comparator.comparing(Employee::getGender)
+ .thenComparing(Employee::getAge)
+ // 性别,年龄都进行倒序排序
+ .reversed()
+ );
+ employees.forEach(System.out::println);
+
+ // 自定义排序器
+ employees.sort((em1, em2) -> {
+ if (em1.getAge().equals(em2.getAge())) {
+ return 0;
+ }
+ return em1.getAge() - em2.getAge() > 0 ? -1 : 1;
+ });
+ employees.forEach(System.out::println);
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @Data
+ @AllArgsConstructor
+ public static class Employee {
+
+ private Integer id;
+ // 年龄
+ private Integer age;
+ // 性别
+ private String gender;
+ private String firstName;
+ private String lastName;
+ private String date;
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "id=" + id +
+ ", age=" + age +
+ ", gender='" + gender + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", date='" + date + '\'' +
+ '}';
+ }
+
+ // 年龄大于70的谓语
+ static Predicate ageGreaterThan70 = e -> e.getAge() > 70;
+
+ static Predicate ageLessThan18 = e -> e.getAge() < 18;
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java
new file mode 100644
index 0000000..ae7e1d4
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemo01.java
@@ -0,0 +1,37 @@
+package com.learnjava.lambda;
+
+/**
+ * @author bruis
+ * lambda表达式
+ */
+public class LambdaDemo01 {
+
+ /**
+ * 打印内部类
+ */
+ interface Printer {
+ void print(String content);
+// void print(String content, String operator);
+ }
+
+ public static void printSomething(String content, Printer printer) {
+ printer.print(content);
+ }
+
+ public static void main(String[] args) {
+// Printer printer = (String content) -> {
+// System.out.println(content);
+// };
+
+// 去掉参数类型,只有一个参数时可以去掉括号
+// Printer printer = (content) -> {
+// System.out.println(content);
+// };
+
+// 只有一个参数提
+// Printer printer = val -> System.out.println(val);
+
+ Printer printer = System.out::println;
+ printSomething("hello lambda", printer);
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java
new file mode 100644
index 0000000..e1aa8e6
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaDemoForList.java
@@ -0,0 +1,88 @@
+package com.learnjava.lambda;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author bruis
+ * Lambda的List demo
+ */
+public class LambdaDemoForList {
+
+ public static String[] arrays = {"Monkey", "Lion", "Giraffe", "Lemur"};
+
+ public static String[] arrays2 = {"Monkey", "Lion", "Giraffe", "Lemur", "Lion"};
+
+ public static void main(String[] args) {
+// test01();
+// test02();
+ test03();
+ }
+
+ /**
+ * 去重Distinct + 排序Sort
+ */
+ public static void test03() {
+ // 去重
+ List uniqueAnimals = Stream.of(arrays2)
+ .distinct()
+ .collect(Collectors.toList());
+ System.out.println(uniqueAnimals);
+ // 排序
+ List sortedAnimals = Stream.of(arrays)
+ // 对字母是按照自然顺序进行排序的
+ .sorted()
+ .collect(Collectors.toList());
+ System.out.println(sortedAnimals);
+ }
+
+ /**
+ * Limit + Skip 数据截取
+ */
+ public static void test02() {
+
+ /**
+ * 截取前两位字符串
+ */
+ List limitN = Stream.of(arrays)
+ .limit(2)
+ .collect(Collectors.toList());
+
+ /**
+ * 过滤掉前两位元素
+ */
+ List skipN = Stream.of(arrays)
+ .skip(2)
+ .collect(Collectors.toList());
+
+ System.out.println(limitN);
+ System.out.println(skipN);
+ }
+
+ /**
+ * 过滤 + map处理
+ */
+ public static void test01() {
+ List nameStrList = Arrays.asList("abc", "efg", "hig", "hii", "klm");
+
+ List result = nameStrList
+ .stream()
+ .filter(s -> s.startsWith("h"))
+ .map(String::toUpperCase)
+ // 调用自定义的方法
+ .map(MyStringUtils::myToUpperCase)
+ .collect(Collectors.toList());
+
+ for (String name : result) {
+ System.out.println(name);
+ }
+ }
+
+ private static class MyStringUtils {
+ public static String myToUpperCase(String str) {
+ return str.toUpperCase();
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java
new file mode 100644
index 0000000..cc03752
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapDemo.java
@@ -0,0 +1,139 @@
+package com.learnjava.lambda;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author bruis
+ * 操作Map
+ */
+public class LambdaMapDemo {
+
+ public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+
+ public static void main(String[] args) {
+// test01();
+// test02();
+ test03();
+// test04();
+ }
+
+ /**
+ * 需求,将employee集合中的元素根据date进行排序
+ */
+ public static void test01() {
+ List sortedByDate = LambdaComparatorDemo.employees
+ .stream()
+ .sorted()
+ .sorted((a, b) -> {
+ int result;
+ try {
+ result = getDate(b.getDate()).compareTo(getDate(a.getDate()));
+ } catch (Exception e) {
+ result = 0;
+ }
+ return result;
+ })
+ .collect(Collectors.toList());
+ System.out.println(sortedByDate);
+ }
+
+ /**
+ * HashMap的merge方法,如果key相同,则通过merge来对key相同的元素进行处理
+ */
+ public static void test02() {
+ String key = "money";
+ Map map = new HashMap(){{put(key, 100);}};
+
+ // 第三个参数时BiFunction,聚合函数(可以这么理解)
+ map.merge(key,100,(oldValue, newValue) -> oldValue + newValue);
+// map.merge(key, 100,Integer::sum);
+ System.out.println(map);
+ }
+
+ /**
+ * 对map进行排序
+ */
+ public static void test03() {
+ Map codes = new HashMap<>();
+ codes.put("2021-03", 1);
+ codes.put("2021-02", 49);
+ codes.put("2021-05", 33);
+// codes.put("2021-04-01", 1);
+// codes.put("2021-04-15", 49);
+// codes.put("2021-04-10", 33);
+// codes.put("2021-04-05", 86);
+// codes.put("2021-04-20", 92);
+
+ // 先将Map转化为List,通过collect处理后再转为Map
+ Map sortedMap = codes.entrySet()
+ .stream()
+// .sorted((c1, c2) -> c2.getValue().compareTo(c1.getValue()))
+// .sorted(Map.Entry.comparingByValue())
+ .sorted((c1, c2) -> c2.getKey().compareTo(c1.getKey()))
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ (oldVal, newVal) -> oldVal,
+ LinkedHashMap::new
+ )
+ );
+ sortedMap.entrySet().forEach(System.out::println);
+
+ }
+
+ /**
+ * 将list转为map,并用其中一个value作为key
+ */
+ public static void test04() {
+ LinkedHashMap collect = LambdaComparatorDemo.employees
+ .stream()
+ .collect(
+ Collectors.toMap(
+ LambdaComparatorDemo.Employee::getDate,
+ // 这样是返回本身对象的一个表达式,还可以用Function.identity()
+ // Function.indentity() 就是 t -> t
+// employee -> employee,
+ Function.identity(),
+ (oldVal, newVal) -> {
+ // 重复的key就将年纪相加,然后FirstName通过--->加起来
+ oldVal.setAge(oldVal.getAge() + newVal.getAge());
+ oldVal.setFirstName(oldVal
+ .getFirstName()
+ .concat("--->")
+ .concat(newVal.getFirstName()));
+ return oldVal;
+ },
+ LinkedHashMap::new
+ )
+ );
+ // 这样打印出的map元素不好观察
+// System.out.println(collect);
+// collect.entrySet().forEach(System.out::println);
+
+ LinkedHashMap sortedCollect = collect
+ .entrySet()
+ .stream()
+ .sorted((a, b) -> b.getKey().compareTo(a.getKey()))
+ .collect(
+ Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ // 上面已经对重复key做处理了,这里就直接范围oldVal就行
+ (oldVal, newVal) -> oldVal,
+ LinkedHashMap::new
+ )
+ );
+
+ // 根据日期排序
+ sortedCollect.entrySet()
+ .forEach(System.out::println);
+ }
+
+ public static Date getDate(String date) throws Exception {
+ return format.parse(date);
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java
new file mode 100644
index 0000000..d78e73f
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaMapMerge.java
@@ -0,0 +1,58 @@
+package com.learnjava.lambda;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 关于Map的合并操作
+ *
+ * @author lhy
+ * @date 2021/7/20
+ */
+public class LambdaMapMerge {
+
+ public static void main(String[] args) {
+// mapMerge();
+// mapMerge2();
+ }
+
+ /**
+ * value为int类型的map merge操作,将两个map,相同key merge在一起
+ *
+ * key:string
+ * value:int
+ */
+ public static void mapMerge() {
+ Map map1= new HashMap<>();
+ map1.put("one",1);
+ map1.put("two",2);
+ map1.put("three",3);
+ Map map2= new HashMap<>();
+ map2.put("one",1);
+ map2.put("two",2);
+
+ map1.forEach((key, value) -> map2.merge(key, value, Integer::sum));
+ System.out.println(map2);
+ }
+
+ /**
+ * value为int类型的map merge操作,将两个map,相同key merge在一起
+ *
+ * key:string
+ * value:String
+ */
+ public static void mapMerge2() {
+ Map map1= new HashMap<>();
+ map1.put("one","1");
+ map1.put("two","2");
+ map1.put("three","3");
+ Map map2= new HashMap<>();
+ map2.put("one","1");
+ map2.put("two","2");
+
+ map1.forEach((key, value) -> map2.merge(key, value,(total, num) -> String.valueOf(Integer.parseInt(total) + Integer.parseInt(num))));
+
+ System.out.println(map2);
+
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java
new file mode 100644
index 0000000..b656747
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/LambdaReduceDemo.java
@@ -0,0 +1,104 @@
+package com.learnjava.lambda;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * @author bruis
+ * 累加器
+ */
+public class LambdaReduceDemo {
+
+ public static void main(String[] args) {
+// test01();
+// test02();
+ test03();
+ }
+
+ /**
+ * 对整形进行操作
+ */
+ public static void test01() {
+ List numbers = Arrays.asList(1, 2, 3, 4, 5);
+ Integer reduce = numbers
+ .stream()
+ .reduce(0, LambdaReduceDemo::mySum);
+// .reduce(0, Integer::sum);
+// .reduce(0, (total, element) -> total + element);
+ System.out.println(reduce);
+ }
+
+ /**
+ * 对字符串进行操作
+ */
+ public static void test02() {
+ List letters = Arrays.asList("a", "b", "c", "d", "e");
+ String reduce = letters
+ .stream()
+ .reduce("", String::concat);
+// .reduce("", (totol, element) -> totol.concat(element));
+ System.out.println(reduce);
+ }
+
+ /**
+ * 操作Employee集合元素,将所有员工的年龄通过reduce累加起来
+ *
+ * U identity, BiFunction accumulator, BinaryOperator combiner
+ * reduce的三个参数:
+ * 1)初始值;
+ * 2)累加器(可自己实现逻辑)
+ * 3) 合并器(parallelStream模式时的合并)
+ *
+ */
+ public static void test03() {
+ // 将Employee集合元素转化为Integer集合元素(流)
+ // map的操作就是将a类型元素转化为b类型元素
+ Stream integerStream = LambdaComparatorDemo
+ .employees
+ .stream()
+ .map(LambdaComparatorDemo.Employee::getAge);
+
+ // 求所有员工的年龄
+ Integer totalAge = integerStream.reduce(0, Integer::sum);
+ System.out.println(totalAge);
+
+ // 数据量大的话,可以用并行计算
+ // 先讲员工集合转化为“并行流”
+ Stream parallelStream = LambdaComparatorDemo
+ .employees
+ .parallelStream()
+ .map(LambdaComparatorDemo.Employee::getAge);
+
+ // 相比于普通的单个流,parallelStream多了个合并器,将多个CPU计算的结果再合并到同一个流
+ Integer reduce = parallelStream.reduce(0, Integer::sum, Integer::sum);
+ System.out.println(reduce);
+
+ // 可以不用map将employee转化为Integer对象,可以直接reduce操作,最后通过Integer::sum这个合并器来将结果合并为Integer类型
+ Integer total = LambdaComparatorDemo
+ .employees
+ .stream()
+ .reduce(0, (subTotal, emp) -> subTotal + emp.getAge(), Integer::sum);
+ System.out.println(total);
+
+ Integer total2 = LambdaComparatorDemo
+ .employees
+ .stream()
+ .reduce(0, LambdaReduceDemo::mySum2, Integer::sum);
+ System.out.println(total2);
+ }
+
+ /**
+ * 可作为BiFunction,传入到reduce作为入参
+ * @param a
+ * @param b
+ * @return
+ */
+ public static Integer mySum(int a, int b) {
+ return a + b;
+ }
+
+ public static Integer mySum2(int a, LambdaComparatorDemo.Employee employee) {
+ return a + employee.getAge();
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java
new file mode 100644
index 0000000..3962b48
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/StreamMatchDemo.java
@@ -0,0 +1,49 @@
+package com.learnjava.lambda;
+
+import java.util.Optional;
+
+/**
+ * @author bruis
+ */
+public class StreamMatchDemo {
+
+ public static void main(String[] args) {
+// test01();
+ test02();
+ }
+
+ /**
+ * 判断是否有年龄大于70的员工
+ */
+ public static void test01() {
+ boolean isExistAgeThan70 = LambdaComparatorDemo.employees
+ .stream()
+ // 使用了Employee的谓语语句(这种写法方便复用)
+ .anyMatch(LambdaComparatorDemo.Employee.ageGreaterThan70);
+// .anyMatch(e -> e.getAge() > 70);
+ System.out.println(isExistAgeThan70);
+
+ boolean isExistAgeLessThan18 = LambdaComparatorDemo.employees
+ .stream()
+ .noneMatch(LambdaComparatorDemo.Employee.ageLessThan18);
+
+ System.out.println(isExistAgeLessThan18);
+ }
+
+ /**
+ * 元素查找与Optional
+ */
+ public static void test02() {
+
+ Optional employeeOptional = LambdaComparatorDemo.employees
+ .stream()
+ .filter(e -> e.getAge() > 400)
+ .findFirst();
+
+ // Optional#get 会报空
+// System.out.println(employeeOptional.get());
+ LambdaComparatorDemo.Employee employee = employeeOptional.orElse(null);
+ System.out.println(employee == null);
+ }
+
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java b/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java
new file mode 100644
index 0000000..93c120c
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/lambda/StreamParallelDemo.java
@@ -0,0 +1,10 @@
+package com.learnjava.lambda;
+
+/**
+ *
+ * 串行、并行 todo
+ *
+ * @author bruis
+ */
+public class StreamParallelDemo {
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java
new file mode 100644
index 0000000..5068206
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/optimization/BeanCopierDemo.java
@@ -0,0 +1,170 @@
+package com.learnjava.optimization;
+
+import org.springframework.cglib.beans.BeanCopier;
+
+/**
+ *
+ * 经过测试,BeanCopier性能是BeanUtils10倍左右。
+ *
+ * BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能:
+ *
+ *
+ * @author lhy
+ * @date 2021/7/21
+ */
+public class BeanCopierDemo {
+
+ private static final BeanCopier BEAN_COPIER = BeanCopier.create(Person.class, PersonVo.class, false);
+
+ public static void main(String[] args) {
+ Person person = new Person("zs", "high School", 16, 177, 126);
+ PersonVo vo = new PersonVo();
+
+ BEAN_COPIER.copy(person, vo, null);
+
+ System.out.println(vo);
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ public static class PersonVo {
+ private String name;
+ private String grade;
+ private Integer age;
+ private Integer height;
+ private Integer weight;
+
+ @Override
+ public String toString() {
+ return "PersonVo{" +
+ "name='" + name + '\'' +
+ ", grade='" + grade + '\'' +
+ ", age=" + age +
+ ", height=" + height +
+ ", weight=" + weight +
+ '}';
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getGrade() {
+ return grade;
+ }
+
+ public void setGrade(String grade) {
+ this.grade = grade;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public Integer getHeight() {
+ return height;
+ }
+
+ public void setHeight(Integer height) {
+ this.height = height;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(Integer weight) {
+ this.weight = weight;
+ }
+ }
+
+ public static class Person {
+ private String name;
+ private String grade;
+ private Integer age;
+ private Integer height;
+ private Integer weight;
+
+ public Person(String name, String grade, Integer age, Integer height, Integer weight) {
+ this.name = name;
+ this.grade = grade;
+ this.age = age;
+ this.height = height;
+ this.weight = weight;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getGrade() {
+ return grade;
+ }
+
+ public void setGrade(String grade) {
+ this.grade = grade;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public Integer getHeight() {
+ return height;
+ }
+
+ public void setHeight(Integer height) {
+ this.height = height;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(Integer weight) {
+ this.weight = weight;
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java
new file mode 100644
index 0000000..88de1a0
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/optimization/CaffeineDemo.java
@@ -0,0 +1,131 @@
+package com.learnjava.optimization;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.RemovalListener;
+
+import java.math.BigDecimal;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Caffeine 代码Demo(SpringBoot自带的缓存类)
+ *
+ *
+ * @author lhy
+ * @date 2021/7/22
+ */
+public class CaffeineDemo {
+
+ private static Cache productVoCache;
+
+ public static RemovalListener listener = (k, v, cause) -> {
+ // 业务逻辑
+
+
+ // 触发异常
+ switch (cause) {
+ // 过期
+ case EXPIRED:
+ break;
+ // 手动删除
+ case EXPLICIT:
+ break;
+ // 被替换
+ case REPLACED:
+ break;
+ // 垃圾回收
+ case COLLECTED:
+ break;
+ // 超过数量限制
+ case SIZE:
+ break;
+ default:
+ break;
+ }
+ };
+
+ public static void main(String[] args) {
+ // 初始化
+ // afterPropertiesSet();
+ }
+
+ /**
+ * 模拟Spring的类初始化的时候对缓存进行初始化
+ */
+ public static void afterPropertiesSet() {
+ productVoCache = Caffeine.newBuilder()
+ .softValues()
+ .refreshAfterWrite(7200, TimeUnit.SECONDS)
+ .removalListener(listener)
+ // .build(k -> loadSync(k))
+ // 非static类中,可以使用这种方式.build(this::loadSync);
+ .build(CaffeineDemo::loadSync);
+ }
+
+ /**
+ * 获取对应缓存内容
+ * @param key
+ * @return
+ */
+ public static ProductVo getProductVo(String key) {
+ return productVoCache.get(key, CaffeineDemo::loadSync);
+ }
+
+ /**
+ * 对对应商品进行缓存
+ * @param key
+ */
+ public static void putProductVo(String key) {
+ productVoCache.put(key, loadSync(key));
+ }
+
+ private static ProductVo loadSync(String key) {
+ // 业务逻辑
+ return new ProductVo();
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ public static class ProductVo {
+
+ private String productName;
+
+ private BigDecimal price;
+
+ public ProductVo() {}
+
+ public String getProductName() {
+ return productName;
+ }
+
+ public void setProductName(String productName) {
+ this.productName = productName;
+ }
+
+ public BigDecimal getPrice() {
+ return price;
+ }
+
+ public void setPrice(BigDecimal price) {
+ this.price = price;
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java
new file mode 100644
index 0000000..fe81a19
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeDemo.java
@@ -0,0 +1,70 @@
+package com.learnjava.optimization;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ * 代码优化技巧总结
+ *
+ * @author lhy
+ * @date 2021/7/19
+ */
+public class OptimizeDemo {
+ public static void main(String[] args) {
+ Map map = new HashMap<>();
+ mergeData(map);
+
+ Map concurrentMap = new ConcurrentHashMap<>();
+ concurrentMergeData(concurrentMap);
+ }
+
+ /**
+ * 对于通过map来聚合数据(非Lambda方式)
+ * @param map
+ */
+ public static void mergeData(Map map) {
+ String key = "mapKey";
+ int value = 1;
+ // 普通方式
+ if (map.containsKey(key)) {
+ map.put(key, map.get(key) + value);
+ } else {
+ map.put(key,value);
+ }
+
+ // 简洁方式
+ Integer mapValue = map.get(key);
+ if (null != mapValue) {
+ mapValue += value;
+ } else {
+ mapValue = value;
+ }
+ map.put(key, mapValue);
+ }
+
+ /**
+ * 针对mergeData里map的put操作,在并发情况下会存在put的时候,以及有其他线程已经put成功了,导致线程不安全,
+ * 所以需要使用并发集合列的putIfAbsent方法
+ * @param map
+ */
+ public static void concurrentMergeData(Map map) {
+ String key = "mapKey";
+ int value = 1;
+ Integer mapValue = map.get(key);
+ if (null != mapValue) {
+ mapValue += value;
+ } else {
+ mapValue = value;
+ }
+ map.putIfAbsent(key, mapValue);
+
+ // computeIfAbsent方法对map中的key只进行重新计算,如果不存在这个key,则添加到map中
+ map.computeIfAbsent(key, (k) -> {
+ // 其他计算
+ int a = 1, b = 2;
+ return a + b;
+ });
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java
new file mode 100644
index 0000000..6f131f6
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/optimization/OptimizeUtilDemo.java
@@ -0,0 +1,21 @@
+package com.learnjava.optimization;
+
+/**
+ * 优化工具类例子
+ *
+ * @author lhy
+ * @date 2021/7/20
+ */
+public class OptimizeUtilDemo {
+
+ /**
+ * 超快深拷贝工具类BeanCopier
+ */
+ private BeanCopierDemo beanCopierDemo;
+
+ /**
+ * Caffeine Cache
+ */
+ private CaffeineDemo caffeineDemo;
+
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java b/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java
new file mode 100644
index 0000000..c04119e
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/proxy/dynamicproxy/DemoInvokerHandler.java
@@ -0,0 +1,53 @@
+package com.learnjava.proxy.dynamicproxy;
+
+import com.learnjava.proxy.staticproxy.RealSubject;
+import com.learnjava.proxy.staticproxy.Subject;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author bruis
+ *
+ * jdk动态代理
+ *
+ */
+public class DemoInvokerHandler implements InvocationHandler {
+
+ // 真正的业务对象
+ private Object realSubject;
+
+ public DemoInvokerHandler(Object realSubject) {
+ this.realSubject = realSubject;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ System.out.println("代理预处理操作");
+
+ // 调用真正的代理对象逻辑
+ Object result = method.invoke(realSubject, args);
+
+ System.out.println("代理后处理操作");
+ return result;
+ }
+
+ /**
+ * 创建代理对象并返回
+ * @return
+ */
+ public Object getProxy() {
+ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+ realSubject.getClass().getInterfaces(), this);
+ }
+
+ public static void main(String[] args) {
+ RealSubject realSubject = new RealSubject();
+ DemoInvokerHandler invokerHandler = new DemoInvokerHandler(realSubject);
+ Subject proxy = (Subject) invokerHandler.getProxy();
+
+ // 拿到业务对象,执行业务逻辑,此时业务逻辑已经被代理对象代理了
+ proxy.operation();
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java
new file mode 100644
index 0000000..885687d
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/JdkStaticProxy.java
@@ -0,0 +1,16 @@
+package com.learnjava.proxy.staticproxy;
+
+/**
+ * @author bruis
+ *
+ * JDK静态代理
+ *
+ * 特点:
+ * 1. 静态代理模式需要在编译模式为每个业务类创建代理类Proxy,当需要代理的类很多时,就会出现大量的Proxy类
+ *
+ */
+public class JdkStaticProxy {
+
+
+
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java
new file mode 100644
index 0000000..600ccc1
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/RealSubject.java
@@ -0,0 +1,10 @@
+package com.learnjava.proxy.staticproxy;
+
+public class RealSubject implements Subject {
+
+ @Override
+ public void operation() {
+ System.out.println("这是真正的业务类");
+ }
+
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java
new file mode 100644
index 0000000..d48cf48
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/Subject.java
@@ -0,0 +1,5 @@
+package com.learnjava.proxy.staticproxy;
+
+public interface Subject {
+ public void operation();
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java
new file mode 100644
index 0000000..f151e73
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/proxy/staticproxy/SubjectProxy.java
@@ -0,0 +1,22 @@
+package com.learnjava.proxy.staticproxy;
+
+public class SubjectProxy implements Subject {
+
+ private RealSubject realSubject;
+
+ public SubjectProxy() {
+ realSubject = new RealSubject();
+ }
+
+ @Override
+ public void operation() {
+ System.out.println("代理预处理逻辑");
+ realSubject.operation();
+ System.out.println("代理后处理逻辑");
+ }
+
+ public static void main(String[] args) {
+ SubjectProxy proxy = new SubjectProxy();
+ proxy.operation();
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java b/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java
new file mode 100644
index 0000000..426ef67
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/reference/PhantomRefTest.java
@@ -0,0 +1,35 @@
+package com.learnjava.reference;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+public class PhantomRefTest {
+
+ public static void main(String[] args) throws InterruptedException {
+ ReferenceQueue referenceQueue = new ReferenceQueue();
+
+ // 10mb
+ byte[] buffer = new byte[1024 * 1024 * 10];
+
+ PhantomReference phantomReference = new PhantomReference(buffer, referenceQueue);
+
+ // 字节数组对象,失去了强引用
+ buffer = null;
+
+ Reference ref0 = referenceQueue.poll();
+
+ System.out.println("gc 执行之前, refQueue中是否有数据?" + (ref0 != null ? "有" : "没有"));
+ System.out.println("gc 执行之前, ref引用的对象:" + phantomReference.get());
+
+ System.gc();
+ // 确保gc程序执行
+ Thread.sleep(1000);
+
+ System.out.println("gc 执行之后, ref引用的对象:" + phantomReference.get());
+
+ Reference ref = referenceQueue.poll();
+ System.out.println("gc 执行之后, refQueue中是否有数据?" + (ref != null ? "有" : "没有"));
+ System.out.println("referenceQueue 中获取的 ref与 weakReference中的是否一致?" + (ref == phantomReference));
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java b/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java
new file mode 100644
index 0000000..38e706e
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/reference/SoftReferenceTest.java
@@ -0,0 +1,35 @@
+package com.learnjava.reference;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 软引用
+ */
+public class SoftReferenceTest {
+ public static void main(String[] args) throws InterruptedException {
+ List refList = new ArrayList();
+
+ for (int i = 0; i < 1000; i++) {
+ // 10mb
+ byte[] buffer = new byte[1024 * 1024 * 10];
+
+ SoftReference softReference = new SoftReference(buffer);
+
+ refList.add(softReference);
+ }
+
+ System.gc();
+ Thread.sleep(1000);
+
+ Iterator it = refList.iterator();
+
+ while (it.hasNext()) {
+ Reference ref = it.next();
+ System.out.println("当前ref引用的对象:" + ref.get());
+ }
+ }
+}
diff --git a/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java
new file mode 100644
index 0000000..2e2ec2d
--- /dev/null
+++ b/JdkLearn/src/main/java/com/learnjava/reference/WeakReferenceTest.java
@@ -0,0 +1,66 @@
+package com.learnjava.reference;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * soft软引用 gc堆内存不够时,可能会回收
+ * weak弱引用 gc时,内存一定会回收不可用对象
+ * phantom虚引用
+ */
+public class WeakReferenceTest {
+ public static void main(String[] args) throws Exception {
+// test01();
+ test02();
+ }
+
+ private static void test01() throws InterruptedException {
+ List refList = new ArrayList();
+
+ for (int i = 0; i < 1000; i++) {
+ // 10mb
+ byte[] buffer = new byte[1024 * 1024 * 10];
+
+ WeakReference weakReference = new WeakReference(buffer);
+
+ refList.add(weakReference);
+ }
+
+ // 将buffer直接全部回收了
+ System.gc();
+ Thread.sleep(1000);
+ Iterator iterator = refList.iterator();
+ while (iterator.hasNext()) {
+ Reference ref = iterator.next();
+ System.out.println("当前ref引用的对象:" + ref.get());
+ }
+ }
+
+ private static void test02() throws InterruptedException {
+ ReferenceQueue refQueue = new ReferenceQueue();
+
+ // 10 mb
+ byte[] buffer = new byte[1024 * 1024 * 10];
+ WeakReference weakReference = new WeakReference(buffer, refQueue);
+ // 失去强引用关联
+ buffer = null;
+
+ Reference ref0 = refQueue.poll();
+ System.out.println("gc 执行之前, refQueue中是否有数据?" + (ref0 != null ? "有" : "没有"));
+ System.out.println("gc 执行之前, ref引用的对象:" + weakReference.get());
+
+ System.gc();
+ // 确保gc程序执行
+ Thread.sleep(1000);
+
+ System.out.println("gc 执行之后, ref引用的对象:" + weakReference.get());
+
+ Reference ref = refQueue.poll();
+ System.out.println("gc 执行之后, refQueue中是否有数据?" + (ref != null ? "有" : "没有"));
+ System.out.println("referenceQueue 中获取的 ref与 weakReference中的是否一致?" + (ref == weakReference));
+ }
+}
diff --git a/README.md b/README.md
index ca6e8a3..fae4153 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,10 @@
-
+
+
+
+
@@ -43,7 +46,9 @@ Java流行框架源码分析,学习以及总结。项目持续更新中,不
✅ Dubbo源码
-Netty源码
+✅ Netty源码
+
+✅ RocketMQ源码
MyBatis源码
@@ -69,6 +74,8 @@ SpringCloud源码
- [一篇文章快速深入学习ThreadLocal](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/JDK/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%BF%AB%E9%80%9F%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0ThreadLocal.md)
- [深入学习Java volatile关键字](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Java%20volatile%E5%85%B3%E9%94%AE%E5%AD%97.md)
- [深入学习Thread底层原理](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0Thread%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81.md)
+ - [深入学习JDK1.7、8 HashMap扩容原理]()
+ - [开源项目里那些看不懂的位运算分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/JDK/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E9%87%8C%E9%82%A3%E4%BA%9B%E7%9C%8B%E4%B8%8D%E6%87%82%E7%9A%84%E4%BD%8D%E8%BF%90%E7%AE%97%E5%88%86%E6%9E%90.md)
- Spring源码学习
- Spring版本:5.2.1.RELEASE
@@ -91,15 +98,9 @@ SpringCloud源码
- [深入浅出SpringBoot源码——SpringFactoriesLoader](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94SpringFactoriesLoader.md)
- [深入浅出SpringBoot源码——监听器与事件机制](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%9B%91%E5%90%AC%E5%99%A8%E4%B8%8E%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6.md)
- - [深入浅出SpringBoot源码——系统初始化器](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%B3%BB%E7%BB%9F.md)
+ - [深入浅出SpringBoot源码——系统初始化器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E7%B3%BB%E7%BB%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%99%A8.md)
- [深入浅出SpringBoot源码——启动加载器](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/%E6%B7%B1%E5%85%A5SpringBoot%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%94%E2%80%94%E5%90%AF%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%99%A8.md)
-
-- Netty源码学习
- - [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
- - [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
- - [Netty中的EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
-
- SpringSecurity&OAuth2源码学习
- SpringSecurity版本:5.1.0.RELEASE
- [深入浅出SpringSecurity和OAuth2(一)—— 初识SpringSecurity](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/SpringSecurity/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%B3%BB%E7%BB%9F%E5%AD%A6%E4%B9%A0SpringSecurity%E5%92%8COAuth2%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20%E5%88%9D%E8%AF%86SpringSecurity.md)
@@ -109,16 +110,57 @@ SpringCloud源码
- Dubbo底层源码解析
- Dubbo底层源码版本:2.7.8
+ - [Dubbo底层源码学习—— 源码搭建](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E6%BA%90%E7%A0%81%E6%90%AD%E5%BB%BA.md)
- [Dubbo底层源码学习(一)—— Dubbo的URL](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84URL.md)
- - [Dubbo底层源码学习(二)—— Dubbo的SPI](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6.md)
+ - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8A%EF%BC%89.md)
+ - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(中)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo底层源码学习%EF%BC%88二%EF%BC%89——%20Dubbo的SPI机制%EF%BC%88中%EF%BC%89.md
+)
+ - [Dubbo底层源码学习(二)—— Dubbo的SPI机制(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20Dubbo%E7%9A%84SPI%E6%9C%BA%E5%88%B6%EF%BC%88%E4%B8%8B%EF%BC%89.md)
- Dubbo底层源码学习(三)—— Dubbo的注册中心
- Dubbo底层源码学习(四)—— Dubbo的注册中心缓存机制
- Dubbo底层源码学习(五)—— Dubbo的注册中心重试机制
- - Dubbo底层源码学习(六)—— Dubbo的服务暴露
+ - [Dubbo底层源码学习(六)—— Dubbo的服务暴露](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Dubbo/Dubbo%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E6%9C%8D%E5%8A%A1%E6%9A%B4%E9%9C%B2.md)
- Dubbo底层源码学习(七)—— Dubbo的服务消费
+
+- Netty底层源码解析
+ - Netty版本:4.1.43.Final
+ - [二进制运算以及源码、反码以及补码学习](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/%E4%BA%8C%E8%BF%9B%E5%88%B6.md)
+ - [Netty源码包结构](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E6%BA%90%E7%A0%81%E5%8C%85%E7%BB%93%E6%9E%84.md)
+ - [Netty底层源码解析-EventLoopGroup](https://github.com/coderbruis/JavaSourceLearning/blob/master/note/Netty/Netty%E4%B8%AD%E7%9A%84EventLoopGroup%E6%98%AF%E4%BB%80%E4%B9%88.md)
+ - [Netty底层源码解析-初始Netty及其架构](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%88%9D%E5%A7%8BNetty%E5%8F%8A%E5%85%B6%E6%9E%B6%E6%9E%84.md)
+ - [Netty底层源码解析-Netty服务端启动分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-Netty%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%90%AF%E5%8A%A8%E5%88%86%E6%9E%90.md)
+ - [Netty底层源码解析-NioEventLoop原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioEventLoop%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
+ - [Netty底层源码解析-ChannelPipeline分析(上)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8A%EF%BC%89.md)
+ - [Netty底层源码解析-ChannelPipeline分析(下)](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-ChannelPipeline%E5%88%86%E6%9E%90%EF%BC%88%E4%B8%8B%EF%BC%89.md)
+ - [Netty底层源码解析-NioServerSocketChannel接受数据原理分析](https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Netty/Netty%E5%BA%95%E5%B1%82%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-NioServerSocketChannel%E6%8E%A5%E5%8F%97%E6%95%B0%E6%8D%AE%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90.md)
+ - Netty底层源码解析-NioSocketChannel接受、发送数据原理分析
+ - Netty底层源码解析-FastThreadLocal原理分析
+ - Netty底层源码解析-内存分配原理分析
+ - Netty底层源码解析-RocketMQ底层使用到的Netty
+ - [Netty底层的优化总结]()
+ - [实战+原理效果更佳!强烈推荐闪电侠大佬实战课:《Netty 入门与实战:仿写微信 IM 即时通讯系统》](https://juejin.cn/book/6844733738119593991)
+
+Netty实战课相关点位于:Spring-Netty,com/bruis/learnnetty/im包下,有需要的读者可前往查看。
+
+
+- RocketMQ底层源码解析
+ - RocketMQ版本:4.9.0
+ - RocketMQ底层源码解析-RocketMQ环境搭建
+ - RocketMQ底层源码解析-本地调试RocketMQ源码
+ - RocketMQ底层源码解析-NameServer分析
持续更新中...
-
+
+todo
+
+2021年年底完成了人生的两件大事,所以一直没时间持续输出源码分析,2022年开始需要继续努力,继续完成这个源码分析项目!
+
+- 完成Netty剩余源码分析文章
+- 完成RocketMQ剩余源码分析文章
+- 完成Dubbo剩余源码分析文章
+- C语言基础学习(为Redis底层源码学习做准备)
+- Redis底层源码分析
+- JUC底层源码分析
# 支持
diff --git a/Spring-Netty/pom.xml b/Spring-Netty/pom.xml
index 3cfc328..82d7684 100644
--- a/Spring-Netty/pom.xml
+++ b/Spring-Netty/pom.xml
@@ -16,7 +16,6 @@
1.8
- 4.1.42.Final
@@ -35,7 +34,12 @@
io.netty
netty-all
- ${netty-all.version}
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.76
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
new file mode 100644
index 0000000..4a701b4
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/NettyClient.java
@@ -0,0 +1,106 @@
+package com.bruis.learnnetty.im.client;
+
+import com.bruis.learnnetty.im.client.handler.*;
+import com.bruis.learnnetty.im.codec.PacketDecoder;
+import com.bruis.learnnetty.im.codec.PacketEncoder;
+import com.bruis.learnnetty.im.codec.Spliter;
+import com.bruis.learnnetty.im.console.ConsoleCommandManager;
+import com.bruis.learnnetty.im.console.LoginConsoleCommand;
+import com.bruis.learnnetty.im.model.LoginRequestPacket;
+import com.bruis.learnnetty.im.model.MessageRequestPacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import java.util.Date;
+import java.util.Scanner;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Description 客户端
+ * @Author luohaiyang
+ * @Date 2022/3/22
+ */
+public class NettyClient {
+ private static final int MAX_RETRY = 5;
+ private static final String HOST = "127.0.0.1";
+ private static final int PORT = 8000;
+
+
+ public static void main(String[] args) {
+ NioEventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap
+ .group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
+ .option(ChannelOption.SO_KEEPALIVE, true)
+ .option(ChannelOption.TCP_NODELAY, true)
+ .handler(new ChannelInitializer() {
+ @Override
+ public void initChannel(SocketChannel ch) {
+ // 拆包粘包处理
+ ch.pipeline().addLast(new Spliter());
+ // 编码
+ ch.pipeline().addLast(new PacketDecoder());
+ // 登录响应
+ ch.pipeline().addLast(new LoginResponseHandler());
+ // 消息返回
+ ch.pipeline().addLast(new MessageResponseHandler());
+ ch.pipeline().addLast(new CreateGroupResponseHandler());
+ ch.pipeline().addLast(new JoinGroupResponseHandler());
+ ch.pipeline().addLast(new QuitGroupResponseHandler());
+ ch.pipeline().addLast(new ListGroupMembersResponseHandler());
+ ch.pipeline().addLast(new GroupMessageResponseHandler());
+ ch.pipeline().addLast(new LogoutResponseHandler());
+ // 解码
+ ch.pipeline().addLast(new PacketEncoder());
+ }
+ });
+
+ connect(bootstrap, HOST, PORT, MAX_RETRY);
+ }
+
+ private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
+ bootstrap.connect(host, port).addListener(future -> {
+ if (future.isSuccess()) {
+ System.out.println(new Date() + ": 连接成功,启动控制台线程……");
+ Channel channel = ((ChannelFuture) future).channel();
+ startConsoleThread(channel);
+ } else if (retry == 0) {
+ System.err.println("重试次数已用完,放弃连接!");
+ } else {
+ // 第几次重连
+ int order = (MAX_RETRY - retry) + 1;
+ // 本次重连的间隔
+ int delay = 1 << order;
+ System.err.println(new Date() + ": 连接失败,第" + order + "次重连……");
+ bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit
+ .SECONDS);
+ }
+ });
+ }
+
+ private static void startConsoleThread(Channel channel) {
+ ConsoleCommandManager consoleCommandManager = new ConsoleCommandManager();
+ LoginConsoleCommand loginConsoleCommand = new LoginConsoleCommand();
+ Scanner scanner = new Scanner(System.in);
+
+ new Thread(() -> {
+ while (!Thread.interrupted()) {
+ if (!SessionUtil.hasLogin(channel)) {
+ loginConsoleCommand.exec(scanner, channel);
+ } else {
+ consoleCommandManager.exec(scanner, channel);
+ }
+ }
+ }).start();
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java
new file mode 100644
index 0000000..04125de
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/CreateGroupResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.CreateGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class CreateGroupResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, CreateGroupResponsePacket msg) throws Exception {
+ System.out.print("群创建成功,id 为[" + msg.getGroupId() + "], ");
+ System.out.println("群里面有:" + msg.getUserNameList());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java
new file mode 100644
index 0000000..3dc9921
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/GroupMessageResponseHandler.java
@@ -0,0 +1,20 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.GroupMessageResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class GroupMessageResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, GroupMessageResponsePacket responsePacket) {
+ String fromGroupId = responsePacket.getFromGroupId();
+ Session fromUser = responsePacket.getFromUser();
+ System.out.println("收到群[" + fromGroupId + "]中[" + fromUser + "]发来的消息:" + responsePacket.getMessage());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java
new file mode 100644
index 0000000..cc7efea
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/JoinGroupResponseHandler.java
@@ -0,0 +1,21 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.JoinGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class JoinGroupResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, JoinGroupResponsePacket responsePacket) throws Exception {
+ if (responsePacket.isSuccess()) {
+ System.out.println("加入群[" + responsePacket.getGroupId() + "]成功!");
+ } else {
+ System.err.println("加入群[" + responsePacket.getGroupId() + "]失败,原因为:" + responsePacket.getReason());
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java
new file mode 100644
index 0000000..0117eac
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/ListGroupMembersResponseHandler.java
@@ -0,0 +1,18 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.ListGroupMembersResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class ListGroupMembersResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, ListGroupMembersResponsePacket responsePacket) {
+ System.out.println("群[" + responsePacket.getGroupId() + "]中的人包括:" + responsePacket.getSessionList());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
new file mode 100644
index 0000000..282afcc
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LoginResponseHandler.java
@@ -0,0 +1,33 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.LoginResponsePacket;
+import com.bruis.learnnetty.im.session.Session;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description 登录响应的reponse
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LoginResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, LoginResponsePacket loginResponsePacket) throws Exception {
+ String userId = loginResponsePacket.getUserId();
+ String userName = loginResponsePacket.getUserName();
+
+ if (loginResponsePacket.isSuccess()) {
+ System.out.println("[" + userName + "]登录成功,userId 为: " + loginResponsePacket.getUserId());
+ SessionUtil.bindSession(new Session(userId, userName), ctx.channel());
+ } else {
+ System.out.println("[" + userName + "]登录失败,原因:" + loginResponsePacket.getReason());
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ System.out.println("客户端连接被关闭");
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java
new file mode 100644
index 0000000..78fd173
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/LogoutResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.LogoutResponsePacket;
+import com.bruis.learnnetty.im.util.SessionUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class LogoutResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, LogoutResponsePacket logoutResponsePacket) {
+ SessionUtil.unBindSession(ctx.channel());
+ }
+}
\ No newline at end of file
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
new file mode 100644
index 0000000..ea6fac1
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/MessageResponseHandler.java
@@ -0,0 +1,19 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.MessageResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/23
+ */
+public class MessageResponseHandler extends SimpleChannelInboundHandler {
+ @Override
+ protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageResponsePacket messageResponsePacket) throws Exception {
+ String fromUserId = messageResponsePacket.getFromUserId();
+ String fromUserName = messageResponsePacket.getFromUserName();
+ System.out.println(fromUserId + ":" + fromUserName + " -> " + messageResponsePacket.getMessage());
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java
new file mode 100644
index 0000000..be82bd5
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/client/handler/QuitGroupResponseHandler.java
@@ -0,0 +1,22 @@
+package com.bruis.learnnetty.im.client.handler;
+
+import com.bruis.learnnetty.im.model.QuitGroupResponsePacket;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/24
+ */
+public class QuitGroupResponseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, QuitGroupResponsePacket responsePacket) throws Exception {
+ if (responsePacket.isSuccess()) {
+ System.out.println("退出群聊[" + responsePacket.getGroupId() + "]成功!");
+ } else {
+ System.out.println("退出群聊[" + responsePacket.getGroupId() + "]失败!");
+ }
+ }
+}
diff --git a/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java
new file mode 100644
index 0000000..35aa573
--- /dev/null
+++ b/Spring-Netty/src/main/java/com/bruis/learnnetty/im/codec/PacketCodecHandler.java
@@ -0,0 +1,35 @@
+package com.bruis.learnnetty.im.codec;
+
+import com.bruis.learnnetty.im.model.Packet;
+import com.bruis.learnnetty.im.model.PacketCodeC;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageCodec;
+
+import java.util.List;
+
+/**
+ * @Description
+ * @Author luohaiyang
+ * @Date 2022/3/25
+ */
+@ChannelHandler.Sharable
+public class PacketCodecHandler extends MessageToMessageCodec {
+
+ public static final PacketCodecHandler INSTANCE = new PacketCodecHandler();
+
+ private PacketCodecHandler() {}
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, Packet msg, List