草庐IT

SpringBoot自动配置(装配)流程

博客小新 2023-04-19 原文

源码分析

SpringBoot自动配置流程

​ 首先,我们要了解在@SpringBootApplication注解的内部,还具有@EnableAutoConfiguration,@SpringBootConfiguration,@ComponentScan三个主要注解。

@SpringBootConfiguration  //标注该类是配置类,需要通过该类查找自动配置文件
@EnableAutoConfiguration	//自动配置的关键注解 其内部就是执行自动配置的代码
@ComponentScan(excludeFilters = { 
  //type : 要使用的筛选器类型 , classes 指定类型筛选器 
  //TypeExcludeFilter.class 筛选掉spirngBootApplication中被指定排除的配置类
  @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),	
  //AutoConfigurationExcludeFilter 将配置类与spirng.factories中的EnableAutoConfiguration对应的配置类进行对比匹配, 如果一致,会被排除掉
	@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }
 )	//扫描指定包的文件,将带有特定注解的类注入到Bean中
public @interface SpringBootApplication {

}

@ComponentScan

  1. @ComponentScan注解主要用来扫描我们项目中的所有被像@service ,@Repository , @Controller,@configuration 等注解修饰的类, 将其注入到我们的IOC容器中,其中也包括我们的自动配置的文件:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)  //表示可以重复利用@ComponentScan注解
      /**
	*作用 : 可以扫描指定的包,如果未指定包范围,将从该注解标注类所在的包进行扫描,
	*  		 	与XML形式的<context:component scan>不同的是 @componentScan没有Config属性(true	 *	就开启了属性自动注入的功能,如果是false就是关闭属性自动注入的功能),因为使用
	*  @ComponentScan则默认所有的类都进行自动注入,会将所有扫描到的组件注入到IOC容器中
	*/
public @interface ComponentScan {
}

@SpringBootConfiguration

  1. @SpringBootConfiguration 是SpringBoot替代@Configuration的注解,增加了自动找到配置的功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration   //表示这是一个配置类 通过其间接了解到,@SpringBootApplication也是一个配置类
/**
  * 用作Spring的标准@Configuration注解的替代,以便可以自动找到配置
  */
public @interface SpringBootConfiguration {
}

@EnableAutoConfiguration

  1. @EnableAutoConfiguration注解就是启动自动配置的关键注解,其内部使用了@import注解引入了一个AutoConfigurationImportSelector 自动配置类选择器
@AutoConfigurationPackage //自动配置所在包注解,通过basePackages指定配置所在的包或者通过basePackageClasses指定基本包类,如果未指定,会默认注册指定注解类所在的包
//AutoConfigurationImportSelector自动配置选择器,实现了ImportSelector接口,重写了selectImports方法,自动配置的具体实现就在其内部进行
//ImportSelector接口作用 :根据给定的选择条件(通常是一个或多个注解属性)确定应导入哪个配置类。
@Import(AutoConfigurationImportSelector.class) 
public @interface EnableAutoConfiguration {

}

​ 在其内部重写了selectImports方法, 通过调用getAutoConfigurationEntry()方法根据传入的注解元数据,获取到自动配置类的实体,而后从实体中获取具体的配置信息,配置信息在实体内部是一个list集合,所以将其转化为String数组后返回。

//为方便显示及理解,省略了该类实现的部分接口和具体的代码实现,需要了解可进入源码查看 
public class AutoConfigurationImportSelector implements DeferredImportSelector {
  @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
	//AnnotationMetadata: 配置类的注解元数据,也就是配置类的注解信息
  //调用getAutoConfigurationEntry()方法根据传入的注解信息,获取并返回自动配置类的实体
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); 
    //从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}

getAutoConfigurationEntry()

//可以先看下获取的大致流程,而后进入查看器方法内部的具体实现
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	//1.  从注解元数据中获取注解的相应属性,将相应属性存储到map中返回
  	//1.1AnnotationAttributes是一个Map集合,其继承了LinkedHashMap
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
  	//2. 通过getCandidateConfigurations()方法根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置候选项
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	//2.1 通过removeDuplicates()方法对自动配置的类名进行去重处理
  configurations = removeDuplicates(configurations);
  	//3. 根据注解元数据和注解属性获取到需排除配置项
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  	//3.1检查是否有无效的排除类存在
		checkExcludedClasses(configurations, exclusions);
  	//3.2从自动配置候选项中删除需要排除的配置项 
		configurations.removeAll(exclusions);
  	//4. 调用getConfigurationClassFilter()方法获取到获取配置的所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
		configurations = getConfigurationClassFilter().filter(configurations);
  	//5. 根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集
		fireAutoConfigurationImportEvents(configurations, exclusions);
  	//6. 创建一个新的配置实体ConfigurationEntry并返回,包含需要配置项configurations,和被排除配置项exclusions
		return new AutoConfigurationEntry(configurations, exclusions);
	}

