怎样在程序运行时更改分辨率_在运行时更改应用程序行为

在这个两篇系列文章的第1部分中 ,您了解了如何使用Groovy bean使Spring应用程序更加灵活。 Spring的Groovy支持使您可以使用已编译或脚本化的Groovy语言Bean,并以几种不同的方式(包括lang XML模式和Grails Bean Builder)对其进行配置。 将Groovy脚本集成到应用程序中时,可以在Bean创建过程中包括其他逻辑(例如,确定创建Bean时使用哪种实现策略)。 您还可以使用单独的脚本化Groovy Bean提供额外的部署和打包灵活性。

Spring动态语言支持的最有趣和最强大的功能可能是能够在您的应用程序运行时监视和检测对动态语言脚本的更改,并自动在Spring应用程序上下文中重新加载更改的bean。 在运行的应用程序中自动刷新Spring Bean的潜在用例非常广泛。 一些示例包括:

  • PDF生成(帐单,发票,销售报告,投资报告,收据,时间表等)
  • 电子邮件范本
  • 报告生成
  • 外部化的业务逻辑,领域特定语言(DSL)和规则引擎
  • 系统管理任务
  • 更改日志记录级别和运行时调试

我相信您会想到更多潜在用途。 本文向您展示如何向Spring应用程序中添加bean刷新,并探讨其工作原理。 可以下载本文所有示例的完整源代码( 请参阅下载 )。

可刷新Groovy豆

第1部分中 ,您定义了PdfGenerator接口,并在应用程序CLASSPATH中的GroovyPdfGenerator.groovy脚本文件中将其实现为Groovy类( GroovyPdfGenerator )。 您通过指定Groovy脚本的位置来配置基于Groovy的pdfGenerator bean。 清单1显示了使用lang XML模式的接口,实现和配置:

清单1. PdfGenerator接口,实现和配置
// PdfGenerator.java
public interface PdfGenerator {
    byte[] pdfFor(Invoice invoice);
}

// GroovyPdfGenerator.groovy
class GroovyPdfGenerator implements PdfGenerator {

    String companyName

    public byte[] pdfFor(Invoice invoice) {
        ...
    }

}

// applicationContext.xml
<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy">
    <lang:property name="companyName" value="Groovy Bookstore"/>
</lang:groovy>

到目前为止,一切都很好。 您有一个在Groovy中实现的名为pdfGenerator的bean,它位于应用程序CLASSPATH 。 创建Spring应用程序上下文时,Spring读取脚本,将其编译为Java类,并在应用程序上下文中实例化GroovyPdfGenerator 。 依赖pdfGenerator任何其他类都可以简单地将其声明为依赖项,然后Spring照常将它们连接在一起。

这是真正有趣的地方。 假设您经常更改PDF生成的代码,并且希望在应用程序运行时进行更改并使它们立即生效,则希望这样做。 Spring使添加此功能变得微不足道。 您需要做的就是将refresh-check-delay属性添加到定义您的bean的<lang:groovy>元素中。 该属性定义了毫秒数,在此毫秒数后,Spring将检查对基础Groovy脚本的更改。 如果检测到脚本的变化(例如,在.groovy作为脚本的时间戳自上次检查改变),那么Spring读取脚本,编译它,并替换旧pdfGenerator豆与新的。 它做到了这一点,而没有任何使用pdfGenerator知道有关更改的任何信息。

清单2显示了配置了10秒钟(10,000毫秒)的刷新检查延迟的pdfGenerator bean。 添加refresh-check-delay ,Spring会在基础GroovyPdfGenerator.groovy脚本文件更改时将bean配置为自动刷新。

清单2.向脚本化的bean定义添加refresh-check-delay
<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
             refresh-check-delay="10000">
    <lang:property name="companyName" value="Refreshable Groovy Bookstore"/>
</lang:groovy>

