我已经开发了一个简单的 注释 界面
@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 {
}
,但是现在日志返回字符串${my.vlaue}
,而不返回application.properties
中的值。
我知道可以在注释中使用${}
指令,因为我总是使用@RestController
这样的@GetMapping(path = "${path.value:/}")
并且一切正常。
我在Github存储库上的解决方案:https://github.com/federicogatti/annotatedexample
答案 0 :(得分:2)
您不能直接作为annotation attribute's value must be a constant expression.
您可以做的是,可以将foo值作为@CustomAnnotation(foo = "my.value")
之类的字符串传递,并创建建议AOP以获取注释字符串值并在应用程序属性中查找。
使用@Pointcut
,@AfterReturn
创建AOP或提供其他匹配@annotation
,方法等的AOP,并将逻辑写入相应字符串的lookup属性。
在主应用程序上配置@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
在官方指南中查找更多内容:Aspect Oriented Programming with Spring
答案 1 :(得分:2)
首先,我想向您展示一个不使用Spring Boot自动配置功能的独立应用程序。希望您能体会到Spring为我们所做的工作。
我们的想法是使用ConfigurableBeanFactory
设置StringValueResolver
,它将了解我们的上下文(尤其是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());
答案 2 :(得分:0)
确保带注释的类具有 <ItemGroup>
<None Remove="ClientApp\components\componentname\componentname.ts" />
</ItemGroup>
和@Component
注释,然后Spring会将此类识别为Spring组件,并进行必要的配置以将值插入其中。
答案 3 :(得分:0)
您可以使用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);
答案 4 :(得分:0)
您可以查看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对此进行了测试,它按预期工作。
请记住,这是一个最小的示例。您可能要处理类缺少注释和解析值(如果未设置值且没有默认值)的错误情况。
答案 5 :(得分:0)
要从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();
}
}