下面了解以下getAutoConfigurationEntry()内部调用的方法源码

从注解元数据中返回相应的属性信息

  1. getAttributes(AnnotationMetadata annotationMetadata)

    /**
     * 从注解元数据中返回相应的属性信息。
     * param 注解元数据信息
     * return   注解元数据的属性信息 其本质是一个Map集合
     */
    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
     /**
     * getAnnotationClass() 返回源注解类 -->EnableAutoConfiguration.class
     * getAnnotationClass().getName(); 获取注解类的完全限定类名
    */
      String name = getAnnotationClass().getName(); 
    /**
     * metadata.getAnnotationAttributes(String annotationName,boolean classValuesAsString) 
     * 作用: 检索给定注解的属性
     * @param1 要查找的注解类的完全限定类名
     * @param2 是否将类引用转换为String类名,以便作为返回Map中的值公开,而不是可能必须首先加载的类引用
     *
     *AnnotationAttributes.fromMap(@Nullable Map<String, Object> map);
     * 基于给定的集合返回AnnotationAttributes实例。如果该集合是AnnotationAttributes实例或其子类,它将被强制转换并立即返回,而无需创建新实例。否则,将通过将提供的映射传递给AnnotationAttributes的map)的构造函数来创建新实例。其参数是一个Map类型的注解属性数据源,也就是attrbuties
     */
      	AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
      	/**
      	 *Assert类 是一个协助验证参数的断言实用程序类,详细使用可以查看其源码
    	 * Assert.notNull(@Nullable Object object, Supplier<String> messageSupplier)方法
      	 * 作用 : 判断对象是不是null, 如果为null,报错提示
      	 * param1 : 要进行判断的对象
    	 * param2 : 如果为null,要给予返回的异常信息
    	  */
    		Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()+ " annotated with " + ClassUtils.getShortName(name) + "?");
    		//返回注解元数据的属性信息map集合
      	return attributes;
    	}
    

获取应该进行自动配置的类名

  1. getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes);

    /**
     *根据注解元数据和注解的属性信息 获取应该进行自动配置的类名,可以理解为自动配置的候选项(初选名单) 
     *param1 元注解数据 
     *param2 元注解数据的属性信息集合	
     *return List<String> 存储的数据就是应该继续宁自动配置的类名
    */
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
       		//SpringFactoriesLoader是一个用于框架内部使用的通用工厂加载机制
    		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    				getBeanClassLoader());
    		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    				+ "are using a custom packaging, make sure that file is correct.");
    		return configurations;
    }
    
    

对自动配置项进行去重处理

  1. 1 configurations = removeDuplicates(configurations);对自动配置的类名进行去重处理
//通过removeDuplicates()方法对自动配置的类名进行去重处理
//利用Set集合数据不重复特性,将list集合存储到LinkedHashSet集合中进行去重处理,而后再将去重的结果存储到List集合中返回 
protected final <T> List<T> removeDuplicates(List<T> list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
} 

从自动配置项中筛选被排除配置项

  1. configurations.removeAll(exclusions);
//从自动配置候选项中筛选需排除配置项

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  	//创建一个需排除配置项集合excluded
		Set<String> excluded = new LinkedHashSet<>();
  	//从属性信息集合中获取到key为exclude的值,将其存储到excluded集合中
		excluded.addAll(asList(attributes, "exclude"));
  	//从属性信息集合中获取到key为excludeName的数据,返回的是一个字符串数组,返回后将其转化为List集合,存储到excluded集合中
		excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
  /**
   * getExcludeAutoConfigurationsProperty():
   *返回 spring.autoconfigure.exclude 属性排除的自动配置
   */
  	excluded.addAll(getExcludeAutoConfigurationsProperty());
		return excluded;
}

-----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
-----------------------------------------------------------------------------------------

//attributes.getStringArray("excludeName")
public String[] getStringArray(String attributeName) {
		return getRequiredAttribute(attributeName, String[].class);
}

exclude 和excludeName 都是指定某些类在项目启动时不进行自动配置,其一般在@SpringBootApplication 中进行配置。

检查是否有无效的排除类存在

  1. 1 configurations.removeAll(exclusions);
//检查是否有无效的排除类存在
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
	//创建一个用于存储无效配置项的集合		
  List<String> invalidExcludes = new ArrayList<>(exclusions.size());
	//循环需排除配置项
  for (String exclusion : exclusions) {
    //根据类的全限定名判断该类是否存在且可以被加载,并且 需排除配置项集合是否包含该类
			if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
        //如果存在,且不再需排除配置项的集合中,将其添加到无效配置项集合中
				invalidExcludes.add(exclusion);
			}
		}
  	//如果无效配置项集合不为空,说明存在无效配置项
		if (!invalidExcludes.isEmpty()) {
      //处理无效配置项 --> 报错 IllegalStateException 无效状态异常
			handleInvalidExcludes(invalidExcludes);
		}
}
-----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
-----------------------------------------------------------------------------------------
 