现在,如果在应用程序运行时对GroovyPdfGenerator.groovy脚本进行了更改,Spring会检测到该更改并在运行时重新加载pdfGenerator bean,而无需重新启动。 请注意,仅在延迟时间过去之后,才对刷新检查进行检查, 并且在可刷新bean上调用了一个方法。 例如,假设pdfGenerator bean的刷新检查延迟为10秒,但是50秒钟内没有在bean上调用任何方法。 在这种情况下,Spring会在50秒后而不是每10秒检查一次是否需要刷新。 换句话说,Spring不会主动轮询脚本以获取更改; 相反,它确定自上次调用方法以来经过的时间,然后计算该时间是否超过刷新检查延迟。 仅当经过的时间超过刷新检查延迟时,Spring才会检查脚本是否已更改,从而确定是否需要刷新。 另一方面,假设pdfGenerator bean负担很重,并且其方法每秒被调用多次。 使用10秒的refresh-check-delay ,无论使用多少次,该bean的重装速度都不能超过每10秒一次。 因此,您不必担心Spring是否正在消耗系统资源来主动轮询Groovy脚本。 不是。

如果您的Spring应用程序中有多个脚本化的 Groovy bean,并且想要为所有它们设​​置刷新检查延迟的默认值,则可以使用<lang:defaults>元素轻松地做到这一点,如清单1所示。 3:

清单3.设置默认的刷新检查延迟
<lang:defaults refresh-check-delay="20000"/>

使用清单3所示的<lang:defaults> , 所有脚本化动态语言Bean(那些用Groovy,JRuby,BeanShell等编写的动态语言Bean)都具有20秒的刷新检查延迟。 您可以简单地通过为每个Bean添加refresh-check-delay属性来覆盖单个Bean的默认值,该属性应该具有与默认值不同的值。 您甚至可以通过将refresh-check-delay设置为负值来关闭单个脚本bean的自动刷新行为,如清单4所示:

清单4.覆盖默认的refresh-check delay
<lang:defaults refresh-check-delay="20000"/>

<lang:groovy id="pdfGenerator"
             script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
             refresh-check-delay="60000">
    <lang:property name="companyName" value="Refreshable Groovy Bookstore"/>
</lang:groovy>

<lang:groovy id="invoiceEmailer"
             script-source="classpath:groovierspring/GroovyInvoiceEmailer.groovy"
             refresh-check-delay="-1"/>

在清单4中,您可以看到默认的刷新检查延迟为20秒。 但是,我已将pdfGenerator bean的刷新检查延迟配置为60秒,并完全在invoiceEmailer bean上关闭了刷新检查。

使用Grails Bean Builder配置可刷新的Groovy Bean

第1部分中 ,您了解了如何使用Grails Bean Builder(请参阅参考资料 )以编程方式定义Spring Bean。 如果您使用的是Bean Builder,则可以相对轻松地向您的Bean添加自动刷新-尽管在这种情况下,由于Bean Builder与<lang:groovy>语法糖不等效,因此Spring的内部结构更加暴露。 清单5显示了如何向所有脚本化的bean添加默认刷新检查,以及如何为单个bean设置刷新延迟:

清单5.使用Grails Bean Builder配置可刷新的Groovy Bean
def builder = new grails.spring.BeanBuilder()
builder.beans {
    scriptFactoryPostProcessor(ScriptFactoryPostProcessor) {
        defaultRefreshCheckDelay = 20000
    }
    pdfGenerator(GroovyScriptFactory,
                 'classpath:groovierspring/GroovyPdfGenerator.groovy') { bean ->
        companyName = 'Refreshable Bean Builder Bookstore'
        bean.beanDefinition.setAttribute(
            ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, 60000)
    }
}

清单5中的Bean Builder配置在逻辑上等同于清单4中的pdfGenerator bean的配置。 您可以使用ScriptFactoryPostProcessor bean的defaultRefreshCheckDelay属性在所有脚本bean上设置默认的刷新检查延迟。 要在使用Bean Builder时为单个bean设置刷新检查延迟,必须在基础Spring bean定义上设置一个属性。 当使用基于XML的<lang:groovy>配置时,Spring会为您处理基础细节,而使用Bean Builder时,您需要动手自己动手做。 注意,您还需要在pdfGenerator bean的闭包中声明一个bean参数,以便在bean定义上设置属性。

定制Groovy bean

现在,您已经了解了如何使用可刷新bean功能使Groovy bean在运行时自动更新,以及如何在运行时使应用程序更加动态。 Spring的Groovy支持提供了使Groovy bean更加灵活的另一种方式:定制。 定制是一种将定制逻辑注入Groovy bean创建过程的方法。 GroovyObjectCustomizer接口(如清单6所示)允许您在新创建的GroovyObject上执行自定义逻辑:

清单6. GroovyObjectCustomizer接口
public interface GroovyObjectCustomizer {
    void customize(GroovyObject goo);
}

GroovyObjectCustomizer是Spring在创建Groovy bean之后调用的回调。 您可以将其他逻辑应用于Groovy bean或执行元编程魔术,例如替换对象的元类(请参阅参考资料 )。 清单7显示了一个实现,它记录了在Groovy bean上执行一个方法所花费的时间:

清单7.性能日志记录GroovyObjectCustomizer
public class PerformanceLoggingCustomizer implements GroovyObjectCustomizer {

    public void customize(GroovyObject goo) {
        DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) {
            @Override
            public Object invokeMethod(Object object, String method, Object[] args) {
                long start = System.currentTimeMillis();
                Object result = super.invokeMethod(object, method, args);
                long elapsed = System.currentTimeMillis() - start;
                System.out.printf("%s took %d millis on %s\n", method, elapsed, object);
                return result;
            }
        };
        metaClass.initialize();
        goo.setMetaClass(metaClass);
    }
}

清单7中PerformanceLoggingCustomizer替换了GroovyObject的元类,并重写了invokeMethod以便添加性能计时逻辑。 接下来,您需要配置您的定制程序,以便将其应用于一个或多个Groovy Bean。 清单8显示了如何使用<lang:groovy>中的customizer-ref属性向现有的Groovy bean中添加定制器:

清单8.配置一个Groovy对象定制器
<bean id="performanceLoggingCustomizer"
      class="groovierspring.PerformanceLoggingCustomizer"/>

<lang:groovy id="pdfGenerator"
    refresh-check-delay="60000"
    script-source="classpath:groovierspring/GroovyPdfGenerator.groovy"
    customizer-ref="performanceLoggingCustomizer">
    <lang:property name="companyName" value="Customized Groovy Bookstore"/>
</lang:groovy>

现在,当调用GroovyPdfGenerator任何方法时,您将在标准输出上看到类似以下的输出。 (如果您认为使用日志记录框架会更好,那可能是对的!)

pdfFor took 18 millis on groovierspring.GroovyPdfGenerator@f491a6

向Groovy bean添加定制非常简单。 困难的部分是实现实际的定制逻辑-也就是说,在创建Groovy bean时要执行的操作。 您已经看到使用<lang:groovy>及其customizer-ref属性的配置。 如果您更喜欢使用Grails Bean Builder来构建Spring Bean,则很容易做到。 清单9显示了如何添加peformanceLoggingCustomizer bean:

清单9.使用Grails Bean Builder添加一个Groovy对象定制器
builder.beans {
    performanceLoggingCustomizer(PerformanceLoggingCustomizer)
    scriptFactoryPostProcessor(ScriptFactoryPostProcessor) {
        defaultRefreshCheckDelay = 20000
    }
    pdfGenerator(GroovyScriptFactory,
                 'classpath:groovierspring/GroovyPdfGenerator.groovy',
                 performanceLoggingCustomizer) { bean ->
        companyName = 'Refreshable Bean Builder Bookstore'
        bean.beanDefinition.setAttribute(
            ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, 60000)
    }
}

Groovier数据库

走出JAR的,Spring提供了通过弹簧加载内嵌脚本和脚本的支持Resource抽象化(参见相关主题 )。 在第1部分中,您看到了内联脚本和基于Resource的脚本,特别是CLASSPATH资源。 您刚刚使用可刷新bean添加了更多动态行为。 Spring的加载,编译和刷新动态语言Bean的能力取决于ScriptSource接口,如清单10所示(省略Javadocs):

清单10. ScriptSource接口
public interface ScriptSource {

    String getScriptAsString() throws IOException;

    boolean isModified();

    String suggestedClassName();
}

ScriptSource定义了三种方法:一种用于获取脚本源代码,一种用于确定脚本是否已被修改,以及一种返回建议的脚本类名称的方法。 Spring提供了此接口的两种实现: StaticScriptSourceResourceScriptSource 。 在Spring配置文件中定义脚本时,可以使用StaticScriptSourceResourceScriptSource用于从任何Resource (例如,从文件中的脚本, CLASSPATH或URL中)加载脚本。

