Mp.weixin.qq.com/s/zmuY7VJBD…


History abandoned XML as it began to taste the sweetness of annotations.





The Spring Empire, in particular, is full of notes.





Annotation from a “guest” to provide ancillary information, transformed into a mainstay of the “king”.





Annotations have made it onto the imperial stage and are sure to leave a mark just like XML.









Reconceptualize annotations








Annotation actually means annotation, annotation. It’s like taking notes on the side of a book.





If the printed content of the book is considered the original information, then the written notes are additional information that has been added, and this additional information does not destroy the original information.





So annotations are a way to add additional data to the original data without damaging the original data.





Java introduced annotations in 1.5. Annotations are attached to program code but have no effect on how it runs. And can provide a piece of data that is different from the program code, often called “metadata.”





Annotations must be “hosted”, so annotations are usually marked on classes/interfaces/methods/fields/method parameters/generic types, etc.





If you think of the object information generated by a type as data, the information about the type itself is “metadata.” It defines fields, methods, etc.





Obviously, these types of metadata information are retrieved by reflection, and since annotations are metadata, they are also retrieved by reflection.









A detailed introduction to Java annotations








First of all, the program code that can annotate solutions is called “elements”, so classes, interfaces, methods, and so on are elements.





From an element’s point of view, an annotation must be an annotation of an element, because annotations cannot stand on their own and must be attached to an element.





From an annotation standpoint, an element is an annotated element, so it is also called an annotated element or an annotated element.





The reason I have to mention annotated elements is because there’s an interface in the Java API called AnnotatedElement.





An element or annotated element is the program code, it’s the body, and annotations are appendages that provide additional information and serve the body.





So when we define an annotation, the annotation itself is program code, so it becomes the body. The interesting thing is that you can annotate this code.





These annotations then become annotations that define annotations, or service annotations, and are therefore called “meta-annotations”. Most of you should be familiar with this.





There are two meta annotations that need special attention. One is @inherited and the other is @repeatable. The latter is new in JDK1.8.





Inherited refers to the Inherited nature of an annotation:





The annotations annotated by the meta annotation are inheritable at the time of use. Conversely, what is not marked by it is not inheritable when used.





This inheritance applies in one and only one case (as of Java8), when annotations on a super class can be automatically passed to a sub class.





Again, inheritance only applies to annotations on a class (that is, class). Other annotations such as interfaces, methods, fields, method parameters, generic types, etc., do not support inheritance.





In this case, no error is reported even if an inheritable annotation is used, only that the annotation is not inherited.





If both a subclass and a parent class have the same inherited annotation, the subclass’s annotation overrides the parent class’s annotation.





In fact, it can be interpreted as looking for annotations on the class first, if there is no annotation, and the annotation to look for is inherited, will continue to look for the parent class.





Repeatable @ indicates that the comment is Repeatable:





As anyone who has used annotations will notice, an annotation can only appear once on the same element, and if it happens more than once, the IDE will simply prompt an error.





This indicates that annotations are not repeatable. To solve this problem, the @repeatable meta annotation was introduced in JDK1.8.





Annotations defined using this meta-annotation are repeatable, that is, can be annotated repeatedly. When there are two or more annotations, you have an array of annotations.





Because when multiple things of the same type are put together, that’s the scenario for an array. Therefore, there are special requirements when defining repeatable annotations.





You also need to define a corresponding “container” annotation. The container annotation must define a value attribute whose type is an array of repeatable annotations.





@Repeatable(Bar.class)public @interface Foo {}public @interface Bar { Foo[] value(); }Copy the code



Foo is superlabeled with @REPEATable, so it is a Repeatable annotation, so you need a container annotation, which is also specified by @REPEATable, in this case Bar.





Since Bar is a container for Foo, we define a value() property of type Foo[]. In addition to the value attribute, you can define other attributes, but they all have to have default values, so keep that in mind.









There are only a few apis for manipulating annotations








There are altogether 7 methods, which can be divided into 3 groups, and they are very characteristic:





1) A method name with nothing in it is concerned with annotations on the class and annotations inherited from the parent class.





