[英]Interface Annotation does not accept application.properties value
我開發了一個簡單的注釋界面
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String foo() default "foo";
}
然后我測試它注釋一個類
@CustomAnnotation
public class AnnotatedClass {
}
並使用方法調用它
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
logger.info(customAnnotation.foo());
}
並且一切正常,因為它記錄了foo 。 我也嘗試將注釋類更改為@CustomAnnotation(foo = "123")
,所有工作都正常,因為它記錄了123 。
現在我希望application.properties
檢索傳遞給注釋的值,所以我已將注釋類更改為
@CustomAnnotation(foo = "${my.value}")
public class AnnotatedClass {
}
但現在日志返回String ${my.vlaue}
而不是application.properties
的值。
我知道可以在注釋中使用${}
指令,因為我總是使用像@RestController
這樣的@GetMapping(path = "${path.value:/}")
並且一切正常。
我在Github存儲庫上的解決方案: https : //github.com/federicogatti/annotatedexample
您不能直接執行某些操作,因為annotation attribute's value must be a constant expression.
您可以做的是,您可以將foo值作為字符串傳遞給@CustomAnnotation(foo = "my.value")
並創建建議AOP以獲取注釋字符串值並在應用程序屬性中查找。
創建AOP與@Pointcut
, @AfterReturn
或提供他人匹配@annotation
,方法等,並寫你的邏輯來查找屬性對應的字符串。
在主應用程序上配置@EnableAspectJAutoProxy
或按配置類進行設置。
添加aop依賴項: spring-boot-starter-aop
使用切入點創建@Aspect
。
@Aspect public class CustomAnnotationAOP { @Pointcut("@annotation(it.federicogatti.annotationexample.annotationexample.annotation.CustomAnnotation)") //define your method with logic to lookup application.properties
在官方指南中查看更多內容: 使用Spring進行面向方面編程
首先,我想向您展示一個不使用Spring Boot自動配置工具的獨立應用程序。 我希望你能體會到Spring為我們做了多少。
我們的想法是使用StringValueResolver
設置ConfigurableBeanFactory
,它將了解我們的上下文(特別是application.yaml
屬性)。
class Application {
public static void main(String[] args) {
// read a placeholder from CustomAnnotation#foo
// foo = "${my.value}"
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String foo = customAnnotation.foo();
// create a placeholder configurer which also is a properties loader
// load application.properties from the classpath
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("application.properties"));
// create a factory which is up to resolve embedded values
// configure it with our placeholder configurer
ConfigurableListableBeanFactory factory = new DefaultListableBeanFactory();
configurer.postProcessBeanFactory(factory);
// resolve the value and print it out
String value = factory.resolveEmbeddedValue(foo);
System.out.println(value);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
String foo() default "foo";
}
@CustomAnnotation(foo = "${my.value}")
class AnnotatedClass {}
現在,我將演示如何在Spring Boot應用程序中執行此操作。
我們將注入ConfigurableBeanFactory
(已經配置)並解析與前一個片段類似的值。
@RestController
@RequestMapping("api")
public class MyController {
// inject the factory by using the constructor
private ConfigurableBeanFactory factory;
public MyController(ConfigurableBeanFactory factory) {
this.factory = factory;
}
@GetMapping(path = "/foo")
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String foo = customAnnotation.foo();
// resolve the value and print it out
String value = factory.resolveEmbeddedValue(foo);
System.out.println(value);
}
}
我不喜歡在業務邏輯代碼中混合使用BeanFactory
等低級Spring組件,因此我強烈建議我們將類型縮小為StringValueResolver
並將其注入。
@Bean
public StringValueResolver getStringValueResolver(ConfigurableBeanFactory factory) {
return new EmbeddedValueResolver(factory);
}
要調用的方法是resolveStringValue
:
// ...
String value = resolver.resolveStringValue(foo);
System.out.println(value);
我們可以編寫一個基於接口類型生成代理的方法; 它的方法將返回已解析的值。
這是該服務的簡化版本。
@Service
class CustomAnnotationService {
@Autowired
private StringValueResolver resolver;
public <T extends Annotation> T getAnnotationFromType(Class<T> annotation, Class<?> type) {
return annotation.cast(Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{annotation},
((proxy, method, args) -> {
T originalAnnotation = type.getAnnotation(annotation);
Object originalValue = method.invoke(originalAnnotation);
return resolver.resolveStringValue(originalValue.toString());
})));
}
}
注入服務並按如下方式使用它:
CustomAnnotation customAnnotation = service.getAnnotationFromType(CustomAnnotation.class, AnnotatedClass.class);
System.out.println(customAnnotation.foo());
確保Annotated Class具有@Component
注釋以及@CustomAnnotation(foo = "${my.value}")
,然后Spring會將此類識別為Spring組件,並進行必要的配置以插入值。
您可以使用ConfigurableBeanFactory.resolveEmbeddedValue
解決${my.value}
進入application.properties值。
@CustomAnnotation(foo="${my.value}")
@lombok.extern.slf4j.Slf4j
@Service
public class AnnotatedClass {
@Autowired
private ConfigurableBeanFactory beanFactory;
public void foo() {
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String fooValue = customAnnotation.foo().toString();
String value = beanFactory.resolveEmbeddedValue(fooValue);
log.info(value);
}
}
如果您還想解析表達式,則應考慮使用EmbeddedValueResolver
。
EmbeddedValueResolver resolver = new EmbeddedValueResolver(beanFactory);
final String value = resolver.resolveStringValue(fooValue);
您可以查看Spring的RequestMappingHandlerMapping
以了解它們是如何做到的,它使用的是EmbeddedValueResolver
。 您可以將bean工廠注入任何spring組件,然后使用它來構建自己的解析器:
@Autowired
public void setBeanFactory(ConfigurableBeanFactory beanFactory)
{
this.embeddedValueResolver = new EmbeddedValueResolver(beanFactory);
CustomAnnotation customAnnotation = AnnotatedClass.class.getAnnotation(CustomAnnotation.class);
String fooValue = customAnnotation.foo();
System.out.println("fooValue = " + fooValue);
String resolvedValue = embeddedValueResolver.resolveStringValue(fooValue);
System.out.println("resolvedValue = " + resolvedValue);
}
假設您在屬性中設置了foo.value=hello
,輸出將如下所示:
fooValue = ${foo.value}
resolvedValue = hello
我使用Spring Boot 2.0.2測試了它,它按預期工作。
請記住,這是一個最小的例子。 您可能希望處理類上缺少注釋和缺少已解析值的錯誤情況(如果未設置該值且沒有默認值)。
要從application.propertie
讀取屬性,需要定義PropertyPlaceholderConfigurer
並將其映射到屬性文件。
基於XML的配置:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations" value="classpath:application.properties" />
</bean>
對於基於注釋:可以使用如下:
@Configuration
@PropertySource(
value{"classpath:properties/application.properties"},ignoreResourceNotFound=true)
public class Config {
/**
* Property placeholder configurer needed to process @Value annotations
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.