Search code examples
javaspring-bootannotationsaopspring-cloud-config

How to send a variable from yml as an attribute for custom annotation in Spring Boot projects?


I want to create a custom annotation to use it in Spring Boot projects at methods from RestController. I'll use a Before Advice to create it and I want send information to the advice. I want to use a variable url and this variable can have 2 different values for 2 different environments, so I want to configure it inside cloud config on the application.yml of the service and send it as an attribute to the external library. The business logic for the annotation will be implemented in an outside library and I'll import the dependency in all the services.

@PostMapping("/test")
@Traceable(url = "${url.external}")
public Data test(@RequestBody Data data, HttpServletRequest servletRequest) {

    System.out.println("Inside controller, data = " + data);

    return data;
}

I know how to send a constant or how to send the variable data as attributes. But I don't know how to send a value from application.yml as an attribute. I want to send this variable from the application.yml of the microservice because the external library isn't a Spring Boot project and it doesn't have an application.yml. How can I do that? Thank you!


Solution

  • If your question is about evaluating a Spel variable reference programmatically in your aspect, here's what you can do:

    Aspect

    @Aspect
    @Component
    public class TraceableAspect {
        private final EmbeddedValueResolver embeddedValueResolver;
    
        public TraceableAspect(ConfigurableBeanFactory beanFactory) {
            this.embeddedValueResolver = new EmbeddedValueResolver(beanFactory);
        }
    
        // use your actual Traceable package name here
        @Before(value = "@annotation(your.package.Traceable)")
        public void handleTraceable(JoinPoint joinPoint) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Traceable traceable = method.getAnnotation(Traceable.class);
            String urlExpression = traceable.url();
            String url = embeddedValueResolver.resolveStringValue(urlExpression);
    
            // your code that uses the resolved `url` ...
        }
    }
    

    Also, you can just make your aspect implement a EmbeddedValueResolverAware interface and get the resolver injected by Spring for you instead of creating it manually.

    Just for completeness:

    Traceable annotation

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Traceable {
        String url();
    }
    

    application.yaml

    url.external: http://stackoverflow.com
    

    Make sure to have the org.springframework.boot:spring-boot-starter-aop dependency in your project if you don't have it already.