I have a Spring Boot 2.7.6 application. I have some entities that are managed via various Mongo repositories. I would like to extend my entities with metadata. So I thought I'd build a metadata repository that contains the metadata for all entities and extend the entities with an interface IMetadata that contains a default implementation. This method should then load the data from the metadata repository using an aspect and then return it.
This is what my classes look like:
package io.zzzz.interfaces;
//imports are there
public interface IMetadata {
@JsonIgnore
default List<Metadata> getMetadata() {
return null;
}
package io.zzzz.entities;
//imports are there
@Getter
@Setter
@NoArgsConstructor
@Configurable
@Document("samples")
public class SampleEntity implements IMetadata {
@Id private String id;
// ... many other fields
package io.zzzz;
//imports are there
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass=true)
@Slf4j
public class MetadataAspect implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
metadataService = context.getBean(MetadataService.class);
}
@Around("execution(* getMetadata(..))")
public Object getMetadataAdvice(ProceedingJoinPoint joinPoint) {
log.debug("Accessing metadata ...");
var metadataOwner = joinPoint.getTarget();
var md = metadataService.getMetadataForEntity((IMetadata) metadataOwner);
log.debug("Accessed metadata.");
return md;
}
}
I have added the following dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.3.24</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
I added @EnableLoadTimeWeaving to my application and also added a javaagent to the VM Options -javaagent:<path>/spring-instrument-5.3.24.jar.
Because none of this worked, I created the following aop.xml in META-INF:
<aspectj>
<weaver>
<include within="io.zzzz.entities..*"/>
</weaver>
<aspects>
<aspect name="io.zzzz.MetadataAspect"/>
</aspects>
</aspectj>
I added the VM option -Daj.weaving.verbose=true to extend the logging during startup to see if something happened. I see some weavering, so that should at least basically work. If I formulate the aspect and aop-config a bit more generally, then I also see that, for example, service methods are called and passed through the aspect, but my getMetadata method is not.
I don't know what to do. Maybe my idea won't work at all.
No, it is still unmanaged without any
@Component,@Serviceor similar Spring component annotations. As the name implies, it simply makes an unmanaged object configurable, i.e. you can inject Spring beans into it using@Autowired. See this Baeldung tutorial and of course the Spring documentation for more information:Please note that you need to activate native Aspect weaving in combination with the
spring-aspectsaspect library in order for@Configurableto have any effect viaAnnotationBeanConfigurerAspect.Update: I forgot to mention, that if you want to apply AOP to non-managed classes, you have a choice to either make them managed as mentioned before or to use native AspectJ, which has no such Spring-related limitations and can be applied to POJO code, with or without Spring. AspectJ also does not require or create any proxies.