/**
 *ClassUtils.isPresent() 根据类名称判断是否存在并且可以加载,如果类或其依赖项之一不存在或无法     加载返回false
 * param1 className 要检查的类的名称
 * param2 classLoader 要使用的类加载器(如果为null,表示默认的类加载器)
 */
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
      //forName(类名称,类加载器) 用于替换Class.forName()方法, 并且还返回所提供名称的类实例
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// Typically ClassNotFoundException or NoClassDefFoundError...
			return false;
		}
}

从自动配置项中删除需要被排除的配置项

  1. 2 configurations.removeAll(exclusions);
/**
 *从自动配置候选项中删除需要排除的配置项  
 * 集合A.removeAll(集合B);作用就是从集合A数据项中删除掉集合B所包含的元素
 */
configurations.removeAll(exclusions);

创建配置类过滤器对配置项进行筛选过滤

  1. configurations = getConfigurationClassFilter().filter(configurations);
//通过getConfigurationClassFilter()获取所有AutoConfigurationImportFilter的实现类(对spring.factories进行过滤的类),而后调用filter方法对配置文件进行筛选,而后返回需要自动配置的类
configurations = getConfigurationClassFilter().filter(configurations);

-----------------------------------------------------------------------------------------
/*下面方法是上面方法所调用的个别方法源码,不深究者可以略过*/
-----------------------------------------------------------------------------------------

//获取配置类过滤器
private ConfigurationClassFilter getConfigurationClassFilter() {
  //this.configurationClassFilter当前类的配置类过滤器是不是为null 
		if (this.configurationClassFilter == null) {
      // 获取AutoConfigurationImportFilter过滤器的实现类集合
      List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
     
      for (AutoConfigurationImportFilter filter : filters) {
				invokeAwareMethods(filter);  //在监听器注入是有描述,两者使用的同一方法
			}
      //实例化配置类过滤器 ,根据 类加载器和过滤器实现类实例化配置类过滤器
      //ConfigurationClassFilter类内部含有类加载器和过滤器实现类集合的属性
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
  	//返回配置类过滤器
		return this.configurationClassFilter;
}
//getAutoConfigurationImportFilters(); 获取AutoConfigurationImportFilter过滤器的实现类集合
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
  /**
   * List<T>  loadFacotries(Class<T> factoryType, @Nullable ClassLoader classLoader)
   * 使用给定的类加载器从{"META-INF/spring.factories"}加载并实例化指定过滤器工厂的实现类
   * 在结果返回之前会对结果集进行排序
   * param1 表示工厂的接口或者抽象类,-->生成其子类 
	 * param2 当前类的类加载器-->用于加载抽象类的实现类
	 * return 返回指定接口或者抽象类的实现类List集合
	 */
  	//返回AutoConfigurationImportFilter实现类的集合
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
	}

//configurations = getConfigurationClassFilter().filter(configurations);
//过滤 自动配置项
List<String> filter(List<String> configurations) {
			long startTime = System.nanoTime();
  		//将将配置转化为字符串数组
			String[] candidates = StringUtils.toStringArray(configurations);
			boolean skipped = false;
  		
			for (AutoConfigurationImportFilter filter : this.filters) {
        //循环过滤条件与配置项进行一一匹配,剔除掉条件不成立的配置项
				boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
				for (int i = 0; i < match.length; i++) {
					if (!match[i]) {
						candidates[i] = null;
						skipped = true;
					}
				}
			}
  		//如果全都符合则直接返回配置项集合
			if (!skipped) {
				return configurations;
			}
  		//创建结果集集合
			List<String> result = new ArrayList<>(candidates.length);
			for (String candidate : candidates) {
				//配置项不为null就添加到配置类中
        if (candidate != null) {
					result.add(candidate);
				}
			}
  		//返回结果
			return result;
		}
	}

创建配置类监听器对自动配置进行监听

  1. fireAutoConfigurationImportEvents(configurations, exclusions);
//根据spring.factories文件中的AutoConfigurationImportListener事件监听器发布并处理监听事件,最后根据多次过滤、判重返回配置类合集

