IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring populateBean注入属性-@Value @Autowired到底怎么解析的? -> 正文阅读

[Java知识库]Spring populateBean注入属性-@Value @Autowired到底怎么解析的?

目录

?前言

函数型接口

属性注入样例

????????资源环境和资源列表的实例化,资源的扫描保存

扫描配置文件,实例化资源文件

将资源实例保存到环境变量的资源文件中

?环境及资源文件列表的初始化

属性注入-populateBean

?@Value 和 @Autowired 是怎么解析的?

embeddedValueResolvers 什么时候初始化以及解析 @Value 的函数型接口什么时候放入列表中的?

?刚刚我们遍历的资源文件列表,跟我们前面存进去的资源列表是同一个吗?


?前言

?????????本文需要一些前置知识,可以观看先观看什么是 BeanDefinition? 以及 Spring 怎么创建一个 bean以及Bean工厂后置处理器之 ConfigurationClassPostProcessor- Spring 到底怎么扫描到它所需要管理的bean的?通过这两篇文章之后大概就知道 Spring 的 bean 是怎么创建出来的了,只有bean被创建出来了,才能对其进行属性赋值。

????????本文首先将 Spring 实现属性注入的核心代码给抽离出来做一个小 demo,简化其过程,主要先了解一下 Spring 注入属性的各个主要流程,然后等熟悉了主要流程之后再去看源码就会简单一些了。因为源码里用到了很多函数型接口,所以开始之前先介绍一下函数型接口。

函数型接口

? ? ? ? 顾名思义,函数型接口首先是一个接口,那么什么是函数型接口呢,就是在接口上加一个@FunctionalInterface 注解,该接口有且只有一个抽象方法,我们知道,通常函数间调用都需要传参,参数类型可以是Integer, String 或者各种自定义的 Object 的类型,但是其实参数不仅可以传Object 的类型,还可以传接口。以此可以实现对方法的抽象。

? ? ? ? 首先定义一个函数型接口

@FunctionalInterface
public interface MyFunction<Integer> {

    Integer getResult(Integer param);
}

? ? ? ? 测试方法

public class testFunction {

    public static Integer doubleInteger(Integer integer, MyFunction<Integer> myFunction) {
        return myFunction.getResult(integer);
    }

    public static void main(String[] args) {
        //实例化函数型接口,重写函数型接口的抽象方法
        MyFunction<Integer> myFunction = new MyFunction<Integer>() {
            @Override
            public Integer getResult(Integer param) {
                return param * 2;
            }
        };
        Integer doubleNum1 = doubleInteger(3, myFunction);

        //定义 lamda 表达式
        Integer doubleNum2 = doubleInteger(3, num -> num * 2);

        System.out.println(doubleNum1);
        System.out.println(doubleNum2);
        //执行结果一样,都是6
    }
}

? ? ? ? 由上可以看出,doubleInteger 的第二个参数为函数型接口类型,main 函数调用的时候,会传入一个函数型接口类型的参数,简单的话也可以传入?lamda 表达式, 传入的方法为函数型接口调用其抽象方法时真正会调用的实例方法。

属性注入样例

public class PropertyMain {