(2) if the name of a method with Declared is used, it means that only the annotations on this class are concerned and will not be inherited from the parent class.





3) The ByType in the method name indicates that repeatable annotations will be treated in addition to the original functionality.





Take a look at these three groups:





Gets this class and inherits from its parent class





Annotation[] getAnnotations()<T extends Annotation> T getAnnotation(Class<T> annotationClass)default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)Copy the code

Gets only that of this class





Annotation[] getDeclaredAnnotations()default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)Copy the code



Having special treatment for repeatable annotations





default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)Copy the code



The solution is:





For a repeatable annotation, if it is not obtained, the container annotation will be attempted, and if it is obtained, its value property will be returned. Otherwise, the repeatable annotation does not exist.





It is important to note that these methods can only get annotations on elements, not meta-annotations on those annotations. See an example.





Define A meta-annotation @a as follows:





public @interface A {}Copy the code



Define an annotation @b with the annotation @a as follows:





@Apublic @interface B {}Copy the code



Define a class C with the @b annotation as follows:





@Bpublic class C {}Copy the code



Where Class C’s Class<? C class is an annotated element on which each of the seven apis just described can only get annotation @b, not meta annotation @A.





Can be considered as can not leapfrog, can only get the closest to their own level of annotations, in addition to those other one to multiple levels of annotations are not available.





So how do I get annotation @A? The answer is simple, as many of you know.





Annotate @b’s Class<? > object (i.e., b.class) is also an annotated element. Call the above API on it to get the annotation @a.





Because annotation @A is the closest annotation to B. Ha ha.





This is actually a recursive idea, so if you want to fully manipulate Java annotations, you basically have to use a recursive way to achieve.





Several apis return arrays of annotations, and the order of annotations in the array is specified:





A) Appear in the annotation array from top left to bottom right according to their position in the source code.





B) If you are considering inheriting annotations from a parent class, the parent class’s annotations come before the child class’s annotations.





C) If a subclass is considered to override (also known as overwriting) the parent class annotation, the position is counted as the parent class and the annotation returned is the child class.





Java support for annotations only goes so far, and it’s up to you to use them.









Spring’s extension of annotation meaning








Spring extends annotations in three areas:





1) Annotations and meta-annotations can be similar in some ways





If you have A meta-annotation @a that defines an annotation @b, there’s nothing in Java about what the relationship between @A and @b is, or whether there should be one.





Spring does, however, specify that @A and @b are similar in some ways. Let’s look at an example.





@Component indicates that a class is a Component. It is both an annotation and a meta-annotation because the @repository, @Service, and @Controller annotations use it when they are defined.





From a bean annotation perspective, these four annotations are similar in that they allow a bean to be registered, and they are all components. The difference is that the last three annotations focus more on one aspect of functionality.





By analogy, the relationship between meta-annotations and annotations is similar to the inheritance relationship between subclasses and superclasses in object-oriented. Superclass means general, subclass means concrete.





2) Multiple meta-annotations are combined to produce a new annotation





When there are a lot of annotations, it is also a bit troublesome. It is better to combine them and replace them with a new annotation, which not only retains the original function, but also reduces the number of annotations.





There are more cases like this in Spring, such as in Spring MVC:





@RestController = @Controller + @ResponseBody





As in Spring Boot:





@SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan





This is a really nice feature. But the underlying code needs to be recursive and generic enough.





By analogy, this situation is similar to the composition of classes in object-oriented, where multiple classes with different functions are combined to form a comprehensive class.





3) Open up the attribute transmission channel from bottom to top





Both annotations and meta-annotations can define attributes, and sometimes must be set to reasonable values to make sense. And that creates a problem.





Since we can only set properties for annotations, how do we set properties for meta-annotations? There is nothing official in Java about this, so there is no way to set attributes for meta-annotations through annotations.





But both of the previous extensions ran into this problem. To make it clearer, look at an example like this:





The @Component annotation has a String value attribute that specifies a name for the Component. The @Controller annotation defined with it also has this property.





When we use @Controller, we can only set the property value to itself. We can’t set the property value directly to its @Component meta-annotation, but the property value must be passed up from @Controller to @Component in order to ensure that the overall extension is complete.





