Grails 1.2参考文档速读(16):服务层
转载
服务层的作用就是封装复杂业务逻辑,尤其是这种逻辑涉及到多个Domain Class的时候。
我们在前面的文章中已经看到,实际上在Controller中就可以畅通无阻的使用Domain Class,那为什么还要搞出来一个单独的服务层呢?原因很简单,职责清晰。从架构上讲,Controller仍属于Web层的范畴,它的主要作用还是为了View的显示做好准备工作,或是针对请求准备好响应的数据。而业务逻辑则属于业务层的内容,两者混在一起,不仅让Controller变得复杂,而且也为测试造成了麻烦。基于这种原因,一般建议:不要在Controller中包含核心业务逻辑,凡是涉及多个Domain Class的操作,就封装成Service,由Controller调用。
服务的创建非常简单,使用命令:grails create-service,即可。和其他的Grails组件一样,服务也有自己的存放位置:grails-app/services。当然,服务本身也同样是普通的Groovy类。
服务要和多个Domain Class打交道,这当然就离不开事务了。关于编程性事务的内容,我们已经在GORM部分讲过,在此就不再啰嗦。现在来看看声明性事务是如何定义的,这也很简单,只要在服务类中写上:static transactional = true就行了:
class CountryService {
static transactional = true
}
特点:
- 服务缺省使用声明性事务,将其设为false即关闭
- 声明性事务只有在DI方式才能工作,使用new方式将无效。
- 缺省的级别是PROPAGATION_REQUIRED
Grails也完全支持Spring的事务注解:
import org.springframework.transaction.annotation.*
class BookService {
@Transactional(readOnly = true)
def listBooks() { Book.list() }
@Transactional
def updateBook() { }
}
在使用事务注解时,无需任何配置,只需使用即可,Grails会负责把这些东西自动串起来。
用过Spring的读者应该对Spring的scope不会陌生,它决定了bean的创建方式和生命周期,跟并发性不无联系。一般状况,服务是Singleton,scope的值包括:
- prototype:新实例/注入时
- request:新实例/request
- flash:新实例/flash
- flow:新实例/flow
- conversation:新实例/conversation
- session:新实例/session
- singleton(缺省):自始至终一个实例
改变缺省的Scope(在服务定义中)增加:static scope = "以上之一"。
至于DI,Grails遵守的约定适用于Controller、Service、Domain Class、Command Object、Tag等:
class BookController {
def bookService //对应BookService
…
}
需要注意:
- 按名字,不支持按类型
- 在声明服务时,不建议使用强类型,因为当类型变化时,reload时DI会出现问题。
除了一般的Groovy类,我们同样可以在Java类中使用服务:
- 方法1:使用带包名的Service
- 方法2:定义接口;Service实现接口;
不论以上哪种方式,在Java端:
- 代码:src/java
package bookstore;
public class BookConsumer {
private BookStore store;
public void setBookStore(BookStore storeInstance) {
this.store = storeInstance;
}
…
}
- bean定义:grails-app/conf/spring/resources.xml
<bean id="bookConsumer" class="bookstore.BookConsumer">
<property name="bookStore" ref="bookService" />
</bean>