如何实现带参数的自定义Spring Boot测试注解(类似@LocalPort)?
@LocalSomePort Annotation with Protocol Parameters Great question! Let's break down how to build your custom annotation that works like @LocalPort but accepts additional protocol parameters—since @Value alone can't handle the extra enum input you need. Here are two robust, Spring-aligned approaches:
Option 1: Custom AutowireCandidateResolver (Your Initial Idea)
This is actually the official approach Spring uses for @LocalPort—so you're already on the right track! It gives you tight integration with Spring's autowiring lifecycle. Here's how to implement it:
Define your custom annotation
Start by creating the annotation itself, which accepts yourProtocolenum as a parameter:@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface LocalSomePort { Protocol value(); // Your enum: HTTP, HTTPS, etc. }Create a custom
AutowireCandidateResolver
Extend Spring'sContextAnnotationAutowireCandidateResolverand overridegetSuggestedValueto add your port lookup logic based on the protocol:public class LocalSomePortAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver { @Override @Nullable public Object getSuggestedValue(DependencyDescriptor descriptor) { LocalSomePort annotation = descriptor.getAnnotation(LocalSomePort.class); if (annotation != null) { Protocol protocol = annotation.value(); // Replace with your actual port lookup logic (e.g., from environment, server beans) String portPropertyKey = switch(protocol) { case HTTP -> "local.server.port"; case HTTPS -> "local.management.port"; // Or your custom property key default -> throw new IllegalArgumentException("Unsupported protocol: " + protocol); }; // Return the property key wrapped in ${} to let Spring resolve it, or return the direct value return "${" + portPropertyKey + "}"; } return super.getSuggestedValue(descriptor); } }Register the resolver with Spring
Configure Spring to use your resolver via a@TestConfigurationclass (for test contexts):@TestConfiguration public class LocalSomePortConfig implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext context) { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); beanFactory.setAutowireCandidateResolver(new LocalSomePortAutowireCandidateResolver()); } }Include this config in your tests with
@Import(LocalSomePortConfig.class).
Option 2: SpEL with Custom Static Logic (Lighter Alternative)
If you want a quicker implementation without overriding resolver logic, use Spring Expression Language (SpEL) paired with a meta-annotation wrapping @Value:
Create a static port resolver helper
Encapsulate your port lookup logic in a static class:public class PortResolver { public static String getPort(Protocol protocol) { // Same port lookup logic as before return switch(protocol) { case HTTP -> System.getProperty("local.server.port", "8080"); case HTTPS -> System.getProperty("local.management.port", "8443"); default -> throw new IllegalArgumentException("Unsupported protocol: " + protocol); }; } }Define your annotation as a
@Valuemeta-annotation
Use SpEL to call your static resolver method, passing the annotation's protocol parameter directly:@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Value("#{T(com.yourpackage.PortResolver).getPort(#root.annotation.value())}") public @interface LocalSomePort { Protocol value(); }This works out of the box in Spring contexts—no extra resolver registration needed.
Which Option Should You Choose?
- Option 1 is ideal if you need to integrate with Spring's bean lifecycle (e.g., handling scoped beans, or port lookup depends on bean state). It's fully aligned with how Spring implements built-in annotations like
@LocalPort. - Option 2 is perfect for simple, stateless port resolution. It's quicker to implement and requires less boilerplate.
Either approach will let you create a clean, reusable @LocalSomePort(Protocol.HTTP) annotation for your tests!
内容的提问来源于stack exchange,提问作者WonChul Heo

