0x00. Preface
CC chain 1 has been analyzed by many predecessors. At the beginning, it was good, but later, I always felt confused. What I learned from various materials was also a little bit of understanding. There are many questions that may be quite difficult for me or similar friends who have a weak foundation in Java. Fortunately, I record some points of doubt in addition to the regular chain analysis, for the reference of new partners to follow. Therefore, this article is suitable for friends who are just starting to analyze CC chain 1. If there are any mistakes, please also point them out by the big guys, and I am very grateful.
0x01. Complete exp
It feels very unattractive to paste code directly, so I have uploaded the picture. Everyone can also get the exp from other master's articles.
0x02. hashmap.put("value",....)
Why call hashmap.put()

Because the chain is handled during deserialization from the readObject method of AnnotationInvocationHandler, the purpose is to call the memberValue.setValue() method within the readObject method. However, this method is in a for-each loop, and when we obtain the AnnotationInvocationHandler object through reflection, we need to pass a map value to the constructor. This map corresponds to the memberValues within the for loop body. If memberValues is empty, the for loop becomes empty and will directly jump out of the loop body without executing the code inside. Therefore, in order to enter the for loop, there must be values in the map, so we need to call hashmap.put() to put data into it.
2. Why must the first position of hashmap.put() be the value?
Because after we pass the map, the for loop in the body will obtain the memberType of name through the statement Class<?> memberType = memberTypes.get(name);, we mentioned that during the reflection of AnnotationInvocationHandler, the second parameter is map, and the first parameter is set to Target.class annotation. This is because the Target.class annotation contains the value key, so it can make memberType not empty, thereby satisfying the condition memberType != null. Of course, as long as the annotation provided is not empty, you can set other annotations, such as Repeatable.class,Retention.class, etc.
Therefore, the memberTypes provided earlier contains the content of the annotation's value
So, when executing Class<?> memberType = memberTypes.get(name);, this name is derived from the first line String name = memberValue.getKey();, and it can be seen that name comes from the memberValues we provide, and we obtain the map's key value through memberValue.getKey(). Therefore, the second statement will not cause memberType to be null when executing .get(name), thus entering the if loop.
0x03. The setValue() method in AbstractInputCheckedMapDecorator
Forgive the newbies, when analyzing other articles, it is really hard to understand why, when looking for the checkSetValue method of TransformedMap, it goes to the setValue method of AbstractInputCheckedMapDecorator. After asking AI and analyzing the inheritance relationship, I found the following points:
1. TransformedMap does not have the setValue() method.
The type of memberValue is determined by memberValues, and the corresponding memberValues we pass in is TransformedMap. However, there is no setValue method in the TransformedMap class, so at this time, we will follow the chain to find this method in the parent class of TransformedMap. And TransformedMap inherits from AbstractInputCheckedMapDecorator (this is the key point).
The following code exists in the AbstractInputCheckedMapDecorator class:
The code in these three figures can be understood as rewriting the logic of the for-each function (we don't delve into the specifics, just understand that it has changed the underlying logic code of the following for loop), and I can know from the arrow that the map is generated through the MapEntry class, and when we look at the MapEntry class, we will find that there is a setValue function, which is one link mentioned in the chain.
So when we are in a for loop in AnnotationInvocationHandler, memberValue will execute the setValue in MapEntry due to the rewritten loop in the parent class AbstractInputCheckedMapDecorator of TransformedMap.
2. How is the parent controllable as the desired Map type when setValue is called?
Similarly, through the above analysis, we can find that TransformedMap goes to the parent class AbstractInputCheckedMapDecorator to find setValue because it cannot find setValue. So when we are in a loop, inside the entrySet() function, the this value is passed to the EntrySet constructor method, where this refers to TransformedMap, and the parent in the EntrySet constructor method refers to the TransformedMap represented by this. Therefore, when we finally set the value, it is done through TransformedMap.
You can see the changes in parent and this by debugging.
This is the confusion I encountered in CC chain 1. If fellow friends have any doubts, please leave a comment for discussion. If seniors find any errors, please correct them, and I will be very grateful.

评论已关闭