Отражение bean-компонентов, вызванных аннотацией @Value, на которое стоит обратить внимание

Java
Отражение bean-компонентов, вызванных аннотацией @Value, на которое стоит обратить внимание

Чтение свойств конфигурационного файла

Недавно я оптимизировал код загрузки файла, который включает в себя путь к изображению. Я настроил некоторые свойства файла конфигурации, но в процессе оптимизации у меня появилось новое понимание bean-компонентов. Поскольку он включает в себя чтение свойств файла конфигурации, давайте сначала посмотрим, как получить свойства файла конфигурации.

Аннотация @Value

Напишите некоторые свойства в файле конфигурации (application.yml)

server:
  port: 9199

file:
  server-name: oss.file.com/    #服务器的域名
  mapper: file/    #映射的名字
  images: images/   #存储图片的文件夹
  video: video/        #存放视频的文件夹

Класс FileConfig

package com.ymy.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class FileConfig {

    @Value("${file.server-name}")
    private  String serverName;

    @Value("${file.mapper}")
    private String mapper;

    @Value("${file.images}")
    private String images;

    @Value("${file.video}")
    private String video;

}

@Data: аннотации в зависимостях ломбока, он сгенерирует соответствующие методы get, set, toString, что упростит объем нашего кода. Сборка @Configuration, собранный ею бин сканируется контекстом спринга, это немного похоже на бин в xml конфигурации springmvc, эту аннотацию надо добавить, иначе конфигурационный файл не может быть прочитан, конечно, это также можно заменить на @Component . @Value: свойства, используемые для чтения файла конфигурации, в формате ${соответствующие свойствам в файле конфигурации}.

модульный тест

package com.ymy;

import com.ymy.config.FileConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Slf4j
class StaticConfigVarApplicationTests {

    @Autowired
    private FileConfig fileConfig;

    @Test
    void valueTest() {

        log.info("服务名:{},映射:{},存储图片的文件夹:{},存放视频的文件夹:{}",fileConfig.getServerName(),fileConfig.getMapper(),fileConfig.getImages(),fileConfig.getVideo());
        log.info("图片目录:{}",fileConfig.getServerName()+fileConfig.getMapper()+fileConfig.getImages()+"123.jpg");
        log.info("视频目录:{}",fileConfig.getServerName()+fileConfig.getMapper()+fileConfig.getVideo()+"123.mp4");
    }

}

Результаты теста следующие:

2020-03-19 15:56:42.227  INFO 19884 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 服务名:oss.file.com/,映射:file/,存储图片的文件夹:images/,存放视频的文件夹:video/
2020-03-19 15:56:42.229  INFO 19884 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 图片目录:oss.file.com/file/images/123.jpg
2020-03-19 15:56:42.230  INFO 19884 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 视频目录:oss.file.com/file/video/123.mp4

Видя такой результат, значит, чтение конфигурационного файла прошло успешно, но есть еще один момент, который меня очень не устраивает, то есть объект FileConfig еще нужно инжектить, можно ли получить свойства конфигурационного файла без инъекций? Об этом я расскажу позже, давайте посмотрим, есть ли другие способы чтения конфигурационных файлов? Ответ определенно да, а затем посмотрите вниз.

@ConfigurationProperties

Его реализация очень проста, нужно всего лишь изменить одно место, смотрите код:

package com.ymy.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "file")
public class FileConfig {

    private  String serverName;

    private String mapper;

    private String images;

    private String video;
}

Мы удалили аннотацию @Value для свойства и добавили: @ConfigurationProperties(prefix = "file") в bean-компонент. @ConfigurationProperties(): эта аннотация допускает однозначное соответствие между свойствами компонента и свойствами файла конфигурации, префикс: префикс файла конфигурации, требования к формату: свойства компонента должны совпадать со свойством имена конфигурационного файла.

Никакой другой код менять не нужно, мы запускаем модульный тест напрямую:

2020-03-19 16:21:00.271  INFO 1456 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 服务名:oss.file.com/,映射:file/,存储图片的文件夹:images/,存放视频的文件夹:video/
2020-03-19 16:21:00.273  INFO 1456 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 图片目录:oss.file.com/file/images/123.jpg
2020-03-19 16:21:00.273  INFO 1456 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 视频目录:oss.file.com/file/video/123.mp4

