随着Java更新的新特性,例如体系结构决策及其要求。当前,云计算通常要求应用程序除了初始内存量少之外还要拥有更好的启动性。因此,有必要重新设计框架的制作方式,以消除反射的瓶颈。
在框架中,反射在发挥着重要作用,无论是经典的ORM还是JAX-RS之类的REST API 。通过大量的减少各种操,从而使Javaer的工作变得更轻松。
对于终端用户(这里指的是使用这些框架的用户)整个过程只需在类中添加一些符号,所有操作即可正常运行。它们的类元数据将被读取并用于促进某些进程。当前,执行这种类型的最流行的方法是通过内省,从而使Java的动态语言概念轻而易举地生产出来。
由于创建了大量资源,并提供了此类工作的示例和文档,因此在框架内使用反射API简化了此类工作。但是,由于某些原因,我们在这里讨论两个问题:启动应用程序延迟和内存消耗。
启动应用程序延迟:所有处理和数据结构将在执行时执行。想象一下一个依赖项注入引擎,它需要逐级扫描,检查范围,依赖项等等。因此,需要分析的类别越多,所需的处理就越多,并且大大的增加响应时间。
内存消耗:每个类都需要遍历以在Class中搜索元数据,有一个ReflectionData 缓存加载了该类的所有信息,即搜索诸如getSimpleName()之类的简单信息,所有元数据信息都将通过SoftReference加载和引用,这需要花费一些时间才能从内存中取出。
总之:反射方法在初始内存消耗和启动应用程序延迟都存在问题。这是因为在应用程序启动后就立即执行数据,分析和解析器处理。随着类数量的增加,内存和运行时消耗趋于增加。
解决这些问题的方法是,使框架在编译时而不是在运行时执行这些操作:
-
当应用程序启动时,元数据和系统将准备就绪。
-
无需调用反射类,包括ReflectionData,从而减少了启动时的内存消耗。
-
无需担心Type Erasure的影响。
避免反射的另一点是,我们可以更轻松地使用AoT,并通过GraalVM创建本机代码,这是一个令人兴奋的可能性,尤其是对于无服务器概念。该程序运行一次,然后将整个资源返回给操作系统。
演示代码
在解释了读数类型的概念后,下一步将是创建一个简单的工具,该工具将Java类从某些表示将要映射的实体的表示法转换为Map。将被转换的属性,以及将是唯一标识符的字段。让我们按照下面的代码所示执行所有操作:
@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 "";
}
为了简化与反射或其他选项的比较,将创建一个接口,该接口负责与Map的相互转换。
import java.util.Map;
public interface Mapper {
<T> T toEntity(Map<String, Object> map, Class<T> type);
<T> Map<String, Object> toMap(T entity);
}
为了比较这两种解决方案,第一个实现将是通过反射实现的。一点是,有几种处理反射的策略,例如,结合使用带有Introspector的“ java.beans”包;但是,在此示例中,我们将以最简单的方式进行操作以展示其工作原理。
public class ReflectionMapper implements Mapper {
@Override
public <T> T toEntity(Map<String, Object> map, Class<T> type) {
Objects.requireNonNull(map, "Map is required");