[转]Grails和 Spring

2024-01-02 09:18
文章标签 java spring grails

本文主要是介绍[转]Grails和 Spring,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:http://justjavac.iteye.com/blog/705558


这一节适合于高级用户, Spring 框架,和想通过 插件开发来配置Grails的开发人员。

14.1 Grails内部实现

实际上Grails是变相的 Spring MVC 应用. Spring MVC是Spring框架内置的 MVC web开发框架.虽然从易用性来说Spring MVC比不上Struts这样的框架,但它的设计和架构都非常优秀,正适合在其基础之上构建另一个像Grails这样的框架。

Grails在以下方面利用了Spring MVC:

  • 基本控制器逻辑 - Grails继承Spring的 DispatcherServlet并使用它作为代理将请求转发给 Grails的控制器
  • 数据绑定和校验 - Grails的校验 和 数据绑定正是建立在Spring的数据绑定和校验之上
  • 运行时配置 - Grails的整个"约定优先配置"机制全部用Spring来实现ApplicationContext
  • 事务处理 - Grails GORM使用Spring的事务处理

也就是说Grails内嵌Spring并在框架的各个环节上使用Spring.

 

Grails ApplicationContext

Spring开发人员经常热衷于想知道Grails中的ApplicationContext实例是怎么创建的.基本过程如下:

  • Grails通过web-app/WEB-INF/applicationContext.xml创建一个父ApplicationContext对象。 这个ApplicationContext对象设置 GrailsApplication 对象 and GrailsPluginManager对象.
  • 使用这个 ApplicationContext 作为父对象 Grails通过“约定优先”分析 GrailsApplication 对象构建一个子ApplicationContext对象,此对象作为web应用的 ApplicationContext

 

配置Spring Beans

大部分Grails的配置都是在运行时进行. 每个 插件 都可以配置在上面创建的ApplicationContext对象中注册过的Spring bean. For a reference as to which beans are configured refer to the reference guide which describes each of the Grails plug-ins and which beans they configure.

14.2 配置其他Bean

使用XML

Beans可用过grails-app/conf/spring/resources.xml 来配置. 这个文件是一个标准的Spring配置文件,在Spring Spring参考文档中对如何配置Spring Beans有详细描述。下面是一个简单的例子:

 

<bean id="myBean" class="my.company.MyBeanImpl"></bean>

配置完毕后, myBean就可以在Grails 控制器,标签库,服务等很多地方引用:

 

class ExampleController {

def myBean }

 

引用现有的Spring bean

resources.xml中声明的bean也可以通过约定来引用Grails类 . 比如, 如果你想在你的bean中引用BookService这样一个service,你可以用如下的代码:

 

<bean id="myBean" class="my.company.MyBeanImpl"><property name="bookService" ref="bookService" />        
</bean>

这个bean本身需要一个 public setter方法,在Groovy中这样定义:

 

package my.company
class MyBeanImpl {BookService bookService
}

或在Java中:

 

package my.company;
class MyBeanImpl {private BookService bookService;public void setBookService(BookService theBookService) {this.bookService = theBookService;}
}

既然大部分Grails配置都是在运行时通过约定机制来完成,大部分bean并不需要声明, 但仍然可以在Spring应用中进行引用. 如你需要引用一个Grails DataSource 你可以这样:

 

<bean id="myBean" class="my.company.MyBeanImpl"><property name="bookService" ref="bookService" />        <property name="dataSource" ref="dataSource" />
</bean>

或者你需要引用Hibernate SessionFactory:

 

<bean id="myBean" class="my.company.MyBeanImpl"><property name="bookService" ref="bookService" />        <property name="sessionFactory" ref="sessionFactory" />
</bean>

所有提供的bean既说明可参考插件开发文档.

 

使用Spring DSL

如果你想使用Grails提供的 Spring DSL ,你必须创建grails-app/conf/spring/resources.groovy 文件,定义一个 beans属性块:

 

beans = {// 定义的beans
}

同样在的配置可以应用于XML例子:

 

beans = {myBean(my.company.MyBeanImpl) {bookService = ref("bookService")}  
}

这样做最大的好处是你能够在bean的定义中混合各种逻辑,如基于 environment:

 

import grails.util.*
beans = {switch(GrailsUtil.environment) {case "production":myBean(my.company.MyBeanImpl) {bookService = ref("bookService")}

breakcase "development":myBean(my.company.mock.MockImpl) {bookService = ref("bookService")} break} }

 