    public static final Log logger = LogFactory.getLog(PropertyMain.class);

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        //获取 Spring 上下文, Spring 项目启动的入口
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestProperty.class);

        //第一部分,获取 @PropertySource, 并拿到注解配置的一些主要信息,如文件路径,编码格式等。

        //通过类信息,获取元数据,转化为元数据之后,能够更方便的访问类基本信息,类涉及的注解信息。
        AnnotationMetadata metadata = AnnotationMetadata.introspect((Class<?>) TestProperty.class);
        //获取 @PropertySource 注解的内容
        Map<String, Object> attributeMap = metadata.getAnnotationAttributes(PropertySource.class.getName(), false);
        AnnotationAttributes currentPropertySource = new AnnotationAttributes(attributeMap);
        //获取配置文件的路径
        String[] locations = currentPropertySource.getStringArray("value");
        System.out.println(Arrays.toString(locations));

        //获取资源管理器工厂类, 默认为PropertySourceFactory.class,可以通过该工厂指定文件路径,实例化文件 Spring 中对应的资源文件对象
        Class<? extends PropertySourceFactory> factoryClass = currentPropertySource.getClass("factory");
        //获取资源工厂实例
        PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
            new DefaultPropertySourceFactory() : BeanUtils.instantiateClass(factoryClass));
        //获取指定文件的名称,默认为空
        String name = currentPropertySource.getString("name");
        //获取文件的编码规则,默认为空
        String encoding = currentPropertySource.getString("encoding");
        if (!StringUtils.hasLength(encoding)) {
            encoding = null;
        }
        if (!StringUtils.hasLength(name)) {
            name = null;
        }

        //第二部分,实例化资源文件,加入 Spring 内部的资源列表中保存

        //获取当前配置环境实例
        ConfigurableEnvironment environment = context.getEnvironment();
        //获取配置文件在 Spring 中对应的对象列表
        MutablePropertySources propertySources = environment.getPropertySources();
        //自定义资源加载器
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        for (String location : locations) {
            //获取文件路径
            String resolvedLocation = environment.resolveRequiredPlaceholders(location);
            //加载资源文件
            Resource resource = resourceLoader.getResource(resolvedLocation);
            try {
                //通过工厂的方式创建资源对象
                org.springframework.core.env.PropertySource<?> propertySource1 = factory.createPropertySource(name, new EncodedResource(resource, encoding));
                //将新加载到的资源,加入到资源列表 propertySourceList 中,Spring内部,就是靠这个列表来保存资源文件的。
                propertySources.addLast(propertySource1);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //第三部分,实例化 bean,doGetBean的前部分

        //首先实例化 bean, 即先实例化bean定义,再根据bean定义,最后通过构造器反射获取对象,这一部分简过,前序文章已经讲过
        RootBeanDefinition beanDefinition = new RootBeanDefinition(PropertyBean.class);
        //构造器反射获取实例 candidate.newInstance();
        Constructor<?>[] candidates = PropertyBean.class.getDeclaredConstructors();
        PropertyBean propertyBean = (PropertyBean) candidates[0].newInstance();
        System.out.println(propertyBean);
        //第四部分,并对其进行属性赋值

        //至此已经拿到了一个实例化的bean,但是还没有进行属性赋值,接下来就要做这件事,首先拿到所有标注了@Autowired, @Value或者@Inject注解的属性信息保存到fields列表里。
        List<Field> fields = new ArrayList<>();
        //通过反射工具类,遍历所有属性, 这里第二个参数就用到了函数型接口
        //在doWithLocalFields方法中调用fc.doWith(field)时,就会进入传入的lamda表达式函数里,
        ReflectionUtils.doWithLocalFields(PropertyBean.class, field -> {
            //findAutowiredAnnotation 用来判断,属性上是否标注了@Autowired, @Value或者@Inject注解
            MergedAnnotation<?> ann = findAutowiredAnnotation(field);
            if (ann != null) {
                //符合条件的参数,保存到 fields 中
                fields.add(field);
            }
        });
        //至此拿到了所有需要注入信息的属性
        for (Field field : fields) {

            //通过属性信息,实例化属性描述文件,用于更好的访问属性信息
            DependencyDescriptor desc = new DependencyDescriptor(field, false);
            //获取 @Value 引用的值,有兴趣可以看下 getSuggestedValue, 它指定获取 Value.class 类型的属性,就获取配置的值,如 ${test.name}
            Object value = new ContextAnnotationAutowireCandidateResolver().getSuggestedValue(desc);
            System.out.println("value:" + value);

            if (value != null) {
                if (value instanceof String) {
                    //先去除掉 ${}
                    String strValue = resolveEmbeddedValue(value);
                    //从环境中获取属性文件信息
                    //MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
                    //遍历所有的资源文件,寻找属性值
                    for (org.springframework.core.env.PropertySource<?> currentSource : propertySources) {
                        //这里就相当于从 map 里面取值,获取属性值
                        Object realValue = currentSource.getProperty(strValue);
                        if (realValue != null) {
                            System.out.println("realValue:" + realValue);
                            value = realValue;
                        }
                    }
                }
            } else {
                //这个样例项目,我只配置了@Value 和 @Autowired ,所以这里就是需要注入 bean 属性了。
                ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
                //获取属性类型,即该属性 bean 是哪个类型的
                Class<?> dependencyType = desc.getDependencyType();
                //在容器里查询该类型的 bean,因为同一个类型,可能实例化多个不同的 bean,所以用数组接收
                String[] candidateBeans = beanFactory.getBeanNamesForType(dependencyType);
                System.out.println(Arrays.toString(candidateBeans));
                //这里我们简化一下,假设只有一个候选 bean,当然实际情况确实会有多个,如果有多个的化,就获取@Qualifier指定优先获取的那个
                String beanName = candidateBeans[0];
                //获取到bean名称之后,就可以调用容器的getBean方法,来创建bean实例
                Object beanObj = beanFactory.getBean(beanName);
                value = beanObj;
            }

            //属性赋值
            if (value != null) {
                ReflectionUtils.makeAccessible(field);
                field.set(propertyBean, value);
            }
        }

        System.out.println(propertyBean);
        //至此。属性赋值过程结束。属性也都赋值成功了。
    }

    //获取 @Value 的值,简化版
    private static String resolveEmbeddedValue(Object value) {
        String newValue = (String) value;
        newValue = newValue.replace("${", "");
        newValue = newValue.replace("}", "");
        return newValue;
    }


    //判断属性上面是否标注了 @Autowired, @Value 或 @Inject
    private static MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
        //把三种类型放入列表中
        Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
        autowiredAnnotationTypes.add(Autowired.class);
        autowiredAnnotationTypes.add(Value.class);
        try {
            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) {
            logger.info(ex);
            // JSR-330 API not available - simply skip.
        }

        //获取属性上的所有注解
        MergedAnnotations annotations = MergedAnnotations.from(ao);
        //遍历刚刚填入三种了类型的列表。看看属性的注解是否包含这三个属性之一
        for (Class<? extends Annotation> type : autowiredAnnotationTypes) {
            MergedAnnotation<?> annotation = annotations.get(type);
            if (annotation.isPresent()) {
                return annotation;
            }
        }
        return null;
    }

}
//配置类
@Configuration
@ComponentScan("com.bean")
@PropertySource(value = "test.properties", name = "testProperty")
public class TestProperty {
}
@Component
public class PropertyBean {

