由“如何更好地配置Module”引发的...

公司有一个服役了N年的基于Java技术实现的业务系统,其中Module的配置比较死板:比如配置某个Module的label,通过更改displayFields和separator来实现,如果displayFields和separator分别设置成“jobCode,nickname,sponsor”和" : ",则label被拼成形如"ABB1-705 : Abbott : Sam"的字符串。问题来了:如果我希望分隔符不是单一的" : "呢?如果我希望显示成"ABB1-705 : Abbott ( sponsored by Sam )"呢?

 

一个最容易想到的办法就是用包含占位符的字符串(跟公式或格式类似)来定义label,比如可以把label设置成"#{jobCode} : #{nickname} ( sponsored by #{sponsor} )",在运行期把具体值代入公式,计算出显示结果,计算的方法如下:

private String assembleLabel(String label,Object bean) {
    label.replaceAll(/#\{.[^\}]+\}/){
        def arr = it[2..-2].split('\\.')
        def r = bean
        for(int j=0;j<arr.length;j++){
            r = r?."${arr[j]}"
        }
        r
    }
}

写出这个方法,自己感觉颇有些得意

:其中的

def r = bean
for(int j=0;j<arr.length;j++){
    r = r?."${arr[j]}"
} 

支持形如#{person.profile.name}的占位符,不光可以处理一层的属性提取,更深层次的object navigation都没问题(我使用Db4o,它的transparent activation特性威力相当惊人)。

 

看起来似乎很完美了,但是我们得记住:There's nothing that lends itself to the “one size fits all” paradigm.

 