静态脚本和基于Resource的脚本提供了广泛的位置来定义脚本,但是出于多种原因,您可能希望将数据库用作脚本位置。 例如,许多组织不允许文件系统访问生产机器,或者它们可能需要部署为WAR或EAR文件。 另外,数据库是大多数组织已经使用并熟悉的事务性资源。 数据库还提供了一种相对简单的方法来集中化数据访问和安全性,而无需了解有关文件系统,服务器等的详细信息。 最后,将脚本存储在数据库中意味着您可以通过允许用户编辑脚本来从应用程序更新脚本。 (当然,当您将活动代码存储在数据库中时,考虑潜在的安全隐患并适当保护应用程序很重要。)

假设您想将Groovy脚本存储在关系数据库中。 从Spring 2.5开始,您可以创建新类型的脚本,但是必须创建自己的ScriptSource并扩展某些Spring类。 具体来说,您需要定义自己的ScriptSource实现并修改Spring的ScriptFactoryPostProcessor以便它知道如何使用新类型的ScriptSource

清单11个实现一个DatabaseScriptSource使用了Spring JDBC来加载脚本从关系数据库:

清单11. DatabaseScriptSource实现
public class DatabaseScriptSource implements ScriptSource {

    private final String scriptName;
    private final JdbcTemplate jdbcTemplate;
    private Timestamp lastKnownUpdate;

    private final Object lastModifiedMonitor = new Object();

    public DatabaseScriptSource(String scriptName, DataSource dataSource) {
        this.scriptName = scriptName;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public String getScriptAsString() throws IOException {
        synchronized (this.lastModifiedMonitor) {
            this.lastKnownUpdate = retrieveLastModifiedTime();
        }
        return (String) jdbcTemplate.queryForObject(
                "select script_source from groovy_scripts where script_name = ?",
                new Object[]{ this.scriptName }, String.class);
    }

    public boolean isModified() {
        synchronized (this.lastModifiedMonitor) {
            Timestamp lastUpdated = retrieveLastModifiedTime();
            return lastUpdated.after(this.lastKnownUpdate);
        }
    }

    public String suggestedClassName() {
        return StringUtils.stripFilenameExtension(this.scriptName);
    }

    private Timestamp retrieveLastModifiedTime() {
        return (Timestamp) this.jdbcTemplate.queryForObject(
                "select last_updated from groovy_scripts where script_name = ?",
                new Object[]{ this.scriptName }, Timestamp.class);
    }
}

清单11中的DatabaseScriptSource相当简单,尽管您可以根据它期望的数据库表结构使它更通用。 它假定一个名为groovy_scripts的表,该表的groovy_scripts script_namescript_sourcelast_updated 。 它支持从groovy_scripts表中加载脚本并检查修改。

现在,您需要教Spring识别DatabaseScriptSource 。 为此,必须扩展ScriptFactoryPostProcessor并重写convertToScriptSource方法,该方法负责将脚本源定位符(例如, classpath:groovierspring/GroovyPdfGenerator.groovy )转换为ScriptSource 。 清单12显示了ScriptFactoryPostProcessor的默认实现:

清单12. ScriptFactoryPostProcessorconvertToScriptSource方法
protected ScriptSource convertToScriptSource(
        String beanName, String scriptSourceLocator, ResourceLoader resourceLoader) {

    if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
        return new StaticScriptSource(
                scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
    }
    else {
        return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator));
    }
}

如您所见,默认实现仅处理内联脚本和基于资源的脚本。 您可以创建一个新的子类ScriptFactoryPostProcessor并覆盖convertToScriptSource从你的数据库还加载脚本,使用DatabaseScriptSource ,如清单13所示:

清单13. CustomScriptFactoryPostProcessor实现
public class CustomScriptFactoryPostProcessor extends ScriptFactoryPostProcessor {

    public static final String DATABASE_SCRIPT_PREFIX = "database:";

    private DataSource dataSource;

    @Required
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    protected ScriptSource convertToScriptSource(String beanName,
                                                 String scriptSourceLocator,
                                                 ResourceLoader resourceLoader) {
        if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
            return new StaticScriptSource(
                scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
        }
        else if (scriptSourceLocator.startsWith(DATABASE_SCRIPT_PREFIX)) {
            return new DatabaseScriptSource(
                scriptSourceLocator.substring(DATABASE_SCRIPT_PREFIX.length()),
                dataSource);
        }
        else {
            return new ResourceScriptSource(
                resourceLoader.getResource(scriptSourceLocator));
        }
    }

}