    @Value("${test.name}")
    private String name;

    @Inject
    private PropertyBeanA propertyBeanA;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "PropertyBean{" +
            "name='" + name + '\'' +
            ", propertyBeanA=" + propertyBeanA +
            '}';
    }
}
@Service
public class PropertyBeanA {
}

? 以上代码注释已经很清楚了,非常建议大家自己多跑几次,自己观察观察效果,本人引用的依赖包为 5.2.9 的 Spring 依赖包,不过各个版本应该差距不大。我这里把 maven 依赖也都贴出来

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>


    </dependencies>

此外,为了测试,还需要加一个配置文件

?这里简单总结一下,以上代码共分为如下几个部分?

  1. 通过配置类,扫描到 @PropertySource 注解,获取到配置文件的信息,如配置文件路径,编码格式等。
  2. 加载资源文件实例化,放入 Spring 环境变量中的资源文件列表中保存。
  3. 实例化bean,不是本文的重点,所以简单提了一下
  4. 获取类信息需要注入属性值的属性值,如果是字符串类型,则去Spring环境变量的资源文件列表中遍历查询,看是否有文件包含这个属性。如果是对象类型的属性,则通过容器的getBean方法,获取到一个实例对象。获取到属性值之后,直接对属性进行赋值。

资源环境和资源列表的实例化,资源的扫描保存

扫描配置文件,实例化资源文件

? ? ? ? 从上面我们已经知道了,通过解析 @PropertySource 属性可以获得配置文件的一些关键信息,那么 Spring 是什么时候解析的呢?相信看了前面链接文章的同学已经知道了,那就是解析配置文件的时候解析的,我把解析配置文件的方法再次拿过来

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

? ? ? ? 通过注解我们可以发现,以下这段逻辑是处理 @PropertySource 注解的

// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

????????可以看到,首先会获取配置文件列表,然后逐个处理对应的配置文件,首先跟踪一下获取配置文件列表的方法 AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)?

static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
			Class<?> containerClass, Class<?> annotationClass) {

		return attributesForRepeatable(metadata, containerClass.getName(), annotationClass.getName());
	}

? ? ? ? 可以看到,返回结果是 Set 类型,但是该方法没有什么处理逻辑,而是调用了另外一个方法

static Set<AnnotationAttributes> attributesForRepeatable(
			AnnotationMetadata metadata, String containerClassName, String annotationClassName) {

		Set<AnnotationAttributes> result = new LinkedHashSet<>();

		// Direct annotation present?
        //这里第二个参数是不是很眼熟?我们在样例代码中,就是这么获取注解属性的
		addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false));

		// Container annotation present?
		Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false);
		if (container != null && container.containsKey("value")) {
			for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
				addAttributesIfNotNull(result, containedAttributes);
			}
		}

		// Return merged result
		return Collections.unmodifiableSet(result);
	}

? ? ? ? 这里我们注意到 addAttributesIfNotNull 方法的第二个参数,就是我们在样例代码中获取属性注解的方法,所以这里我们可以知道,第二个参数获取到的文件属性,会存入到第一个参数result集合里,最终返回。这里我们拿到了 @PropertySource 注解的属性信息集合,然后回到解析注解属性的方法 processPropertySource(propertySource)?

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
			catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
				// Placeholders not resolvable or resource not found when trying to open it
				if (ignoreResourceNotFound) {
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					throw ex;
				}
			}
		}
	}

? ? ? ? 相信大家看到这里,更是眼熟了,跟样例代码可以说是一毛一样,都是获取到文件的名称,编码格式,路径信息等,因为样例代码都已经加注释了,所以这里就不写注释了。然后也可以注意到,最后会遍历一个个路径,加载并实例化资源文件。那么就进入到下一步了,资源文件实例化完成之后会放入到哪里去了?可以看到调用了 addPropertySource 方法,它就是保存资源实例对象的方法。

将资源实例保存到环境变量的资源文件中

????????

private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
        //跟样例代码一样,获取环境的资源文件列表
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
        //判断是否已经加载过
		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}
        //调用addLast,或者addBefore方法,将当前扫描到的资源文件实例放入资源列表中
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}

? ? ? ? 看到这里,我们就完成了资源文件的扫描,实例化,和保存操作了。后续就可以通过遍历资源文件列表,来寻找@Value需要的属性,是否在文件里面能找到了。可是这里又有一个问题,这个保存方法,直接是从当前环境中获取到了资源文件列表,那么环境什么时候实例化的,资源文件列表又是什么时候初始化的呢?

?环境及资源文件列表的初始化

? ? ? ? 大家如果还有印象的话,实例化 Spring 上下文的时候,会调用其构造方法

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}

? ? ? ? 其实,环境及资源列表的初始化,就是在 this 里面完成的

public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

? ? ? ? 再点进去reader的构造方法

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
		this(registry, getOrCreateEnvironment(registry));
	}

? ? ? ? 这里就看到初始化环境资源的方法了

private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry instanceof EnvironmentCapable) {
			return ((EnvironmentCapable) registry).getEnvironment();
		}
		return new StandardEnvironment();
	}

? ? ? ? 这里会默认返回?StandardEnvironment 实例,这里环境资源实例化好了,那么资源列表一定在这个实例的内部,我们继续进入 StandardEnvironment?

public class StandardEnvironment extends AbstractEnvironment {
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

    public StandardEnvironment() {
    }

    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
    }
}

? ? ? ? 可以看到,customizePropertySources 方法,也是调用 addLast 方法,保存进去了两个系统资源文件,那么 customizePropertySources 方法什么时候调用的呢?一定是他的父类,继续往上跟,果然在 AbstractEnvironment 类中看到了下面这一行实例化资源文件列表的方法。

private final MutablePropertySources propertySources = new MutablePropertySources();

? ? ? ? 然后看下父类的构造方法,它调用了 customizePropertySources 方法

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}

