Spring:Grails 的基础

工程 | Peter Ledbrook | 2010 年 6 月 8 日 | ...

在 SpringSource 的 Groovy & Grails 培训课程中,我们强调 Grails 是站在巨人的肩膀上。其中一个巨人就是 Spring。如果没有它,Grails 的开发速度肯定不会这么快。它可能也没有那么灵活,无法轻松地与企业 Java 系统集成。看看可用插件的数量:许多插件都基于具有 Spring 支持的 Java 库。

在这篇文章中,我首先想看看 Grails 如何使用 Spring,然后介绍您可以访问这种原始强大功能和灵活性的各种方法。

Spring 的子类

您可能没有意识到这一点,但是当您创建一个 Grails 应用程序时,您也创建了一个 Spring MVC 应用程序。在幕后,Grails 创建了 Spring MVC 的变体DispatcherServlet并配置了一堆 Bean 来完成繁重的工作。当然,这意味着您的应用程序底层有一个 Spring 上下文——稍后您将看到您可以访问它。

以下是一些示例,如果您查看典型 Grails 应用程序的 Spring 上下文,您会发现这些 Bean

  • grailsApplication - 表示当前应用程序及其资源的 Bean
  • pluginManager - 插件管理器,您可以查询有关已加载插件的信息
  • jspViewResolver - 用于 GSP 的自定义 MVC 视图解析器,回退到 JSP
  • messageSource - 本地化消息的来源
  • localeResolver - 如何确定用户的区域设置
  • annotationHandlerMapping - 允许使用@Controller注解
这只是 Grails 创建的众多 Bean 中的一部分。事实上,您可能会惊讶地发现,您所有的应用程序控制器、标签库、域类和服务也都是 Bean。

Grails 在哪些其他方面依赖于 Spring?首先是数据绑定:Spring 执行将字符串数据物理绑定到对象属性的所有操作。它不仅仅是 Web 层:GORM 使用 Spring 的 Hibernate 模板来保存和查询域类。也许最重要的是,Grails 使用 Spring 的事务管理。正如我之前提到的,许多插件都利用了 Java 库提供的 Spring 集成。

因此,Grails 应用程序实际上是一个 Spring 应用程序。这就引出了一个问题:当您需要时,如何利用底层的 Spring 组件?

与 Spring 交谈

您可以轻松地编写 Grails 应用程序,即使是很复杂的应用程序,也不必担心 Spring。这很好,因为这意味着新开发人员不必立即学习这项技术。但是,仅仅因为您**不必**直接与 Spring 交互并不意味着它很难。

您可以采取几种方法,我将依次介绍每种方法。

服务和自动装配

Spring 的关键是 Bean 的创建和装配。您通常需要提供一些配置来执行此操作,但 Grails 服务为您提供了一种轻量级、基于约定的替代方案。正如我之前所说,您在 Grails 应用程序中创建的几个工件会自动成为 Spring Bean,但服务可以让您拥有最多的控制权。

原理很简单:在grails-app/services下创建一个类,后缀为Service,您将在应用程序中自动获得一个新的(单例)Spring Bean。该 Bean 的名称是什么?很简单:类名,第一个字母小写。例如,类SecurityService将生成一个名为“securityService”的 Bean。AuditReportService同样会变成“auditReportService” Bean。

将其他 Bean 装配到您的服务(以及所有其他核心 Grails 工件)中也同样简单:声明一个与您想要的 Bean 同名的属性。例如,假设我想从另一个服务(或者可能是控制器)中使用“auditReportService” Bean。我可以像这样将其装配进去

class MyService {
    def auditReportService
    ...
}

我相信您会同意这非常简单。这是 Spring 自动装配的一个示例。即使您为属性指定了类型,Grails 也会根据名称装配 Bean。

服务的另一个有用特性是它们默认情况下是事务性的。这使得它们成为抽象数据访问和为您的应用程序构建健壮体系结构的绝佳方式。一种典型的方法是为您的服务创建不同的“网关”:HTML UI、XML REST 接口、通过 RMI 进行远程调用等,所有这些都调用您的服务。

最后一点:我说过 Grails 会将您的服务实例化为单例 Bean,但您可以根据每个服务的不同更改此行为。只需将静态scope属性添加到您的服务类中,如下所示

class MyService {
    static scope = "request"
    ...
}

如您所见,当您使用服务时,您可以几乎不费吹灰之力就获得 Spring 的许多主要好处。这很好,但是如果您有一些现有的类想要将其转换为 Bean 怎么办?也许您是用 Java 编写的,或者它们打包在 JAR 文件中。使用普通的 Spring,您需要手动配置它们。幸运的是,您可以在 Grails 中执行相同的操作。

手动定义 Bean

在很久以前,在注解出现之前,您会为 Spring 应用程序创建一到多个 XML 格式的 Bean 描述符文件。Grails 允许您使用grails-app/conf/spring/resources.xml文件执行相同的操作。它没有任何特殊之处,因此标准的 Spring 文档适用!

