Notes on Java 8 - Chapter 23 notes

Keywords: Java Database Spring Mybatis

It's all notes of personal learning process, not summary, no reference value, but this book is great

Basic grammar

Definition annotation

Annotations usually contain elements that represent specific values. These values can be used by programs or tools when analyzing and processing annotations. The annotated element looks like an interface method, but you can specify a default value for it.

for example

// annotations/UseCase.java
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    int id();
    String description() default "no description";
}

Meta annotation

The element of Retention annotation is very important, and the RUNTIME annotation reminds me of spring annotation development

RUNTIME: the VM will also retain annotations during RUNTIME, so the information of annotations can be read through reflection mechanism.

Write annotation processor

// annotations/UseCaseTracker.java
import java.util.*;
import java.util.stream.*;
import java.lang.reflect.*;
public class UseCaseTracker {
    public static void
    trackUseCases(List<Integer> useCases, Class<?> cl) {
        for(Method m : cl.getDeclaredMethods()) {
            UseCase uc = m.getAnnotation(UseCase.class);
            if(uc != null) {
                System.out.println("Found Use Case " +
                        uc.id() + "\n " + uc.description());
                useCases.remove(Integer.valueOf(uc.id()));
            }
        }
        useCases.forEach(i ->
                System.out.println("Missing use case " + i));
    }
    public static void main(String[] args) {
        List<Integer> useCases = IntStream.range(47, 51)
                .boxed().collect(Collectors.toList());
        trackUseCases(useCases, PasswordUtils.class);
    }
}

It is to get all the methods of the class by using the reflection method, and then get the annotation for the method. I have looked at the source code. The methods for getting the annotation for the method object are getannotation (class < T > Annotation class) gets the annotation of a fixed class, as shown in the above code; getdeclaredanotions() gets all the annotations, and returns the array of annotation class (this seems to be the parent class of all annotations); and getParameterAnnotations() gets the annotation of parameters of this method: returns an array of arrays of annotations that represents the annotations on the formal parameters, in declaration order
Note that useCases.remove(Integer.valueOf(uc.id())); this line uses the method of remove(Object m), that is, to delete the first m object, and useCases.remove(uc.id()) calls remove(int m), that is, to delete the m object.

Annotation element

Default limit

First, an element cannot have an indefinite value. That is, the element either has a default value or provides the value of the element when using annotations.

Generate external file

Object / relationship mapping: it's Mybatis that kind of thing that maps JavaBeans to the database

Alternatives

Annotation does not support inheritance

Implementation processor

// annotations/database/TableCreator.java
// Reflection-based annotation processor
// {java annotations.database.TableCreator
// annotations.database.Member}
package annotations.database;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class TableCreator {
    public static void
    main(String[] args) throws Exception {
        if (args.length < 1) {
            System.out.println(
                    "arguments: annotated classes");
            System.exit(0);
        }
        for (String className : args) {
            Class<?> cl = Class.forName(className);
            DBTable dbTable = cl.getAnnotation(DBTable.class);
            if (dbTable == null) {
                System.out.println(
                        "No DBTable annotations in class " +
                                className);
                continue;
            }
            String tableName = dbTable.name();
            // If the name is empty, use the Class name:
            if (tableName.length() < 1)
                tableName = cl.getName().toUpperCase();
            List<String> columnDefs = new ArrayList<>();
            for (Field field : cl.getDeclaredFields()) {
                String columnName = null;
                Annotation[] anns =
                        field.getDeclaredAnnotations();
                if (anns.length < 1)
                    continue; // Not a db table column
                if (anns[0] instanceof SQLInteger) {
                    SQLInteger sInt = (SQLInteger) anns[0];
                    // Use field name if name not specified
                    if (sInt.name().length() < 1)
                        columnName = field.getName().toUpperCase();
                    else
                        columnName = sInt.name();
                    columnDefs.add(columnName + " INT" +
                            getConstraints(sInt.constraints()));
                }
                if (anns[0] instanceof SQLString) {
                    SQLString sString = (SQLString) anns[0];
                    // Use field name if name not specified.
                    if (sString.name().length() < 1)
                        columnName = field.getName().toUpperCase();
                    else
                        columnName = sString.name();
                    columnDefs.add(columnName + " VARCHAR(" +
                            sString.value() + ")" +
                            getConstraints(sString.constraints()));
                }
                StringBuilder createCommand = new StringBuilder(
                        "CREATE TABLE " + tableName + "(");
                for (String columnDef : columnDefs)
                    createCommand.append(
                            "\n " + columnDef + ",");
                // Remove trailing comma
                String tableCreate = createCommand.substring(
                        0, createCommand.length() - 1) + ");";
                System.out.println("Table Creation SQL for " +
                        className + " is:\n" + tableCreate);
            }
        }
    }

    private static String getConstraints(Constraints con) {
        String constraints = "";
        if (!con.allowNull())
            constraints += " NOT NULL";
        if (con.primaryKey())
            constraints += " PRIMARY KEY";
        if (con.unique())
            constraints += " UNIQUE";
        return constraints;
    }
}

