Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >「Spring 源码分析」Profile

「Spring 源码分析」Profile

原创
作者头像
花言不知梦
修改于 2020-05-18 03:17:15
修改于 2020-05-18 03:17:15
1.1K00
代码可运行
举报
文章被收录于专栏:花言不知梦花言不知梦
运行总次数:0
代码可运行

Profile

profile 定义了一组有逻辑关系的 bean定义,当且仅当 profile 被激活的时候,才会注入到容器当中。也就是说,程序只需要构建一次,就可以部署到多个环境当中,而不用修改所有配置,指定哪一个profile需要被激活即可

源码分析(细节比较多,得捋清楚)

主要是通过ConfigFileApplicationListener的内部类Loader进行加载

  1. 更新 profiles集,添加存在已经激活的profile值,如果不存在,添加默认指定的profile值到profiles集当中
  2. 确定搜索范围(路径),获取配置文件名({name}-{profile}),使用PropertiesPropertySourceLoader和YamlPropertySourceLoader这两个加载器添加相应的文件扩展名,进行加载
  3. 将加载后的loaded列表逆序,遍历该列表中的每一个MutablePropertySources对象,依次注入到environment对象的propertySources属性值当中,这里就意味着完成profile所指定的一组bean的注册工作
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1. 进入 onApplicationEnvironmentPreparedEvent(event)方法 -- ConfigFileApplicationListener类
   该方法的作用是遍历所有的后处理器,依次调用后处理器,执行相应的方法
   我们需要关注的是 ConfigFileApplicationListener类 这个后处理器执行的操作
   private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        // 遍历所有的后处理器,只要是 EnvironmentPostProcessor类型,就添加到 postProcessors列表当中
        List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
        // 把本身也添加到 postProcessors列表当中
        // 因为 ConfigFileApplicationListener类 实现了 EnvironmentPostProcessor接口
        postProcessors.add(this);
        // 对 postProcessors列表中的后处理器按Order值进行排序
        AnnotationAwareOrderComparator.sort(postProcessors);
        Iterator var3 = postProcessors.iterator();

        while(var3.hasNext()) {
            // 获取后处理器
            EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
            // 执行后处理器的 postProcessEnvironment方法
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }

   }
    
2. 进入 postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()) -- ConfigFileApplicationListener类
   该方法的作用是调用自定义的 addPropertySources方法向环境中添加属性源
   public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        // 向环境中添加属性源
        this.addPropertySources(environment, application.getResourceLoader());
   }
   
3. 进入 addPropertySources(environment, application.getResourceLoader())方法
   该方法的作用是向环境中添加属性源,主要是RandomValuePropertySource 和 application-{profile}.(properties|yml)配置文件中的属性源
   protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        // 添加 RandomValuePropertySource(随机类型)属性源
        RandomValuePropertySource.addToEnvironment(environment);
        // 从 application-{profile}.(properties|yml)配置文件中加载属性源
        (new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
   }
   
   == ConfigFileApplicationListener的内部类Loader 构造方法==
   Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        this.logger = ConfigFileApplicationListener.this.logger;
        this.loadDocumentsCache = new HashMap();
        this.environment = environment;
        // 初始化占位符解析器
        this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
        // 一般情况下,使用默认的类加载器
        this.resourceLoader = (ResourceLoader)(resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
        // 使用 SpringFactoriesLoader类加载所有jar包下的 META-INF目录的 spring.factories文件的 PropertySourceLoader类数组
        // 最终会得到两个实现类,一个是PropertiesPropertySourceLoader类,一个是YamlPropertySourceLoader类
        // PropertiesPropertySourceLoader类 支持properties和xml文件,解析成Properties,然后封装成PropertiesPropertySource
        // YamlPropertySourceLoader类 支持yml和yaml文件,解析成Map,然后封装成MapPropertySource
        this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, this.getClass().getClassLoader());
    }
   
