SpringBoot启动报错Could not resolve placeholder ‘XXX.XXX‘ in value
SpringBoot启动项目时报错:Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException:Error creating bean with name 'msgReceiver':
SpringBoot启动项目时报错:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'msgReceiver':
Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException:
Could not resolve placeholder 'file.post-url' in value "${file.post-url}"
还是一样,遇到SpringBoot启动报错,从最后看起,很明显这里是在msgReceiver中因为找不到file.post-url的定义。
查看MsgReceiver的代码:
@Value("${file.post-url}")
private String postUrl;
问题就出在这里,这里将配置文件里的file.post-url的值赋给了postUrl,但是SpringBoot启动时,没有在配置文件application.yml(application.properties)中找到关于file:post-url:(file.post-url=)的定义。
可以去查看一下,有没有这个定义,没有加上即可。
笔者遇到的问题是,配置文件中有相关定义,但是启动依然报错。这是因为生成的Class文件中配置没有及时被更新,可以右键项目,找到Maven—>Reload Project、右键项目->Reload From Disk以及Build标签下的Build Project来刷新项目代码和重新build来解决。这三个操作是解决代码和生成的class文件不匹配的三板斧,遇到此类问题都可以这样操作。
源码解读:
我们可以再看看Value这个注解在源码中是怎么定义的:
/**
* Annotation at the field or method/constructor parameter level
* that indicates a default value expression for the affected argument.
*
* <p>Typically used for expression-driven dependency injection. Also supported
* for dynamic resolution of handler method parameters, e.g. in Spring MVC.
*
* <p>A common use case is to assign default field values using
* "#{systemProperties.myProp}" style expressions.
*
* <p>Note that actual processing of the {@code @Value} annotation is performed
* by a {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} which in turn means that you <em>cannot</em> use
* {@code @Value} within
* {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} or
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
* types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
* class (which, by default, checks for the presence of this annotation).
*
* @author Juergen Hoeller
* @since 3.0
* @see AutowiredAnnotationBeanPostProcessor
* @see Autowired
* @see org.springframework.beans.factory.config.BeanExpressionResolver
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
/**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
}
发现它是一个可以应用在Filed(成员变量)、Method(方法)、Parameter(参数)和Annotation_Type(注解类型)的运行时注解。用来指定被注解的Argument(就是前面提到的四种类型)的默认值。
关于RetentionPolicy有疑问的同学可以继续点进Retention源码:
/**
* Indicates how long annotations with the annotated type are to
* be retained. If no Retention annotation is present on
* an annotation type declaration, the retention policy defaults to
* {@code RetentionPolicy.CLASS}.
*
* <p>A Retention meta-annotation has effect only if the
* meta-annotated type is used directly for annotation. It has no
* effect if the meta-annotated type is used as a member type in
* another annotation type.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
它是定义注解类型会被保持多久。默认值是CLASS。另外只有在元注解类型被注解直接使用时,Retention注解才生效。如果元注解类型被用作另一个注解类型的成员变量,就不起作用。
元注解可以理解为注解的注解。注解的成员变量,就可以把注解看做是一个特殊的类,类就会有成员变量,比如这里的value就是Retention的一个成员变量。
那么怎么写才是注解的成员变量,怎么写才是注解直接使用呢?
成员变量就跟类的定义一样,在注解里写一个成员变量。而直接使用,就需要把这个注解写成一个单独的类,用@Interface修饰。也就是说它是一个注解的注解,也就是为什么说Retention是一个元注解了。
那么怎么这两种方式怎么使用的呢?这里举个例子:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
像这样,就是成员变量的使用方式,使用key=value的方式。
而元注解就像这样使用:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention
这里Retention就是个元注解,他是注解在注解上的,是注解的注解。
再看RetentionPolicy是怎么定义的:
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
它有三个枚举值,SOURCE、CLASS和RUNTIME。SOURCE是注解将会被编译器丢弃,CLASS是注解会 被编译器记录在Class文件中,RUNTIME值注解不仅会被记录在Class文件,还将在运行时被虚拟机保持,这样他们就可以被反射地读到。
这里就是解释了注解的生命周期,从源代码—>被编译器编译进Class文件—>运行时被虚拟机保持。
那么什么时候用哪个呢?
一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解;
如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,使用SOURCE 注解。
如果您觉得文章写得还不错,不妨点个赞。如果有什么疑惑或者不对的地方,可以留下评论,看到我会及时回复的。所有关注都会回关!
更多推荐
所有评论(0)