blob: 67a167782af5d2c2f58f33fd08e0ed0f9e9ecffa [file] [log] [blame] [view] [edit]
[//]: # " "
[//]: # " Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved. "
[//]: # " "
[//]: # " This program and the accompanying materials are made available under the "
[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
[//]: # " http://www.eclipse.org/legal/epl-2.0. "
[//]: # " "
[//]: # " This Source Code may also be made available under the following Secondary "
[//]: # " Licenses when the conditions for such availability set forth in the "
[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
[//]: # " version 2 with the GNU Classpath Exception, which is available at "
[//]: # " https://www.gnu.org/software/classpath/license.html. "
[//]: # " "
[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
[//]: # " "
* TOC
{:toc}
# The Spring/HK2 Bridge
The Spring/HK2 Bridge can be used to inject [Spring][spring] services
into HK2 services or inject HK2 services into [Spring][spring] services.
## Definitions
+ A [Spring][spring] service is a service that is instantiated (created) by [Spring][spring]
+ An HK2 service is a service that is instantiated (created) by HK2
## Injecting Spring services into HK2 services
[Spring][spring] services can be injected into any injection point in HK2.
In order to do this you must tell HK2 about the [Spring][spring] [BeanFactory][beanfactory]
which has the [Spring][spring] bean definitions. This is accomplished in two steps.
In the first step you should have the [ServiceLocator][servicelocator] that contains services
you wish to be injected with [Spring][spring] services.
You must initialize this [ServiceLocator][servicelocator] with some required Spring/HK2 bridge services.
You can do this using the utility class [SpringBridge][springbridge].
This is a code snippet that initializes a [ServiceLocator][servicelocator]:
```java
SpringBridge.getSpringBridge().initializeSpringBridge(aServiceLocator);
```
In the second step you must tell your initialized [ServiceLocator][servicelocator] about the
specific [Spring][spring] [BeanFactory][beanfactory] that you want it to look for services in.
You do this with the [SpringIntoHK2Bridge][springintohk2bridge] service that was added in the previous step.
The following code snippet adds a [Spring][spring] [BeanFactory][beanfactory] to be searched for services when injecting into HK2 services:
```java
public void tieBeanFactoryToLocator(ServiceLocator aServiceLocator, BeanFactory springFactory) {
SpringIntoHK2Bridge springBridge = aServiceLocator.getService(SpringIntoHK2Bridge.class);
springBridge.bridgeSpringBeanFactory(springFactory);
}
```
Any [Spring][spring] [BeanFactory][beanfactory] added with the bridgeSpringBeanFactory method
will be searched for services that HK2 cannot otherwise find.
For example, if you have a service called SpringService that is created by
[Spring][spring], you can inject it into an HK2 service
(called HK2Service) like this:
```java
@Service
public class HK2Service {
@Inject
private SpringService springService;
}
```
## Injecting HK2 services into Spring services
HK2 services can be injected into [Spring][spring] services. A HK2 service
can be injected into any place a normal Spring Bean can be injected. For example, if you have an HK2 service
named HK2Service that is to be injected
into a [Spring][spring] service (called SpringService) your code
could look like this:
```java
public class SpringService {
private HK2Service hk2Service;
public void setHK2Service(HK2Service hk2Service) {
this.hk2Service = hk2Service;
}
}
```
All HK2 services are in a [Spring][spring]
[Scope][scope]
that is usually named "hk2". In order to do this we have provided an implementation of
[Scope][scope]
called
SpringScopeImpl.
SpringScopeImpl
is a [Spring][spring] service
that either takes a [ServiceLocator][servicelocator] instance
or the name of a [ServiceLocator][servicelocator] as attributes.
This implementation of
[Scope][scope]
can be given to any [Spring][spring]
[ConfigurableBeanFactory][configurablebeanfactory].
The usual way this is done is in a [Spring][spring] beans.xml. The following
stanza adds a
SpringScopeImpl
for a
[ServiceLocator][servicelocator] named HK2ToSpringTest
into a [Spring][spring] beans.xml:
```xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="hk2">
<bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
<property name="ServiceLocatorName" value="HK2ToSpringTest" />
</bean>
</entry>
</map>
</property>
</bean>
```
An HK2 service is then defined by adding it to the [Spring][spring] beans.xml by setting its scope to "hk2".
The id of the HK2 service is formatted as per the utility API BuilderHelper.createTokenizedFilter.
In the usual case this means that the id of the bean is the [Contract][contract] to be
looked up (though other things can be specified such as name or qualifiers).
The following is an example [Spring][spring] beans.xml stanza that defines an HK2 service.
```xml
<bean id="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
class="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
scope="hk2" />
```
In the stanza above the scope was set to "hk2," implying that the HK2 SpringScopeImpl
will be used to lookup the bean.
This bean can then be injected into any other [Spring][spring] bean in the normal way.
The following stanza injects HK2Service into SpringService:
```xml
<bean id="SpringService" class="org.jvnet.hk2.spring.bridge.test.hk2tospring.SpringService">
<property name="HK2Service" ref="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service" />
</bean>
```
To make it clear, the following is the entire [Spring][spring] beans.xml which injects HK2Service into SpringService:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="hk2">
<bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
<property name="ServiceLocatorName" value="HK2ToSpringTest" />
</bean>
</entry>
</map>
</property>
</bean>
<bean id="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
class="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service"
scope="hk2" />
<bean id="SpringService" class="org.jvnet.hk2.spring.bridge.test.hk2tospring.SpringService">
<property name="HK2Service" ref="org.jvnet.hk2.spring.bridge.test.hk2tospring.HK2Service" />
</bean>
</beans>
```
## Bi-Directional Spring/HK2 Bridge
When using Spring and HK2 bi-directionally it is important to remember that Spring instantiates beans
as soon as they are satisfied (i.e., as soon as all references are available). This can make bootstrapping
difficult as Spring may try to instantiate beans before they are available in HK2. In order to avoid this
you can use any of the methods Spring allows for lazy initialization. The following Spring beans.xml
file uses the lazy-init mechanism for this, but other mechanism (such as the use of proxies) can
also be used.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="hk2">
<bean class="org.jvnet.hk2.spring.bridge.api.SpringScopeImpl" >
<property name="ServiceLocatorName" value="BiDirectionalSpringBridge" />
</bean>
</entry>
</map>
</property>
</bean>
<bean id="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0"
class="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0"
scope="hk2"
lazy-init="true"/>
<bean id="SpringService1_1"
class="org.jvnet.hk2.spring.bridge.test.bidirectional.SpringService1_1"
lazy-init="true">
<property name="HK2Service1_0" ref="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_0" />
</bean>
<bean id="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2"
class="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2"
scope="hk2"
lazy-init="true"/>
<bean id="SpringService1_3"
class="org.jvnet.hk2.spring.bridge.test.bidirectional.SpringService1_3"
lazy-init="true">
<property name="HK2Service1_2" ref="org.jvnet.hk2.spring.bridge.test.bidirectional.HK2Service1_2" />
</bean>
</beans>
```
In the above example SpringService1_3 has HK2Service1_2 injected into it, while HK2Service1_2 has
SpringService1_1 injected into it (though you can't see that in the XML stanza), and SpringService1_1
has HK2Service1_0 injected into it. Since everything in the beans.xml is lazily initialized you
can start the Spring BeanFactory either before or after you initialize the ServiceLocator.
[spring]: http://www.springsource.com/
[beanfactory]: http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html
[servicelocator]: apidocs/org/glassfish/hk2/api/ServiceLocator.html
[contract]: apidocs/org/jvnet/hk2/annotations/Contract.html
[configurablebeanfactory]: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/config/ConfigurableBeanFactory.html
[scope]: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/factory/config/Scope.html
[springintohk2bridge]: apidocs/org/jvnet/hk2/spring/bridge/api/SpringIntoHK2Bridge.html
[springbridge]: apidocs/org/jvnet/hk2/spring/bridge/api/SpringBridge.html