4. 进入 load()方法
   该方法的作用是加载所有可能的profile
   void load() {
        FilteredPropertySource.apply(this.environment, "defaultProperties", ConfigFileApplicationListener.LOAD_FILTERED_PROPERTY, (defaultProperties) -> {
            // 初始化集合
            // 未处理的数据集合
            this.profiles = new LinkedList();
            // 已处理的数据集合
            this.processedProfiles = new LinkedList();
            // 被 spring.profiles.active指定的集合
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap();
            // 4.1 加载存在已经激活的 profiles
            this.initializeProfiles();
            
            // 遍历profiles
            while(!this.profiles.isEmpty()) {
                // 从 profiles集合中获取 profile
                ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
                // 如果 profile不是默认指定的 profile,且不为null
                // 其中,isDefaultProfile方法体定义 profile != null && !profile.isDefaultProfile()
                if (this.isDefaultProfile(profile)) {
                    // 添加 profiles资源到环境中
                    this.addProfileToEnvironment(profile.getName());
                }
                
                // 4.2 确定搜索范围,获取对应的配置文件名,并使用相应加载器加载
                this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
                // 将处理完的 profile添加到 processedProfiles列表当中,表示已经处理完成
                this.processedProfiles.add(profile);
            }
            
            // 采用消极策略的DocumentFilterFactory对象进行处理
            this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
            // 顺序颠倒下,保证了优先add的是带profile的,而默认的profile是优先级最低
            this.addLoadedPropertySources();
            // 更新 activeProfiles列表
            this.applyActiveProfiles(defaultProperties);
        });
    }

4.1 进入 initializeProfiles( )方法,更新 profiles集

  1. 加载 通过spring.profiles.active指定激活的profile 和 通过spring.profiles.include指定叠加激活的profile
  2. 如果 profiles集为空,则尝试加载 通过spring.profiles.default指定默认的profile,如果没有,就返回"default"
代码语言:java
AI代码解释
复制
 4.1 进入 this.initializeProfiles()方法
    该方法的作用是加载存在已经激活的 profiles
    private void initializeProfiles() {
        // 默认配置文件为null,以便优先被处理,具有最小优先级
        this.profiles.add((Object)null);
        // 4.1.1 判断当前环境是否配置 spring.profiles.active属性
        // 也就是遍历环境中所有的属性源集合,查看是否有名称为 spring.profiles.active的属性源
        // 比如说,在命令行参数当中添加 --spring.profiles.active=dev,
        //        或配置系统属性 System.setProperty("spring.profiles.active","dev"),那么就会去创建一个dev的 profile
        // 如果没有,就返回空集;如果有,就添加到 activatedViaProperty集中
        Set<ConfigFileApplicationListener.Profile> activatedViaProperty = this.getProfilesFromProperty("spring.profiles.active");
        // 4.1.1 判断当前环境是否配置 spring.profiles.include属性
        // 也就是遍历环境中所有的属性源集合,查看是否有名称为 spring.profiles.include的属性源
        // 如果没有,就返回空集;如果有,就添加到 includedViaProperty集中
        Set<ConfigFileApplicationListener.Profile> includedViaProperty  = this.getProfilesFromProperty("spring.profiles.include");
        // 4.1.2 如果没有特别指定的话,就是 application.properties 和 application-default.properties配置
        // 如果特别指定的话,就是 application.properties 和 已经激活的 profile
        // 返回该环境其他显式激活的配置文件集,已经添加过了,就不会再重复添加(除了 spring.profiles.active 和 spring.profiles.include指定的配置以外 )
        List<ConfigFileApplicationListener.Profile> otherActiveProfiles = this.getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
        // 将 otherActiveProfiles集添加到 profiles集当中
        this.profiles.addAll(otherActiveProfiles);
        // 将 includedViaProperty集添加到 profiles集当中
        this.profiles.addAll(includedViaProperty);
        // 4.1.3
        // 将 activatedViaProperty集添加到 profiles集中,以确保 spring.profiles.active指定的值生效
        // 同时移除默认配置
        this.addActiveProfiles(activatedViaProperty);
        // 如果 profiles集仍然为null,即没有指定,就会创建默认的profile-- 这就说明了为什么 spring.profiles.active指定的配置文件会和 default配置文件互斥的原因 --if (this.profiles.size() == 1) {
            // 如果没有用 spring.profiles.default指定profile值,那么默认返回default
            String[] var4 = this.environment.getDefaultProfiles();
            int var5 = var4.length;
            
            // 遍历返回的默认值,依次添加到profiles集当中
            for(int var6 = 0; var6 < var5; ++var6) {
                String defaultProfileName = var4[var6];
                ConfigFileApplicationListener.Profile defaultProfile = new ConfigFileApplicationListener.Profile(defaultProfileName, true);
                this.profiles.add(defaultProfile);
            }
        }

    }
    