Мы обнаружили, что информация файла конфигурации также была прочитана.Этот метод требует, чтобы имя атрибута бина совпадало с именем атрибута файла конфигурации.В противном случае он не будет прочитан.Интересно, нашли ли вы проблема, то есть сервер определяется через FileConfig.Имя такое: имя_сервера, файл конфигурации сконфигурирован как: имя_сервера, удивительно, что он вообще его нашел, почему? @ConfigurationProperties по умолчанию удалит специальный символ в середине имени свойства, и он не чувствителен к регистру.

Объект среды

Вышеупомянутые два метода чтения могут быть известны многим людям, но кто-нибудь понял объект Окружающей среды?

Среда — это абстракция, интегрированная в контейнер, который моделирует два ключевых аспекта среды приложения: профили и свойства.

Профиль — это определенная группа bean-компонентов с логическим названием, зарегистрированная в контейнере только тогда, когда данный профиль активен. Компоненты можно назначать профилям, определенным в XML или с помощью аннотаций. Роль объекта Environment, связанного с профилями, состоит в том, чтобы определить, какие профили (если есть) активны в данный момент и какие профили (если есть) должны быть активны по умолчанию.

Свойства играют важную роль почти во всех приложениях и могут происходить из различных источников: файлы свойств, системные свойства JVM, переменные системной среды, JNDI, параметры контекста сервлета, специальные объекты свойств, карты и т. д. Роль объекта Environment, связанного со свойствами, заключается в предоставлении пользователям удобного интерфейса службы для настройки источников свойств и разрешения свойств из них.

Это введение официального сайта. Выглядит головокружительно? Нам нужно только сосредоточиться на ключевом моменте, то есть Environment управляет всеми файлами конфигурации.

Давайте посмотрим, как Environment получает свойства конфигурационного файла.Приведенный выше код:

package com.ymy;

import com.ymy.config.FileConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;

@SpringBootTest
@Slf4j
class StaticConfigVarApplicationTests {

    @Autowired
    private Environment environment;

    @Test
    void environmentTest() {
        log.info("服务名:{}",environment.getProperty("file.server-name"));
        log.info("映射:{}",environment.getProperty("file.mapper"));
        log.info("存放图片的文件夹:{}",environment.getProperty("file.imagese"));
        log.info("存放视频的文件夹:{}",environment.getProperty("file.video"));
    }
}

Мы запускаем модульные тесты, чтобы увидеть результаты

2020-03-19 21:20:19.855  INFO 1680 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 服务名:oss.file.com/
2020-03-19 21:20:19.857  INFO 1680 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 映射:file/
2020-03-19 21:20:19.857  INFO 1680 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 存放图片的文件夹:images/
2020-03-19 21:20:19.857  INFO 1680 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 存放视频的文件夹:video/

Верно, это так просто Вы считаете, что три вышеуказанных способа получения конфигурационных файлов очень просты? На самом деле способов реализации несколько.Я покажу код здесь.Конкретные результаты тестирования показывать не буду.Чтобы познакомиться с нами как можно скорее,я непосредственно покажу код ключа ниже.

Свойства прочитаны

package com.ymy.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.util.Properties;

@Slf4j
public class MyPropertiesConfig {

