SpringBoot2.动态@Value的实现方式

网友投稿 237 2022-12-29

SpringBoot2.动态@Value的实现方式

title: SpringBoot2.动态@Value实现

前言

前面文章有详细描述过各个不同阶段对于bean的扩展接口

所以今天就基于BeanPostProcessor实现Spring中的@Value注解值动态变化

基于上面也可以实现一个配置中心,比如说Apollo

具体的实现步骤分为如下几步

1.通过BeanPostProcessor取得有使用@Value注解的bean,并存储到map中

2.动态修改map中的bean字段的值

获取bean

首先写一个类实现BeanPostProcessor接口,只需要使用其中的一个函数就可以。前后都可以用来实现,并不影响最终的使用,因为咱们只是需要bean的实例。

接下来看一下具体实现代码

package com.allen.apollo;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.context.annotation.Configuration;

import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

import java.util.LinkedList;

import java.util.List;

import java.util.Set;

@Configuration

public class SpringValueProcessor implements BeanPostProcessor {

private final PlaceholderHelper placeholderHelper = new PlaceholderHelper();

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

if (beanName.equals("springValueController")) {

Class obj = bean.getClass();

List fields = findAllField(obj);

for (Field field : fields) {

Value value = field.getAnnotation(Value.class);

if (value != null) {

Set keys = placeholderHelper.extractPlaceholderKeys(value.value());

for (String key : keys) {

SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);

SpringValueCacheMap.map.put(key, springValue);

}

}

}

}

return bean;

}

private List findAllField(Class clazz) {

final List res = new LinkedList<>();

ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {

@Override

public void doWith(Field field) throws IllegalArgumentException, IllegalAccessExhttp://ception {

res.add(field);

}

});

return res;

}

}

上面的代码咱们就已经拿到了SpringValueController这个实例bean并存储到了map当中,下面看一下测试代码

/**

* cache field,存储bean 字段

*/

package com.allen.apollo;

import com.google.common.collect.LinkedListMultimap;

import com.google.common.collect.Multimap;

public class SpringValueCacheMap {

public static final Multimap map = LinkedListMultimap.create();

}

package com.allen.apollo;

import java.lang.ref.WeakReference;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.lang.reflect.Type;

import org.springframework.core.MethodParameter;

public class SpringValue {

private MethodParameter methodParameter;

private Field field;

private WeakReference beanRef;

private String beanName;

private String key;

private String placeholder;

private Class> targetType;

private Type genericType;

private boolean isjson;

public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) {

this.beanRef = new WeakReference<>(bean);

this.beanName = beanName;

this.field = field;

this.key = key;

this.placeholder = placeholder;

this.targetType = field.getType();

this.isJson = isJson;

if (isJson) {

this.genericType = field.getGenericType();

}

}

public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) {

this.beanRef = new WeakReference<>(bean);

this.beanName = beanName;

this.methodParameter = new MethodParameter(method, 0);

this.key = key;

this.placeholder = placeholder;

Class>[] paramTps = method.getParameterTypes();

this.targetType = paramTps[0];

this.isJson = isJson;

if (isJson) {

this.genericType = method.getGenericParameterTypes()[0];

}

}

public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {

if (isField()) {

injectField(newVal);

} else {

injectMethod(newVal);

}

}

private void injectField(Object newVal) throws IllegalAccessException {

Object bean = beanRef.get();

if (bean == null) {

return;

}

boolean accessible = field.isAccessible();

field.setAccessible(true);

field.set(bean, newVal);

field.setAccessible(accessible);

}

private void injectMethod(Object newVal)

throws InvocationTargetException, IllegalAccessException {

Object bean = beanRef.get();

if (bean == null) {

return;

}

methodParameter.getMethod().invoke(bean, newVal);

}

public String getBeanName() {

return beanName;

}

public Class> getTargetType() {

return targetType;

}

public String getPlaceholder() {

return this.placeholder;

}

public MethodParameter getMethodParameter() {

return methodParameter;

}

public boolean isField() {

return this.field != null;

}

public Field getField() {

return field;

}

public Type getGenericType() {

return genericType;

}

public boolean isJson() {

TVeSubTl return isJson;

}

boolean isTargetBeanValid() {

return beanRef.get() != null;

}

@Override

public String toString() {

Object bean = beanRef.get();

if (bean == null) {

return "";

}

if (isField()) {

return String

.format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass().getName(), field.getName());

}

return String.format("key: %s, beanName: %s, method: %s.%s", key, beanName, bean.getClass().getName(),

methodParameter.getMethod().getName());

}

}

package com.allen.apollo;

import com.google.common.base.Strings;

import com.google.common.collect.Sets;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.config.BeanExpressionContext;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;

import org.springframework.beans.factory.config.Scope;

import org.springframework.util.StringUtils;

import java.util.Set;

import java.util.Stack;

/**

* Placeholder helper functions.

*/