14.3 运行时Spring与Beans DSL

Grails提供BeanBuilder的目的是提供一种简化的方法来关联使用Spring的各中依赖关系.

这是因为Spring的常规配置方法(通过XML)在本质上是静态的,除了通过程序方式来动态产生XML配置文件外,很难在运行时修改和添加程序配置。而且这种方法非常繁琐,也容易出错. Grails的BeanBuilder 改变了这一点,它可以让你在运行时通过系统属性和环境属性来动态改变程序逻辑.

这使得程序代码动态适配它的环境,避免不必要的重复代码(如在Spring中为测试环境,开发环境和生产环境做不同的配置)

 

BeanBuilder 类

Grails提供了 grails.spring.BeanBuilder 类使用动态Groovy来创建bean的声明. 基本点如下:

 

import org.apache.commons.dbcp.BasicDataSource
import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean;
import org.springframework.context.ApplicationContext;

def bb = new grails.spring.BeanBuilder()

bb.beans {dataSource(BasicDataSource) {driverClassName = "org.hsqldb.jdbcDriver"url = "jdbc:hsqldb:mem:grailsDB"username = "sa"password = ""}sessionFactory(ConfigurableLocalSessionFactoryBean) {dataSource = dataSourcehibernateProperties = [ "hibernate.hbm2ddl.auto":"create-drop","hibernate.show_sql":true ]} }

ApplicationContext appContext = bb.createApplicationContext()

 

在  插件 和  grails-app/conf/spring/resources.groovy 文件中你不需要创建一个 BeanBuilder实例, 它在 doWithSpring 和  beans块中都隐式存在.

上面这个例子说明了如果使用 BeanBuilder类来配置某个特定的Hibernate数据源。

实际上,每个方法调用( dataSource 和 sessionFactory 调用) 都映射到Spring中的bean的名字. 方法的第一个参数是bean的class名字, 最后一个参数是一个块(block). 在块内部可以用标准的Groovy语法设置bean的属性。

通过bean的名字自动查找bean的引用. 通过上面的sessionFactory bean解析dataSource可以看点这一点。

也可以通过builder设置一些与bean管理相关的特殊的bean属性,如:

 

sessionFactory(ConfigurableLocalSessionFactoryBean) { bean ->bean.autowire = 'byName'       // Autowiring behaviour. The other option is 'byType'. [autowire]bean.initMethod = 'init'       // Sets the initialisation method to 'init'. [init-method]bean.destroyMethod = 'destroy' // Sets the destruction method to 'destroy'. [destroy-method]bean.scope = 'request'         // Sets the scope of the bean. [scope]dataSource = dataSourcehibernateProperties = [ "hibernate.hbm2ddl.auto":"create-drop","hibernate.show_sql":true  ]
}

括号中的字符串对应于Spring XML定义中相应的bean 属性名。

 

在Spring MVC中使用BeanBuilder

如果想在Spring MVC中使用BeanBuilder,你必须确保grails-spring-<version>.jar 包含在classpath中. 还要在/WEB-INF/web.xml文件中做如下设置:

 

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.groovy</param-value>
</context-param>
<context-param><param-name>contextClass</param-name><param-value>org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext</param-value>
</context-param>

然后在创建 /WEB-INF/applicationContext.groovy文件并配置如下:

 

beans {dataSource(org.apache.commons.dbcp.BasicDataSource) {driverClassName = "org.hsqldb.jdbcDriver"url = "jdbc:hsqldb:mem:grailsDB"username = "sa"password = ""}
}

 

从文件系统中加载bean定义

你可以使用BeanBuilder并使用下面的语法 来加载在外部Groovy脚本中定义的bean:

 

def bb = new BeanBuilder()
bb.loadBeans("classpath:*SpringBeans.groovy")

def applicationContext = bb.createApplicationContext()

这里BeanBuilder 将加载在classpath中以SpringBeans.groovy结尾的Groovy文件并将它们解析成bean的定义.这里是一个范例脚本文件:

 

beans {dataSource(BasicDataSource) {driverClassName = "org.hsqldb.jdbcDriver"url = "jdbc:hsqldb:mem:grailsDB"username = "sa"password = ""}sessionFactory(ConfigurableLocalSessionFactoryBean) {dataSource = dataSourcehibernateProperties = [ "hibernate.hbm2ddl.auto":"create-drop","hibernate.show_sql":true  ]}
}

 