补充(initializeProfiles()方法里面的子方法阐述)
   4.1.1
   == getProfilesFromProperty("spring.profiles.active")方法 == 
   该方法的作用是遍历环境中的已加载的所有属性源,以获取其中 spring.profiles.active指定的值,作为profile值
   private Set<ConfigFileApplicationListener.Profile> getProfilesFromProperty(String profilesProperty) {
        // 判断当前环境的已加载的所有属性源是否包含名为 spring.profiles.active指定的属性源
        // 相比如通过命令行参数或者系统环境指定的 spring.profiles.active对应的值都会被扫描到
        if (!this.environment.containsProperty(profilesProperty)) {
            return Collections.emptySet(); // 一般情况下,返回空集
        } else {
            // 通过当前environment对象实例化Binder对象
            Binder binder = Binder.get(this.environment);
            // 遍历环境中所有的属性源集合,查看是否有名称为 profilesProperty的属性源
            Set<ConfigFileApplicationListener.Profile> profiles = this.getProfiles(binder, profilesProperty);
            return new LinkedHashSet(profiles);
        }
   }Binder类进行补充说明
   Binder类是一个容器对象,其中绑定来自当前Environment对象的一个或多个ConfigurationPropertySources属性
   下面这个方法就看出,通过传入的environment对象,去获取所有ConfigurationPropertySource类型的属性源
   public static Binder get(Environment environment, BindHandler defaultBindHandler) {
        Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
        PropertySourcesPlaceholdersResolver placeholdersResolver = new PropertySourcesPlaceholdersResolver(environment);
        return new Binder(sources, placeholdersResolver, (ConversionService)null, (Consumer)null, defaultBindHandler);
   }
   
   4.1.2
   == getOtherActiveProfiles(activatedViaProperty, includedViaProperty)方法 == 
   该方法的作用是获取其他显式配置激活的profile,之前添加的,不再重复添加
   private List<ConfigFileApplicationListener.Profile> getOtherActiveProfiles(Set<ConfigFileApplicationListener.Profile> activatedViaProperty, Set<ConfigFileApplicationListener.Profile> includedViaProperty) {
        // 从 activeProfiles集获取之前注册的激活profile
        return (List)Arrays.stream(this.environment.getActiveProfiles()).map(ConfigFileApplicationListener.Profile::new).filter((profile) -> {
            // 其中,filter()方法保存当前满足过滤条件的元素
            return !activatedViaProperty.contains(profile) && !includedViaProperty.contains(profile);
        }).collect(Collectors.toList());
   }
   
   4.1.3
   == addActiveProfiles(activatedViaProperty)方法 == 
   该方法的作用是将 activatedViaProperty集添加到 profiles队列中
     确保 spring.profiles.active指定的 profile会生效,也就是迭代的激活的 profiles会覆写默认的配置(队列)
   void addActiveProfiles(Set<ConfigFileApplicationListener.Profile> profiles) {
        if (!profiles.isEmpty()) {
            // 之前已经将 spring.profiles.active指定的profile添加进去,就不会再次添加
            // 判断激活标志是否为true
            // 也就是判断 spring.profiles.active指定的profile是否添加到 profiles队列当中
            if (this.activatedProfiles) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
                }
            
            // 如果之前没有添加,就将 spring.profiles.active指定的profile添加到 profiles队列当中
            // spring.profiles.active指定的 profile也就是 activatedViaProperty集
            } else {
                this.profiles.addAll(profiles);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
                }

                this.activatedProfiles = true; // 将激活标志置为false
                // 移除默认指定的 profile
                // 如果此 profile不为null,并且是 spring.profiles.default指定的 profile
                this.removeUnprocessedDefaultProfiles();
            }
        }
   }

