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 注解。

如果您觉得文章写得还不错,不妨点个赞。如果有什么疑惑或者不对的地方,可以留下评论,看到我会及时回复的。所有关注都会回关!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