author:阿风/Alan
公众号:阿风的JAVA
Spring Boot在进行SpringApplication对象实例化时会加载META-INF/spring.factories文件,将该配置文件中的配置载入到Spring容器。
让我们j进入@SpringBootApplication
123456789101112
@Target({ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan( ... ...)public @interface SpringBootApplication { ... ...}
@Target({ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
… …
)
public @interface SpringBootApplication {
… …
}
进入@EnableAutoConfiguration
123456789
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration { ... ...}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
… …
}
进入AutoConfigurationImportSelector.class找到的selectImports方法
123456789101112131415161718
public class AutoConfigurationImportSelector{ public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = this.getAttributes(annotationMetadata); //这里的调用的getCandidateConfigurations()方法 ListString configurations = this.getCandidateConfigurations()方法(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); SetString exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } }
public class AutoConfigurationImportSelector{
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//这里的调用的getCandidateConfigurations()方法
ListString configurations = this.getCandidateConfigurations()方法(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
SetString exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
找到getCandidateConfigurations()方法:
123456
protected ListString getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //进入SpringFactoriesLoader.loadFactoryNames() 方法 ListString configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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 ListString getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//进入SpringFactoriesLoader.loadFactoryNames() 方法
ListString configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
进入SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
1234
public static ListString loadFactoryNames(Class? factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
public static ListString loadFactoryNames(Class? factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
该方法又调用了loadSpringFactories()方法
12345678910
private static MapString, ListString loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMapString, String result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { EnumerationURL urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); ... ... } }
private static MapString, ListString loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMapString, String result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
EnumerationURL urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”);
… …
}
}
由此我们可以看到自动配置加载的文件:”META-INF/spring.factories”
然后spring boot会根据对应的jar文件进行相应的自动配置
举例说明,文件上传:
1.我们从spring.factories找到文件上传的部分,搜索Multipart可以查看到下面的全类名
1
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration
2.查看MultipartAutoConfiguration类
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package org.springframework.boot.autoconfigure.web.servlet;import javax.servlet.MultipartConfigElement;import javax.servlet.Servlet;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.multipart.MultipartResolver;import org.springframework.web.multipart.commons.CommonsMultipartResolver;import org.springframework.web.multipart.support.StandardServletMultipartResolver; @Configuration@ConditionalOnClass({Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class})@ConditionalOnProperty( prefix = "spring.servlet.multipart", name = {"enabled"}, matchIfMissing = true)@ConditionalOnWebApplication( type = Type.SERVLET)@EnableConfigurationProperties({MultipartProperties.class})public class MultipartAutoConfiguration { private final MultipartProperties multipartProperties; public MultipartAutoConfiguration(MultipartProperties multipartProperties) { this.multipartProperties = multipartProperties; } @Bean @ConditionalOnMissingBean({MultipartConfigElement.class, CommonsMultipartResolver.class}) public MultipartConfigElement multipartConfigElement() { return this.multipartProperties.createMultipartConfig(); } @Bean( name = {"multipartResolver"} ) @ConditionalOnMissingBean({MultipartResolver.class}) public StandardServletMultipartResolver multipartResolver() { StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver(); multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily()); return multipartResolver; }}
package org.springframework.boot.autoconfigure.web.servlet;
import javax.servlet.MultipartConfigElement;
import javax.servlet.Servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
@Configuration
@ConditionalOnClass({Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class})
@ConditionalOnProperty(
prefix = “spring.servlet.multipart”,
name = {“enabled”},
matchIfMissing = true
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({MultipartProperties.class})
public class MultipartAutoConfiguration {
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
this.multipartProperties = multipartProperties;
}
@Bean
@ConditionalOnMissingBean({MultipartConfigElement.class, CommonsMultipartResolver.class})
public MultipartConfigElement multipartConfigElement() {
return this.multipartProperties.createMultipartConfig();
}
@Bean(
name = {“multipartResolver”}
)
@ConditionalOnMissingBean({MultipartResolver.class})
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
}
我们可以看到其中的配置
12345678910
@Bean( name = {"multipartResolver"} ) @ConditionalOnMissingBean({MultipartResolver.class}) public StandardServletMultipartResolver multipartResolver() { StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver(); multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily()); return multipartResolver; } //默认的multipartResolver配置是StandardServletMultipartResolver
@Bean(
name = {“multipartResolver”}
)
@ConditionalOnMissingBean({MultipartResolver.class})
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
//默认的multipartResolver配置是StandardServletMultipartResolver
我们再来看StandardServletMultipartResolver这个对文件上传的的解决方案
将普通的request转换为StandardMultipartHttpServletRequest
1234
@Overridepublic MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException { return new StandardMultipartHttpServletRequest(request, this.resolveLazily);}
@Override
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
而StandardMultipartHttpServletRequest内部封装了StandardMultipartFile
12345678910
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {/** * Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object. */@SuppressWarnings("serial")private static class StandardMultipartFile implements MultipartFile, Serializable { private final Part part; private final String filename; ...
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
/**
* Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object.
*/
@SuppressWarnings(“serial”)
private static class StandardMultipartFile implements MultipartFile, Serializable {
private final Part part;
private final String filename;
…
这个就是默认的springboot的文件上传的解决方案,也就是其MultipartFile默认的类型
因为其实一个私有的内部类StandardMultipartFile,对外不暴露,所以并不能对其进行额外的操作,如果想要将MultipartFile转换为一个File类型,并没有提供这样的一个操作,而springboot也提供了另一个MultipartFile的子类型CommonsMultipartFile
对于这样的一个类型,springboot也提供了解决方案CommonsMultipartResolver,因为之前配置的MultipartResolver的出了@Bean注解还有一个@ConditionalOnMissingBean({MultipartResolver.class})意思是当MultipartResolver.class不存在才会加载这个默认的类型,所以我们只需要配置一个自己的MultipartResolve就可以使用了
1234567891011121314151617181920
//使用CommonsMultipartResolver前提,需要导入commons-fileupload依赖 !--文件上传-- dependency groupIdcommons-fileupload/groupId artifactIdcommons-fileupload/artifactId version1.3.1/version /dependency@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setDefaultEncoding("UTF-8"); multipartResolver.setMaxUploadSize(20971520); multipartResolver.setMaxInMemorySize(1048576); multipartResolver.setResolveLazily(true); return multipartResolver;} //需要注意的问题://因为其走的是自动配置的类,所以我们在启动springboot的时候需要排除相应的自动配置//@SpringBootApplication(exclude = { MultipartAutoConfiguration.class})
//使用CommonsMultipartResolver前提,需要导入commons-fileupload依赖
!–文件上传–
dependency
groupIdcommons-fileupload/groupId
artifactIdcommons-fileupload/artifactId
version1.3.1/version
/dependency
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setDefaultEncoding(“UTF-8”);
multipartResolver.setMaxUploadSize(20971520);
multipartResolver.setMaxInMemorySize(1048576);
multipartResolver.setResolveLazily(true);
return multipartResolver;
}
//需要注意的问题:
//因为其走的是自动配置的类,所以我们在启动springboot的时候需要排除相应的自动配置
//@SpringBootApplication(exclude = { MultipartAutoConfiguration.class})
然后就可以强转类型了
1234
MultipartFile file = xxx; CommonsMultipartFile cf= (CommonsMultipartFile)file; DiskFileItem fi = (DiskFileItem)cf.getFileItem();File f = fi.getStoreLocation();
MultipartFile file = xxx;
CommonsMultipartFile cf= (CommonsMultipartFile)file;
DiskFileItem fi = (DiskFileItem)cf.getFileItem();
File f = fi.getStoreLocation();
每个自动配置都有其不同的解决步骤,如果将默认的自动配置改为自定义的配置,只需要搜索spring.factories中的自动配置类,查看相应的类,写出对应的操作即可
文章如有错误,请您一定指出,感谢之至!
如果你有不同的见解,欢迎留言
图片可能来源于网络,如有侵权请告知。
文章中的资料有时忘记书写来源,如果需求请告知
最后:关注一下呗
长按二维码识别关注