? ? ? ? 至此为止,环境也实例化好了,资源文件列表也实例化好了,所以它才可以在解析配置文件的时候直接使用。

? ? ? ? 通过上面的介绍,我们知道怎么加载配置文件,也知道配置文件怎么保存和保存的位置了,下面正式介绍下 @Value 是怎么注入的。因为之前的文章已经讲了 doGetBean 到获取实例对象的方法,所以接下来假设当前对象已经实例化好了。就等着属性注入了。

属性注入-populateBean

? ? ? ? @Value 和 @Autowired 是怎么解析的?

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		if (bw == null) {
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
        //这里是 Spring 给用户开了一个后门,让用户自己进行属性赋值,只需要实现InstantiationAwareBeanPostProcessor 接口,并重写postProcessAfterInstantiation方法,如果该方法返回 false, 就不会在进行下面的属性赋值操作了
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						return;
					}
				}
			}
		}

		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        //在写@Autowired注解的时候,可以指定是按照name还是按照type 进行属性注入
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
			pvs = newPvs;
		}

		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    //调用后置处理器的方法来进行属性注入
					PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						if (filteredPds == null) {
							filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
						}
						pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvsToUse == null) {
							return;
						}
					}
					pvs = pvsToUse;
				}
			}
		}
		if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		if (pvs != null) {
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

? ? ? ? 从上面的分析来看,一般情况下是调用后置处理器的 postProcessProperties 方法来进行属性解析注入的。而我们本文的重点是解析@Value 和 @Autowired,那么着眼点就在AutowiredAnnotationBeanPostProcessor 这个后置处理器。那么就看下这个后置处理器解析属性的方法

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        //获取注入元数据
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
            //调用注入方法来实现属性注入
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

? ? ? ? 这个方法有两个重要方法,一是获取注入元数据,还记得文章开头的样例代码吗?属性注入,首先要获取到所有满足条件的属性信息,比如标注了@Autowired, @Value, @Inject 的注解,那么获取 InjectionMetadata 就是获取所有满足要求的属性信息列表,然后把它包装在InjectionMetadata 中,metadata.inject 就是相当于遍历属性信息,分别获取属性的属性值,然后注入该属性中。我们分别分析下这两个方法。首先看第一个方法,如何获取所有需要注入的属性信息。

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Quick check on the concurrent map first, with minimal locking.
        //尝试获取缓存,减少重复解析所耗费的时间
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        // needsRefresh 方法,为了判断是否已经解析过了这个 beanName 对应的元数据
        // 此外,如果 beanName 相同,但是类信息不同,也要重新解析
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
                    //第一次解析时,从缓存中肯定是获取不到的,所以会调用这个方法获取元数据
					metadata = buildAutowiringMetadata(clazz);
                    //放入缓存
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}
        //初始化属性列表,包括本类和父类的属性和方法
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;
        
		do {
            //保存属性和方法列表,只包含本类的信息
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
            //亲爱的,告诉我看到这个你眼熟吗?
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});

			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});

			elements.addAll(0, currElements);
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);
        //实例化一个包装实例,本质上还是属性列表 elements 
		return InjectionMetadata.forElements(elements, clazz);
	}

? ? ? ? ?相信大家看了样例代码之后,也就能很容易理解这一段了,它解析了需要自动注入的属性和方法,最终放入到数组 elements 中,并实例化一个包装类实例。至此,已经拿到了所有需要自动注入的属性信息(方法的本篇文章暂时掠过)

? ? ? ? 拿到属性信息之后,就要进入第二阶段了,解析属性值,并将属性的值符给属性。这里跟一下 第二个方法 metadata.inject(bean, beanName, pvs)。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
            //这里就是遍历刚刚第一步得到的数组信息
			for (InjectedElement element : elementsToIterate) {
				if (logger.isTraceEnabled()) {
					logger.trace("Processing injected element of bean '" + beanName + "': " + element);
				}
                //如果是属性则调用属性的解析方法,如果数需要注入的方法,则调用方法实例对应的inject方法。分别对应 AutowiredFieldElement 和 AutowiredMethodElement
				element.inject(target, beanName, pvs);
			}
		}
	}