    public static Properties get(String relpath){
        Properties p = null;
        try {
             p = PropertiesLoaderUtils.loadProperties(new EncodedResource(new ClassPathResource(relpath),"utf-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return p;
    }
}

@Test
    void PeopertiesTest() {
        Properties properties = MyPropertiesConfig.get("application.yml");
        System.out.println(properties.getProperty("server-name"));
    }

Свойства конфигурационного файла, читаемые Properties, немного отличаются от предыдущих. Ему не нужен префикс, потому что Properties обрабатывает каждую строку конфигурационного файла. Например, имя сервера под файлом разбивается на два ключа-значения. Pairs , file="" servername=oss.file.com/, поэтому вам нужно быть внимательным при чтении файла конфигурации таким образом.

@PropertySource

Этот метод чтения будет иметь недостаток, то есть комментарии будут читаться вместе, поэтому вы должны быть внимательны при использовании этого метода для чтения информации файла конфигурации.

package com.ymy.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Data
@Slf4j
@Configuration
@ConfigurationProperties
@PropertySource(value = "classpath:application.yml",encoding = "utf-8")
public class FileConfigByPropertisSource {

    private  String servername;

    private String mapper;

    private String images;

    private String video;
}

@Test
    void fileConfigByPropertisSourceTest() {
        log.info("服务名:{}",fileConfigByPropertisSource.getServername());
    }

Этот способ чтения по-прежнему зависит от @ConfigurationProperties. Лучше то, что он может загрузить указанный файл конфигурации. Я не указал здесь префикс, поэтому его здесь нельзя читать. Чтобы получить конфигурацию, которую мы настроили в файле конфигурации. Для некоторых свойств нам по-прежнему нужен @ConfigurationProperties(prefix = "file"). @ConfigurationProperties может читать указанный файл конфигурации с преимуществами @PropertySource. Если @PropertySource отсутствует, его можно прочитать только в application/yml/application Файл .properties.

Сначала введен способ прочтения файла конфигурации, давайте посмотрим на какую проблему я столкнулся? Это очень странный вопрос.

Статические переменные Прочитайте свойства файла конфигурации

Да, у меня странная проблема, когда я использую статические переменные для чтения свойств файла конфигурации, я сначала объясню, как это произошло, а затем пройдусь по коду.

Когда я оптимизировал файл конфигурации, полученный в результате загрузки файла, я обнаружил, что свойства файла конфигурации, полученные с помощью аннотации @Value, необходимо внедрить в bean-компонент, который управляет файлом конфигурации, где он используется, что меня не устраивало, поэтому Я подумал об использовании статических переменных для чтения. Чтобы получить эти свойства конфигурации, см. код.

package com.ymy.config;

import com.ymy.enums.FileTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class FileConfig {

    private static   String servername;

    private static String mapper;

    private static String images;

    private static String video;

    private static String apk;

    @Value("${file.server-name}")
    public void setServername(String servername) {
        this.servername = servername;
    }
    @Value("${file.mapper}")
    public void setMapper(String mapper) {
        this.mapper = mapper;
    }
    @Value("${file.images}")
    public void setImages(String images) {
        this.images = images;
    }
    @Value("${file.video}")
    public void setVideo(String video) {
        this.video = video;
    }
    @Value("${file.apk}")
    public  void setApk(String apk) {
        FileConfig.apk = apk;
    }

    public static String getImages() {
        return images;
    }

    public static String getVideo() {
        return video;
    }

    public static String getApk() {
        return apk;
    }

    public static String getImagePath(String relPath){

        return getlocation(relPath,FileTypeEnum.IMG.value());
    }

    public static String getVideoPath(String relPath){

        return getlocation(relPath,FileTypeEnum.VIDEO.value());
    }

    public static String getApkPath(String relPath){

        return getlocation(relPath,FileTypeEnum.APK.value());
    }

    private static String getlocation(String relPath, Integer type) {

        return servername+mapper+FileTypeFactory.map.get(type)+relPath;
    }
}

Объясните код выше 1. Определите статические переменные для получения свойств файла конфигурации 2. Укажите метод set отдельно (метод set должен быть статическим). 3. Используйте аннотацию @Value для метода set. 4. Соберите bean-компонент и добавьте в класс аннотацию @Configuration.

Таким образом, статические переменные могут получить свойства файла конфигурации, и я продолжил выполнять хитрую операцию, заключающуюся в использовании класса перечисления для сохранения типа файла, например: 1=картинка; 2= video; 3=apk , а затем создайте фабрику для управления этими тремя типами, код выглядит следующим образом:

перечисляемый класс

package com.ymy.enums;

public enum FileTypeEnum {

    IMG(1),

    VIDEO(2),

    APK(3);

    private  Integer index;

    private FileTypeEnum(Integer value) {
        this.index = value;
    }

    public Integer value() {
        return this.index;
    }
}

Заводской класс

package com.ymy.config;

import com.ymy.enums.FileTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class FileTypeFactory {
    public static final Map<Integer,String> map = new HashMap<Integer, String>();
    static {
        map.put(FileTypeEnum.IMG.value(), FileConfig.getImages());
        map.put(FileTypeEnum.VIDEO.value(),FileConfig.getVideo());
        map.put(FileTypeEnum.APK.value(),FileConfig.getApk());
    }
}

Тем не менее, проблема последует, пожалуйста, посмотрите модульный тест

 @Test
    void pathTest() {
        log.info("图片文件夹:{},视频文件夹:{},apk文件夹:{}",FileConfig.getImages(),FileConfig.getVideo(),FileConfig.getApk());
        String relImgPath = "123.png";
        String relVideoPath = "123.mp4";
        String relApkPath = "123.apk";
        log.info("图片的绝对路径:{}",FileConfig.getImagePath(relImgPath));
        log.info("视频的绝对路径:{}",FileConfig.getVideoPath(relVideoPath));
        log.info("apk的绝对路径:{}",FileConfig.getApkPath(relApkPath));
    }

На этот раз мы обнаружим волшебную проблему, то есть в абсолютном пути к полученному файлу есть нулевое значение, но в имени папки, полученном отдельно, есть значение, посмотрите результат:

2020-03-20 15:19:44.545  INFO 16956 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 图片文件夹:images/,视频文件夹:video/,apk文件夹:apk/
2020-03-20 15:19:44.547  INFO 16956 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 图片的绝对路径:oss.file.com/file/null123.png
2020-03-20 15:19:44.547  INFO 16956 --- [           main] com.ymy.StaticConfigVarApplicationTests  : 视频的绝对路径:oss.file.com/file/null123.mp4
2020-03-20 15:19:44.547  INFO 16956 --- [           main] com.ymy.StaticConfigVarApplicationTests  : apk的绝对路径:oss.file.com/file/null123.apk

Я только что использовал заводской прокси, но не могу найти каталог?

Тот факт, что FileConfig.getImages() может получить значение, указывает на то, что статическая переменная images получила свойства файла конфигурации, но почему значение теряется заводским прокси?

Этот менталитет сломан, можно ли это терпеть? Решил копнуть поглубже, хочу разобраться почему не могу получить эти три значения, как проанализировать проблему? Затем нам нужно начать с порядка выполнения бинов.

Порядок загрузки внутреннего кода бина

Внутри bean-компонента есть статические блоки кода, блоки кода, конструкторы и некоторые методы, но только первые три выполняются при создании экземпляра bean-компонента, так каков же порядок их выполнения?

Мы напрямую используем код, чтобы доказать порядок их выполнения, давайте преобразуем фабрику FileTypeFactory

package com.ymy.config;

import com.ymy.enums.FileTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class FileTypeFactory {

    public FileTypeFactory(){
        log.info("我是文件类型工厂的构造函数,我被加载了");
    }

    public static final Map<Integer,String> map = new HashMap<Integer, String>();
    static {
        log.info("我是文件类型工厂的静态代码块,我被加载了");
        map.put(FileTypeEnum.IMG.value(), FileConfig.getImages());
        map.put(FileTypeEnum.VIDEO.value(),FileConfig.getVideo());
        map.put(FileTypeEnum.APK.value(),FileConfig.getApk());
    }

    {
        log.info("我是文件类型工厂的代码块,我被加载了");
    }
}

модульный тест

@Test
    void loadTest() {

        FileTypeFactory f1 = new FileTypeFactory();

        FileTypeFactory f2 = new FileTypeFactory();

    }

Объект FileTypeFactory был создан мной дважды.Что касается того, почему он создается дважды, разве я не могу увидеть их порядок выполнения один раз? Сначала посмотрим на результаты выполнения

2020-03-20 15:52:02.700  INFO 12940 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的静态代码块,我被加载了
2020-03-20 15:52:02.701  INFO 12940 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的代码块,我被加载了
2020-03-20 15:52:02.701  INFO 12940 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的构造函数,我被加载了
2020-03-20 15:52:02.701  INFO 12940 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的代码块,我被加载了
2020-03-20 15:52:02.701  INFO 12940 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的构造函数,我被加载了

Инициализировать первый экземпляр: сначала загружается статический блок кода --> загружается блок кода --> загружается конструктор

Таким образом, мы можем сделать вывод: статический блок кода > блок кода > конструктор.

Теперь посмотрим на инициализацию второго экземпляра, мы обнаруживаем, что выполняются только блок кода и конструктор, поэтому здесь мы можем сделать вывод, то есть: статический блок кода будет загружен только тогда, когда будет создан экземпляр bean-компонента для первого время .

Последовательность выполнения Bean и Bean до

Теперь хочу узнать порядок выполнения бинов и бинов.Я создал три новых бина: user1, user2 и user3, и прописал в них статические блоки кода, блоки кода и конструкторы, а потом запускаем основную программу, чтобы посмотреть Порядок загрузки из трех бобов.

package com.ymy.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class User1 {

    public User1(){
        log.info("我是User1的构造函数");
    }

    {
        log.info("我是User1的代码块");
    }
    static {
        log.info("我是User1的静态代码块");
    }
}

package com.ymy.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class User2 {

    public User2(){
        log.info("我是User2的构造函数");
    }

    {
        log.info("我是User2的代码块");
    }
    static {
        log.info("我是User2的静态代码块");
    }
}

package com.ymy.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class User3 {

    public User3(){
        log.info("我是User3的构造函数");
    }

    {
        log.info("我是User3的代码块");
    }
    static {
        log.info("我是User3的静态代码块");
    }
}

Обратите внимание, что здесь я использую аннотацию @Configuration. Поскольку здесь я создаю новый проект springboot, я напрямую запускаю метод mian. Давайте вместе посмотрим на результаты выполнения:

2020-03-20 16:11:40.885  INFO 8932 --- [           main] com.ymy.StaticConfigVarApplication       : Starting StaticConfigVarApplication on LAPTOP-3GLHJRE9 with PID 8932 (D:\springboot\static-config-var\target\classes started by admin in D:\springboot)
2020-03-20 16:11:40.887  INFO 8932 --- [           main] com.ymy.StaticConfigVarApplication       : No active profile set, falling back to default profiles: default
2020-03-20 16:11:41.447  INFO 8932 --- [           main] com.ymy.test.User1                       : 我是User1的静态代码块
2020-03-20 16:11:41.448  INFO 8932 --- [           main] com.ymy.test.User2                       : 我是User2的静态代码块
2020-03-20 16:11:41.451  INFO 8932 --- [           main] com.ymy.test.User3                       : 我是User3的静态代码块
2020-03-20 16:11:41.692  INFO 8932 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 9199 (http)
2020-03-20 16:11:41.699  INFO 8932 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-20 16:11:41.699  INFO 8932 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-20 16:11:41.778  INFO 8932 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-20 16:11:41.779  INFO 8932 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 858 ms
2020-03-20 16:11:41.831  INFO 8932 --- [           main] com.ymy.test.User1                       : 我是User1的代码块
2020-03-20 16:11:41.831  INFO 8932 --- [           main] com.ymy.test.User1                       : 我是User1的构造函数
2020-03-20 16:11:41.831  INFO 8932 --- [           main] com.ymy.test.User2                       : 我是User2的代码块
2020-03-20 16:11:41.831  INFO 8932 --- [           main] com.ymy.test.User2                       : 我是User2的构造函数
2020-03-20 16:11:41.832  INFO 8932 --- [           main] com.ymy.test.User3                       : 我是User3的代码块
2020-03-20 16:11:41.832  INFO 8932 --- [           main] com.ymy.test.User3                       : 我是User3的构造函数
2020-03-20 16:11:41.958  INFO 8932 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-20 16:11:42.172  INFO 8932 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9199 (http) with context path ''
2020-03-20 16:11:42.176  INFO 8932 --- [           main] com.ymy.StaticConfigVarApplication       : Started StaticConfigVarApplication in 1.764 seconds (JVM running for 2.908)

После загрузки программы мы обнаруживаем, что первыми выполняются блоки статического кода пользователя 1, пользователя 2 и пользователя 3. После выполнения соответствующих блоков кода и конструкторов мы также обнаруживаем, что порядок выполнения этих трех компонентов — сверху вниз. Таким образом, порядок выполнения между bean-компонентами, подключенными с помощью @Configuration между несколькими bean-компонентами:

Блок статического кода user1 > блок статического кода user2 > блок статического кода user3 > блок кода user1 > конструктор user1 > блок кода пользователя 2 > конструктор пользователя 2 > блок кода пользователя 3 > конструктор пользователя 3

Увидев этот порядок выполнения, я, кажется, понял, почему фабрика не может получить свойства FileConfig, давайте посмотрим

Я добавил конструктор в bean-компонент FileConfig, потому что свойства назначаются только при их создании.

 FileConfig(){
        log.info("这是文件配置类的构造函数");
    }

Фабрика FileTypeFactory не была изменена, давайте снова запустим проект, чтобы увидеть результаты.

2020-03-20 16:30:43.965  INFO 11920 --- [           main] com.ymy.StaticConfigVarApplicationTests  : Starting StaticConfigVarApplicationTests on LAPTOP-3GLHJRE9 with PID 11920 (started by admin in D:\springboot\static-config-var)
2020-03-20 16:30:43.966  INFO 11920 --- [           main] com.ymy.StaticConfigVarApplicationTests  : No active profile set, falling back to default profiles: default
2020-03-20 16:30:44.731  INFO 11920 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的静态代码块,我被加载了
2020-03-20 16:30:44.909  INFO 11920 --- [           main] com.ymy.config.FileConfig                : 这是文件配置类的构造函数
2020-03-20 16:30:44.939  INFO 11920 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的代码块,我被加载了
2020-03-20 16:30:44.940  INFO 11920 --- [           main] com.ymy.config.FileTypeFactory           : 我是文件类型工厂的构造函数,我被加载了

Это подтверждает мою мысль.Сначала загружается блок статического кода фабричного класса, а конструкция класса конфигурации загружается позже, поэтому свойство чтения пусто.Поскольку блок статического кода выполняется при инициализации объекта, то Мне просто нужно поместить его в класс конфигурации и выполнить.Самый простой и эффективный способ - удалить аннотацию @Configuration класса фабрики FileTypeFactory, чтобы фабрика не загружалась при запуске программы, а была вызывается при старте программы.В это время класс конфигурации уже загружен.Почему раньше нужно было добавить аннотацию? Может быть, мой мозг залит, но благодаря этой аннотации я на волне узнал о разнице между аннотацией @Configuration и аннотацией @Component.

Разница между аннотацией @Configuration и аннотацией @Component

Мы не задействовали аннотацию @Component в только что проанализированном коде, почему мы узнали целую волну их отличий? Я всегда думал, что они работают одинаково, и, зная, что я использовал их оба в своих тестах, я понял, что ошибался.

На первый взгляд, @Configuration является производным классом @Component, поэтому @Configuration также должен уметь делать то, что может делать @Component.Хотя это восприятие правильное, оно не является строгим.Мы по-прежнему используем только что созданный user1, 2, и 3. Чтобы проиллюстрировать их различие, мы заменяем их аннотации сборки на @Component.

Другие не нужно модифицировать, давайте посмотрим непосредственно на их порядок выполнения

2020-03-20 16:42:40.274  INFO 21048 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 725 ms
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User1                       : 我是User1的静态代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User1                       : 我是User1的代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User1                       : 我是User1的构造函数
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User2                       : 我是User的静态代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User2                       : 我是User的代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User2                       : 我是User的构造函数
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User3                       : 我是User3的静态代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User3                       : 我是User3的代码块
2020-03-20 16:42:40.311  INFO 21048 --- [           main] com.ymy.test.User3                       : 我是User3的构造函数

Помните порядок выполнения bean-компонентов, связанных с @Configuration?

Блок статического кода user1 > блок статического кода user2 > блок статического кода user3 > блок кода user1 > конструктор user1 > блок кода пользователя 2 > конструктор пользователя 2 > блок кода пользователя 3 > конструктор пользователя 3

Вы нашли что-нибудь? Порядок выполнения бинов, собранных @Component, фактически изменился и выглядит следующим образом:

USER1 Статический блок Кода> Блок кода USER1> USER1 Конструктор> Узел2 Статический блок Кода> User2 Code Block User2 Constructor> User3 Статический блок Кода> User3 Block> Constructor User3

Видно, что порядок выполнения бинов, собранных этими двумя, сильно изменился, почему так?

Почему порядок выполнения bean-компонентов, собранных с помощью аннотации @Configuration и аннотации @Component, отличается?

Это связано с тем, что аннотация @Configuration динамически проксируется (CGLIB), легко понять, что после выполнения блока статического кода блок кода реального экземпляра будет выполняться в прокси, а не сам, поэтому мы увидим все компоненты. собранные с помощью @Configuration сначала выполняют блоки статического кода, а затем отдельно выполняют их конструкторы, а аннотация @Component этого не делает, поэтому выполняется строго в порядке выполнения бина, что мы и видим Почему порядок выполнения bean-компоненты, собранные с помощью аннотации @Configuration и аннотации @Component, отличаются? Если вас интересуют аннотации весны, у меня есть официальный китайский документ весны здесь:Официальная китайская документация Spring.

Суммировать

Это конец написания. Весна все еще такая мощная, и наше понимание ее все еще такое тонкое. В последнее время я много работаю, чтобы углубить свое понимание весны. Я надеюсь написать больше красивых статей в будущем, если не это Оптимизация, может быть, я всегда думал, что аннотация @Configuration и аннотация @Component — это одно и то же, они действительно живут и учатся.