private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
  	//从{ "META-INF/spring.factories"}加载并实例化自动配置类监听器			  AutoConfigurationImportListener的实现类集合
		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
  	//如果监听器不为空的话
		if (!listeners.isEmpty()) 
       //创建fireAutoConfigurationImportEvents监听事件 
		  AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
  		//循环遍历 判断listener是否是 Aware 通过Aware接口 实现对bean各阶段的监听
			for (AutoConfigurationImportListener listener : listeners) {
        //通过Aware类的实现类对监听器进行配置 -->解这一模块,可以重点关注以下Aware接口
				invokeAwareMethods(listener);  
				//进行自动配置的导入 event 到自动配置时进行的事件-->对自动配置的监听
        listener.onAutoConfigurationImportEvent(event);
			}
		}
}
//根据Aware类对bean的各阶段进行监听配置
private void invokeAwareMethods(Object instance) {
  	//判断监听器是否是Aware或其实现类
		if (instance instanceof Aware) {
		
      if (instance instanceof BeanClassLoaderAware) {
       /**
			 * BeanClassLoaderAware 允许bean知道bean的回调ClassLoader,即当前bean工厂用来加载bean类的类加载器。这主要是由框架类来实现的,这些框架类必须通过名称来获取应用程序类,尽管它们本身可能
从共享类加载器加载的。
			 * 方法 setBeanClassLoader(ClassLoader classLoader);
			 *		将bean的类加载器 提供给bean实例的回调。 
			 * 		作用范围: 在填充普通bean属性之后,初始化回调之前调用
			 */
				((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
			}
			if (instance instanceof BeanFactoryAware) {
      /**
			 * BeanFactoryAware 表示接口知道其拥有的{BeanFactory}的bean实现。
			 *注意 :大多数的bean都可以通过属性注入和构造注入接收对bean的引用
			 *方法 :setBeanFactory(BeanFactory beanFactory) 
			 *		向bean实例提供拥有工厂的回调。
			 * 		作用范围: 在填充普通bean属性之后,初始化回调之前调用,
			 */
        
				((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
			}
			if (instance instanceof EnvironmentAware) {
      /**
			 * EnvironmentAware 表示该接口可以收到其运行环境的通知
			 *方法 :setEnvironment(Environment environment);
			 *		设置此组件的运行环境
			 */
        
				((EnvironmentAware) instance).setEnvironment(this.environment);
			}
			if (instance instanceof ResourceLoaderAware) {
              /**
			 * ResourceLoaderAware 接口,该接口可以收到该对象运行的ResourceLoader资源封装类加载器
			 *方法 :setResourceLoader(ResourceLoader resourceLoader);
			 *作用 :设置此对象运行的ResourceLoader。可能是一个ResourcePatternResolver
			 *作用范围: 在填充普通bean属性之后,初始化回调之前调用
			 */
        
				((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
			}
		}
}

创建新的配置实体后返回SelectImports方法体内

  1. return new AutoConfigurationEntry(configurations, exclusions);
根据需要配置项和被排除项实例化新的配置实体,并返回
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
			this.configurations = new ArrayList<>(configurations);
			this.exclusions = new HashSet<>(exclusions);
}

将配置实体中的配置信息转化为字符串数组返回,完成注入

//获取最终要导入的配置实体
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); 
//从配置实体中获取具体的配置信息,返回的是一个list集合,而后通过toStringArray()方法转存到字符串数组中返回
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

本笔记个人查看源码时根据立即理解缩写,如有错误可留言告知,谢谢

有关SpringBoot自动配置(装配)流程的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  5. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  6. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  7. 神州数码无线产品(AC+AP)配置 - 2

    注意:本文主要掌握DCN自研无线产品的基本配置方法和注意事项,能够进行一般的项目实施、调试与运维AP基本配置命令AP登录用户名和密码均为:adminAP默认IP地址为:192.168.1.10AP默认情况下DHCP开启AP静态地址配置:setmanagementstatic-ip192.168.10.1AP开启/关闭DHCP功能:setmanagementdhcp-statusup/downAP设置默认网关:setstatic-ip-routegeteway192.168.10.254查看AP基本信息:getsystemgetmanagementgetmanaged-apgetrouteAP配

  8. hadoop安装之保姆级教程(二)之YARN的配置 - 2

    1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模

  9. Ruby 默认将 IRB 配置为 Pretty_Inspect - 2

    我是ruby​​的新手,正在配置IRB。我喜欢pretty-print(需要'pp'),但总是输入pp来漂亮地打印它似乎很麻烦。我想做的是默认情况下让它漂亮地打印出来,所以如果我有一个var,比如说,'myvar',然后键入myvar,它会自动调用pretty_inspect而不是常规检查。我从哪里开始?理想情况下,我将能够向我的.irbrc文件添加一个自动调用的方法。有什么想法吗?谢谢! 最佳答案 irb中默认pretty-print对象正是hirb被迫去做。Theseposts解释hirb如何将几乎所有内容转换为ascii表。虽

  10. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

随机推荐