\n" +
90 | "关注{bugstack虫洞栈}公众号获取源码,回复<基于JavaAgent的全链路监控>";
91 |
92 | FullHttpResponse response = new DefaultFullHttpResponse(
93 | HttpVersion.HTTP_1_1,
94 | HttpResponseStatus.OK,
95 | Unpooled.wrappedBuffer(sendMsg.getBytes(Charset.forName("UTF-8"))));
96 | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
97 | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
98 | response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
99 | ctx.write(response);
100 | ctx.flush();
101 | }
102 |
103 | @Override
104 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
105 | ctx.flush();
106 | }
107 |
108 | @Override
109 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
110 | ctx.close();
111 | cause.printStackTrace();
112 | }
113 |
114 | }
115 | ```
116 |
117 | >server/NettyServer.java | 模版代码基本没变,和其他的代码类似
118 |
119 | ```java
120 | /**
121 | * 虫洞栈:https://bugstack.cn
122 | * 公众号:bugstack虫洞栈 {获取学习源码}
123 | * Create by fuzhengwei on 2019
124 | */
125 | public class NettyServer {
126 |
127 | public static void main(String[] args) {
128 | new NettyServer().bing(7397);
129 | }
130 |
131 | private void bing(int port) {
132 | //配置服务端NIO线程组
133 | EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
134 | EventLoopGroup childGroup = new NioEventLoopGroup();
135 | try {
136 | ServerBootstrap b = new ServerBootstrap();
137 | b.group(parentGroup, childGroup)
138 | .channel(NioServerSocketChannel.class) //非阻塞模式
139 | .option(ChannelOption.SO_BACKLOG, 128)
140 | .childOption(ChannelOption.SO_KEEPALIVE, true)
141 | .childHandler(new MyChannelInitializer());
142 | ChannelFuture f = b.bind(port).sync();
143 | System.out.println("itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}");
144 | f.channel().closeFuture().sync();
145 | } catch (InterruptedException e) {
146 | e.printStackTrace();
147 | } finally {
148 | childGroup.shutdownGracefully();
149 | parentGroup.shutdownGracefully();
150 | }
151 |
152 | }
153 |
154 | }
155 | ```
156 |
157 | ## 测试结果
158 |
159 | >启动NettyServer
160 |
161 | ```java
162 | itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获取源码}
163 | URI:/
164 | DefaultHttpRequest(decodeResult: success, version: HTTP/1.1)
165 | GET / HTTP/1.1
166 | cache-control: no-cache
167 | Postman-Token: 28f37dfb-bb5a-4cb2-ae7a-87cf6cda900c
168 | User-Agent: PostmanRuntime/7.6.0
169 | Accept: */*
170 | Host: localhost:7397
171 | accept-encoding: gzip, deflate
172 | content-type: multipart/form-data; boundary=--------------------------359056973355668947377349
173 | content-length: 226
174 | Connection: keep-alive
175 | ----------------------------359056973355668947377349
176 | Content-Disposition: form-data; name="msg"
177 |
178 | 微信公众号:bugstack虫洞栈 | 欢迎关注并获取源码!
179 | ----------------------------359056973355668947377349--
180 |
181 |
182 | Process finished with exit code -1
183 | ```
184 |
185 | >Postman访问;http://localhost:7397 并设置参数
186 |
187 | 
--------------------------------------------------------------------------------
/docs/notes/itstack-demo-any/2020-01-18-似乎你总也记不住,byte的取值范围是-127~128还是-128~127.md:
--------------------------------------------------------------------------------
1 | # 似乎你总也记不住,byte取值范围是 -127~128 还是 -128~127
2 |
3 | ---
4 |
5 | >小傅哥 & [https://bugstack.cn](https://bugstack.cn)
沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。
你用剑🗡、我用刀🔪,好的代码都很烧,望你不吝出招!
6 |
7 | ## 一、前言介绍
8 | 无论在面试过程中还是平时的技术交流中,似乎有很多小伙伴始终记不住java中byte类型的取值范围是多少。究其原因大部分程序员对这个取值范围是不在意的,因为知道与不知道都不影响你完成工作。另外这种知识点压根不是让你死记硬背的,当然如果你是从其他文科专业转过来学编程开发的,还情有可原。但对一个理科生来说,就不太应该了。
9 |
10 | ## 二、取值范围计算
11 |
12 | 在java中,byte占1个字节,8比特位,可以想象成8个小块的数据区间,首位用0、1代表符号位。**0[正]**、**1[负]**,那么绘制出一个表格如下;
13 |
14 | | byte | | | | | | | | |
15 | | :------|:------|:------|:------|:------|:------|:------|:------|------|
16 | |序号| 8 | 7 | 6 |5 |4 |3 |2 |1 |
17 | |2ⁿ| 2^7 |2^6 |2^5 |2^4 |2^3 |2^2 |2^1 | 2^0 |
18 | |值| 128 | 64 | 32 |16 |8 |4 |2 |1 |
19 | |+127| 0 | 1 | 1 |1 |1 |1 |1 |1 |
20 | |-128| 1 | 0 | 0 |0 |0 |0 |0 |0 |
21 |
22 | > +127 二进制求和
23 |
24 | ```java
25 | 2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7
26 | = 2^(n+1) - 1
27 | = 127
28 | ```
29 |
30 | > -128 二进制求和
31 |
32 | ```java
33 | 2^8
34 | = 128
35 | ```
36 |
37 | 好了,现在看懂逻辑就很清晰了,为什么是负数到-128,因为1代表负数的符号位,也就整整好好是2的8次方,-128。
38 |
39 | ## 三、进制数值转换
40 |
41 | 因为java语言与一些其他语言byte的取值范围不同,所以在有时候处理一些文件时候需要进行进制转换。也就是 -128~127 与 0~255 的转换处理;
42 |
43 | 比如我们现在将一个java中byte=120,转换成 0~255取值范围的数值;
44 |
45 | 一般可以进行与运算;
46 |
47 | ```java
48 | 120 & 0x0FF
49 | ```
50 |
51 | 同时还可以进行增位运算;(也就是将8个字节长度的内容,放到16个长度中,进行转换)
52 |
53 | ```java
54 | byte[] val = {-120};
55 | BigInteger bigInteger = new BigInteger(1, val);
56 | //有符号
57 | System.out.println(bigInteger.byteValue());
58 | //无符号(增位)
59 | String str_hex = bigInteger.toString(16);
60 | System.out.println(Integer.parseInt(str_hex, 16)); // 136
61 | ```
62 |
63 | ## 四、解析一段class字节码
64 |
65 | java的类文件都会被编译成class文件,那么class文件需要经过jvm的解析、验证,加载等处理才可以被虚拟机的指令执行操作。
66 |
67 | 如果下是一段class文件的byte数组,将内容解析出对应的结果;
68 |
69 | ```java
70 | public class ClassReaderTest {
71 |
72 | //取部分字节码:java.lang.String
73 | private static byte[] classData = {
74 | -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0,
75 | 59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1,
76 | 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41,
77 | 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3,
78 | 40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76,
79 | 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4};
80 |
81 | public static void main(String[] args) {
82 |
83 | //classData是我们的字节码,第一是-54,因为byte取值范围是-128~+127,所以如果想看到和其他虚拟机一样的值,需要进行与运算。
84 | System.out.println("* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):" + (-54 & 0x0FF));
85 |
86 | //校验魔数
87 | readAndCheckMagic();
88 |
89 | //校验版本号
90 | readAndCheckVersion();
91 |
92 | //接下来会依次读取[可以参照java版本虚拟机代码];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes
93 | }
94 |
95 | /**
96 | * 校验魔数
97 | *
98 | * 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。
99 | * 例如;
100 | * PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头,
101 | * ZIP文件以2字节“PK”(0x50、0x4B)开头
102 | * class文件以4字节“0xCAFEBABE”开头
103 | */
104 | private static void readAndCheckMagic() {
105 | System.out.println("\r\n------------ 校验魔数 ------------");
106 | //从class字节码中读取前四位
107 | byte[] magic_byte = new byte[4];
108 | System.arraycopy(classData, 0, magic_byte, 0, 4);
109 |
110 | //将4位byte字节转成16进制字符串
111 | String magic_hex_str = new BigInteger(1, magic_byte).toString(16);
112 | System.out.println("magic_hex_str:" + magic_hex_str);
113 |
114 | //byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中
115 | long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);
116 | System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);
117 |
118 | //魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算
119 | System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));
120 |
121 | if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {
122 | System.out.println("class字节码魔数无符号16进制数值一致校验通过");
123 | } else {
124 | System.out.println("class字节码魔数无符号16进制数值一致校验拒绝");
125 | }
126 |
127 | }
128 |
129 | /**
130 | * 校验版本号
131 | *
132 | * 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以
133 | * 表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45,
134 | * 从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52}
135 | */
136 | private static void readAndCheckVersion() {
137 | System.out.println("\r\n------------ 校验版本号 ------------");
138 |
139 | //从class字节码第4位开始读取,读取2位
140 | byte[] minor_byte = new byte[2];
141 | System.arraycopy(classData, 4, minor_byte, 0, 2);
142 | //将2位byte字节转成16进制字符串
143 | String minor_hex_str = new BigInteger(1, minor_byte).toString(16);
144 | System.out.println("minor_hex_str:" + minor_hex_str);
145 | //minor_unsigned_int32 转成无符号16进制
146 | int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);
147 | System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);
148 |
149 | //从class字节码第6位开始读取,读取2位
150 | byte[] major_byte = new byte[2];
151 | System.arraycopy(classData, 6, major_byte, 0, 2);
152 | //将2位byte字节转成16进制字符串
153 | String major_hex_str = new BigInteger(1, major_byte).toString(16);
154 | System.out.println("major_hex_str:" + major_hex_str);
155 | //major_unsigned_int32 转成无符号16进制
156 | int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16);
157 | System.out.println("major_unsigned_int32:" + major_unsigned_int32);
158 |
159 | System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32);
160 |
161 | }
162 |
163 | }
164 | ```
165 |
166 | **测试结果:**
167 |
168 | ```java
169 | * byte字节码与运算原值(-54)换行后(-54 & 0x0FF):202
170 |
171 | ------------ 校验魔数 ------------
172 | magic_hex_str:cafebabe
173 | magic_unsigned_int32:3405691582
174 | 0xCAFEBABE & 0x0FFFFFFFFL:3405691582
175 | class字节码魔数无符号16进制数值一致校验通过
176 |
177 | ------------ 校验版本号 ------------
178 | minor_hex_str:0
179 | minor_unsigned_int32:0
180 | major_hex_str:34
181 | major_unsigned_int32:52
182 | 版本号:52.0
183 |
184 | Process finished with exit code 0
185 | ```
186 |
187 | ## 五、综上总结
188 |
189 | - 关于byte在文章;[《用java实现jvm虚拟机》](https://bugstack.cn/itstack-demo-jvm/itstack-demo-jvm.html)中讲过,但是没有单独拿出来分析,现在单独分析下也增强记忆。
190 | - 任何一个可能不起眼的知识点,不是他不重要,而是你还没有用到。就像有句话说,不是读书没用,而是你没用。国语博大精深!
191 | - 认认真真对待每一个知识点,不断的夯实自己的地基,这就像是盖房子在打地基一样。越深越稳,最终所服能于你的上层架构才会更加精进。
192 |
--------------------------------------------------------------------------------
/docs/notes/itstack-demo-agent/2019-07-12-基于JavaAgent的全链路监控三《ByteBuddy操作监控方法字节码》.md:
--------------------------------------------------------------------------------
1 | ## 案例简述
2 | 在第二章中我们已经可以监控方法执行耗时,虽然它能完成我们一些基本需要,但是为了增强代码的扩展性,我们需要使用字节码操作工具ByteBuddy来帮助我们实现更完善的监控程序。
3 | >[Byte Buddy](http://bytebuddy.net/#/) is a code generation and manipulation library for creating and modifying Java classes during the runtime of a Java application and without the help of a compiler. Other than the code generation utilities that [ship with the Java Class Library](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html), Byte Buddy allows the creation of arbitrary classes and is not limited to implementing interfaces for the creation of runtime proxies. Furthermore, Byte Buddy offers a convenient API for changing classes either manually, using a Java agent or during a build.
4 |
5 | ## 环境准备
6 | 1. IntelliJ IDEA Community Edition
7 | 2. jdk1.8.0_45 64位
8 |
9 | ## 配置信息(路径相关修改为自己的)
10 | 1. 配置位置:Run/Debug Configurations -> VM options
11 | 2. 配置内容:-javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs
12 |
13 | ## 代码示例
14 | ```java
15 | itstack-demo-agent-03
16 | ├── pom.xml
17 | └── src
18 | ├── main
19 | │ ├── java
20 | │ │ └── org.itstack.demo.agent
21 | │ │ ├── MethodCostTime.java
22 | │ │ └── MyAgent.java
23 | │ └── resources
24 | │ └── META-INF
25 | │ └── MANIFEST.MF
26 | └── test
27 | └── java
28 | └── org.itstack.demo.test
29 | └── ApiTest.java
30 | ```
31 | >pom.xml (引入ByteBuddy并打入到Agent包中)
32 |
33 | ```xml
34 |
35 |
36 | -Xms512m -Xmx512m
37 | false
38 | true
39 | utf-8
40 | true
41 |
42 | src/main/resources/META-INF/MANIFEST.MF
43 |
44 |
45 |
46 |
47 | javassist
48 | javassist
49 | 3.12.1.GA
50 | jar
51 |
52 |
53 | net.bytebuddy
54 | byte-buddy
55 | 1.8.20
56 |
57 |
58 | net.bytebuddy
59 | byte-buddy-agent
60 | 1.8.20
61 |
62 |
63 |
64 |
65 |
66 | org.apache.maven.plugins
67 | maven-shade-plugin
68 |
69 |
70 | package
71 |
72 | shade
73 |
74 |
75 |
76 |
77 |
78 |
79 | javassist:javassist:jar:
80 | net.bytebuddy:byte-buddy:jar:
81 | net.bytebuddy:byte-buddy-agent:jar:
82 |
83 |
84 |
85 |
86 | ```
87 | >MethodCostTime.java
88 |
89 | ```java
90 | /**
91 | * 博客:http://itstack.org
92 | * 论坛:http://bugstack.cn
93 | * 公众号:bugstack虫洞栈 {获取学习源码}
94 | * create by fuzhengwei on 2019
95 | */
96 | public class MethodCostTime {
97 |
98 | @RuntimeType
99 | public static Object intercept(@Origin Method method, @SuperCall Callable> callable) throws Exception {
100 | long start = System.currentTimeMillis();
101 | try {
102 | // 原有函数执行
103 | return callable.call();
104 | } finally {
105 | System.out.println(method + " 方法耗时: " + (System.currentTimeMillis() - start) + "ms");
106 | }
107 | }
108 |
109 | }
110 | ```
111 |
112 | >MyAgent.java
113 |
114 | ```java
115 | /**
116 | * javaagent
117 | * 博客:http://itstack.org
118 | * 论坛:http://bugstack.cn
119 | * 公众号:bugstack虫洞栈 {获取学习源码}
120 | * create by fuzhengwei on 2019
121 | */
122 | public class MyAgent {
123 |
124 | //JVM 首先尝试在代理类上调用以下方法
125 | public static void premain(String agentArgs, Instrumentation inst) {
126 | System.out.println("this is my agent:" + agentArgs);
127 |
128 | AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
129 | return builder
130 | .method(ElementMatchers.any()) // 拦截任意方法
131 | .intercept(MethodDelegation.to(MethodCostTime.class)); // 委托
132 | };
133 |
134 | AgentBuilder.Listener listener = new AgentBuilder.Listener() {
135 | @Override
136 | public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
137 |
138 | }
139 |
140 | @Override
141 | public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {
142 |
143 | }
144 |
145 | @Override
146 | public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {
147 |
148 | }
149 |
150 | @Override
151 | public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {
152 |
153 | }
154 |
155 | @Override
156 | public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {
157 |
158 | }
159 |
160 | };
161 |
162 | new AgentBuilder
163 | .Default()
164 | .type(ElementMatchers.nameStartsWith("org.itstack.demo.test")) // 指定需要拦截的类
165 | .transform(transformer)
166 | .with(listener)
167 | .installOn(inst);
168 | }
169 |
170 |
171 | //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法
172 | public static void premain(String agentArgs) {
173 | }
174 |
175 | }
176 | ```
177 | >MANIFEST.MF
178 |
179 | ```其他语言
180 | Manifest-Version: 1.0
181 | Premain-Class: org.itstack.demo.agent.MyAgent
182 | Can-Redefine-Classes: true
183 | ```
184 | >ApiTest.java
185 |
186 | ```java
187 | /**
188 | * 博客:http://itstack.org
189 | * 论坛:http://bugstack.cn
190 | * 公众号:bugstack虫洞栈 {获取学习源码}
191 | * create by fuzhengwei on 2019
192 | *
193 | * VM options:
194 | * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-03\target\itstack-demo-agent-03-1.0.0-SNAPSHOT.jar=testargs
195 | */
196 | public class ApiTest {
197 |
198 | public static void main(String[] args) throws InterruptedException {
199 | ApiTest apiTest = new ApiTest();
200 | apiTest.echoHi();
201 | }
202 |
203 | private void echoHi() throws InterruptedException {
204 | System.out.println("hi agent");
205 | Thread.sleep((long) (Math.random() * 500));
206 | }
207 |
208 | }
209 | ```
210 | ## 测试结果
211 |
212 | ```java
213 | this is my agent:testargs
214 | hi agent
215 | private void org.itstack.demo.test.ApiTest.echoHi() throws java.lang.InterruptedException 方法耗时: 329ms
216 | public static void org.itstack.demo.test.ApiTest.main(java.lang.String[]) throws java.lang.InterruptedException 方法耗时: 329ms
217 |
218 | Process finished with exit code 0
219 | ```
220 |
221 |
222 |
--------------------------------------------------------------------------------
/docs/notes/itstack-demo-any/2019-12-21-[有点干货]JDK、CGLIB动态代理使用以及源码分析.md:
--------------------------------------------------------------------------------
1 | # 有点干货 | JDK、CGLIB动态代理使用以及源码分析
2 |
3 | ---
4 |
5 | ## 前言介绍
6 | 在Java中动态代理是非常重要也是非常有用的一个技术点,如果没有动态代理技术几乎也就不会有各种优秀框架的出现,包括Spring。
7 | 其实在动态代理的使用中,除了我们平时用的Spring还有很多中间件和服务都用了动态代理,例如;
8 | 1. RPC通信框架Dubbo,在通信的时候由服务端提供一个接口描述信息的Jar,调用端进行引用,之后在调用端引用后生成了对应的代理类,当执行方法调用的时候,实际需要走到代理类向服务提供端发送请求信息,直至内容回传。
9 | 2. 另外在使用Mybatis时候可以知道只需要定义一个接口,不需要实现具体方法就可以调用到Mapper中定义的数据库操作信息了。这样极大的简化了代码的开发,又增强了效率。
10 | 3. 最后不知道你自己是否尝试过开发一些基于代理类的框架,以此来优化业务代码。也就是将业务代码中非业务逻辑又通用性的功能抽离出来,开发为独立的组件。**推荐个案例,方便知道代理类的应用:**[手写RPC框架第三章《RPC中间件》](https://bugstack.cn/itstack-demo-netty-3/2019/09/03/%E6%89%8B%E5%86%99RPC%E6%A1%86%E6%9E%B6%E7%AC%AC%E4%B8%89%E7%AB%A0-RPC%E4%B8%AD%E9%97%B4%E4%BB%B6.html)
11 |
12 | ## 代理方式
13 | 动态代理可以使用Jdk方式也可以使用CGLB,他们的区别,如下;
14 |
15 | | 类型 | 机制 | 回调方式 |适用场景 | 效率 |
16 | |:--------|:-------|:-------|:-------|:-------|
17 | |JDK | 委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法 |反射 | 目标类是接口类| 效率瓶颈在反射调用稍慢|
18 | | CGLIB| 继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑|通过FastClass方法索引调用 | 非接口类,非final类,非final方法|第一次调用因为要生成多个Class对象较JDK方式慢,多次调用因为有方法索引较反射方式快,如果方法过多switch case过多其效率还需测试 |
19 |
20 | ## 案例工程
21 |
22 | ```java
23 | itstack-demo-test
24 | └── src
25 | ├── main
26 | │ └── java
27 | │ └── org.itstack.demo
28 | │ ├── proxy
29 | │ │ └── cglib
30 | │ │ └── CglibProxy.java
31 | │ ├── jdk
32 | │ │ ├── reflect
33 | │ │ │ ├── JDKInvocationHandler.java
34 | │ │ │ └── JDKProxy.java
35 | │ │ └── util
36 | │ │ └── ClassLoaderUtils.java
37 | │ └── service
38 | │ ├── IUserService.java
39 | │ └── UserService.java
40 | └── test
41 | └── java
42 | └── org.itstack.demo.test
43 | └── ApiTest.java
44 | ```
45 |
46 | ### 基础接口和方法便于验证
47 |
48 | >service/IUserService.java
49 |
50 | ```java
51 | public interface IUserService {
52 |
53 | String queryUserNameById(String userId);
54 |
55 | }
56 | ```
57 |
58 | >service/UserService.java
59 |
60 | ```java
61 | public class UserService implements IUserService {
62 |
63 | public String queryUserNameById(String userId) {
64 | return "hi user " + userId;
65 | }
66 |
67 | }
68 | ```
69 |
70 | ### JDK动态代理
71 |
72 | >reflect/JDKInvocationHandler.java & 代理类反射调用
73 |
74 | - 实现InvocationHandler.invoke,用于方法增强{监控、执行其他业务逻辑、远程调用等}
75 | - 如果有需要额外的参数可以提供构造方法
76 |
77 | ```java
78 | public class JDKInvocationHandler implements InvocationHandler {
79 |
80 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
81 | System.out.println(method.getName());
82 | return "我被JDKProxy代理了";
83 | }
84 |
85 | }
86 | ```
87 |
88 | >reflect/JDKProxy.java & 定义一个代理类获取的服务
89 |
90 | - Proxy.newProxyInstance 来实际生成代理类,过程如下;
91 | 1. Class> cl = getProxyClass0(loader, intfs); 查找或生成指定的代理类
92 | 2. proxyClassCache.get(loader, interfaces); 代理类的缓存中获取
93 | 3. subKeyFactory.apply(key, parameter) 继续下一层
94 | 4. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 生成代理类的字节码
95 |
96 | ```java
97 | public class JDKProxy {
98 |
99 | public static T getProxy(Class interfaceClass) throws Exception {
100 | InvocationHandler handler = new JDKInvocationHandler();
101 | ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
102 | T result = (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler);
103 | return result;
104 | }
105 |
106 | }
107 | ```
108 |
109 | >ApiTest.test_proxy_jdk() & 执行调用并输出反射类的字节码
110 |
111 | - 代理后调用方法验证
112 | - 通过使用ProxyGenerator.generateProxyClass获取实际的字节码,查看代理类的内容
113 |
114 | ```java
115 | @Test
116 | public void test_proxy_jdk() throws Exception {
117 |
118 | IUserService proxy = (IUserService) JDKProxy.getProxy(ClassLoaderUtils.forName("org.itstack.demo.service.IUserService"));
119 | String userName = proxy.queryUserNameById("10001");
120 | System.out.println(userName);
121 |
122 | String name = "ProxyUserService";
123 | byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IUserService.class});
124 |
125 | // 输出类字节码
126 | FileOutputStream out = null;
127 | try {
128 | out = new FileOutputStream(name + ".class");
129 | System.out.println((new File("")).getAbsolutePath());
130 | out.write(data);
131 | } catch (FileNotFoundException e) {
132 | e.printStackTrace();
133 | } catch (IOException e) {
134 | e.printStackTrace();
135 | } finally {
136 | if (null != out) try {
137 | out.close();
138 | } catch (IOException e) {
139 | e.printStackTrace();
140 | }
141 | }
142 |
143 | }
144 | ```
145 |
146 | **输出结果**
147 |
148 | ```java
149 | queryUserNameById
150 | 我被JDKProxy代理了
151 | ```
152 |
153 | **将生成的代理类进行反编译jd-gui**
154 |
155 | 部分内容抽取,可以看到比较核心的方法,也就是我们在调用的时候走到了这里
156 |
157 | ```java
158 | public final String queryUserNameById(String paramString)
159 | throws
160 | {
161 | try
162 | {
163 | return (String)this.h.invoke(this, m3, new Object[] { paramString });
164 | }
165 | catch (Error|RuntimeException localError)
166 | {
167 | throw localError;
168 | }
169 | catch (Throwable localThrowable)
170 | {
171 | throw new UndeclaredThrowableException(localThrowable);
172 | }
173 | }
174 |
175 |
176 | static
177 | {
178 | try
179 | {
180 | m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
181 | m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
182 | m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
183 | m3 = Class.forName("org.itstack.demo.service.IUserService").getMethod("queryUserNameById", new Class[] { Class.forName("java.lang.String") });
184 | return;
185 | }
186 | catch (NoSuchMethodException localNoSuchMethodException)
187 | {
188 | throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
189 | }
190 | catch (ClassNotFoundException localClassNotFoundException)
191 | {
192 | throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
193 | }
194 | }
195 | ```
196 |
197 | ### CGLIB动态代理
198 |
199 | >cglib/CglibProxy.java
200 |
201 | - 提供构造方法,生成CGLIB的代理类,回调this
202 | - intercept可以进行方法的增强,处理相关业务逻辑
203 | - CGLIB是通过ASM来操作字节码生成类
204 |
205 | ```java
206 | public class CglibProxy implements MethodInterceptor {
207 |
208 | public Object newInstall(Object object) {
209 | return Enhancer.create(object.getClass(), this);
210 | }
211 |
212 | public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
213 | System.out.println("我被CglibProxy代理了");
214 | return methodProxy.invokeSuper(o, objects);
215 | }
216 |
217 | }
218 | ```
219 |
220 | >ApiTest.test_proxy_cglib() & 调用代理类
221 |
222 | ```java
223 | @Test
224 | public void test_proxy_cglib() {
225 | CglibProxy cglibProxy = new CglibProxy();
226 | UserService userService = (UserService) cglibProxy.newInstall(new UserService());
227 | String userName = userService.queryUserNameById("10001");
228 | System.out.println(userName);
229 | }
230 | ```
231 |
232 | **输出结果**
233 |
234 | ```java
235 | 我被CglibProxy代理了
236 | hi user 10001
237 | ```
238 |
239 | ## 综上总结
240 |
241 | - 在我们实际使用中两种方式都用所有使用,也可以依照不同的诉求进行选择
242 | - 往往动态代理会和注解共同使用,代理类拿到以后获取方法的注解,并做相应的业务操作
243 | - 有时候你是否会遇到增加AOP不生效,因为有时候有些类是被代理操作的,并没有执行你的自定义注解也就是切面
--------------------------------------------------------------------------------
/docs/notes/itstack-demo-netty/itstack-demo-netty-4/2019-09-13-netty案例,netty4.1源码分析篇四《ByteBuf的数据结构在使用方式中的剖析》.md:
--------------------------------------------------------------------------------
1 | ## 前言介绍
2 | 在Netty中ByteBuf是一个非常重要的类,它可以以高效易用的数据结构方式来满足网络通信过程中处理数据包内字节码序列的移动。
3 |
4 | ## 数据结构
5 |
6 | ```java
7 | +-------------------+------------------+------------------+
8 | | discardable bytes | readable bytes | writable bytes |
9 | | | (CONTENT) | |
10 | +-------------------+------------------+------------------+
11 | | | | |
12 | 0 <= readerIndex <= writerIndex <= capacity
13 | ```
14 | 那么这种数据结构之所以能高效的处理数据传输处理并解决半包粘包,主要得益于ByteBuf中有两个不同的索引;读索引(readIndex)、写索引(writerIndex)。
15 | 读索引:当我们从ByteBuf读取数据时,readerIndex指针位置也会指向到读取字节位置
16 | 写索引:当我们向ByteBuf写入数据时,writerIndex指针位置也会指向到写入字节位置
17 |
18 | discardable bytes:当从ByteBuf读取一部分数据后,这部分数据就属于discardable,他们是可以被废弃的。
19 | readable bytes:剩余可以继续读取的内容区域,就像一个长条的鸡蛋卡槽,一边放一边拿。从已经拿完到剩余的鸡蛋位置属于可拿区域。
20 | writable bytes:同上一样,这一部分就是可以继续放置鸡蛋的位置。
21 |
22 | ## 功能案例
23 |
24 | ```java
25 | // 62 75 67 73 74 61 63 6B B3 E6 B6 B4 D5 BB
26 | public static void main(String[] args) throws UnsupportedEncodingException {
27 | // 1.创建一个非池化的ByteBuf,大小为14个字节
28 | ByteBuf buffer = Unpooled.buffer(14);
29 | System.out.println("1.创建一个非池化的ByteBuf,大小为14个字节");
30 | System.out.println("ByteBuf空间大小:" + buffer.capacity());
31 | // 2.写入3个字节
32 | buffer.writeByte(62);
33 | buffer.writeByte(75);
34 | buffer.writeByte(67);
35 | System.out.println("\r\n2.写入3个字节");
36 | System.out.println("readerIndex位置:" + buffer.readerIndex());
37 | System.out.println("writerIndex位置:" + buffer.writerIndex());
38 | // 3.写入一段字节
39 | byte[] bytes = {73, 74, 61, 63, 0x6B};
40 | buffer.writeBytes(bytes);
41 | System.out.println("\r\n3.写入一段字节");
42 | System.out.println("readerIndex位置:" + buffer.readerIndex());
43 | System.out.println("writerIndex位置:" + buffer.writerIndex());
44 | // 4.读取全部内容
45 | byte[] allBytes = new byte[buffer.readableBytes()];
46 | buffer.readBytes(allBytes);
47 | System.out.println("\r\n4.读取全部内容");
48 | System.out.println("readerIndex位置:" + buffer.readerIndex());
49 | System.out.println("writerIndex位置:" + buffer.writerIndex());
50 | System.out.println("读取全部内容:" + Arrays.toString(allBytes));
51 | // 5.重置指针位置
52 | buffer.resetReaderIndex();
53 | System.out.println("\r\n5.重置指针位置");
54 | System.out.println("readerIndex位置:" + buffer.readerIndex());
55 | System.out.println("writerIndex位置:" + buffer.writerIndex());
56 | // 6.读取3个字节
57 | byte b0 = buffer.readByte();
58 | byte b1 = buffer.readByte();
59 | byte b2 = buffer.readByte();
60 | System.out.println("\r\n6.读取3个字节");
61 | System.out.println("readerIndex位置:" + buffer.readerIndex());
62 | System.out.println("writerIndex位置:" + buffer.writerIndex());
63 | System.out.println("读取3个字节:" + Arrays.toString(new byte[]{b0, b1, b2}));
64 | // 7.读取一段字节
65 | ByteBuf byteBuf = buffer.readBytes(5);
66 | byte[] dst = new byte[5];
67 | byteBuf.readBytes(dst);
68 | System.out.println("\r\n7.读取一段字节");
69 | System.out.println("readerIndex位置:" + buffer.readerIndex());
70 | System.out.println("writerIndex位置:" + buffer.writerIndex());
71 | System.out.println("读取一段字节:" + Arrays.toString(dst));
72 | // 8.丢弃已读内容
73 | buffer.discardReadBytes();
74 | System.out.println("\r\n8.丢弃已读内容");
75 | System.out.println("readerIndex位置:" + buffer.readerIndex());
76 | System.out.println("writerIndex位置:" + buffer.writerIndex());
77 | // 9.清空指针位置
78 | buffer.clear();
79 | System.out.println("\r\n9.清空指针位置");
80 | System.out.println("readerIndex位置:" + buffer.readerIndex());
81 | System.out.println("writerIndex位置:" + buffer.writerIndex());
82 | // 10.ByteBuf中还有很多其他方法;拷贝、标记、跳过字节,多用于自定义解码器进行半包粘包处理
83 | }
84 | ```
85 |
86 | ```java
87 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List