Backend Development 4 min read

Enable Placeholder Expressions in Custom SpringBoot Annotations

Learn how to create a custom SpringBoot annotation that supports ${…} placeholder expressions by adding AspectJ dependencies, defining the annotation, implementing an AOP aspect that resolves placeholders via EnvironmentAware, storing the result in a ThreadLocal, and using it in a service and controller.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Enable Placeholder Expressions in Custom SpringBoot Annotations

Environment: SpringBoot 2.3.8.RELEASE with JDK 1.8.

This tutorial shows how to enable ${...} placeholder expressions in a custom annotation within a SpringBoot application.

Dependencies

<code>&lt;dependency&gt;
    &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
    &lt;artifactId&gt;aspectjrt&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
    &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
    &lt;scope&gt;runtime&lt;/scope&gt;
&lt;/dependency&gt;
</code>

Custom Annotation

<code>@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Manufactur {
    String value() default ""; // manufacturer code
}
</code>

AOP Aspect

The aspect implements

EnvironmentAware

to obtain the

Environment

object, parses the annotation on the target method, resolves the placeholder, and stores the result in a

ThreadLocal

.

<code>@Component
@Aspect
public class ManufacturAspect implements EnvironmentAware {
    private static final Logger logger = LoggerFactory.getLogger(ManufacturAspect.class);
    private Environment environment;

    @Pointcut("@annotation(com.pack.annotation.Manufactur)")
    private void info() {}

    @Before("info()")
    public void execBefore(JoinPoint jp) {
        MethodSignature sign = (MethodSignature) jp.getSignature();
        Method method = sign.getMethod();
        Manufactur manu = method.getAnnotation(Manufactur.class);
        String value = manu.value();
        logger.info("获取到注解值:{}", value);
        BusinessService.code.set(this.environment.resolvePlaceholders(value));
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
</code>

Service Usage

<code>@Service
public class BusinessService {
    public static ThreadLocal<String> code = new ThreadLocal<>();
    private static Logger logger = LoggerFactory.getLogger(BusinessService.class);

    @Manufactur("${manufactur.code}-#{1 + 3}")
    public String invoke(String id) {
        String sno = code.get();
        logger.info("自定义注解动态获取属性值:{}", sno);
        // todo
        return sno;
    }
}
</code>

Controller Test

<code>@RestController
@RequestMapping("/business")
public class BusinessController {
    @Resource
    private BusinessService bs;

    @GetMapping("/{id}")
    public Object home(@PathVariable String id) {
        return bs.invoke(id);
    }
}
</code>

The resolved value is stored in the

ThreadLocal

by the AOP aspect and can be accessed in the service method.

With these steps, the custom annotation now supports placeholder resolution.

JavaAOPPlaceholderCustom AnnotationSpringBootenvironment
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.