欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring 自定义让@Value被解析到

程序员文章站 2022-06-24 19:41:47
目录spring 自定义让@value解析到spring4自定义@value功能演示@value的用法spring 自定义让@value解析到@value 可以给字段赋值背景@value通常与@pro...

spring 自定义让@value解析到

@value 可以给字段赋值

背景

@value通常与@propertysource(value = “db.properties”) 组合使用读取配置注入参数,那如果我们的值是其它存储,如何才能自动赋值

实现原理

实现很简单

//自动注入此对象 
 @autowired
    private environment environment;
    @postconstruct
    public void init() {
       
       //拿到些对象
        mutablepropertysources propertysources = ((configurableenvironment) this.environment).getpropertysources();
        propertysourcefactory factory = beanutils.instantiateclass(defaultpropertysourcefactory.class);
        //构造pathresource
        pathresource pathresource = new pathresource("/users/xx/soft/sp.properties");
        try {
            org.springframework.core.env.propertysource<?> sd = factory.createpropertysource("sd", new encodedresource(pathresource));
            //设置值
            propertysources.addfirst(sd);
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }

主要是通过代码得到propertysource 这个对象,然后得到environment这个对象,设置值就可以了

spring4自定义@value功能

本文章使用的spring版本4.3.10.release

@value在spring中,功能非常强大,可以注入一个配置项,可以引用容器中的bean(调用其方法),也可以做一些简单的运算

如下的一个简单demo,

演示@value的用法

import org.springframework.stereotype.service; 
/**
 * 测试bean 
 */
@service("userservice")
public class userservice {
 
 public int count() {
  return 10;
 }
 
 public int max(int size) {
  int count = count();
  return count > size ? count : size;
 }
}
import org.springframework.beans.factory.initializingbean;
import org.springframework.beans.factory.annotation.value;
import org.springframework.stereotype.component;
@component
public class apprunner implements initializingbean {
 
 /**
  * 引用一个配置项
  */
 @value("${app.port}")
 private int port;
 
 /**
  * 调用容器的一个bean的方法获取值
  */
 @value("#{userservice.count()}")
 private int usercount;
 
 /**
  * 调用容器的一个bean的方法,且传入一个配置项的值作为参数
  */
 @value("#{userservice.max(${app.size})}")
 private int max;
 
 /**
  * 简单的运算
  */
 @value("#{${app.size} <= '12345'.length() ? ${app.size} : '12345'.length()}")
 private int min;
 
 //测试
 public void afterpropertiesset() throws exception {
  system.out.println("port : " + port);
  system.out.println("usercount : " + usercount);
  system.out.println("max : " + max);
  system.out.println("min : " + min);
 }
}

app.properties

app.port=9090
app.size=3

import org.springframework.context.annotation.annotationconfigapplicationcontext;
import org.springframework.context.annotation.componentscan;
import org.springframework.context.annotation.propertysource;
@componentscan
@propertysource("classpath:app.properties")
public class app {
 
    public static void main( string[] args) {
     annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(app.class);
     context.close();
    }
}

运行,输出结果

port : 9090
usercount : 10
max : 10
min : 3

一般的用法就是这样,用于注入一个值。

那么,能否做到,我给定一个表达式或者具体的值,它能帮忙计算出表达式的值呢? 也就是说,实现一个@value的功能呢?

方法如下:

import org.springframework.beans.factory.config.beanexpressioncontext;
import org.springframework.beans.factory.config.beanexpressionresolver;
import org.springframework.beans.factory.config.configurablebeanfactory;
import org.springframework.context.expression.standardbeanexpressionresolver;
public class valueutil {
 private static final beanexpressionresolver resolver = new standardbeanexpressionresolver();
 
 /**
  * 解析一个表达式,获取一个值
  * @param beanfactory
  * @param value 一个固定值或一个表达式。如果是一个固定值,则直接返回固定值,否则解析一个表达式,返回解析后的值
  * @return
  */
 public static object resolveexpression(configurablebeanfactory beanfactory, string value) {
  string resolvedvalue = beanfactory.resolveembeddedvalue(value);
  
  if (!(resolvedvalue.startswith("#{") && value.endswith("}"))) {
   return resolvedvalue;
  }
  
  return resolver.evaluate(resolvedvalue, new beanexpressioncontext(beanfactory, null));
 }
}

具体使用如下:

import org.springframework.context.annotation.annotationconfigapplicationcontext;
import org.springframework.context.annotation.componentscan;
import org.springframework.context.annotation.propertysource;
 
@componentscan
@propertysource("classpath:app.properties")
public class app {
 
    public static void main( string[] args) {
     annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(app.class);
     //计算一个具体的值(非表达式)
     system.out.println(valueutil.resolveexpression(context.getbeanfactory(), "1121"));
     //实现@value的功能
     system.out.println(valueutil.resolveexpression(context.getbeanfactory(), "${app.port}"));
     system.out.println(valueutil.resolveexpression(context.getbeanfactory(), "#{userservice.count()}"));
     system.out.println(valueutil.resolveexpression(context.getbeanfactory(), "#{userservice.max(${app.size})}"));
     system.out.println(valueutil.resolveexpression(context.getbeanfactory(), "#{${app.size} <= '12345'.length() ? ${app.size} : '12345'.length()}"));
     context.close();
    }
}

运行输出如下:

1121
9090
10
10
3

发现已经实现了@value的功能

最后,可能有人就有疑问了,这有什么用呢?我直接用@value难道不好吗?

对于大部分场景下,的确直接用@value就可以了。但是,有些特殊的场景,@value做不了

比如说

我们定义一个注解

@retention(runtime)
@target(type)
public @interface job {
 string cron();
}

这个注解需要一个cron的表达式,我们的需求是,使用方可以直接用一个cron表达式,也可以支持引用一个配置项(把值配置到配置文件中)

比如说

@job(cron = "0 0 12 * * ?")
@job(cron = "${app.job.cron}")

这种情况@value就做不到,但是,可以用我上面的解决方案。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。