绑定变量

如果从脚本中加载bean,可以通过创建Groovy Binding对象来实现绑定:

 

def binding = new Binding()
binding.foo = "bar"

def bb = new BeanBuilder() bb.binding = binding bb.loadBeans("classpath:*SpringBeans.groovy")

def ctx = bb.createApplicationContext()

14.4 BeanBuilder DSL

使用构建器参数

可以通过在bean的class和最后一个closure之间定义的方法来定义构建器参数:

bb.beans {exampleBean(MyExampleBean, "firstArgument", 2) {someProperty = [1,2,3]}
}

 

配置 BeanDefinition (使用工厂方法)

传给closure的第一个参数是一个bean配置对象引用,你可以使用它来配置工厂方法,调用 AbstractBeanDefinition的方法:

 

bb.beans {exampleBean(MyExampleBean) { bean ->bean.factoryMethod = "getInstance"bean.singleton = falsesomeProperty = [1,2,3]}
}

你也可以通过bean 定义方法的返回值来配置bean:

 

bb.beans {def example = exampleBean(MyExampleBean) {someProperty = [1,2,3]}example.factoryMethod = "getInstance"
}

 

使用工厂bean(Factory beans)

Spring提供了工厂bean的概念,即bean不是从class创建,而是由这些工厂创建 defines the concept of factory beans and often a bean is created not from a class, but from one of these factories. 在这种情况下bean没有class,你必须将工厂bean的名字传给定义的bean:

 

bb.beans {myFactory(ExampleFactoryBean) {someProperty = [1,2,3]}myBean(myFactory) {name = "blah"}
}

注意:上面的例子中我们传递的是 myFactory bean而不是一个clas. 另一个常见的需求是提供调用工厂bean的工厂方法名,可以用下面的Groovy语法做到这一点:

 

bb.beans {myFactory(ExampleFactoryBean) {someProperty = [1,2,3]}myBean(myFactory:"getInstance") {name = "blah"}
}

这里 ExampleFactoryBean 的 getInstance会被调用来创建myBean bean.

 

运行时创建 bean的引用

有时只有在运行是才知道需要创建的bean的名字. 在这种情况情况下你可以使用字符串替换来实现动态调用:

 

def beanName = "example"
bb.beans {"${beanName}Bean"(MyExampleBean) {someProperty = [1,2,3]}
}

在这个例子中,使用早先定义的 beanName 变量来调用bean.

另外, 可使用ref来动态引用在运行时才知道的bean的名字,如下面的代码:

 

def beanName = "example"
bb.beans {"${beanName}Bean"(MyExampleBean) {someProperty = [1,2,3]}anotherBean(AnotherBean) {example = ref("${beanName}Bean")}
}

这里AnotherBean属性通过运行时对 exampleBean的引用来设置 . 也可以通过 ref 来引用在父ApplicationContext定义的bean, ApplicationContext在 BeanBuilder的构建器中提供:

 

ApplicationContext parent = ...//
der bb = new BeanBuilder(parent)
bb.beans {anotherBean(AnotherBean) {example = ref("${beanName}Bean", true)}
}

这里第二个参数 true 指定了在父ApplicationContext中查找bean的引用.

 

使用匿名内部bean

你可以通过将属性块付给bean的一个属性来使用匿名内部bean,这个属性块提供一个bean的类型参数:

 

bb.beans {marge(Person.class) {name = "marge"husband =  { Person p ->name = "homer"age = 45props = [overweight:true, height:"1.8m"]}children = [bart, lisa]}bart(Person) {name = "Bart"age = 11}lisa(Person) {name = "Lisa"age = 9}
}

在上面的例子中我们将marge bean的husband属性 赋值一个属性块(参数类型是Person)的方式创建一个内部bean引用. 如果你有一个工厂bean你也可以忽略类型参数,直接使用传进进来的bean的定义:

 

bb.beans {personFactory(PersonFactory.class)marge(Person.class) {name = "marge"husband =  { bean ->bean.factoryBean = "personFactory"bean.factoryMethod = "newInstance"name = "homer"age = 45props = [overweight:true, height:"1.8m"]}children = [bart, lisa]}
}

 

抽象bean和父子bean定义

要创建一个抽象bean,定义一个没有class 的bean:

 