This code is very good, but you need to manually pass in the fully qualified class name of the Member as the parameter. Note that the getdeclaredanotions method is used to get the annotation of the field. The book explains that: because there is no inheritance mechanism for the annotation, if you want to get the behavior of approximate multimodality, using getdeclaredanotions() seems to be the only way.
I don't quite understand what it means

Using javac to process annotations

This section mainly deals with how to handle the annotation of @ Retention(RetentionPolicy.SOURCE). After compiling, the annotation information of this annotation element will be lost

Simplest processor

Check the decompiled code of SimpleTest.class as follows, and find that the annotation information has been deleted

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD,
        ElementType.CONSTRUCTOR,
        ElementType.ANNOTATION_TYPE,
        ElementType.PACKAGE, ElementType.FIELD,
        ElementType.LOCAL_VARIABLE})
public @interface Simple {
    String value() default "-default-";
}

package chapter23.simplest;

// annotations/simplest/SimpleProcessor.java
// A bare-bones annotation processor

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import java.util.*;

@SupportedAnnotationTypes("chapter23.simplest.Simple")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SimpleProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        for (TypeElement t : annotations)
            System.out.println(t);
        for (Element el : env.getElementsAnnotatedWith(Simple.class))
            display(el);
        return false;
    }

    private void display(Element el) {
        System.out.println("==== " + el + " ====");
        System.out.println(el.getKind() +
                " : " + el.getModifiers() +
                " : " + el.getSimpleName() +
                " : " + el.asType());
        if (el.getKind().equals(ElementKind.CLASS)) {
            TypeElement te = (TypeElement) el;
            System.out.println(te.getQualifiedName());
            System.out.println(te.getSuperclass());
            System.out.println(te.getEnclosedElements());
        }
        if (el.getKind().equals(ElementKind.METHOD)) {
            ExecutableElement ex = (ExecutableElement) el;
            System.out.print(ex.getReturnType() + " ");
            System.out.print(ex.getSimpleName() + "(");
            System.out.println(ex.getParameters() + ")");
        }
    }
}

The book says it needs to be called
Javac - processor annotations. Simple. Simpleprocessor simpletest. Java, the code will print out the results. I tried for half a day, but I didn't print anything. It's empty

@SupportedAnnotationTypes and @ SupportedSourceVersion are respectively used to specify the fully qualified class name and source code version of the annotation
Env.getelementsnannotatedwith (Simple. Class) obtains all elements with Simple annotation and prints various information of these elements
Ai Ai Ai Ai I have transferred it out. I need to compile manually in order. I need to enter the src folder to start compiling. The class file generated by idea is in the out folder, which is separate from the source file. So I can't debug it. First compile the Simple class, because other classes depend on this class, then compile the SimpleTest class, and finally compile the SimpleTest class

The compilation results are as follows

The object of Element type is obtained through env.getelementsnannotatedwith (Simple. Class). I understand that it is the Element with Simple annotation. It may be a method, a class or a member, and then it is processed in the display method
At the same time, the author uses the downward transformation

Element can only perform operations that are common to all basic objects parsed by the compiler, while things like classes and methods have additional information to extract. So (if you read the right document, but I didn't find it in any document - I had to look for clues through StackOverflow) you check which ElementKind it is, and then convert it down to a more specific element type, inject TypeElement for CLASS and ExecutableElement for METHOD. At this point, you can call other methods for these elements.

It seems that the document author missed this step

More complex processors




The function is to extract the public non-static methods in the multiplexer, build a new interface, and specify the name of the generated interface through the @ ExtractInterface annotation
The specific details are explained very well in the book, I won't go into details

Published 42 original articles, won praise 13, visited 20000+
Private letter follow

Posted by guoxin on Mon, 02 Mar 2020 20:30:38 -0800