spring自定义@value注解

b1uwtaje  于 2021-06-30  发布在  Java
关注(0)|答案(1)|浏览(526)

我想创建一个自定义接口来注入这样的属性。。。

interface Property<T> { T get(); }

然后我想设置 get() 使用自定义注解调用,如。。。

@interface Key { String name(); String fallback() default ""; }

然后在我的应用程序中使用这个,比如。。。

@key(name = "my.string.property", fallback = "some default value")
Property<String> myStringProperty;

@key(name = "my.number.property", fallback = "1")
Property<Integer> myNumberProperty;

我们之所以要这样做,而不是使用 @Value 注解是用一个新的propertychanged事件将这些对象挂接到预先存在的系统事件中,该事件可以更新 get() 方法(我们还将在运行分布式系统时持久化这些更新,该系统可以随时创建新节点),并且还将在我们的uis系统管理页面中公开这些属性。
我已经设法让这项工作的领域与我的自定义注解注解使用 ReflectionUtils#doWithFields 从我自己实现的 BeanPostProcessor#postProcessBeforeInitialization . 这更像是一种黑客行为,因为spring做了所有的注入,然后我们通过反射来更新字段,所以在注解构造函数param时这不起作用。我用了这个指南,https://www.baeldung.com/spring-annotation-bean-pre-processor.
所以我的问题是,有没有一种方法可以为spring实现一个工厂对象,在这里我可以编写代码来读取注解,并在此基础上注入一个实现,这样我就不需要使用反射,无论我在哪里注入,它都能工作,因为它将是springs正常注入生命周期的一部分?

eivgtgni

eivgtgni1#

因此,我找到了一种方法来实现beandefinitionregistrypostprocessor,使用org.reflections lib查找属性对象上的所有键注解。
然后,我可以为每个键创建一个自定义bean定义,然后使用该键作为限定符进行注册,以允许spring注入我的所有属性对象。
所以第一件事就是将限定符注解添加到我的键注解中。

@Qualifier
@interface Key {
  String name();
  String fallback() default "";
}

下一步是创建beandefinitionregistrypostprocessor接口的实现,它将bean定义注册到要在运行时注入的属性接口的具体实现、构造函数参数和通过使用反射扫描包找到的键注解中的限定符
(这是将反射的使用从在我的bean中设置对象改为仅使用它来动态查找键/属性并使其可供注入的关键)

@Component
public class PropertyBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    Reflections reflections = new Reflections(ClasspathHelper.forPackage("com.package.to.scan"),
            new FieldAnnotationsScanner(), new MethodParameterScanner());

    registerBeansForConstructors(registry, reflections.getConstructorsWithAnyParamAnnotated(Key.class));
    registerBeansForMethods(registry, reflections.getMethodsWithAnyParamAnnotated(Key.class));
    registerBeansForFields(registry, reflections.getFieldsAnnotatedWith(Key.class));
}

private void registerBeansForFields(BeanDefinitionRegistry registry, Set<Field> fields) {
    for (Field field : fields) {
        Class<?> parameterType = field.getType();
        Annotation[] annotations = field.getDeclaredAnnotations();
        Type genericType = field.getGenericType();

        registerBeanIfPropertyType(registry, parameterType, genericType, annotations);
    }
}

private void registerBeansForMethods(BeanDefinitionRegistry registry, Set<Method> methods) {
    for (Method method : methods) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Annotation[][] annotations = method.getParameterAnnotations();
        Type[] genericTypes = method.getGenericParameterTypes();

        registerBeansForParameters(registry, parameterTypes, annotations, genericTypes);
    }
}

private void registerBeansForConstructors(BeanDefinitionRegistry registry, Set<Constructor> constructors) {
    for (Constructor constructor : constructors) {
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        Annotation[][] annotations = constructor.getParameterAnnotations();
        Type[] genericTypes = constructor.getGenericParameterTypes();

        registerBeansForParameters(registry, parameterTypes, annotations, genericTypes);
    }
}

private void registerBeansForParameters(BeanDefinitionRegistry registry, Class<?>[] parameterTypes, Annotation[][] annotations, Type[] genericTypes) {
    for (int i = 0; i < parameterTypes.length; i++) {
        Class<?> parameterType = parameterTypes[i];
        Annotation[] parameterAnnotations = annotations[i];
        Type genericType = genericTypes[i];

        registerBeanIfPropertyType(registry, parameterType, genericType, parameterAnnotations);
    }
}

private void registerBeanIfPropertyType(BeanDefinitionRegistry registry, Class<?> parameterType, Type genericType, Annotation[] parameterAnnotations) {
    if (!Property.class.isAssignableFrom(parameterType)) {
        return;
    }

    Arrays.stream(parameterAnnotations)
            .filter(annotation -> Key.class.isAssignableFrom(annotation.annotationType()))
            .map(Key.class::cast)
            .findFirst()
            .ifPresent(key -> register(registry, key, genericType));
}

private void register(BeanDefinitionRegistry registry, Key key, Type type) {
    registry.registerBeanDefinition(key.name(), createDefinition(key, type));
    log.info("registered property: {}", key);
}

public static BeanDefinition createDefinition(Key key, Type type) {
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(PropertyImpl.class);
    beanDefinition.setConstructorArgumentValues(createConstructorArgumentValues(key, type));
    beanDefinition.addQualifier(createAutowireCandidateQualifier(key));
    return beanDefinition;
}

private static AutowireCandidateQualifier createAutowireCandidateQualifier(Key key) {
    AutowireCandidateQualifier autowireCandidateQualifier = new AutowireCandidateQualifier(Key.class);
    autowireCandidateQualifier.setAttribute("name", key.name());
    autowireCandidateQualifier.setAttribute("fallback", key.fallback());
    return autowireCandidateQualifier;
}

private static ConstructorArgumentValues createConstructorArgumentValues(Key key, Type type) {
    ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
    constructorArgumentValues.addIndexedArgumentValue(1, key);
    constructorArgumentValues.addIndexedArgumentValue(2, getPropertyType(type));
    return constructorArgumentValues;
}

private static Class<?> getPropertyType(Type type) {
    if (!(type instanceof ParameterizedType)) {
        throw new RuntimeException("field " + type.getTypeName() + " is not parameterised");
    }

    ParameterizedType parameterizedType = (ParameterizedType) type;
    Type[] actualGenericTypeArguments = parameterizedType.getActualTypeArguments();

    if (actualGenericTypeArguments.length != 1) {
        throw new RuntimeException("invalid number of generics: " + Arrays.stream(actualGenericTypeArguments).map(Type::getTypeName).collect(Collectors.toList()));
    }

    return TypeToken.of(actualGenericTypeArguments[0]).getRawType();
}

最后,我发现这对我来说是可行的,但是可能有更好的方法来创建一个spring可以触发的工厂,而不是使用反射库,因为这不是最快的

相关问题