public class PlaceholderHelper {

private static final String PLACEHOLDER_PREFIX = "${";

private static final String PLACEHOLDER_SUFFIX = "}";

private static final String VALUE_SEPARATOR = ":";

private static final String SIMPLE_PLACEHOLDER_PREFIX = "{";

private static final String EXPRESSION_PREFIX = "#{";

private static final String EXPRESSION_SUFFIX = "}";

/**

* Resolve placeholder property values, e.g.

*

*

* "${somePropertyValue}" -> "the actual property value"

*/

public Object resolvePropertyValue(ConfigurableBeanFactory beanFactory, String beanName, String placeholder) {

// resolve string value

String strVal = beanFactory.resolveEmbeddedValue(placeholder);

BeanDefinition bd = (beanFactory.containsBean(beanName) ? beanFactory

.getMergedBeanDefinition(beanName) : null);

// resolve expressions like "#{systemProperties.myProp}"

return evaluateBeanDefinitionString(beanFactory, strVal, bd);

}

private Object evaluateBeanDefinitionString(ConfigurableBeanFactory beanFactory, String value,

BeanDefinition beanDefinition) {

if (beanFactory.getBeanExpressionResolver() == null) {

return value;

}

Scope scope = (beanDefinition != null ? beanFactory

.getRegisteredScope(beanDefinition.getScope()) : null);

return beanFactory.getBeanExpressionResolver()

.evaluate(value, new BeanExpressionContext(beanFactory, scope));

}

/**

* Extract keys from placeholder, e.g.

*

*

*

*

*

*

*

*

*/

public Set extractPlaceholderKeys(String propertyString) {

Set placeholderKeys = Sets.newHashSet();

if (!isNormalizedPlaceholder(propertyString) && !isExpressionWithPlaceholder(propertyString)) {

return placeholderKeys;

}

Stack stack = new Stack<>();

stack.push(propertyString);

while (!stack.isEmpty()) {

String strVal = stack.pop();

int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);

if (startIndex == -1) {

placeholderKeys.add(strVal);

continue;

}

int endIndex = findPlaceholderEndIndex(strVal, startIndex);

if (endIndex == -1) {

// invalid placeholder?

continue;

}

String placeholderCandidate = strVal.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);

// ${some.key:other.key}

if (placeholderCandidate.startsWith(PLACEHOLDER_PREFIX)) {

stack.push(placeholderCandidate);

} else {

// some.key:${some.other.key:100}

int separatorIndex = placeholderCandidate.indexOf(VALUE_SEPARATOR);

if (separatorIndex == -1) {

stack.push(placeholderCandidate);

} else {

stack.push(placeholderCandidate.substring(0, separatorIndex));

String defaultValuePart =

normalizeToPlaceholder(placeholderCandidate.substring(separatorIndex + VALUE_SEPARATOR.length()));

if (!Strings.isNullOrEmpty(defaultValuePart)) {

stack.push(defaultValuePart);

}

}

}

// has remaining part, e.g. ${a}.${b}

if (endIndex + PLACEHOLDER_SUFFIX.length() < strVal.length() - 1) {

String remainingPart = normalizeToPlaceholder(strVal.substring(endIndex + PLACEHOLDER_SUFFIX.length()));

if (!Strings.isNullOrEmpty(remainingPart)) {

stack.push(remainingPart);

}

}

}

return placeholderKeys;

}

private boolean isNormalizedPlaceholder(String propertyString) {

return propertyString.startsWith(PLACEHOLDER_PREFIX) && propertyString.endsWith(PLACEHOLDER_SUFFIX);

}

private boolean isExpressionWithPlaceholder(String propertyString) {

return propertyString.startsWith(EXPRESSION_PREFIX) && propertyString.endsWith(EXPRESSION_SUFFIX)

&& propertyString.contains(PLACEHOLDER_PREFIX);

}

private String normalizeToPlaceholder(String strVal) {

int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);

if (startIndex == -1) {

return null;

}

int endIndex = strVal.lastIndexOf(PLACEHOLDER_SUFFIX);

if (endIndex == -1) {

return null;

}

return strVal.substring(startIndex, endIndex + PLACEHOLDER_SUFFIX.length());

}

private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {

int index = startIndex + PLACEHOLDER_PREFIX.length();

int withinNestedPlaceholder = 0;

while (index < buf.length()) {

if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {

if (withinNestedPlaceholder > 0) {

withinNestedPlaceholder--;

index = index + PLACEHOLDER_SUFFIX.length();

} else {

return index;

}

} else if (StringUtils.substringMatch(buf, index, SIMPLE_PLACEHOLDER_PREFIX)) {

withinNestedPlaceholder++;

index = index + SIMPLE_PLACEHOLDER_PREFIX.length();

} else {

index++;

}

}

return -1;

}

}

package com.allen.apollo;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.util.StringUtils;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.InvocationTargetException;

@RestController

@Slf4j

public class SpringValueController {

@Value("${test:123}")

public String zax;

@Value("${test:123}")

public String test;

@Value(("${zed:zed}"))

public String zed;

@GetMapping("/test")

public String test(String a, String b) {

if (!StringUtils.isEmpty(a)) {

try {

for (SpringValue springValue : SpringValueCacheMap.map.get("test")) {

springValue.update(a);

}

for (SpringValue springValue : SpringValueCacheMap.map.get("zed")) {

springValue.update(b);

}

} catch (IllegalAccessException | InvocationTargetException e) {

e.printStackTrace();

}

}

return String.format("test: %s, zax: %s, zed: %s", test, zax, zed);

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:股票数据接口api免费(股票数据接口api免费软件)
下一篇:股票数据接口api 免费下载(股票API接口)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~