
spring在读取yml、properties等文件中的配置时,可直接使用@Value注解。而且@Value除了支持String,int等类型的数据,还支持数组、Map、bean多种类型数据注入,应用起来非常方便。不过在使用这个注解的过程中也有需要注意的点。
其中一点就是静态属性的注入时机,如果使用方法不当,静态属性无法完成属性注入;第二点是需要用什么方式,才可以在在静态方法、静态代码块中获取配置的属性信息。
本文主要是对@Value在这两种情况下的使用进行说明,同时简单讲解一下@Value注入的原理。
在进入@Value使用介绍之前,先说下对于包含静态方法、静态代码块、@PostConstruct方法、默认构造方法的代码执行顺序是什么样的。
static修饰的静态方法、代码块、静态属性,都是在类加载的时候就进行加载,并且静态代码块是会主动执行,静态方法可以直接通过类名引用。类实例是在类加载之后才进行的。
下边的例子针对静态代码块和默认构造参数、PostConstruct方法进行对比,根据输出结果可知静态代码块是先加载,然后是默认构造函数,最后才是PostConstruct修饰的方法。所以如果想要在静态代码块中使用spring注入的属性需要做些特殊处理(后边会讲到)。
@Component
public class FuncLoadOrderService {
//记录每个步骤执行的顺序
private static AtomicLong step = new AtomicLong(0);
//默认构造参数
public FuncLoadOrderService() {
System.out.println("construct run step " + step.getAndIncrement());
}
//静态代码块
static {
System.out.println("static block run step " + step.getAndIncrement());
}
//
@PostConstruct
public void constructFunc() {
System.out.println("PostConstruct run step " + step.getAndIncrement());
}
}
结果输出
static block run step 0
construct run step 1
PostConstruct run step 2
此处说的普通属性为非static变量,类似如下声明
//这就是一个普通属性
private String testConfigId;
结论先行。对于这种注入方式,由于实例是在默认构造参数执行之后才会创建,且方法加载顺序为 静态代码块 --> 静态方法 --> 默认构造参数 --> PostConstruct修饰的方法。
所以普通属性使用@Value注入的变量,只有在PostConstruct修饰的方法可以取到值,即只有对象bean完成了初始化才可以获取到配置值。
想在PostConstruct前的几步中取到值需要直接读取配置文件,加载内容。
属性的普通注入方式如下,直接使用@Value注解就可以注入配置文件中的配置。
@Value("${test.configId}")
private String testConfigId;
这种方式是工作中比较常用的注入方式了,但是因为value是在类实例创建之后才注入的,所以这里有两个注意点
1.默认构造参数中无法获取注入的value
2.static修饰的方法无法获取注入的value
如果一定想要在默认构造参数里获取@Value注入的值怎么办呢?想一想为什么默认构造参数无法使用@Value注入的值~~
是不是因为此时bean还没有创建,类对象还没有实例化,所以所有依赖Bean创建方式来注入值的方式都不可以使用,因此可以考虑直接读取配置文件来获取值。
这里提供两种方式。第一种是使用 YamlMapFactoryBean 将配置文件的内容读到map中
YamlMapFactoryBean yaml = new YamlMapFactoryBean();
yaml.setResources(new ClassPathResource("application.yml"));
Map<String, Object> configMap = yaml.getObject();
这个方式可以达到目的,但是数据是嵌套的map,没有按照key展开,使用起来不是很方便。
比如配置文件中内容如下,如果用YamlMapFactoryBean方式读配置,只能先 map.get(“test”) 获取返回值,进行类型转换,再做其他处理
test:
configId: 1oiieuu
configMap: "{\"id\":123,\"key\":12333}"
第二种方式和第一个类似,只不过读出来的数据是按照key做了展开。这种方式使用的是 YamlPropertiesFactoryBean 。
YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("application.yml"));
Properties properties = yamlProperties.getObject();
对于上边提到的同样的配置,如果想读configId,可以直接使用 properties.get(“test.configId”) / properties.getProperty(“test.configId”) 等方法。
使用 YamlPropertiesFactoryBean 的方式最接近直接用@Value,强烈推荐!!
对于static代码块和默认构造方法,想要获取配置文件中的值,只能通过将配置文件读到内存,转成map、properties等方法(也可以转json、yaml等),来获取值。
上边讲到普通的变量使用@Value修饰,在静态方法中获取不到值,是因为静态方法中想要获取普通变量,需要用new来创建对象,new出来的对象没有注入@Value,所以想要在静态方法中使用@Value修饰的对象,需要把对象定义为static类型,代码如下
private static String configId;
@Value("${test.configId}")
public void setConfigId(String configId) {
FuncLoadOrderService.configId = configId;
}
public static void staticFuncTest() {
System.out.println("static func " + FuncLoadOrderService.configId);
}
上边我们先给出了在不同情况下使用@Value会出现的情况,并给出了解决方案,下边我们来简单看下@Value为什么有些情况不能完成value注入。
结论先行。
对于@Value修饰的属性、方法,在底层处理的时候和@Autowired处理逻辑是一样的。
处理逻辑是由AutowiredAnnotationBeanPostProcessor类的内部类,AutowiredFieldElement 和 AutowiredMethodElement进行具体逻辑处理。
其中AutowiredFieldElement处理使用了相关注解的属性
AutowiredMethodElement处理使用了相关注解的方法
以属性处理链路为例,给出处理逻辑的调用链
AutowiredFieldElement#inject#resolveDependency#doResolveDependency#convertIfNecessary
//AutowiredAnnotationBeanPostProcessor默认构造方法,把此类可以处理的类型加入到列表中,从代码中可以看到这个类可以处理 @Value、@Autowired、@Inject三种注解
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
内部类AutowiredFieldElement的关键源码如下。
从源码中可看到处理逻辑都是针对bean来进行处理,而static修饰的方法、属性,是在bean中获取不到,所以static属性使用@Value无法注入对应的值。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
//在缓存中,直接进行属性处理
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
//设置属性信息
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
//加入缓存处理
}
}
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
AutowiredMethodElement和AutowiredFieldElement一样,都是继承自 InjectionMetadata.InjectedElement,所以对于使用@Value的方法能够完成注入。
下边这段代码中configId能够注入,就是因为@Value使用在方法上,在bean加载时会将configId装配到bean中。
private static String configId;
@Value("${test.configId}")
public void setConfigId(String configId) {
FuncLoadOrderService.configId = configId;
}
public static void staticFuncTest() {
System.out.println("static func " + FuncLoadOrderService.configId);
}
@Value是平时用的比较多的注解,使用时也会遇到某些情况注入失败,所以进行了一番了解并将了解的结论分享给大家,文章中可能会有错误,欢迎大家指出。
在查文档时看到了@Value的很多用法,有些是平时基本没有基础的很是新奇,下一篇和大家分享一下。
很长时间没有写过文章了,写起来不太顺手,有语句不通的地方也欢迎指出,定改!
java静态方法加载顺序详解
@value 注入静态属性_为什么 @Value 可以获取配置中心的值
Spring @Value 注解赋值
Spring深入解析 bean 的属性填充 二 AutowiredFieldElement#inject
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.
在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型
转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev
我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我
如thisanswer中所述,Array.new(size,object)创建一个数组,其中size引用相同的object。hash=Hash.newa=Array.new(2,hash)a[0]['cat']='feline'a#=>[{"cat"=>"feline"},{"cat"=>"feline"}]a[1]['cat']='Felix'a#=>[{"cat"=>"Felix"},{"cat"=>"Felix"}]为什么Ruby会这样做,而不是对object进行dup或clone? 最佳答案 因为那是thedocumenta
假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum
我的rails3.1.6应用程序中有一个自定义访问器方法,它为一个属性分配一个值,即使该值不存在。my_attr属性是一个序列化的哈希,除非为空白,否则应与给定值合并指定了值,在这种情况下,它将当前值设置为空值。(添加了检查以确保值是它们应该的值,但为简洁起见被删除,因为它们不是我的问题的一部分。)我的setter定义为:defmy_attr=(new_val)cur_val=read_attribute(:my_attr)#storecurrentvalue#makesureweareworkingwithahash,andresetvalueifablankvalueisgiven
我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li