13.3 Dependency Injection and Services
Dependency Injection Basics
A key aspect of Grails services is the ability to use Spring Framework's dependency injection features. Grails supports "dependency injection by convention". In other words, you can use the property name representation of the class name of a service to automatically inject them into controllers, tag libraries, and so on.
As an example, given a service called BookService
, if you define a property called bookService
in a controller as follows:
class BookController {
def bookService
...
}
In this case, the Spring container will automatically inject an instance of that service based on its configured scope. All dependency injection is done by name. You can also specify the type as follows:
class AuthorService {
BookService bookService
}
NOTE: Normally the property name is generated by lower casing the first letter of the type. For example, an instance of the BookService class would map to a property named bookService . |
To be consistent with standard JavaBean conventions, if the first 2 letters of the class name are upper case, the property name is the same as the class name. For example, the property name of the JDBCHelperService
class would be JDBCHelperService
, not jDBCHelperService
or jdbcHelperService
.
See section 8.8 of the JavaBean specification for more information on de-capitalization rules.
Only the top level object is subjected to injection as traversing all nested objects to perform injection would be a performance issue. |
Be careful when injecting the non-default datasources. For example, using this config:
dataSources:
dataSource:
pooled: true
jmxExport: true
.....
secondary:
pooled: true
jmxExport: true
.....
You can inject the primary dataSource
like you would expect:
class BookSqlService {
def dataSource
}
But to inject the secondary
datasource, you have to use Spring’s Autowired
injection or resources.groovy
.
class BookSqlSecondaryService {
@Autowired
@Qualifier('dataSource_secondary')
def dataSource2
}
Dependency Injection and Services
You can inject services in other services with the same technique. If you had an AuthorService
that needed to use the BookService
, declaring the AuthorService
as follows would allow that:
class AuthorService {
def bookService
}
Dependency Injection and Domain Classes / Tag Libraries
You can even inject services into domain classes and tag libraries, which can aid in the development of rich domain models and views:
class Book {
...
def bookService
def buyBook() {
bookService.buyBook(this)
}
}
Since Grails 3.2.8 this is not enabled by default. If you want to enable it again, take a look at Spring Autowiring of Domain Instance |
Service Bean Names
The default bean name which is associated with a service can be problematic if there are multiple services with the same name defined in different packages. For example consider the situation where an application defines a service class named com.demo.ReportingService
and the application uses a plugin named ReportingUtilities
and that plugin provides a service class named com.reporting.util.ReportingService
.
The default bean name for each of those would be reportingService
so they would conflict with each other. Grails manages this by changing the default bean name for services provided by plugins by prefixing the bean name with the plugin name.
In the scenario described above the reportingService
bean would be an instance of the com.demo.ReportingService
class defined in the application and the reportingUtilitiesReportingService
bean would be an instance of the com.reporting.util.ReportingService
class provided by the ReportingUtilities
plugin.
For all service beans provided by plugins, if there are no other services with the same name within the application or other plugins in the application then a bean alias will be created which does not include the plugin name and that alias points to the bean referred to by the name that does include the plugin name prefix.
For example, if the ReportingUtilities
plugin provides a service named com.reporting.util.AuthorService
and there is no other AuthorService
in the application or in any of the plugins that the application is using then there will be a bean named reportingUtilitiesAuthorService
which is an instance of this com.reporting.util.AuthorService
class and there will be a bean alias defined in the context named authorService
which points to that same bean.