along with Java Updated new features , For example, architecture decisions and their requirements . current , Cloud computing usually requires applications to have better bootability in addition to a small amount of initial memory . therefore , It is necessary to redesign the way the frame is made , To eliminate the bottleneck of reflection .

In the frame , Reflexes play an important role in the process , Whether it's classic ORM still JAX-RS Or something REST API . Through a large number of reduction in a variety of exercises , So that Javaer It's easier to do your job .

For end users ( This refers to the users who use these frameworks ) All you need to do is add some symbols to the class , All operations can run normally . Their class metadata will be read and used to facilitate certain processes . current , The most popular way to perform this type is through introspection , So that Java The concept of dynamic language is easily produced .

Due to the creation of a large number of resources , Examples and documents of this kind of work are provided , So reflection is used in the frame API This kind of work is simplified . however , For some reason , We are here to discuss two issues : Startup application latency and memory consumption .

Application startup delay :
All processing and data structures will be executed at execution time . Imagine a dependency injection engine , It needs to be scanned step by step , Scope of inspection , Dependencies, etc . therefore , The more categories need to be analyzed , The more processing is required , And greatly increase the response time .

Memory consumption :
Each class needs to be traversed to Class Search metadata in , There is one ReflectionData  The cache loads all the information for the class , That is to search for getSimpleName() Simple information like that , All metadata information will be sent through SoftReference Load and reference , This will take some time to get out of memory .

in short : Reflection method has problems in initial memory consumption and application startup delay . This is because the data is executed immediately after the application starts , Analysis and parser processing . As the number of classes increases , Memory and runtime consumption tend to increase .

The solution to these problems is , Make the framework perform these operations at compile time rather than at run time :

*
When the application starts , Metadata and systems will be ready .

*
There is no need to call the reflection class , include ReflectionData, This reduces the memory consumption at startup .

*
Don't worry Type Erasure The impact of .

Another point to avoid reflection is , We can use it more easily AoT, And passed GraalVM Create native code , It's an exciting possibility , Especially for the no server concept . The program runs once , The entire resource is then returned to the operating system .

 

Demo code

After explaining the concept of reading type , The next step will be to create a simple tool , The tool will Java Class from some representation of the entity to be mapped to Map. Properties to be converted , And the field that will be the unique identifier . Let's do everything as shown in the following code :
@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {    String value() default "";}@Documented
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)
public @interface Column {    String value() default "";}@Documented
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)
public @interface Id {    String value() default "";}
To simplify the comparison with reflection or other options , An interface is created , This interface is responsible for communicating with Map The mutual transformation between the two .
import java.util.Map;public interface Mapper {
    <T>  T toEntity(Map<String, Object> map, Class<T> type);
    <T> Map<String, Object> toMap(T entity);}
To compare the two solutions , The first implementation will be through reflection . One thing is , There are several strategies for dealing with reflection , for example , Combined use with Introspector Of “
java.beans” package ; however , In this example , We will show how it works in the simplest way .
public class ReflectionMapper implements Mapper {    @Override
    public <T> T toEntity(Map<String, Object> map, Class<T> type) {
        Objects.requireNonNull(map, "Map is required");
        Objects.requireNonNull(type, "type is required");
        final Constructor<?>[] constructors = type.getConstructors();
        try {            final T instance = (T) constructors[0].newInstance();
            for (Field field : type.getDeclaredFields()) {
                write(map, instance, field);            }
            return instance;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException exception) {
            throw new RuntimeException("An error to field the entity process", exception);
        }    }    @Override    public <T> Map<String, Object> toMap(T entity) {
        Objects.requireNonNull(entity, "entity is required");
        Map<String, Object> map = new HashMap<>();
        final Class<?> type = entity.getClass();
        final Entity annotation = Optional.ofNullable(
                type.getAnnotation(Entity.class))
                .orElseThrow(() -> new RuntimeException("The class must have Entity annotation"));
        String name = annotation.value().isBlank() ? type.getSimpleName() : annotation.value();
        map.put("entity", name);
        for (Field field : type.getDeclaredFields()) {            try {
                read(entity, map, field);
            } catch (IllegalAccessException exception) {
                throw new RuntimeException("An error to field the map process", exception);
            }        }        return map;    }
    private <T> void read(T entity, Map<String, Object> map, Field field) throws IllegalAccessException {
        final Id id = field.getAnnotation(Id.class);
        final Column column = field.getAnnotation(Column.class);
        final String fieldName = field.getName();        if (id != null) {
            String idName = id.value().isBlank() ? fieldName : id.value();
            field.setAccessible(true);
            final Object value = field.get(entity);
            map.put(idName, value);        } else if (column != null) {
            String columnName = column.value().isBlank() ? fieldName : column.value();
            field.setAccessible(true);
            final Object value = field.get(entity);
            map.put(columnName, value);        }    }
    private <T> void write(Map<String, Object> map, T instance, Field field) throws IllegalAccessException {
        final Id id = field.getAnnotation(Id.class);
        final Column column = field.getAnnotation(Column.class);
        final String fieldName = field.getName();        if (id != null) {
            String idName = id.value().isBlank() ? fieldName : id.value();
            field.setAccessible(true);
            final Object value = map.get(idName);
            if (value != null) {                field.set(instance, value);
            }        } else if (column != null) {
            String columnName = column.value().isBlank() ? fieldName : column.value();
            field.setAccessible(true);
            final Object value = map.get(columnName);
            if (value != null) {                field.set(instance, value);
            }        }    }}
After building the mapper , The next step is to make a small example . therefore , Let's create one Animal entity .
@Entity("animal")public class Animal {    @Id    private String id;
    @Column("native_name")    private String name;    public Animal() {    }
    public Animal(String id, String name) {        this.id = id;
        this.name = name;    }    public String getId() {        return id;    }
    public void setId(String id) {        this.id = id;    }
    public String getName() {        return name;    }
    public void setName(String name) {        this.name = name;    }}
public class ReflectionMapperTest {    private Mapper mapper;    @BeforeEach
    public void setUp() {        this.mapper = new ReflectionMapper();    }
    @Test    public void shouldCreateMap() {
        Animal animal = new Animal("id", "lion");
        final Map<String, Object> map = mapper.toMap(animal);
        Assertions.assertEquals("animal", map.get("entity"));
        Assertions.assertEquals("id", map.get("id"));
        Assertions.assertEquals("lion", map.get("native_name"));    }    @Test
    public void shouldCreateEntity() {
        Map<String, Object> map = new HashMap<>();        map.put("id", "id");
        map.put("native_name", "lion");
        final Animal animal = mapper.toEntity(map, Animal.class);
        Assertions.assertEquals("id", animal.getId());
        Assertions.assertEquals("lion", animal.getName());    }}
such , The implementation of reflection is demonstrated . If you want to use this type of tool in other projects , You can create a small project and do the same as adding other dependencies , And these operations and reads are performed at run time .

It should be noted that , In reflection , There are a number of options and strategies to use it , for example , Create an internal cache of these metadata , To avoid constant use ReflectionData Or avoid compiling classes at execution time from this information .

however , most important of all , The whole process will occur at execution time . To move processing to compilation , We will use Java Annotation Processor API.

Classes that are to become entities in the process need to be extended AbstractProcessor class , use SupportedAnnotationTypes Annotations define which classes will be read at compile time , And the process method of defining code core . This method will perform all analyses . The final step is to register the class as SPI, And the code will be ready to run at compile time .
@SupportedAnnotationTypes("org.soujava.medatadata.api.Entity")
public class EntityProcessor extends AbstractProcessor {//…     @Override
    public boolean process(Set<? extends TypeElement> annotations,
                           RoundEnvironment roundEnv) {
        final List<String> entities = new ArrayList<>();
        for (TypeElement annotation : annotations) {
            roundEnv.getElementsAnnotatedWith(annotation)
                    .stream().map(e -> new ClassAnalyzer(e, processingEnv))
                    .map(ClassAnalyzer::get)
                    .filter(IS_NOT_BLANK).forEach(entities::add);        }
        try {            if (!entities.isEmpty()) {
                createClassMapping(entities);
                createProcessorMap();            }
        } catch (IOException exception) {            error(exception);        }
        return false;    }//… }

The important point is that ,Java Annotation processing requires more configuration steps than reflection . however , In the first step , The next step is often related to reflection API be similar . Can pass pom.xml In file annotationProcessorPaths Tag to complete the dependency on this library type . A big advantage is that these dependencies are only visible within the compilation scope . in other words , You can add dependencies to generate classes , For example, using Mustache, You don't have to worry about these dependencies at run time .

After adding a dependency to a project and executing it , Will be in target/generated-sources Generate class in folder . In this example , The birth of all classes is attributed to Mustache project .

@Generated(value= "Soujava ClassMappings Generator", date = "2021-01-21T13:08:48.618494")
public final class ProcessorClassMappings implements ClassMappings {
    private final List<EntityMetadata> entities;
    public ProcessorClassMappings() {        this.entities = new ArrayList<>();
        this.entities.add(new org.soujava.metadata.example.PersonEntityMetaData());
        this.entities.add(new org.soujava.metadata.example.AnimalEntityMetaData());
        this.entities.add(new org.soujava.metadata.example.CarEntityMetaData());
}
usually , The end-user functionality of this library will not change much , Because the user will continue to annotate in the entity , however , All processing logic has been brought to compile time .@Entity("animal")
public class Animal {    @Id    private String id;    @Column("native_name")
    private String name;    public Animal() {    }
    public Animal(String id, String name) {        this.id = id;
        this.name = name;    }    public String getId() {        return id;    }
    public void setId(String id) {        this.id = id;    }
    public String getName() {        return name;    }
    public void setName(String name) {        this.name = name;    }}
public class ProcessorMapperTest {    private Mapper mapper;    @BeforeEach
    public void setUp() {        this.mapper = new ProcessorMapper();    }
    @Test    public void shouldCreateMap() {
        Animal animal = new Animal("id", "lion");
        final Map<String, Object> map = mapper.toMap(animal);
        Assertions.assertEquals("animal", map.get("entity"));
        Assertions.assertEquals("id", map.get("id"));
        Assertions.assertEquals("lion", map.get("native_name"));    }    @Test
    public void shouldCreateEntity() {
        Map<String, Object> map = new HashMap<>();        map.put("id", "id");
        map.put("native_name", "lion");
        final Animal animal = mapper.toEntity(map, Animal.class);
        Assertions.assertEquals("id", animal.getId());
        Assertions.assertEquals("lion", animal.getName());    }}
above , We discussed the advantages and disadvantages of reflection . use Java The annotation processor introduces an example , And show it Java AOT The advantages of , And convert it to native .

Every choice has its disadvantages . When deleting an application , be-all JIT Optimization will be lost , And there is a saying , With the development of technology ,JVM Will be more efficient than native code . The definition of performance is very complex , And it's not just about application startup time .

Do you need a learning group full of technology atmosphere ? Just scan the code :

Technology
©2019-2020 Toolsou All rights reserved,
Solve in servlet The Chinese output in is a question mark C String function and character function in language MySQL management 35 A small coup optimization Java performance —— Concise article Seven sorting algorithms (java code ) use Ansible Batch deployment SSH Password free login to remote host according to excel generate create Build table SQL sentence Spring Source code series ( sixteen )Spring merge BeanDefinition Principle of Virtual machine installation Linux course What are the common exception classes ?