Since Java itself does not mention this, what does Spring do? Look at the source code, in fact, the method is very simple:





public @interface Component {    String value() default ""; }@Componentpublic @interface Controller { @AliasFor(annotation = Component.class) String value() default""; }Copy the code



That’s right, using the @Aliasfor annotation to set up a mapping that goes up, essentially specifying which meta annotation to which attribute the value of an attribute should go to.





This method is not easy ah, ha ha. But it’s not always easy to implement.





Similarly, when using object-oriented inheritance and composition relationships, data is passed around.





When inheriting, a subclass calls the constructor of its parent class. When a subclass passes data to its parent class, it passes data from the bottom up.





Composition is when the new class calls the constructors of the classes involved in composition, passing data to them, as if passing data from the outside in.





I think it’s a little bit clearer now. To tell you the truth:





Spring’s extension of annotations allows annotations themselves to support inheritance and composition, so there are data transfer issues involved.





This is exactly what it looks like, except that object-oriented data transfer is naturally supported and annotation needs to be handled by Spring itself.





It also illustrates another point:





The laws of inheritance and composition can be applied to many things, and both seem “perfect.”









Spring’s method of getting all the annotation information








Annotation information is part of bean definition information, and Spring uses the ASM framework to read bytecode file contents to obtain bean definition information. That was the core of the previous article in this issue.





As mentioned at the end of the article, one of the drawbacks of this approach to annotations is that you can only get the explicitly set value of the annotation property, not the default value of the annotation property.





This information is not in the bean’s bytecode file. This information is in the bytecode file annotating itself. Does Spring continue to use ASM to read the bytecode files of the annotations themselves? Read on to find out.





Since Spring has expanded the meaning of annotations, those meta-annotations in addition to annotations, and meta-annotations of meta-annotations (and beyond), are important to the bean definition.





It also refers to the explicit property values and default property values of these meta-annotations at all levels. It also involves the up-passing of attribute values (that is, the handling of @Aliasfor annotations).





It’s not necessarily a particularly complicated problem, but it’s certainly a particularly tedious one. So what should Spring do? It’ll make a lot of sense if you analyze it together.





First of all, we should not only pay attention to the notes, but also pay attention to the notes of the notes that are meta notes, and the notes of the meta notes that are meta notes, such a processing direction along the notes, meta-notes up.





Theoretically there could be an infinite number of levels, but at least there could be several. You can think of this as a vertical problem.





Second, the attribute of the annotation may be another annotation that is the new annotation, and the attribute of the new annotation may be another annotation that is the new annotation, such a processing direction along the annotation, the new annotation.





Theoretically there could be an infinite number of levels, but at least there could be several. You can think of this as a horizontal problem.





The problem has been identified, so what should be done about it? Let’s analyze it again.





Vertical is often called “inheritance” and horizontal is often called “nesting.” Whether inheritance or nesting, they have a common feature, they are constantly repeating themselves.





For situations where you’re repeating yourself all the time, there’s a noun in the data structure that goes with it, and that’s right, recursion. Well, everybody knows. Ha ha.





Recursion can solve the problem of structure duplication, so what method should be used to obtain attribute data? As mentioned in the last article, there are only two methods, reflection and bytecode reading.





For bean (that is, class) information and Method information, Spring chooses to read bytecode. But for the annotated information, Spring chose reflection instead.





Here’s why: (I’m guessing)





Read bytecode with ASM. ASM uses the visitor pattern, which already has recursion in it, so it’s a little tricky to understand.





If the bytecode of different annotations is read recursively, it is equivalent to rubbing the two layers of recursion together, and the code complexity is high.





Annotations are relatively fixed and few in number, and are loaded by the JVM without much consumption.





Using reflection to get annotation information is relatively simple, and Java supports it just fine.





The information retrieved using reflection is sufficient; there is no need to read from the bytecode.





In short, the retrieval of annotation information is based on reflection.






PSSpring’s code implementation of annotations will be covered later.





Source code address:


https://github.com/coding-new-talking/java-code-demo.git