4.2 进入 load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer)方法

  1. 获取配置文件的加载路径,如果 spring.config.location指定配置文件的路径,则以指定的为准;如果没有,查看 spring.config-addition.location有无指定额外的路径,如果有,路径为指定的加上默认的路径;如果都没有,则以默认的路径为准(file:./config/,file:./,classpath:/config/,classpath:/)
  2. 获取配置文件的前缀名,根据已有的信息构成完整前缀。判断 spring.config.name是否指定前缀的名称(name值),如果没有,默认为"application"。拼接成完整前缀,路径 + {name} + "-" + {profile}就是完整前缀。
  3. 使用PropertiesPropertySourceLoader和YamlPropertySourceLoader加载器去加载,以PropertiesPropertySourceLoader为例,添加相应的文件扩展名,尝试去加载,先生成配置文件的Resouce对象,解析后生成PropertySource对象,封装到ConfigFileApplicationListener.Document对象中
代码语言:java
AI代码解释
复制
4.2 load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false))
   该方法的作用是确定搜索范围,获取对应的配置文件名,并使用相应的加载器加载
   private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
       // 4.2.1 获取加载配置文件的路径
       this.getSearchLocations().forEach((location) -> {
           // 判断指定的搜索范围是否是文件夹
           // 如果是文件夹,需要进一步搜索,找到相应的配置文件
           // 如果不是文件夹,说明有可能一次性提供配置文件,直接去加载即可
           boolean isFolder = location.endsWith("/");
           // 4.2.2 如果是文件夹,需要调用 getSearchNames()方法进一步找到配置文件
           // 如果没有用 spring.config.name指定配置文件的前缀,默认是返回"application"
           Set<String> names = isFolder ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
           names.forEach((name) -> {
               // 4.2.3 加载相应路径下的配置文件,一般是 {name}-{profile}.(properties|yml)
               this.load(location, name, profile, filterFactory, consumer);
           });
       });
  }
  
