背景

在写代码的过程中需要用到一些自定义annotation,但是annotation中的String类型的value需要字符串常量,于是想是否可以将@Value所支持的表达式移植到自定义注解中。

实现

  1. Spring中的具体实现

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
} catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}

以上是核心代码

  • resolveEmbeddedValue方法用于执行${}类型的表达式,如${spring.application.name},可以实现将配置文件中的值注入到表达式中。
  • evaluateBeanDefinitionString方法用于执行#${}类型表达式,如#{T(System).currentTimeMillis()}#{systemProperties['user.name']},有一定的代码执行能力。
  1. 抽取所需要的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public <T> T contextLoads(ConfigurableBeanFactory beanFactory, String expression, Class<T> resultType) {
String s = beanFactory.resolveEmbeddedValue(expression);
T result = null;
if (Objects.nonNull(beanFactory.getBeanExpressionResolver())) {
Object evaluateResult = beanFactory.getBeanExpressionResolver().evaluate(s,
new BeanExpressionContext(beanFactory, null));
TypeConverter converter = beanFactory.getTypeConverter();
try {
result = converter.convertIfNecessary(evaluateResult, resultType);
} catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
log.error("UnsupportedOperationException", ex);
}
}
return result;
}

使用样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@SpringBootTest
@Data
class ApplicationTests {
private static final Logger log = LoggerFactory.getLogger(ApplicationTests.class);
@Autowired
private ConfigurableBeanFactory beanFactory;

@Test
void name() {
Long currentTimeMillis = contextLoads(beanFactory, "#{T(System).currentTimeMillis()}", Long.class);
System.out.println(currentTimeMillis);
}

public <T> T contextLoads(ConfigurableBeanFactory beanFactory, String expression, Class<T> resultType) {
String s = beanFactory.resolveEmbeddedValue(expression);
T result = null;
if (Objects.nonNull(beanFactory.getBeanExpressionResolver())) {
Object evaluateResult = beanFactory.getBeanExpressionResolver().evaluate(s,
new BeanExpressionContext(beanFactory, null));
TypeConverter converter = beanFactory.getTypeConverter();
try {
result = converter.convertIfNecessary(evaluateResult, resultType);
} catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
log.error("UnsupportedOperationException", ex);
}
}
return result;
}

}