Services
Grails Services
A service is a class that holds one or more methods that implement business logic. Logical parts of the business logic are contained in separate service classes. Services are demarcated by transactions to make units of work atomic on the persistence level. When certain types of exceptions are thrown transactions are rolled back.Services need to be registered with the middle tier and also need transactional configuration next to the configuration of optional AOP services.Services can be registered within a number of different scopes, but the default is application (or singleton) scope therefore your code needs to be thread-safe.Coding conventions for services
Service class names should start with the name of the service and end with "Service". A country service would thus be called "CountryService".By default transaction demarcation is enabled for all services, transaction demarcation can be disabled for specific services by creating a "transactional" property and setting it to false:class CountryService {
static transactional = false
}Warning: dependency injection is the only way that declarative transactions work. You will not get a transactional service if you use the new operator such as new BookService()This results in each method of a service getting wrapped in a transaction. The transaction attribute is set to PROPAGATION_REQUIRED for all service methods. This is hard coded into the Service Plugin. PROPAGATION_REQUIRED, as stated in Transaction Manager terms, will use the current transaction if available or create a new one is one is not available.
Services and scopes
By default access to service methods is not synchronised, so nothing prevents concurrent execution of those functions. In fact, because by default the service is a singleton and may be used concurrently, you should be very careful about storing state in a service. Or take the easy (and better) road and never store state in a service.Since Grails 0.6 you can change this behaviour by placing a service in a particular scope. The supported scopes are:- prototype - A new service is created every time it is injected into another class
- request - A new service will be created per request
- flash - A new service will be created for the current and next request only
- flow - In web flows the service will exist for the scope of the flow
- conversation - In web flows the service will exist for the scope of the conversation. ie a root flow and its sub flows
- session - A service is created for the scope of a user session
- singleton (default) - Only one instance of the service ever exists
Note that if your service is flash, flow or conversation scoped (and if you use http session clustering then the session scope too) it will need to implement java.io.Serializable.To enable one of the scopes add a static scope property to your class whose value is one of the above:
static scope = "flow"
Dependency Injection
Other services can be injected into a service by creating corresponding properties. To inject the CountryService create a "countryService" property.def countryService
Earlier versions of Grails supported statically typed injection of services, e.g. {{CountryService countryService}}. Typed injection has an adverse effect on reloading and Grails no longer supports this type of injection.A javax.sql.DataSource instance can be retrieved for the configured database by adding the property:
javax.sql.DataSource dataSource
This could be used in conjunction with Spring's JdbcTemplate to perform direct transactional SQL operations or with Groovy Sql
Initialization
Rather than implementing a constructor for your service class, it is usually more useful to implement the Spring InitializingBean interface so that injected dependencies are available.import org.springframework.beans.factory.InitializingBeanclass ExampleService implements InitializingBean { def grailsApplication def setting void afterPropertiesSet() { this.setting = grailsApplication.config.setting } }
Accessing Services
From a controller
In a typical multitier scenario you can easily access services using dependency injection, for example from a controller:class CountryService {
def String sayHello(String name) {
return "hello ${name}"
}
}class GreetingController {
def countryService
def helloAction = {
render(countryService.sayHello(params.name))
}
}That's all, you can test it by typing this URL http://myserver:8080/myapp/greeting/helloAction?name=Falken in your browser location bar. A page containing "hello Falken" will be returned. See Grails controllers for more details about controllers, e.g. changing to access services by type rather than by name.
From a Domain Class
Services can be accessed from within a domain class by adding the Service as a property and declaring the service to be transient.// Domain class
class MailAction {
String recipient
... def mailService static transients = ['mailService'] void execute() {
def mail = ....
mailService.send(mail)
}
}From a servlet or BootStrap
Alternatively, you can access services from other places of your web application if you need it. Service objects are beans which can be retrieved from the Spring Bean Factory. You can proceed this way:Implement a java interface containing the service method definitions which you want to be available, for example:package serviceinterfaces;public interface CountryServiceInt { public String sayHello(String name); }
Save it in your "<..>/myapp/src/java/serviceinterfaces" directory Modify the CountryService groovy class for implementing the CountryServiceInt interface:
class CountryService implements serviceinterfaces.CountryServiceInt { def String sayHello(String name) { return "hello ${name}" } }
For accessing the service from a servlet you can do this:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes … ApplicationContext ctx = (ApplicationContext) request.getSession().getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT); CountryServiceInt service = (CountryServiceInt) ctx.getBean("countryService"); String str = service.sayHello(request.getParameter.("name")); //do something with str
For accessing the service from within BootStrap.init() you can do this:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes … def init = { servletContext -> ApplicationContext ctx = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT) CountryServiceInt service = (CountryServiceInt) ctx.getBean("countryService") String str = service.sayHello(", Starting Application") //do something with str ...


1 Comment
Post a Comment
Site Login