我必须说,我不太喜欢再编写 XML 了,所以我更喜欢另一种 Bean 定义格式:Grails 的 Spring Bean DSL。以下是在grails-app/conf/spring/resources.groovy:

beans = {
    reportGenerator(org.example.XmlReportGenerator)
}

中定义报表生成器 Bean 的一个非常简单的示例

beans = {
    reportGenerator(org.example.XmlReportGenerator) { bean ->
        bean.autowire = "byName"
        prettyFormat = true
    }
}

好的,它比 XML 格式更简洁,但这是否成为切换的充分理由?对于大多数人来说可能不是。DSL 的真正强大之处在于它是真正的 Groovy,这意味着

  • 您可以混合使用正常的 Groovy 代码,例如条件和循环;以及
  • 您可以为属性值使用真实类型。

更新我已将下面的示例更改为使用检测当前环境的新方法。

以这个例子为例

import grails.util.Environment

beans = {
    if (Environment.current == Environment.PRODUCTION) {
        // Use the real web service for production
        securityService(org.example.WsClientSecurityService) {
            endpoint = "http://..."
        }
    }
    else {
        // Use a dummy service for development and testing
        securityService(org.example.DummySecurityService)  {
            userRoles = [ peter: [ "admin", "user"], tom: [ "user" ] ]
        }
    }
}

它演示了如何使用条件为不同的 Grails 环境配置不同的 Bean 实现。对于生产环境,“securityService”是一个 Web 服务客户端,而对于所有其他环境,我们使用一个虚拟的内存服务。您还可以看到,可以将文字映射分配给Map属性,其他任何类型也是如此。它不仅比 XML 简洁得多,而且您还在使用可以在运行时操作的真实类型和对象。

DSL 比我在这里介绍的要多,因此我建议您查看用户指南以获取更多信息。您会发现 DSL 支持 Spring 命名空间、工厂方法等。

我已经介绍了 Grails 最常见的两个 Spring 集成点,但还有一个:注解。

注解

Spring 2.5 引入了用于各种事物的注解,例如组件、Spring MVC 控制器、事务等。您会很高兴知道您可以在 Grails 应用程序中使用这些注解。例如,假设您在src/java下有一个 Java 类,如下所示
package org.example;

@Component("securityService")
public class DummySecurityService implements SecurityService {
    ...
}

这应该会自动成为一个名为“securityService”的 Spring Bean,但现在还不会发生。还需要执行一步:您必须指定 Grails 应该扫描哪些包以查找 Spring 注解。因此,在此示例中,我们希望扫描org.example包。为此,只需将以下设置添加到grails-app/conf/Config.groovy:

grails.spring.bean.packages = [ "org.example" ]

该类现在将自动创建为 Spring Bean。请注意,Grails 也会扫描所有子包,因此即使该类位于org.example.sub.pkg包中,上述操作也能正常工作。

只要您通过grails.spring.bean.packages指定了包,您甚至可以使用 @Controller 注解将类添加为控制器。如果您决定从 Spring MVC 迁移到 Grails,或者某个团队开发了一些您想要添加到 Grails 应用程序中的 Spring MVC 控制器,这将非常有用。

如您所见,Grails 中有足够多的选项来定义 Bean 以满足大多数人的需求。这仅仅剩下需要介绍的 Spring 应用程序上下文的运行时查询。

运行时交互

许多应用程序发现直接与 Spring 应用程序上下文交互非常有用,无论是查找特定类型的全部 Bean 还是仅仅检索特定 Bean 而无需依赖自动装配。如何访问应用程序上下文?

如果您的类是 Spring Bean,那么您可以简单地实现ApplicationContextAware接口。然后,Spring 会自动将上下文注入到您的applicationContext属性中。或者,您可以注入grailsApplicationBean 并通过以下方式检索上下文grailsApplication.mainContext.

另一方面,如果您的类不受 Spring 管理,则您需要进行一些手动工作。这不太好看,但您可以通过以下代码片段获取上下文

import org.springframework.web.context.support.WebApplicationContextUtils
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.springframework.context.ApplicationContext
...
def ctx = WebApplicationContextUtils.getWebApplicationContext(ServletContextHolder.servletContext)

注意:在 Grails 中,您不会处理单个应用程序上下文。您从上面描述的不同技术获得的应用程序上下文具有父应用程序上下文。父级包含grailsApplication, pluginManager以及从web-app/WEB-INF/applicationContext.xml文件配置的其他 Bean。您可能会发现一些代码允许您以与上述详细描述不同的方式获取应用程序上下文,但您最终可能会获得对父级的引用,该父级不包含服务、控制器等。

总而言之,Grails 从根本上来说是一个伪装成 Spring 应用程序的框架。尽管它隐藏了 Spring,使其在普通检查中难以察觉,但它确实提供了一些强大的技术来直接与 Spring 进行交互。这意味着您可以轻松利用现有的 Java/Spring 库,并使用一个框架,使大型应用程序比以往更容易管理。

获取 Spring 新闻通讯

关注 Spring 新闻通讯

订阅

抢先一步

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部