4.2 补充(load(profile, filterFactory, consumer)方法里面的子方法阐述)
   4.2.1
   ## 检索路径 ##
   private Set<String> getSearchLocations() {
   该方法的作用是获取搜索范围,从以下三个角度
   1. spring.config.location 指定的路径
   2. spring.config.addition-location 指定的路径
   3. 默认路径(file:./config/,file:./,classpath:/config/,classpath:/// 如果环境中有名为 spring.config.location的属性源
       // spring.config.location配置指定了搜索范围,则以它指定的为准
       if (this.environment.containsProperty("spring.config.location")) {
           // 获取 spring.config.location指定的搜索范围
           return this.getSearchLocations("spring.config.location");
       } else {
           // 获取 spring.config.additional-location指定的搜索范围
           Set<String> locations = this.getSearchLocations("spring.config.additional-location");
           // 将默认的搜索范围添加进去
           // 1. file:./config/
           // 2. file:./
           // 3. classpath:/config/
           // 4. classpath:/
           locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/"));
           return locations; // 返回处理后的搜索范围
       }
   }
   
   private Set<String> getSearchLocations(String propertyName) {
   该方法的作用是从指定的值获取搜索范围
       Set<String> locations = new LinkedHashSet();
       String path;
       // 判断环境当中是否存在该属性
       if (this.environment.containsProperty(propertyName)) {
           // 如果存在该属性,则开始遍历解析后的值
           for(Iterator var3 = this.asResolvedSet(this.environment.getProperty(propertyName), (String)null).iterator(); var3.hasNext(); locations.add(path)) {
               path = (String)var3.next();
               // 如果路径不存在"$",一般是占位符
               if (!path.contains("$")) {
                   path = StringUtils.cleanPath(path);
                   // 如果路径不是以url形式表示,即只有提供相对路径
                   // 则添加前缀"file:",变成绝对路径,默认从文件系统加载
                   if (!ResourceUtils.isUrl(path)) {
                       path = "file:" + path;
                   }
                }
           }
       }

       return locations;
  }
  
  4.2.2
  ## 获取配置文件的前缀 ##  
  private Set<String> getSearchNames() {
  该方法的作用是获取配置文件的前缀中name对应的值
  即 {name}-{profile}.(properties|yml)前面的name值,一般默认是application
      // 如果环境中包含 spring.config.name指定的属性值
      if (this.environment.containsProperty("spring.config.name")) {
          // 获取 spirng.config.name指定的属性值
          String property = this.environment.getProperty("spring.config.name");
          // 返回 spirng.config.name指定的配置文件前缀
          return this.asResolvedSet(property, (String)null);
      } else {
          // 返回默认的配置文件前缀"application"
          return this.asResolvedSet(ConfigFileApplicationListener.this.names, "application");
      }
  }
  
  4.2.3
  ## 加载配置文件 ##
  private void load(String location, String name, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
  该方法的作用是针对配置文件的不同前缀,使用不同的方式进行相应的处理 
  此时,前缀等于location + name   
      // StringUtils.hasText(String str)方法是用来检查给定字符串是否包含实际文本
      // 也就是判断name值是否为""," ",null;一般默认值是"application",所以不会进入if语句块当中
      if (!StringUtils.hasText(name)) {
          Iterator var13 = this.propertySourceLoaders.iterator();

          PropertySourceLoader loader;
          do {
              if (!var13.hasNext()) {
                  throw new IllegalStateException("File extension of config file location '" + location + "' is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/'");
              }

              loader = (PropertySourceLoader)var13.next();
          } while(!this.canLoadFileExtension(loader, location));

          this.load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
      } 
       // 针对"application"值,使用相应的属性资源加载器(前面构造Loader的时候已经初始化)进行处理
       // 其中,属性资源加载器有两种
       // 1. PropertiesPropertySourceLoader类 支持properties和xml文件,解析成Properties,然后封装成PropertiesPropertySource
       // 2. YamlPropertySourceLoader类 支持yml和yaml文件,解析成Map,然后封装成MapPropertySource
       else {
          Set<String> processed = new HashSet();
          // 获取迭代器
          Iterator var7 = this.propertySourceLoaders.iterator();
          
          while(var7.hasNext()) {
              // 获取属性资源加载器
              PropertySourceLoader loaderx = (PropertySourceLoader)var7.next();
              // 返回属性资源加载器可以支持的扩展名
              // PropertiesPropertySourceLoader加载器支持以"properties"、"xml"为后缀的配置文件
              // YamlPropertySourceLoader加载器支持以"yml"、"ymal"为后缀的配置文件
              String[] var9 = loaderx.getFileExtensions();
              int var10 = var9.length;

              for(int var11 = 0; var11 < var10; ++var11) {
                  String fileExtension = var9[var11];
                  if (processed.add(fileExtension)) {
                      this.loadForFileExtension(loaderx, location + name, "." + fileExtension, profile, filterFactory, consumer);
                  }
              }
         }

      }
   }
 
   private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
   该方法的作用是根据配置文件的绝对路径名(上面已经添加文件扩展名),来加载配置文件    
       ConfigFileApplicationListener.DocumentFilter defaultFilter = filterFactory.getDocumentFilter((ConfigFileApplicationListener.Profile)null);
       ConfigFileApplicationListener.DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
       // 如果profile不为null的话,配置文件名是 {name}-{profile}.fileExtension
       // 比如遍历到第一个location,使用PropertiesPropertySourceLoader加载器加载时
       //     默认情况是 location:file:./config/,name:application,profile:default,fileExtension:properties
       //     此时的 prefix=file:./config/application,profile=default,fileExtension=properties
       //           profileSpecificFile=file:./config/application-default.properties
       if (profile != null) {
           // 确定配置文件的具体文件名(包括路径和完整的文件名)
           String profileSpecificFile = prefix + "-" + profile + fileExtension;
           this.load(loader, profileSpecificFile, profile, defaultFilter, consumer);
           this.load(loader, profileSpecificFile, profile, profileFilter, consumer);
           Iterator var10 = this.processedProfiles.iterator();

           while(var10.hasNext()) {
               ConfigFileApplicationListener.Profile processedProfile = (ConfigFileApplicationListener.Profile)var10.next();
               if (processedProfile != null) {
                   String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
                   this.load(loader, previouslyLoaded, profile, profileFilter, consumer);
               }
           }
       }

       this.load(loader, prefix + fileExtension, profile, profileFilter, consumer);
   }
   
   private void load(PropertySourceLoader loader, String location, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilter filter, ConfigFileApplicationListener.DocumentConsumer consumer) {
   该方法的作用是将获取配置文件的Resouce对象,解析后生成PropertySource对象,封装到Document对象中    
       try {
           // 获取指定路径匹配的Resource实例
           Resource resource = this.resourceLoader.getResource(location);
           StringBuilder descriptionxx;
           // 如果存在Resource实例,并且不为null
           if (resource != null && resource.exists()) {
               // getFilename()方法返回资源的文件名
               // getFilenameExtension(String path)方法从给定的资源路径获取扩展文件名
               // 一般就是属性资源加载器所支持的 fileExtension,比如"properties"、"xml"、"yml"、"yaml"
               // 判断配置文件的后缀是否存在,如果不存在,会打印日志堆栈信息,方便追踪调试
               if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
                   if (this.logger.isTraceEnabled()) {
                       descriptionxx = this.getDescription("Skipped empty config extension ", location, resource, profile);
                       this.logger.trace(descriptionxx); // 打印日志信息
                   }

               } else {
                   // 此时name:applicationConfig:[profileSpecificFile]
                   // 比如applicationConfig[file:./config/application-default.properties]
                   String name = "applicationConfig: [" + location + "]";
                   List<ConfigFileApplicationListener.Document> documents = this.loadDocuments(loader, name, resource);
                   if (CollectionUtils.isEmpty(documents)) {
                       if (this.logger.isTraceEnabled()) {
                           StringBuilder description = this.getDescription("Skipped unloaded config ", location, resource, profile);
                           this.logger.trace(description);
                       }

                   } else {
                       List<ConfigFileApplicationListener.Document> loaded = new ArrayList();
                       Iterator var10 = documents.iterator();

                       while(var10.hasNext()) {
                           ConfigFileApplicationListener.Document document = (ConfigFileApplicationListener.Document)var10.next();
                           if (filter.match(document)) {
                               this.addActiveProfiles(document.getActiveProfiles());
                               this.addIncludedProfiles(document.getIncludeProfiles());
                               loaded.add(document);
                           }
                       }

                       Collections.reverse(loaded);
                       if (!loaded.isEmpty()) {
                           loaded.forEach((documentx) -> {
                               consumer.accept(profile, documentx);
                           });
                           if (this.logger.isDebugEnabled()) {
                               StringBuilder descriptionx = this.getDescription("Loaded config file ", location, resource, profile);
                               this.logger.debug(descriptionx);
                           }
                       }

                   }
               }
           } else {
               if (this.logger.isTraceEnabled()) {
                   descriptionxx = this.getDescription("Skipped missing config ", location, resource, profile);
                   this.logger.trace(descriptionxx);
               }

           }
       } catch (Exception var12) {
           throw new IllegalStateException("Failed to load property source from location '" + location + "'", var12);
       }
   }

