1. Driving Forces
In a Spring application, injecting one bean into another bean is very common. However, sometimes it's desirable to inject a bean into an ordinary object. For instance, we may want to obtain references to services from within an entity object.
Fortunately, achieving that isn't as hard as it might look. The following sections will present how to do so using the @Configurable annotation and an AspectJ weaver.
2. The @Configurable Annotation
This annotation allows instances of the decorated class to hold references to Spring beans.
2.1. Defining and Registering a Spring Bean
Before covering the @Configurable annotation, let's set up a Spring bean definition:
@Service public class IdService { private static int count; int generateId() { return ++count; } }
This class is decorated with the @Service annotation; hence it can be registered with a Spring context via component scanning.
Here's a simple configuration class enabling that mechanism:
@ComponentScan public class AspectJConfig { }
2.2. Using @Configurable
In its simplest form, we can use @Configurable without any element:
@Configurable public class PersonObject { private int id; private String name; public PersonObject(String name) { this.name = name; } // getters and other code shown in the next subsection }
The @Configurable annotation, in this case, marks the PersonObject class as being eligible for Spring-driven configuration.
2.3. Injecting a Spring Bean into an Unmanaged Object
We can inject IdService into PersonObject, just as we would in any Spring bean:
@Configurable public class PersonObject { @Autowired private IdService idService; // fields, constructor and getters - shown in the previous subsection void generateId() { this.id = idService.generateId(); } }
However, an annotation is only useful if recognized and processed by a handler. This is where AspectJ weaver comes into play. Specifically, the AnnotationBeanConfigurerAspect will act on the presence of @Configurable and does necessary processing.
3. Enabling AspectJ Weaving
3.1. Plugin Declaration
To enable AspectJ weaving, we need the AspectJ Maven plugin first:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.11</version> <!-- configuration and executions --> </plugin>
And it requires some additional configuration:
<configuration> <complianceLevel>1.8</complianceLevel> <Xlint>ignore</Xlint> <aspectLibraries> <aspectLibrary> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </aspectLibrary> </aspectLibraries> </configuration>
The first required element is complianceLevel. A value of 1.8 sets both the source and target JDK versions to 1.8. If not set explicitly, the source version would be 1.3 and the target would be 1.1. These values are obviously outdated and not enough for a modern Java application.
To inject a bean into an unmanaged object, we must rely on the AnnotationBeanConfigurerAspect class provided in the spring-aspects.jar. Since this is a pre-compiled aspect, we would need to add the containing artifact to the plugin configuration.
Note that such a referenced artifact must exist as a dependency in the project:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.7.RELEASE</version> </dependency>
We can find the latest version of spring-aspects on Maven Central.
3.2. Plugin Execution
To instruct the plugin to weave all relevant classes, we need this executions configuration:
<executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions>
Notice the plugin's compile goal binds to the compile lifecycle phase by default.
3.2. Bean Configuration
The last step to enable AspectJ weaving is to add @EnableSpringConfigured to the configuration class:
@ComponentScan @EnableSpringConfigured public class AspectJConfig { }
The extra annotation configures AnnotationBeanConfigurerAspect, which in turn registers PersonObject instances with a Spring IoC container.
4. Testing
Now, let's verify that the IdService bean has been successfully injected into a PersonObject:
@RunWith(SpringRunner.class) @ContextConfiguration(classes = AspectJConfig.class) public class PersonUnitTest { @Test public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() { PersonObject personObject = new PersonObject("Baeldung"); personObject.generateId(); assertEquals(1, personObject.getId()); assertEquals("Baeldung", personObject.getName()); } }
5. Injecting a Bean Into a JPA Entity
From the Spring container's point of view, an entity is nothing but an ordinary object. As such, there's nothing special about injecting a Spring bean into a JPA entity.
However, since injecting into JPA entities is a typical use case, let's cover it in more detail.
5.1. Entity Class
Let's start with the entity class's skeleton:
@Entity @Configurable(preConstruction = true) public class PersonEntity { @Id private int id; private String name; public PersonEntity() { } // other code - shown in the next subsection }
Notice the preConstruction element in the @Configurable annotation: it enables us to inject a dependency into the object before it's fully constructed.
5.2. Service Injection
Now we can inject IdService into PersonEntity, similar to what we did with PersonObject:
// annotations public class PersonEntity { @Autowired @Transient private IdService idService; // fields and no-arg constructor public PersonEntity(String name) { id = idService.generateId(); this.name = name; } // getters }
The @Transient annotation is used to tell JPA that idService is a field not to be persisted.
5.3. Test Method Update
Finally, we can update the test method to indicate that the service can be injected into the entity:
@Test public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() { // existing statements PersonEntity personEntity = new PersonEntity("Baeldung"); assertEquals(2, personEntity.getId()); assertEquals("Baeldung", personEntity.getName()); }
6. Caveats
Although it's convenient to access Spring components from an unmanaged object, it's often not a good practice to do so.
The problem is that unmanaged objects, including entities, are usually part of the domain model. These objects should carry data only to be reusable across different services.
Injecting beans into such objects could tie components and objects together, making it harder to maintain and enhance the application.
7. Conclusion
This tutorial has walked through the process of injecting a Spring bean into an unmanaged object. It also mentioned a design issue associated with dependency injection into objects.
The implementation code can be found over on GitHub.