? ? ? ? 这里我们跟一下 AutowiredFieldElement 的 inject 方法

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 || this.required) {
							this.cachedFieldValue = desc;
                            //如果有标注@DependOn注解的,则先实例化前置bean
							registerDependentBeans(beanName, autowiredBeanNames);
							if (autowiredBeanNames.size() == 1) {
								String autowiredBeanName = autowiredBeanNames.iterator().next();
								if (beanFactory.containsBean(autowiredBeanName) &&
										beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
									this.cachedFieldValue = new ShortcutDependencyDescriptor(
											desc, autowiredBeanName, field.getType());
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
				}
			}
            //通过反射,将该bean的field属性,赋值为 value
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

? ? ? ? 这个方法的开始和结束,我们都在样例中看到了,所以应该很熟悉了,所以重点在于解析获取到 value 的那一步 beanFactory.resolveDependency

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
		else {
            // 判断是否懒加载
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
                //普通属性注入逻辑
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

? ? ? ? 继续跟进

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

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

			Class<?> type = descriptor.getDependencyType();
            //这里解析@Value,获取带有${}的值
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				if (value instanceof String) {
                    //获取字符串类型属性的属性值
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
                    

					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
							converter.convertIfNecessary(value, type, descriptor.getField()) :
							converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}
            //如果需要注入的属性是集合
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}
            //解析符合 @Autowired 属性的类型
			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;

			if (matchingBeans.size() > 1) {
                //当有多个类型匹配的时候,需要获取优先级最高的类型
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
					}
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
                //如果只有一个匹配,那么获取匹配的类的 beanName
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			if (instanceCandidate instanceof Class) {
                //如果是 Class 类型,调用该方法,该方法其实 beanFactory.getBean(beanName); 如此我们便获取到了自动注入对象类型的属性值了。
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

? ? ? ? 从上面的代码,我们很容易就知道如果是 Class 类型的属性,是通过descriptor.resolveCandidate(autowiredBeanName, type, this) 来获取属性值,而该方法进去之后我们可以发现它其实就是调用getBean方法,去获取bean实例。至此 @Autowired 的属性值我们获取到了。

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
			throws BeansException {

		return beanFactory.getBean(beanName);
	}

? ? ? ?现在还查查询字符串类型的属性值没有看了,我们继续跟进 resolveEmbeddedValue 方法

public String resolveEmbeddedValue(@Nullable String value) {
		if (value == null) {
			return null;
		}
		String result = value;
		for (StringValueResolver resolver : this.embeddedValueResolvers) {
			result = resolver.resolveStringValue(result);
			if (result == null) {
				return null;
			}
		}
		return result;
	}

? ? ? ? 这里我们看到是遍历一个数组,且是函数型接口类型的数组,那么现在就有两个问题,1是这个数组的内容什么时候被添加进去的。2是当调用函数型接口的抽象方法resolver.resolveStringValue(result) 时,它真正执行的是哪个方法。

embeddedValueResolvers 什么时候初始化以及解析 @Value 的函数型接口什么时候放入列表中的?

? ? ? ? ?首先实例化很简单,实例化bean工厂时,DefaultListableBeanFactory 的父类?AbstractBeanFactory 中的对该属性在声明时直接就初始化了,如下

/** String resolvers to apply e.g. to annotation attribute values. */
	private final List<StringValueResolver> embeddedValueResolvers = new CopyOnWriteArrayList<>();

? ? ? ? 解析@Value 的函数型接口什么时候放进来的呢?先看一下 refresh 方法? ? ? ??

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
                //实例化所有还没被实例化的bean,一般我们自定义的bean都是在这里初始化
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

? ? ? ? 我们跟进实例化 bean 的 finishBeanFactoryInitialization 方法

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
        //看看看,就在这里
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}

? ? ? ? 如上所示,beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); 这一行代码,就把解析@Value 的函数型接口放进列表里了,当然,刚刚获取 @Value 属性值的时候,也是调用这个lamda表达式的方法getEnvironment().resolvePlaceholders(strVal) ,这里其实可以猜到了,因为前面我们把资源文件实例放入到环境的资源列表里了,而这里又把当前拿到了,调用了当前环境的方法,相比就是拿到当前环境的资源列表了。我们看一下这个方法