- End -(转载请注明出处)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
java基于ssm开发的宠物商城宠物店源码
关于宠物的商店,首页,搜索商品,详情页,可选择尺寸,衣服颜色,根据不同规格显示不同的商品价格,加入购物车,立即购买,评价列表展示,商品详情展示,商品评分,分类商品,标签查询,更多分类查询
飞一样的编程
2023/01/26
6710
java基于ssm开发的宠物商城宠物店源码
SpringBoot集成微信支付JSAPIV3保姆教程
最近为一个公众号h5商城接入了微信支付功能,查找资料过程中踩了很多坑,以此文章记录一下和大家分享
code2roc
2023/09/12
2.1K0
SpringBoot集成微信支付JSAPIV3保姆教程
微信小程序支付Java工具类
引入微信支付开放平台的 API 依赖,以便能够使用 Java 调用相关 API 接口。
默存
2024/04/16
5800
微信小程序支付Java工具类
BS1050-基于springSSM+mysql实现网上商城管理系统
本基于SSM的网上商城管理系统,系统采用多层MVC软件架构,采用Java SpringMVC Mybatis Mysql框架实现互联网电商商品数据的爬虫采集,商品销售数据分析以及商品在线销售电商程序。系统爬虫端主要采用JSOUP+HttpClient实时采集分析互联网电商平台网站数据,
计算机程序优异哥
2022/12/05
5540
MySQL---数据库从入门走向大神系列(十七)-JavaWeb分页技术实例演示2
分页,是一种将所有数据分段展示给用户的技术.用户每次看到的不 是全部数据,而是其中的一部分,如果在其中没有找到自己想要的内容,用户可以通过指定页码或是点上/下一页的方式进行翻页。
谙忆
2021/01/21
4410
MySQL---数据库从入门走向大神系列(十七)-JavaWeb分页技术实例演示2
Java EE 阶段小项目(小型商城商品展示 + 购物车 + 下单 + 付款)
这里就和真真的支付没啥关系了,但是千万别傻乎乎的用自己的支付宝付钱,因为支付宝的沙箱环境已经提供了虚拟卖家账号 和 虚拟商家账号了,在这里我们可以快乐的充值,体验一把有钱人的快乐,也可以下载对应的沙箱环境支付宝进行支付
Gorit
2021/12/08
1.5K0
Java EE 阶段小项目(小型商城商品展示 + 购物车 + 下单 + 付款)
【第十八篇】商城系统-订单中心设计解决方案
  我们需要把相关的静态资源拷贝到nginx,然后动态模板文件拷贝到order项目的templates目录下,然后调整资源的路径。在网关中设置对应的路由即可。