此清单中的CustomScriptFactoryPostProcessorScriptFactoryPostProcessor相似,不同之处在于,如果脚本源定位符以database:例如, database:groovierspring/GroovyPdfGenerator.groovy )开头,则它增加了使用基于数据库的脚本的功能。 理想情况下,此机制将更加灵活(请参阅“ 转向可插入脚本源定位器”边栏)。 但是目前,您已经具有在数据库中存储Groovy脚本的功能。

剩下的唯一任务是将pdfGenerator bean配置为从数据库中读取。 首先,您需要使用清单13所示的CustomScriptFactoryPostProcessor定义一个scriptFactoryPostProcessor bean。 然后,使用数据库脚本源定位符定义pdfGenerator bean。 您可以使用普通的<bean/>语法或更清晰的<lang:groovy>语法定义pdfGenerator bean。 当您使用<lang:groovy> ,Spring会检查ScriptFactoryPostProcessor bean是否在名为scriptFactoryPostProcessor的应用程序上下文中,如果尚未存在,则会自动创建一个。 如果已经定义了scriptFactoryPostProcessor ,Spring将使用该bean,这就是您可以替代自己的自定义实现的方法。 清单14显示了新的配置:

清单14.数据库pdfGenerator bean配置
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/GroovierSpringDataSource"/>

<bean id="scriptFactoryPostProcessor"
      class="groovierspring.CustomScriptFactoryPostProcessor">
    <property name="dataSource" ref="dataSource"/>
</bean>

<lang:groovy id="pdfGenerator"
             refresh-check-delay="60000"
             script-source="database:groovierspring/GroovyPdfGenerator.groovy">
    <lang:property name="companyName" value="Database Groovy Bookstore"/>
</lang:groovy>

清单14中的代码并不比您到目前为止所看到的要复杂。 scriptFactoryPostProcessor bean需要注入一个DataSource ,因此您还定义了dataSource bean。 否则,唯一的区别是从基于CLASSPATH的脚本更改为驻留在数据库中的脚本。 如果您更倾向于使用Grails Bean Builder,则可以轻松地使用它来配置数据源和自定义ScriptFactoryPostProcessor bean。

此时,您可以从数据库中加载Groovy脚本,并在对数据库中的脚本进行更改后也可以刷新它们,从而使Spring已经灵活的Groovy支持更加灵活和动态。 您还了解了如何添加自己的ScriptSource实现以允许从您选择的任何位置加载脚本。

当Groovy脚本变坏时

每个人都可能同意您应该彻底测试您的应用程序,尽管人们可能在确切的使用方法上存在分歧。 例如,是100%的代码覆盖率是必要的,还是可能的,或者仅仅是浪费时间? 无论您的个人观点如何,当您突然有能力将更改部署到正在运行的生产系统中并使这些更改立即生效时,测试就变得尤为重要,就像使用Spring的动态语言支持一样。

如果您决定使用可刷新bean功能,则需要一个可靠的策略来确保新代码正常运行并按预期运行。 如何有效地执行此操作取决于您的情况:

  • 系统有多重要?
  • 如果您弄坏了东西,会有什么影响?
  • 您多快可以解决出现的问题?

您的具体情况可能涉及其他注意事项,但最重要的是,bean刷新功能既强大又有潜在危险。 您需要负责任地使用它。 您可能遇到的两种主要问题是脚本编译错误和运行时错误。

脚本编译错误

假设您在运行时更改了脚本,使其无法编译。 当Spring检测到更改并尝试重新加载Bean时,将引发ScriptCompilationException并包装原始异常,例如Groovy MultipleCompilationErrorsException 。 发生这种情况时,Spring会取消尝试重新加载Bean的尝试,并且原始Bean会不断变化,好像什么都没发生一样。 您的应用程序需要适当地响应ScriptCompilationException 。 通常,您应该显示某种错误消息,并向开发人员或操作人员发送通知(例如电子邮件或即时消息)。 当然,将更改部署到脚本的任何人都应监视应用程序,以确保脚本成功编译并且新bean替换了旧bean。

