Spring 之 @Value 注解原理解析

Spring 之 @Value 注解原理解析

 

 

1、简介

       @Value注解是Spring3.0后提出来的一个注解。注解内容本身非常之简单,但是它提供的功能却非常之强大。其功能是将与配置文件中的键对应的值分配给其带注解的属性。如果我们自定义了 *.properties 配置文件,那么就可以配合@PropertySource注解来获取配置文件的配置项。@Value 注解可以用来将外部的值动态注入到 Bean 中,在 @Value 注解中,可以使${} 与 #{} ,它们的区别如下:

  1. @Value("${}"):可以获取对应属性文件中定义的属性值。
  2. @Value("#{}"):表示 SpEl 表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法。

通过配置文件进行属性注入和通过非配置文件进行属性注入。非配置文件注入的类型如下:

  1. 注入普通字符串。
  2. 书写SpEL表达式(功能强大包括:获取系统属性、调用静态方法、计算、注入bean、调用bean的方法等等~~~)。
  3. 注入文件。如:@Value("classpath:com/demo/config.txt")   使用Resource类型接收。
  4. 注入URL资源。如:@Value("http://www.baidu.com")  使用Resource类型接收。

1.1、可以利用 @Value 注入操作系统属性。

   @Value("#{systemProperties['os.name']}")
   private String osName; // 结果:Windows 10

1.2、注入调用静态方法的结果

  // 生成一个随机数
    @Value("#{ T(java.lang.Math).random() * 1000.0 }")
    private double randomNumber;

1.3、注入其他Bean的属性

    @Value("#{myBeans.name}")
    private String fromAnotherBean;

 

2、原理解析

     在前面SpringIOC原理解析的文章中了解到AbstractAutowireCapableBeanFactory#populateBean方法负责注入bean的属性,其实@Value,@Autowired等注解也是在该方法处理。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    ...
    PropertyDescriptor[] filteredPds = null;
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                // #1
                PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    if (filteredPds == null) {
                        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    }
      // 处理bean的属性值
                    pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
                        return;
                    }
                }
                pvs = pvsToUse;
            }
        }
    }
    ...
}

调用AutowiredAnnotationBeanPostProcessor 中 实现的接口InstantiationAwareBeanPostProcessor#postProcessProperties的扩展方法,这里处理@Value,@Autowired等注解。

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // #获取Class中关于属性注入相关注解的元数据
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);    
    try {
        // #完成属性注入操作
        metadata.inject(bean, beanName, pvs);   
    }
    ...
    return pvs;
}

 

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata -> buildAutowiringMetadata下面我们看最主要的部分buildAutowiringMetadata

如下代码,Spring使用反射获取字段,如果是字段被static修饰,那么在此处是会被排除,使用的是Modifier.isStatic(int mod)方法,通过反射拿到字段后,组装后加入一个名字为injectionMetadataCache的Map中,后面属性填充会直接从这个缓存中获取。

  private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
        Class<?> targetClass = clazz;

        do {
            final LinkedList<InjectionMetadata.InjectedElement> currElements =
                    new LinkedList<InjectionMetadata.InjectedElement>();

            ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
                @Override
                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                    // #1查找field是否有@Autowired,@Value等注解
                    AnnotationAttributes ann = findAutowiredAnnotation(field);
                    if (ann != null) {
                        if (Modifier.isStatic(field.getModifiers())) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("Autowired annotation is not supported on static fields: " + field);
                            }
                            return;
                        }
                        boolean required = determineRequiredStatus(ann);
                        currElements.add(new AutowiredFieldElement(field, required));
                    }
                }
            });
... // #2查找method是否有@Autowired,@Value等注解 elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); }

 

2.2、属性填充

InjectionMetadata#inject方法遍历所有的InjectedElement,调用AutowiredMethodElement,AutowiredFieldElement的inject方法,这里只关注AutowiredFieldElement#inject
InjectionMetadata#inject -> AutowiredFieldElement#inject
protected void inject(Object bean, String beanName, 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<String>(1);
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // #1根据注解元数据,解析属性值
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);   
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        ...
    }
    if (value != null) {
        // #2将解析的结果值注入到字段中
        ReflectionUtils.makeAccessible(field);  
        field.set(bean, value);
    }
}

 

DefaultListableBeanFactory#resolveDependency -> DefaultListableBeanFactory#doResolveDependency

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
        Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        Object shortcut = descriptor.resolveShortcut(this);
        if (shortcut != null) {
            return shortcut;
        }

        Class<?> type = descriptor.getDependencyType();
        // #1如果存在@Value注解,获取注解的value值
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);    
        if (value != null) {
            if (value instanceof String) {
                // #2解析@Value注解的值,如果这里使用占位符引用了配置文件,默认使用PropertyPlaceholderConfigurer#PlaceholderResolvingStringValueResolver解析配置文件中的值。
                String strVal = resolveEmbeddedValue((String) value);   
                BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                value = evaluateBeanDefinitionString(strVal, bd);
            }
            //#3默认使用SimpleTypeConverter将配置值转化为属性要求的类型
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); 
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }

        // #4解析数组、list、map等类型的依赖
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);   
        if (multipleBeans != null) {
            return multipleBeans;
        }

        // #5处理@Autowire,找到候选的bean
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); 
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        // #6存在多个候选的bean,spring要按优先级决定用哪个,较复杂,不深入。
        if (matchingBeans.size() > 1) {
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);  
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    return descriptor.resolveNotUnique(type, matchingBeans);
                }
                else {
                    return null;
                }
            }
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        }
        else {
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        if (autowiredBeanNames != null) {
            autowiredBeanNames.add(autowiredBeanName);
        }
        return (instanceCandidate instanceof Class ?
                descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

 

总结

上面对@Value的使用和原理进行了介绍,其实@Value,@Autowired,@Resource,@Inject这几个的作用都是进行属性装配,只不过他们的方式各有不同,@Value,@Autowired,@Inject是使用AutowiredAnnotationBeanPostProcessor后置处理器进行处理,@Resource则使用CommonAnnotationBeanPostProcessor后置处理器进行处理。


 

 

 

posted @ 2023-07-13 22:54  邓维-java  阅读(3138)  评论(0)    收藏  举报