public String resolvePlaceholders(String text) {
		return this.propertyResolver.resolvePlaceholders(text);
	}

? ? ? ? 没什么逻辑,继续往下走

public String resolvePlaceholders(String text) {
		if (this.nonStrictHelper == null) {
			this.nonStrictHelper = createPlaceholderHelper(true);
		}
		return doResolvePlaceholders(text, this.nonStrictHelper);
	}

? ? ? ? 还是没什么处理逻辑,继续往下走

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
	}

? ? ? ? 这里没什么处理逻辑,但是传入了一个函数型接口,说明等会调用这个函数型接口的抽象方法的时候,会调用 getPropertyAsRawString() 方法。我们先继续往下走

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
		Assert.notNull(value, "'value' must not be null");
		return parseStringValue(value, placeholderResolver, null);
	}

? ? ? ? 还是没处理逻辑,继续走

protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {

		int startIndex = value.indexOf(this.placeholderPrefix);
		if (startIndex == -1) {
			return value;
		}
        这里的value还是带${}的,比如 ${test.name}
		StringBuilder result = new StringBuilder(value);
		while (startIndex != -1) {
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (visitedPlaceholders == null) {
					visitedPlaceholders = new HashSet<>(4);
				}
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				// Recursive invocation, parsing placeholders contained in the placeholder key.
                
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				// Now obtain the value for the fully resolved key...
                //前面通过字符串处理操作,去掉了${},现在只剩下了 test.name, 这里调用了函数型接口的抽象方法,还记得刚刚说的 getPropertyAsRawString 方法吗?现在该去那里看看了
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}
		return result.toString();
	}

? ? ? ? 看了注释之后,我们知道目前已经拿到了去除${}符号的属性值,并且现在要调用回调函数getPropertyAsRawString 了

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
            //遍历资源文件,如果包含key,则返回对应的属性值
			for (PropertySource<?> propertySource : this.propertySources) {
				if (logger.isTraceEnabled()) {
					logger.trace("Searching for key '" + key + "' in PropertySource '" +
							propertySource.getName() + "'");
				}
                //我们就在这里获取到了真正的属性值,这里就类似map的取值操作
				Object value = propertySource.getProperty(key);
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Could not find key '" + key + "' in any property source");
		}
		return null;
	}

? ? ? ? 至此为止,我们遍历所有的资源文件,然后找到匹配key,返回对应的属性值,至此为止,@Value 注解获取属性值的逻辑也结束了。

?刚刚我们遍历的资源文件列表,跟我们前面存进去的资源列表是同一个吗?

? ? ? ? 刚刚遍历查询属性值的时候,我们看到了 propertySources 属性,但是它好像跟前面我们初始化时候的列表不是同一个呢,我们对比一下

?????????

? ? ? ? 由上可以看到,前者是遍历资源文件时用到的列表,后者是初始化和保存资源文件时的列表,他们类型都不一样,直觉上他们并不是同一个列表。

? ? ? ? 那么问题来了,如果他们俩不是同一个,那么前者 PropertySources 是从哪里来的呢?它什么时候实例化的?为什么能通过环境直接获取到?

? ? ? ? 好巧不巧,就在上面这一张图上,环境初始化 propertySources 之后,立刻初始化了?propertyResolver 而且,调用后者初始化方法的时候,把刚刚初始化好的?propertySources 给穿进去了,那么我们跟进去它的构造方法去看一下。

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {

	@Nullable
	private final PropertySources propertySources;


	/**
	 * Create a new resolver against the given property sources.
	 * @param propertySources the set of {@link PropertySource} objects to use
	 */
	public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
		this.propertySources = propertySources;
	}

? ? ? ? 看到这里,大家都应该明白了,调用构造方法之后,propertyResolver 的 propertySources 变量直接指向了构造方法传过来了 propertySources, 说明这两个变量指向的地址是一样的,到此问题,我们的问题也就迎刃而解了。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-24 15:25:20  更:2021-08-24 15:27:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/20 1:45:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码