什么是spring.factories spring.factories是一个配置文件(spring的工厂),放到META-INF文件夹中。存放key-value关系的文件(key就是bean工厂的接口,下面的value就是对应的实现类)。主要用于Spring-Boot中扩展的问题。这个有点像Java SPI。spring.factories文件存放的是全类名,用于加载到Spring的Bean容器中。而不是通过外部声明注入。(Bean工厂实际就是生成Bean)
在spring-core的中SpringFactoriesLoader
类去加载配置在spring.factories中的类(也就是之前定义好的装 好自定义Bean的类)。这个是spring-boot-autoconfigure的jar包中META-INF文件夹中spring.factories中的配置文件。这里面包含初始化(Initializers)的Bean Factory的key org.springframework.context.ApplicationContextInitializer
;应用监听器(Application Listeners)的key org.springframework.context.ApplicationListener
;配置的过滤器(Auto Configuration Import Listeners) org.springframework.boot.autoconfigure.AutoConfigurationImportListener
;自动配置(Auto Configure)org.springframework.boot.autoconfigure.EnableAutoConfiguration
;可以看出用,
分割。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\ org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
Spring如何处理spring.factories文件 在spring-core的包中SpringFactoriesLoader类去加载spring.factories文件。里面有描述他如何加载这些bean工厂。下面我们就看源码去分析一下,加载spring.factories文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>(); public static <T> List<T> loadFactories (Class<T> factoryClass, @Nullable ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null" ); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List<T> result = new ArrayList<>(factoryNames.size()); for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } public static List<String> loadFactoryNames (Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null ) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]" , ex); } } @SuppressWarnings("unchecked") private static <T> T instantiateFactory (String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) { try { Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader); if (!factoryClass.isAssignableFrom(instanceClass)) { throw new IllegalArgumentException( "Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]" ); } return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex); } } }
经过上面的分析我们能给出,他加载Bean工厂的实例和过程。根据类名获取加载类信息。下面是具体的流程图。
上面就是,spring-boot在启动时候加载的Bean工厂。说到这个自动加载Bean工厂,也就是spring-boot很很核心的东西,就是自动配置,在SpringBootApplication类中去启用自动配置,那么好多之前在Jar里面的Bean就可以在Spring启动时候注入进去,而不是要在Xml中做固定的配置,他会自动去找,这样可以使用自动配置。可以使用spring-boot-starter中的jar中bean工厂,直接创建默认的bean。
Spring-Boot的自动配置 @SpringBootApplication注解 一般实现自动配置都是使用@SpringBootApplication
注解,下面我们就分析一下这个注解的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
我们从源码可以看出来@SpringBootApplication
注解是复合注解,包括自动配置的注解@EnableAutoConfiguration
组件包扫描的注解@ComponentScan
还有配置的注解@SpringBootConfiguration
。而自动配置是依赖于@EnableAutoConfiguration
这个注解。
@EnableAutoConfiguration注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
从上面的类中我们只是看到了注解,和一个Impor导入的Selector。这个AutoConfigurationImportSelector
就是自动注入的核心的。AutoConfigurationImportSelector
类方法不多,大多数都是private
和protected
的方法。我们说一下,几个被别人调用的public
方法。
AutoConfigurationImportSelector类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware , ResourceLoaderAware , BeanFactoryAware , EnvironmentAware , Ordered { private static final String[] NO_IMPORTS = {}; private static final Log logger = LogFactory .getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude" ; private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this .beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } protected boolean isEnabled (AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { return getEnvironment().getProperty( EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true ); } return true ; } protected AnnotationAttributes getAttributes (AnnotationMetadata metadata) { String name = getAnnotationClass().getName(); AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(name, true )); Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?" ); return attributes; } protected Class<?> getAnnotationClass() { return EnableAutoConfiguration.class; } protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } 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()) { handleInvalidExcludes(invalidExcludes); } } protected void handleInvalidExcludes (List<String> invalidExcludes) { StringBuilder message = new StringBuilder(); for (String exclude : invalidExcludes) { message.append("\t- " ).append(exclude).append(String.format("%n" )); } throw new IllegalStateException(String .format("The following classes could not be excluded because they are" + " not auto-configuration classes:%n%s" , message)); } protected Set<String> getExclusions (AnnotationMetadata metadata, AnnotationAttributes attributes) { Set<String> excluded = new LinkedHashSet<>(); excluded.addAll(asList(attributes, "exclude" )); excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName" ))); excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; } private List<String> getExcludeAutoConfigurationsProperty () { if (getEnvironment() instanceof ConfigurableEnvironment) { Binder binder = Binder.get(getEnvironment()); return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class) .map(Arrays::asList).orElse(Collections.emptyList()); } String[] excludes = getEnvironment() .getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); return (excludes != null ) ? Arrays.asList(excludes) : Collections.emptyList(); } private List<String> filter (List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); String[] candidates = configurations.toArray(new String[configurations.size()]); boolean [] skip = new boolean [candidates.length]; boolean skipped = false ; for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { invokeAwareMethods(filter); boolean [] match = filter.match(candidates, autoConfigurationMetadata); for (int i = 0 ; i < match.length; i++) { if (!match[i]) { skip[i] = true ; skipped = true ; } } } if (!skipped) { return configurations; } List<String> result = new ArrayList<String>(candidates.length); for (int i = 0 ; i < candidates.length; i++) { if (!skip[i]) { result.add(candidates[i]); } } if (logger.isTraceEnabled()) { int numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms" ); } return new ArrayList<String>(result); } protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters () { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this .beanClassLoader); } private void fireAutoConfigurationImportEvents (List<String> configurations, Set<String> exclusions) { List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this , configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } } protected final <T> List<T> removeDuplicates (List<T> list) { return new ArrayList<>(new LinkedHashSet<>(list)); } protected final List<String> asList (AnnotationAttributes attributes, String name) { String[] value = attributes.getStringArray(name); return Arrays.asList((value != null ) ? value : new String[0 ]); } protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners () { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this .beanClassLoader); } private void invokeAwareMethods (Object instance) { if (instance instanceof Aware) { if (instance instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) instance) .setBeanClassLoader(this .beanClassLoader); } if (instance instanceof BeanFactoryAware) { ((BeanFactoryAware) instance).setBeanFactory(this .beanFactory); } if (instance instanceof EnvironmentAware) { ((EnvironmentAware) instance).setEnvironment(this .environment); } if (instance instanceof ResourceLoaderAware) { ((ResourceLoaderAware) instance).setResourceLoader(this .resourceLoader); } } } }
上面是讲如何通过这个注解获取到,spring.fatories中的bean工厂的配置。然后Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,如果发现注解中存在@Import(ImportSelector)的情况,就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 这里 AutoConfigurationImportSelector的导入@Import(AutoConfigurationImportSelector.class) 就属于这种情况,所以ConfigurationClassParser会实例化一个 AutoConfigurationImportSelector并调用它的 selectImports() 方法。实现Bean的注册和注入。关于ConfigurationClassParser的实现,这里有点长,我们后续在扩展(埋坑)。
自动注入的流程图
总结 在日常工作中,我们可能需要实现一些 Spring Boot Starter 给被人使用,这个时候我们就可以使用这个 Factories 机制,将自己的 Starter 注册到 org.springframework.boot.autoconfigure.EnableAutoConfiguration
命名空间下。这样用户只需要在服务中引入我们的 jar 包即可完成自动加载及配置。Factories 机制可以让 Starter 的使用只需要很少甚至不需要进行配置。
本质上,Spring Factories 机制与 Java SPI 机制原理都差不多:都是通过指定的规则,在规则定义的文件中配置接口的各种实现,通过 key-value 的方式读取,并以反射的方式实例化指定的类型。
了解关于自动注入,也是依赖了几个注解。后面我会写一个关于自己如何实现一个spring-boot-starter。
参考 Spring Boot 自动配置及 Factories 机制总结
Spring EnableAutoConfigurationImportSelector 是如何工作的