设想这样一个scenario: 我有一个名叫Sites and Institutions的Module,它的description告诉软件使用者“标为红色的Site表示其状态为Enrollment,标为灰色表示Declined...”,虽然在Domain SiteStatus中保存的数据不是经常改变的,但一旦改变我们就不得不更新Sites and Institutions的description - 很显然,这样会很“湿” - 严格遵循DRY(Don't Repeat Yourself)原则是good practice

怎样才够DRY?我的做法是,利用Groovy的Closure和BSF。先看Module的定义

class Module {

    String name,icon,label,action
    String description // can be a plain String or a Closure definition script(starts with "GroovyScript:{")

    Class clazz
    Boolean scaffold,prototype
    List children
    Module parent
    String belongsTo
    List sort

    static constraints = {
        name nullable:false
    }

}

这样我就可以这样写了:

Module sites = new Module(
    name: '5it35',description:"""GroovyScript:{o->
      def r = ['<div style="padding:5px;">']
      SiteStatus.findAll(sort:'seq').each{
        r << "<font color=\${it.color}>&#9632; \${it.name}</font><br>"
      }
      r << '</div>'
      r.join('')
    }"""//...
)

同时利用BSF写了个简单的eval来执行script:

  private static final GROOVY_SCRIPT_PATTERN = /^\s*GroovyScript\s*:\s*\{/

  static eval(String s) {
    eval(s,null)
  }

  static eval(String s,Object param) {
    if(s =~ GROOVY_SCRIPT_PATTERN) {
      return getBSFManager().eval("groovy",null,s.replace(GROOVY_SCRIPT_PATTERN,'')).call(param)
    }
    return s
  }

产生的效果是酱紫的:

 

把description写成“GroovyScript: {...}"是为了告诉自定义的解析器:这是个Groovy script,不是普通的String,你需要用BSF来eval. 为什么不把description定义成Object类型,然后给它赋个String或Closure呢?实际上我原来就是这样做的:

def desc = module.description
def result = desc instanceof Closure ? desc() : desc

看起来很美,实践中却出了点小问题:动态更改clousre时无法持久化到Db4o数据库,除非closure是在某个类中写的。我原先在Module类中有直接把description设成一个Closure:

description: {
    def r = ['<div style="padding:5px;">']
    SiteStatus.findAll(sort:'seq').each{
        r << "<font color=${it.color}>&#9632; ${it.name}</font><br>"
    }
    r << '</div>'
    r.join('')
}

它也确实被保存到Db4o的数据库中了(OM中显示了一个名叫com.grs.sctms.Module$_setup_closure1的类):

 

 

但是我在web-based的Grails Console中(服务器端是一GroovyShell在执行我提交的script)执行更改Module description(把它改成另一个Closure)却不能成功。

为了曲线救国,我只能用String类型的description + BSF运行期解析并执行脚本的办法了。实际上这个办法我比较喜欢,因为我可以在不重启应用的情况下,瞬间修改module,同时让修改瞬间生效。我在web-based的Grails Console中执行如下代码:

import com.grs.sctms.*
def oc = ctx.objectContainer
def sites = Module.find(name:'5it35')
println "original value: ${sites.description}"
sites.description  = """
GroovyScript:{o->
   def r = ['<div  style="padding:5px;">']
     SiteStatus.findAll(sort:'seq').each{
       r <<  "<font  color=\${it.color}>&#9632;  \${it.name}</font><br>"
     }
   r  << '</div>'
   r[0..-2].join(':-)')+r[-1]
 }
"""
sites.save()
oc.commit()
println "current value: ${sites.description}"

 浏览器刷新一下,效果立马变成了:

 

 

最后,上一张让我能“操控sctms于万里之外”的web-based Groovy Console的照片(同时感谢这个Grails Plugin的原作者Siegfried Puchbauer and Mingfai Ma,我的Console是在他们提供的源码的基础上修改而得):

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


背景:    8月29日,凌晨4点左右,某服务告警,其中一个节点直接down掉,收到告警的同事让运维重启。    9点左右,内存监控上发现内存异常,堆内存涨速很快,即便GC也没有什么效果,频繁GC。    9点38,服务各种超时,影响整个app使用。处理方式:    当时由于很想要
https://support.smartbear.comeadyapi/docs/soapui/steps/groovy.htmlGettestcaseobjectToobtaintheobjectwhichreferstothecontainingtestcase,usethefollowingcodesnippet:Groovy def case=testRunner.testCase; Byusingthe tes
有几个选项可用于执行自定义JMeter脚本并扩展基线JMeter功能。查看最流行的扩展机制,比较性能并分析哪一个是最好的。  这是乐队之战,JMeter风格。 BeanshellV.JSR223V.JavaRequestSampler 在我们之前的帖子中,  JMeterPerformance和TuningTips  ( 由fantastik
Scala和Java为静态语言,Groovy为动态语言Scala:函数式编程,同时支持面向对象Groovy:jvm上的脚本,较好兼容java语法,Groovy加强了Java集成。 可配置化的优势,可以将一些简单的逻辑公开给外部编辑和使用,增强了互操作性,复杂逻辑来说,可配置化代码的调试则会比较麻烦 Scala和Java
出处:https://www.jianshu.com/p/ce6f8a1f66f4一、一些内部元件的访问testRunner.testCase开头1、向下访问testRunner.testCase.testSteps[testStepName]testRunner.testCase.getTestStepByName("新增一个空间")2、向上访问,用于访问同一项目中的其他testSuites和testCase下
在运行groovy的junit方法时,报了这个错误:java.lang.ExceptionInInitializerError atorg.codehaus.groovy.reflection.ClassInfo.isValidWeakMetaClass(ClassInfo.java:271) atorg.codehaus.groovy.reflection.ClassInfo.getMetaClassForClass(ClassInfo.java:241) atorg.codeha
基本语法1.Grovvy的注释分为//和/**/和java的一样.2.Grovvy语法可以不已分号结尾.3.单引号,里面的内容严格的对应java中的String,不对$符号进行转义.defs1='iamastudent$'printlns1iamastudent$4.双引号“”的内容中如果有$号的话,会先对表达式先求值.de
Tiobe发布了最新一期(3月份)编程语言欢迎度榜单,其榜单根据互联网上有经验的程序员、课程和第三方厂商的数量,并使用搜索引擎(如Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube统计出排名数据。TOP5几乎没有变化,Java和C语言牢牢占据前两名。Python相较去年上升一位进入TOP3,C++下
我有一个Google地图组件,作者可以在其中指定纬度和经度.我正在使用带有正则表达式的常规“输入”类型控件来验证它们是否是数字,但是,当试图解决指定范围的问题时(经度验证该值在[-180,180]内并且纬度[-90,90])但是,通过正则表达式进行验证似乎很麻烦,而且利用inputtype=“numb
我正在为未来的应用程序评估SpringBoot,并希望使用Groovy模板来实现其纯粹的可读性.不幸的是,我在迭代我添加到控制器返回的ModelAndView对象的对象列表时遇到了麻烦.这是我的控制器:@RestController@RequestMapping("/ships")publicclassShipsController{@Autowired
我有一个基于Spring的java应用程序,其中包含一些有用的组件.作为系统的一部分,我有一个groovy脚本,来处理一些报告.我想从groovy脚本中调用spring组件.当我用Java编写时,我需要在@Component中使用@Autowired注释,即@ComponentclassReporter{@AutowiredSearchServicesearchS
在Grailsi18n插件definedthusly中定义了一个messageSourcebean:messageSource(PluginAwareResourceBundleMessageSource){basenames=baseNames.toArray()fallbackToSystemLocale=falsepluginManager=manager....}是否可以覆盖我的resources.groovy中的fa
我正在寻找一种方法来反向工程RDBMS表(MSSQLServer)并生成JPA@EntityGroovy类.我们目前没有选择使用Grails和/或GORM,因此Grailsdb-reverse-engineer插件似乎很接近但不太正确.它生成符合GORM的类而不是JPA实体类.我们目前有一个gradle构建,它利用org.hibernate.tool.ant.Hibe
https://blog.csdn.net/Gdeer/article/details/83062523一、直接运行groovy程序因为groovy插件和android插件不兼容,所以不能在原始项目上使用groovy。 新建module,创一个JavaLibrary,取名lib。  修改lib/build.gradleapplyplugin:'java-library'depe
一、自动生成GET请求脚本1、配置Createascript在ngrinder管理台主页,点击script–>Createascript,并填写脚本名称和请求的url,如下所示:点击Create按钮,nGrinder会自动生成对应的脚本结构,如果没有参数需要设置的话,可以直接运行了。二、详细解析GET请求脚本ngrinder自动生成的脚本
我正在关注使用列表和地图作为构造函数的this博文.为什么以下列表无法强制反对?classTest{staticclassTestObject{privateinta=1;protectedintb=2;publicintc=3;intd=4;Strings="s";}stati
Information:java:Errorsoccurredwhilecompilingmodule'security'Information:javac1.8.0_131wasusedtocompilejavasourcesInformation:2019/6/98:31-Buildcompletedwith1errorand0warningsin3s116msError:java:读取E:\repository\org
ngrinder中的groovy脚本结构类似junit,同时在junit的基础之上封装了自己的注解,用来控制脚本的运行。一、运行逻辑图如下:此处只列出了groovy脚本的逻辑,jython脚本是类似的,在此不再单独介绍。二、各注解的使用比较三、关注点在ngrinder中,通常使用单进程多线程就足够大部分测试了,所以:
我有一个switch语句来处理javaenumfoo,并使用spock编写一些groovy单元测试.我已经添加了一个测试,它验证当前是否处理了每种类型的foo而没有抛出异常.现在我想测试一个无法识别的foo类型会导致抛出异常.要做到这一点,我将不得不嘲笑枚举,并已经看到这里概述的解决方案:MockingJ
我有一个groovy实体ClientInvoiceAttachmentExt,它扩展了java实体ClientInvoiceAttachment.ClientInvoiceAttachment具有@Id注释,但仍然看到“没有为实体指定的标识符”错误这是我的堆栈跟踪[Mar0317:11:54]ERROR|org.springframework.web.context.ContextLoader|Contex