抢先一步
VMware 提供培训和认证,助您快速提升。
了解更多在 SpringSource 的 Groovy & Grails 培训课程中,我们强调 Grails 是站在巨人的肩膀上。其中一个巨人就是 Spring。如果没有它,Grails 的开发速度肯定不会这么快。它可能也没有那么灵活,无法轻松地与企业 Java 系统集成。看看可用插件的数量:许多插件都基于具有 Spring 支持的 Java 库。
在这篇文章中,我首先想看看 Grails 如何使用 Spring,然后介绍您可以访问这种原始强大功能和灵活性的各种方法。
以下是一些示例,如果您查看典型 Grails 应用程序的 Spring 上下文,您会发现这些 Bean
Grails 在哪些其他方面依赖于 Spring?首先是数据绑定:Spring 执行将字符串数据物理绑定到对象属性的所有操作。它不仅仅是 Web 层:GORM 使用 Spring 的 Hibernate 模板来保存和查询域类。也许最重要的是,Grails 使用 Spring 的事务管理。正如我之前提到的,许多插件都利用了 Java 库提供的 Spring 集成。
因此,Grails 应用程序实际上是一个 Spring 应用程序。这就引出了一个问题:当您需要时,如何利用底层的 Spring 组件?
您可以采取几种方法,我将依次介绍每种方法。
原理很简单:在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 中执行相同的操作。
我必须说,我不太喜欢再编写 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,这意味着
更新我已将下面的示例更改为使用检测当前环境的新方法。
以这个例子为例
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 集成点,但还有一个:注解。
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,那么您可以简单地实现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 库,并使用一个框架,使大型应用程序比以往更容易管理。