用户4919348
2022/10/04
7770
【第十八篇】商城系统-订单中心设计解决方案
简单的Spring MVC 程序 原
package group.esperanto.action; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j
南郭先生
2018/08/14
4310
请给出一个MVC模式下用Servlet和jsp分页的HelloWord实际的例子?
马克-to-win:像淘宝百度这样的类似网页,我们用了很多很多。基本上就 是输入查询条件以后,符合查询条件的结果可能会成千上万。我们不能一下子把所有这些结果都列出来,只能一次显示一页。当用户按下一页的时候,才会把下一页 的内容列出来。这样不至于内存崩溃。要达到这样的效果,要用到分页技术。分页技术的核心思想就是把所有的结果放在一个ArrayList的当中(在我们的 例子当中就是ArrayList ret)。马克-to-win:这个ArrayList连同相关参数比如一共有多少行(totalNumberOfRowsInDB),每页有多少行(pageSize),当 前显示是第几页(currentPageNumber),一共有多少页(totalNumberOfPages),都放在Session当中。马克-to -win:返回到jsp后,再从Session当中取出所有这些数据,把它显示出来。注意这里数据库的每行数据都对应一个RegisterRow实例。把 所有这些实例都放在ArrayList当中。如果你觉得全部把结果放在Session当中浪费空间的话,你甚至一次只放5页,当用户访问第5页的时候,你 就再一次访问数据库,把数据放在Session当中。算法由你自己定义,据情况而定。当然,为简单起见,我们的例子是把所有符合条件的数据都给取出来。
马克java社区
2021/07/14
4070
Web-第二十二天 Web商城实战二【悟空教程】
`cname` varchar(20) DEFAULT NULL, #分类名称
Java帮帮
2018/07/27
1.3K0
Web-第二十二天 Web商城实战二【悟空教程】
Spring项目综合整合实践
springmvc提供的ResponseEntity类型,使用它可以很方便地定义返回的HttpHeaders和HttpStatus
张哥编程
2024/12/13
1340
Spring项目综合整合实践
java药店网站药店系统药店源码刷脸支付源码
首页,搜索商品,详情页,根据不同规格显示不同的商品价格,加入购物车,立即购买,评价列表展示,商品详情展示,商品评分,分类商品,标签查询,更多分类查询 ,模拟支付(扫码支付+刷脸支付)
飞一样的编程
2022/12/17
1.1K0
谷粒商城-高级篇(购物车)
​ 参考京东,在点击购物车时,会为临时用户生成一个name为user-key的cookie临时标识,过期时间为一个月,如果手动清除user-key,那么临时购物车的购物项也被清除,所以 user-key 是用来标识和存储临时购物车数据的
OY
2022/03/20
7630
谷粒商城-高级篇(购物车)
杨校老师课堂之Java EE框架实训宠归于好项目开发(二)
1. 编码 ---- 1.1 创建程序包名 cn.javabs.pet.entity cn.javabs.pet.mapper cn.javabs.pet.service.impl cn.javabs.pet.service cn.javabs.pet.controller cn.javabs.pet.util cn.javabs.pet.test ---- 客户模块: 1.2 创建实体类 package cn.javabs.entity; public class Users { // 客户编号
杨校
2020/12/16
7510
Java母婴商城母婴店孕妇商城婴幼儿商城网站系统源码
java使用ssm开发的母婴商城系统,用户可以注册浏览商品,加入购物车或者直接下单购买,在个人中心管理收货地址和订单,管理员也就是商家登录后台可以发布商品,上下架商品,处理待发货订单等。
飞一样的编程
2022/12/30
2.4K0
Spring高级技术应用——百战商城实现(下)
需要用到pojo,但是我们可以通过依赖Mapper项目来简介添加Pojo项目 需要用到Spring Data整合Solr的坐标
时间静止不是简史
2020/07/27
1.3K0
Spring高级技术应用——百战商城实现(下)
JavaEE + BootStrap 实现分页逻辑
Java EE + BootStrap 实现简单分页逻辑 一、项目准备 二、数据库准备 三、视图界面编写 四、后台处理 4.1 封装 BaseDao 4.2 对应的学生实体类 4.3 分页后台逻辑 GetAllServlet 五、运行效果 一、项目准备 准备一张单表,以学生为例,需要如下列,id,sname,sage,sgender index.jsp 用于跳转页面,second.jsp 用于显示分页查询的数据 getAllServlet 用于查询分页数据,并返回给前端 二、数据库准备 我使用的 mysql
Gorit
2021/12/08
1K0
JavaEE + BootStrap 实现分页逻辑
移动商城第七篇【购物车增删改查、提交订单】
把商品加入购物车 接下来我们要做的就是将商品加入到购物车中。我们这次使用的是Cookie来将用户的信息存储起来。那为什么要用cookie呢?? 如果将购物车存储在Session,那么Session里边的值容易丢失。 存储在Cookie中,用户不需要登陆就能够查看得到购物车的数据。 那我们现在决定将购物车存储在Cookie中了,那Cookie中保存的是怎么样的字符串数据呢??? 显而易见我们第一时间想到的就是JSON JSON能够很好地将字符串转成Java对象,将Java对象转成JSON给浏览器进行显示! 看
Java3y
2018/04/02
4.1K0
移动商城第七篇【购物车增删改查、提交订单】
springMVC基础
springMVC中默认的转发视图是InternalResourceView 当控制器方法中所设置的视图名称以“forward:”为前缀时,创建InternalResourceView视图,此时的视图名称不会被springmvc配置文件的视图解析器解析,而是通过转发方式实现跳转。
Java微观世界
2025/01/20
830
springMVC基础
我想在我自己的系统中加入微信支付功能,原来这么简单!!!
  然后添加Swagger的配置文件。创建对应的config包和对应的Swagger2Config配置类
用户4919348
2023/07/20
8460
我想在我自己的系统中加入微信支付功能,原来这么简单!!!
推荐阅读
相关推荐
java基于ssm开发的宠物商城宠物店源码
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验