`
deepinmind
  • 浏览: 449430 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
1dc14e59-7bdf-33ab-841a-02d087aed982
Java函数式编程
浏览量:41419
社区版块
存档分类
最新评论

Java中的动态代码生成

阅读更多
通过程序来生成代码是Java平台的固有特性。当Java程序编译的时候,Java编译器生成的是字节码而不是可执行程序。字节码是Java特有的格式,它本身并没有太大的用处。为了能执行字节码,它会在运行时被JVM的just-in-time编译器翻译成本地的机器代码。

Java的导论就先讲到这吧。大多数Java开发人员应该都听说过JIT编译,但它作为这个平台最强大的功能之一,即便你不了解它的细节,也不影响你正常写你的Java程序。

然而随着POJO革命的进行,Java领域流行起了另一种代码生成的形式。许多现代的Java库和框架都在Java程序运行时通过定义自己的类来实现了很多技巧。

乍一听,这个很有点学院派的感觉。不过检查下你的业务应用的栈跟踪信息吧,你肯定会发现许多运行时生成的类。并且除了JIT编译代码以外,运行时生成的类也是你程序运行的一部分,因此它也是你需要关心的。

为什么我们需要运行时代码生成?

运行时的类定义并非意味着帮那些懒得敲代码的人省了点事。运行时代码生成解决的是Java类型系统的一个很大的短板。在深入细节之前,我们先来简单回顾下它的类型系统有哪些特性。

Java是强类型并且是静态类型的。这么说的话有的程序员会感觉迷糊。我们先跳过这个到处都在谈论的“动态及静态类型的比较”,先假设我们都喜欢强类型和静态类型。这个类型的一大好处就是它的表现力。对于每个变量而言,我们可以立马说出它有什么方法可以被调用。

只要我们不去强制进行类型转化,静态类型在编译期就可以暴露许多程序的错误,甚至都不用启动你的应用。

这个安全性对我们来说非常方便。然而,对于那些写Java框架和库的人来说,有时候就不那么方便了。静态类型意味着应用只能调用它所明确知道的方法。

但这样就和框架的初衷相悖了,它的目的是能不依赖特定的用户领域来提供功能。对于自己开发的公司内部的框架而言,直接依赖于某个特定的领域模型这么做可能还可以接受。

然而,想像下像Spring这样的框架,它得依赖于所有的用户领域类型。这在逻辑上几乎是不太可能的,框架的依赖关系图和框架实际的目的是相左的。第三方代码要去依赖框架的功能。而不是相反的方式。


Java反射来救场了

当然了,避免类似的编译期的问题正是Java反射API在教科书上的经典案例。事实上,Java的反射比它的名声其实要好得多。

Java反射的确会造成一定的运行时开销。但是,这样的开销通常都是方法查找时产生的。尽管查找的方法可以内部缓存起来,但Method类的协议约定了方法实例必须是可访问的,也就是说得是可修改的。这需要在查找方法时返回这些缓存的Method对象的浅拷贝,因此我们希望避免重复地去创建这些拷贝。

然而,一旦你获取到一个方法的实例了,如果它运行的次数足够多,Java运行时会去优化这些反射的调用。这个概念又被称为膨胀,这也是代码生成所顺带实现的一个功能。最后,其实反射调用通常并不会导致性能瓶颈,尽管流传着许多这样的谣言,但那都是老版本的Java上面的事了。

框架使用反射来和用户的代码进行交互确是一种解决方案。但是当用户程序需要织入到框架中的时候,反射就变得没那么有吸引力了。我们来考虑一个简单的案例,让这个问题更清晰一点。假设我们要实现一个非常基础的,注解驱动的安全框架。这个框架是由一个注解来管理的。



@Retention(RetentionPolicy.RUNTIME)
@interface Secured { 
  String requiredUser();
}




使用这个迷你框架的时候,用户可以用@Secured来注解方法以便使得这些方法只能在特定用户登录的条件下才能被调用。不过我们如何实现这个规则?简单的校验用户的方法就是读取方法的注解,将它和当前登录用户的状态进行比较。使用反射可以很容易实现仅当正确的用户登录后才调用这个方法。但我们如何才能在框架外让用户代码能访问到这个逻辑?好吧,我们可以在框架的一个对象内封装这次调用,通过传递一个被封装方法参数的数组来调用这个方法。



interface SecuredMethod {
  Object invoke(Object… args);
}




POJO启示

这样做很简单,我们也已经搞定了。真是这样的吗?这个方法确实是可行的,但我们实现的这个API估计只有它老妈才会喜欢它。

首先来说,当使用这个库的实现时,用户需要显式地添加一个安全检查到方法调用上。因此,安全库会侵入到用户的代码里,如果不小心直接调用了这个方法,就会破坏掉这个注解过的方法的安全。这种东西你可不太愿意把它部署到生产环境去。更糟糕的是,选择这个实现我们会破坏了Java引以为傲的类型安全。由于这个方法的签名比较通用,我们现在可以使用任何参数来调用这个SecuredMethod接口,Java编译器也不会去警告我们。同时,后面我们还得一头扎到栈信息中去分析这里产生的运行时异常。

那么,还有什么方法?好吧,既然你读的是一篇关于代码生成的文章,你会猜到Java类的动态生成应该是一个办法吧。JVM不允许通过任何打补丁的方式来增强一个方法的实现,但你可以利用语言的特性来在子类中去重写方法。多亏了Java的动态方法分发,这使得你可以通过在运行时生成一个子类,把框架的任意功能给注入到用户代码中。并且方便的是,通过super你可以很简单就能调用到用户的方法。

有了这个方法,我们可以按需生成任意类型的子类来实现我们所说的这个安全库了。我们可以将安全校验的代码注入到用户代码中,只有当我们确保的确是合法的时候才会实际去调用那个方法。这么做的话,我们发布的安全库就只需要一个简单的接口就好了:




interface SecurityLibrary {
  <T> Class<? extends T> secure(Class<T> instance);
}




通过代码生成,我们可以很容易将某个SecuredUserType继承UserType来成为它的子类,我们会去重写这个方法并且实现安全校验的逻辑。如果安全校验通过了,这个方法调用会委托到父类的方法中,那里会包含实际的处理逻辑。

最终我们实现了一个POJO框架的基础版本。没有能比这个更透明的安全库了。代码生成的最大的好处在于它可以完整地保全用户的类型。想像下这个API能让你的生活变得多轻松。

由于它不依赖于框架的类型,这使得你可以不用mock框架代码也可以写出单元测试。如果你的需求变了,把这个安全库替换掉也就和替换掉方法上的注解一样非常简单。如果你观察下周围你会发现其实许多应用框架走的都是这条路子。


未完待续。



原创文章转载请注明出处:http://it.deepinmind.com



英文原文链接
0
0
分享到:
评论

相关推荐

    动态代码生成器动态代码生成器动态代码生成器

    Java的`java.lang.reflect.Proxy`和.NET的`System.Linq.Expressions`可用于创建动态代理,它们通常与动态代码生成结合使用。 8. **安全问题**:动态代码生成也带来了一些安全风险,因为它可能导致代码注入攻击。...

    java项目代码生成工具

    总的来说,这个Java代码生成工具结合了FreeMarker的强大模板引擎,为Java开发带来了便利。它不仅减少了繁琐的手动编码工作,还提高了代码质量,使得开发者可以更加专注于设计和优化业务流程。如果你经常处理大量的...

    java代码自动生成工具

    举例来说,Apache MyBatis的MyBatis Generator就是一个广泛应用的Java代码生成工具,它可以自动生成MyBatis的Mapper接口、XML配置文件和实体类。另外,还有一些IDEA、Eclipse等集成开发环境内的插件,如JPA Code ...

    java代码自动生成器 ,通过页面生成

    其次,"一对一"、"一对多"和"多对多"的代码生成是处理数据库中复杂关系的体现。一对一关系通常存在于主从表或者详情与主体这样的场景,代码生成器会为两个表创建关联的实体类,并处理好它们之间的关系映射。一对多...

    根据数据库sql生成java代码代码生成器

    1、本地执行代码生成工具,可生成controller、service、repository、entity、mapper、mapper.xml的增删改查代码。生成的文件复制到项目路径中,修修改改即可实现大部分CRUD功能。 2、工具不需要导入到项目中,本地...

    java插件配置代码生成器

    总的来说,Java插件配置代码生成器是Java插件开发中的一个重要辅助工具,它通过自动化的方式解决了配置代码的编写问题,让开发者更专注于业务创新和功能实现,提高软件开发的效率和质量。对于大型项目或复杂插件系统...

    java代码自动生成器

    Java代码自动生成器是一种高效的开发工具,主要用于简化Java编程过程中的重复性工作,例如创建类、接口、方法等。这种工具通常包含模板引擎,允许开发者根据预设的模板或者自定义模板快速生成符合规范的代码,提高...

    简单的java代码生成器

    【标题】"简单的Java代码生成器"涉及到的是Java编程领域中的自动化工具,它主要用于简化开发者编写重复性代码的过程。在软件开发中,特别是在Java企业级应用开发中,大量的代码是相似或者重复的,如数据库访问层...

    java代码生成工具

    在Java代码生成工具中,MyBatis的相关Mapper XML文件和Mapper接口会被自动生成,便于与数据库进行交互。 4. **Bootstrap**: Bootstrap是Twitter开源的一个前端框架,提供了丰富的CSS和JavaScript组件,用于构建响应...

    java代码生成数字证书

    Java代码生成数字证书涉及到几个关键概念和技术,包括Java的密钥和证书管理、RSA加密算法以及非交互式证书创建。在此,我们将深入探讨这些主题,以便理解如何在Java环境中生成和使用数字证书。 1. **数字证书**:...

    java-swing版代码生成器

    Java Swing版代码生成器是一个基于Java Swing图形用户界面(GUI)的工具,它旨在帮助开发者自动化创建和管理Java代码。Swing是Java平台上的一个组件库,用于构建桌面应用程序,提供了丰富的用户界面元素,如按钮、...

    java代码生成器(mysql版)

    Java代码生成器(MySQL版)是一种实用工具,主要用于自动化Java编程过程中常见的数据库操作代码的编写。这个工具专门针对MySQL数据库,能够根据数据库中的表结构自动生成对应的Java实体类、DAO接口、Service接口及其...

    四种动态生成Java代码的方法

    四种动态生成Java代码的方法四种动态生成Java代码的方法

    Java一键生成代码到controller层【源码】

    3、可大大提高开发效率,数据表创建完以后,自动生成entity,mapper.xml,dao,service,controller,vo,dto相关代码。 4、本项目集成了spring,aop,mybatis plus,swagger2,异常处理,分页,freemarker等多种技术。 5、操作...

    java 可视化代码生成工具

    Java代码生成工具主要服务于Java开发者,可以自动生成常见的Java类、接口、方法等,减少手动编写的基础工作,提高开发效率。例如,它可以自动生成DAO层、Service层、Controller层的代码,对于基于MVC架构的项目特别...

    基于freemarker的JAVA代码生成工具

    "基于Freemarker的JAVA代码生成工具"就是这样一种实用的工具,它能够根据数据库中的表结构自动生成相应的Java源代码。 **Freemarker简介** Freemarker是一个强大的模板引擎,它支持动态语言,常用于生成HTML页面...

    protobuf java代码生成

    总的来说,protobuf Java代码生成是一个便捷的过程,能够帮助开发者快速地实现数据序列化和网络通信。通过编写清晰的.proto文件,可以确保在多个语言之间保持数据格式的一致性,提高代码的可读性和维护性。在Java...

    Java动态生成代码并编译载入.pdf

    2. Java源代码生成:这个步骤涉及到根据业务需求动态地替换模板文件中的占位符,并生成新的Java源代码。这些代码可能涉及复杂的逻辑,包括但不限于控制流程、异常处理、数据处理等。 3. 编译依赖:在编译过程中,...

    基于Java实现,动态生成二维码海报和汉字名字工具_java_代码_下载

    在本项目中,我们关注的是一个基于Java实现的图片生成工具,主要功能是动态生成二维码海报和汉字名字头像。这个工具对于需要快速创建个性化图片的开发者或设计师来说非常实用,尤其是在进行数字营销或者个人品牌推广...

    java代码生成器

    Java代码生成器通常基于模板引擎来实现,例如压缩包中的"freemarker.jar",这是一个流行的Java模板引擎——FreeMarker。FreeMarker是一个与任何Java应用完全分离的模板引擎,它可以动态生成HTML、XML或其他任何形式...

Global site tag (gtag.js) - Google Analytics