Prior to Liferay DXP 7.2, Service Builder modules could only use Spring for dependency injection (DI). Now OSGi Declarative Services (DS) is the default dependency injection mechanism for new Service Builder modules. It’s easier to learn and fosters loose coupling between services. If you have an existing Service Builder module that uses Spring DI, you can modify it to use DS.
Here are the conversion steps:
-
Prepare your project for DS
-
Update your Spring bean classes
-
Resolve any circular dependencies
Now prepare your project.
Step 1: Prepare Your Project for DS
Prepare your project’s metadata, dependencies, and service.xml
for DS.
-
Enable the DS annotation option for your inherited dependencies by adding this line to your
bnd.bnd
file:-dsannotations-options: inherit
-
Since DS Service Builder modules use the AOP API, add it as a compile dependency in
build.gradle
:compileOnly group: "com.liferay:com.liferay.portal.aop.api", version: "1.0.0"
-
Add the
dependency-injector="ds"
attribute to yourservice.xml
file’s<service-builder>
element:<service-builder dependency-injector="ds" >
Step 2: Update Your Spring Bean Classes
Some of your non-generated Spring bean classes must be updated to use DS.
-
Add the
@Component
annotation to your*LocalServiceImpl
,*ServiceImpl
, and*FinderImpl
classes. -
If the class implements a
*Finder
interface, declare the component as that service type. Example:@Component(service = MyFinder.class)
-
If the class implements a remote or local service, declare the component as the
com.liferay.portal.aop.AopService
service type. Example:@Component(service = AopService.class)
-
If it’s a remote service (i.e.,
-ServiceImpl
instead of-LocalServiceImpl
), enable JSON web services by setting these properties in your@Component
annotation:json.web.service.context.name
json.web.service.context.path
Set them to the same values as the properties in your remote service interface’s
@OSGiBeanProperties
annotation. -
If it’s a local service, enable
PersistedModelLocalService
service tracking by setting the@Component
propertymodel.class.name
to the service entity’s fully qualified class name. -
Replace all the
@ServiceReference
and@BeanReference
field annotations with the DS@Reference
annotation. -
Use the
@Reference
field annotation to access any other services you need. -
Run Service Builder to regenerate the interfaces based on your implementation changes.
-
Replace the following methods:
-
afterPropertiesSet() {...}
→activate() {...}
and annotate with@Activate
. -
destroy() {...}
→deactivate() {...}
and annotate with@Deactivate
.
-
Next, you’ll work out any remaining references you need.
Step 3: Resolve Any Circular Dependencies
Circular dependencies occur in a module if two or more of its DS services refer
to each another (either directly or indirectly). A direct reference occurs, for
example, when service A
references service B
, and B
references A
. Here’s
what the service components might look like:
AImpl.java
:
@Component(service = A.class)
public class AImpl implements A {
@Reference
private B _b;
}
BImpl.java
:
@Component(service = B.class)
public class BImpl implements B {
@Reference
private A _a;
}
AImpl
and BImpl
directly depend on each other. This circular dependency
prevents each service component from resolving. DS service activation requires
that all of a service’s dependencies (references) be satisfied.
Note: Service resolution is independent and separate from module (OSGi bundle) resolution:
- Module resolution is determined by the module’s manifest.
- Modules resolve before any of their services become active.
- Services inside a module cannot activate if the module cannot resolve.
- A module can resolve even if none of its services activate.
The example above demonstrates a very small circle, composed of only two
classes, but a circle can compose more classes. For example, A
references B
,
B
references C
, C
references A
. Detecting and resolving such a
dependency can be complicated.
There is no general, correct way to detect and resolve circular dependencies; cases vary. However, Liferay provides tools that facilitate detecting circular dependencies and examining the DS service components involved.
-
system:check
: This Gogo shell command provides several checks, including one that detects inactive service components whose required references are unresolved. -
scr:info [component]
: Execute this Gogo shell command on an unresolved component to report its unresolved references.
Congratulations on converting your service module to use Declarative Services.