但是,所有内容都不会丢失,因为未编译的脚本对已部署的bean没有影响。 因此,您可以解决导致编译异常的问题,然后重试。 假设bean编译成功,Spring对应用程序代码透明地用新的bean替换现有的bean。 现在您就可以进行更改,而无需重新部署或重新启动应用程序。

运行时脚本错误

运行时脚本错误与已编译的代码引发的运行时错误具有相同的问题:它们会在您的应用程序中导致失败情况,这种情况很可能会传播给用户,并导致他们尝试执行的任何操作都会失败。 例如,假设您对GroovyPdfGenerator进行了更改,使其可以编译,但是在尝试生成PDF时会抛出运行时异常。 在这种情况下,使用pdfGenerator的代码必须处理该异常或传播该异常,并且很可能用户会收到一条错误消息,指出无法生成PDF(并且将尽快对其进行修复!)。

但是,与脚本编译错误一样,当运行时脚本错误发生时,所有内容也不会丢失。 实际上,由于可以在运行时更改脚本,因此与编译后的代码相比,它们可以更轻松地修复。 您可以解决脚本中存在的任何问题,并且重新加载脚本后,问题就不再存在。 因此,从特定角度看,在运行时更改代码的能力不仅为您提供了进行更改的更大灵活性,而且还为发生错误时提供了更大的灵活性。 不过,这并不意味着您应该使Spring应用程序中的所有bean都可刷新。 像许多事物一样,可刷新的bean最好是适度使用。

脚本安全

最后但同样重要的是安全性。 确保脚本安全并确保只有授权用户或管理员才能修改它们,这一点很重要。 在某些方面,这与保护应用程序其他任何部分的方式没有什么不同。 例如,大多数应用程序需要确保数据完整性,并限制用户访问特定于其角色或特权的功能。 但是,另一方面,此功能可能会为黑客入侵您的系统打开新的攻击媒介,不仅改变数据,而且改变系统行为。 您当然想减少应用程序的攻击面,并且必须权衡所有设计折衷的利弊。

安全性可能更重要的原因是,有了Spring的动态语言支持,您不仅可以更改系统数据,还可以更改行为。 无论如何,这在某种程度上是正确的:考虑一种注入恶意代码SQL注入攻击,JavaScript跨站点脚本或跨站点请求伪造攻击,这些攻击可以改变或替换系统的行为。 我的观点是,您需要考虑如何确保对Groovy脚本进行适当的控制,尤其是当它们可刷新时。

根据您使用可刷新bean的方式,在运行时更改行为的优势可能会抵消额外的安全风险。 想象一下一个面向客户的销售应用程序,该应用程序需要经常更改规则以向客户提供折扣,或者一个保险应用程序,其业务规则可能经常更改。 在这种情况下,您可以设计用Groovy编写的DSL,销售人员或保险代理可以更改以适应当前的业务需求。 也许您想添加一点逻辑,以对购买$ 50的商品提供10%的折扣。 通过允许用户在正在运行的应用程序中直接编辑DSL的小部分,当然可以适应这种类型的更改。 或者,也许您设计了一个图形编辑器,它们可以用来更改折扣政策。

结论

您现在已经了解了如何使用已编译的Groovy类或动态编译和加载的脚本将Groovy集成到基于Spring的应用程序中。 您还知道如何使脚本化Groovy Bean可刷新,如何在创建时自定义Groovy Bean,甚至如何将它们存储在关系数据库中。 您已经了解了脚本编译和运行时错误如何以不同方式影响正在运行的应用程序,以及与使用需要重新部署或重新启动应用程序的传统体系结构相比,可刷新Bean如何使运行时更正错误。 最后,我简要介绍了脚本化和可刷新Bean的安全性,并指出您需要彻底评估应用程序所需的安全性类型。

Spring和Groovy一起构成了强大的组合:Spring提供了体系结构和基础结构,而Groovy增加了动态功能。 Spring能够在更改Groovy脚本时重新加载它们,从而使您的应用程序进入未知领域。 但是请记住:“强大的力量伴随着巨大的责任。” 为您的应用程序添加更多动态特性可以确保它们更加灵活和强大,但同时也带来了以前可能没有处理过的问题和挑战。


翻译自: https://www.ibm.com/developerworks/java/library/j-groovierspring2/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值