class HolyGrailQuest {def start() { println "lets begin" }
}
class KnightOfTheRoundTable {String nameString leaderKnightOfTheRoundTable(String n) {this.name = n}HolyGrailQuest quest

def embarkOnQuest() {quest.start()} }

def bb = new grails.spring.BeanBuilder() bb.beans {abstractBean {leader = "Lancelot"}… }

这里定义了一个抽象bean,这个bean有一个属性 leader,属性值为 "Lancelot". 要使用抽象bean,只要将它设为要定义的bean的父即可:

 

bb.beans {…quest(HolyGrailQuest)knights(KnightOfTheRoundTable, "Camelot") { bean ->bean.parent = abstractBeanquest = quest}
}

 

当使用父bean时,你必须在设置其他属性前设置parent属性!

如果你要定义一个具有class的抽象bean,可以这样:

 

def bb = new grails.spring.BeanBuilder()
bb.beans {abstractBean(KnightOfTheRoundTable) { bean ->bean.'abstract' = trueleader = "Lancelot"}quest(HolyGrailQuest)knights("Camelot") { bean ->bean.parent = abstractBeanquest = quest}
}

上面例子中我们创建了抽象 KnightOfTheRoundTable 并将它的参数设为abstract. 接下来我们定义了一个knights bean,没有定义它的class,而是继承父bean中定义的class。

 

使用 Spring命名空间

从Spring 2.0开始,通过XML命名空间可以更方便的使用Spring的各种特性. 如果使用 BeanBuilder, 你可以先声明所要使用的Spring命名空间:

 

xmlns context:"http://www.springframework.org/schema/context"

然后调用与命名空间名称和属性匹配的方法:

 

context.'component-scan'( 'base-package' :"my.company.domain" )

通过Spring的命名空间可以做很多有用的事,比如查找JNDI资源:

 

xmlns jee:"http://www.springframework.org/schema/jee"

jee.'jndi-lookup'(id:"dataSource", 'jndi-name':"java:comp/env/myDataSource")

上面的例子通过查找JNDI创建一个 dataSourcebean对象. 通过Spring命名空间,你可以在BeanBuilder中直接访问Spring AOP功能比如下面的代码:

 

class Person {int age;String name;

void birthday() {++age;} } class BirthdayCardSender {List peopleSentCards = []public void onBirthday(Person person) {peopleSentCards << person} }

你可以定义一个AOP aspect pointcut来监测对 birthday() 方法的所有调用:

 

xmlns aop:"http://www.springframework.org/schema/aop"
fred(Person) { name = "Fred" age = 45 
}

birthdayCardSenderAspect(BirthdayCardSender)

aop { config("proxy-target-class":true) { aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) { after method:"onBirthday", pointcut: "execution(void ..Person.birthday()) and this(person)" } } }

14.5 属性占位符配置

通过扩展的Spring的PropertyPlaceholderConfigurer,Grails支持属性占位符配置,这和 外部配置配合使用非常有用。 .

Settings defined in either ConfigSlurper scripts of Java properties files can be used as placeholder values for Spring configuration in grails-app/conf/spring/resources.xml. For example given the following entries in grails-app/conf/Config.groovy (or an externalized config):

 

database.driver="com.mysql.jdbc.Driver"
database.dbname="mysql:mydb"

接着在 resources.xml中用${..}语法定义占位符:

 

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName"><value>${database.driver}</value></property><property name="url"><value>jdbc:${database.dbname}</value></property></bean>

14.6 属性重载

通过扩展的Spring PropertyOverrideConfigurer,Grails提供了对属性重载配置的支持, 外部配置配合使用非常有用。 .

你可以提供一个 ConfigSlurper脚本文件,该文件中定义了一个 beans 属性块,属性块中定义的属性值会覆盖bean中定义的属性值:

 

beans {bookService.webServiceURL = "http://www.amazon.com"
}

重载的属性应用在Spring ApplicationContext 创建之前. 格式如下:

 

[bean name].[property name] = [value]

你也可以提供一个常规的Java属性文件,属性文件中的每个条目加上beans前缀:

 

beans.bookService.webServiceURL=http://www.amazon.com

这篇关于[转]Grails和 Spring的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/562008

相关文章

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Java字符串处理全解析(String、StringBuilder与StringBuffer)

《Java字符串处理全解析(String、StringBuilder与StringBuffer)》:本文主要介绍Java字符串处理全解析(String、StringBuilder与StringBu... 目录Java字符串处理全解析:String、StringBuilder与StringBuffer一、St

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt