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,
Hikvision - Embedded software written test questions C Language application 0 The length of array in memory and structure is 0 In depth analysis data structure --- The preorder of binary tree , Middle order , Subsequent traversal How to do it ipad Transfer of medium and super large files to computer elementui Shuttle box el-transfer Display list content text too long 2019 The 10th Blue Bridge Cup C/C++ A Summary after the National Games ( Beijing Tourism summary )unity Shooting games , Implementation of first person camera python of numpy Module detailed explanation and application case Study notes 【STM32】 Digital steering gear Horizontal and vertical linkage pan tilt Vue Used in Element Open for the first time el-dialog Solution for not getting element