idea建立:File->New->Project...->Spring Initializr->Default:https://start.spring.iojava
web建立:登陸https://start.spring.io/ ,選擇對應springboot版本,設置group和artifact下載就行\、web
注:springboot項目默認沒有加載web,須要本身導入maven依賴spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
這裏採用的是web下載方式,打開後,找到springboot啓動類json
package com.mkpassby.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
@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 { ... }
主要看有springboot
@SpringBootConfiguration:聲明配置類mvc
@EnableAutoConfiguration:自動化配置app
@ComponentScan:包掃描框架
這裏@EnableAutoConfiguration是經過將@Import導入AutoConfigurationImportSelector.class注入bean容器中maven
springboot中大量使用了@import註解:ide
@Import支持三種方式的導入:
直接導入一個配置類或者Bean
導入ImportSelector的實現類
導入ImportBeanDefinitionRegistrar的實現類
public class User { private String name="aaa"; public String getName() { return name; } public void setName(String name) { this.name = name; }}
@Import(User.class) public class App { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext= SpringApplication.run(App.class,args); System.out.println(configurableApplicationContext.getBean(User.class)); System.out.println(configurableApplicationContext.getBean(User.class).getName()); configurableApplicationContext.close(); } }
ImportSelector方法的實現,return new String[]{「com.mkpassby.demo.User」}
public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); }
ImportBeanDefinitionRegistrar和ImportSelector相似,註冊額外的bean
public class UserImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { beanDefinitionRegistry.registerBeanDefinition("User",new RootBeanDefinition(User.class)); } }
SpringBoot啓動由一個main方法調入,由靜態run方法啓動
@SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
run方法中new SpringApplication(),在構造器調用WebApplicationType.deduceFromClasspath(),斷定當前應用類型,我這裏用的是2.1.6.RELEASE的版本,這裏能夠看到有一個WebApplicationType.REACTIVE,這個是對webflux非阻塞web框架的支持。
static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
且在構造器中對全部包下的META-INF/spring.factories中配置的ApplicationContextInitializer和ApplicationListener實例化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
實例化SpringApplication完成後,調用run方法,實例化SpringApplicationRunListener,開啓監聽,準備數據等,其中核心在refreshContext(context)方法。在invokeBeanFactoryPostProcessors()方法中。該方法調用了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())方法,此處第二個傳參getBeanFactoryPostProcessors()傳過來的是
0 = {SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor@3037} 1 = {ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor@3038} 2 = {ConfigurationClassPostProcessor@3787}
,這裏是經過監聽add到list中,此處具體實現須要進一步探究,最終經過postProcessBeanDefinitionRegistry進行BeanDefinition註冊的處理。
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } //獲取全部標記了@Configuration註解類,封裝成BeanDefinitionHolder集合 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 沒有 @Configuration 返回 if (configCandidates.isEmpty()) { return; } // 根據@configuration中@Order排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
繼續跟進postProcessBeanDefinitionRegistry方法,後面調用了org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass方法,這裏對@Configuration類進行了解析,包括@PropertySource,@ComponentScan,@Import,@ImportResource等。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
後續就是實例化全部的bean(Instantiate all remaining (non-lazy-init) singletons.),這裏因爲引入了spring-boot-starter-web依賴,特別說明下RequestMappingHandlerAdapter實例化。
在實例化RequestMappingHandlerAdapter時會調用org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter
@Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); adapter.setContentNegotiationManager(mvcContentNegotiationManager()); adapter.setMessageConverters(getMessageConverters()); adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); adapter.setCustomArgumentResolvers(getArgumentResolvers()); adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice())); adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice())); } AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); configureAsyncSupport(configurer); if (configurer.getTaskExecutor() != null) { adapter.setTaskExecutor(configurer.getTaskExecutor()); } if (configurer.getTimeout() != null) { adapter.setAsyncRequestTimeout(configurer.getTimeout()); } adapter.setCallableInterceptors(configurer.getCallableInterceptors()); adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); return adapter; }
進入getMessageConverters方法中,這裏注意會由configureMessageConverters()方法進入org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConverters
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configureMessageConverters(converters); } }
getMessageConverters方法,這裏主要是從configureMessageConverters初始化messageConverters,之後後面的extendMessageConverters擴展messageCoverters。
protected final List<HttpMessageConverter<?>> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<>(); configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { addDefaultHttpMessageConverters(this.messageConverters); } extendMessageConverters(this.messageConverters); } return this.messageConverters; }
這裏若是須要擴展Http的請求擴展則能夠查看類WebMvcConfigurer接口的註解說明,這裏
主要看
/** * Configure the {@link HttpMessageConverter HttpMessageConverters} to use for reading or writing * to the body of the request or response. If no converters are added, a * default list of converters is registered. * <p><strong>Note</strong> that adding converters to the list, turns off * default converter registration. To simply add a converter without impacting * default registration, consider using the method * {@link #extendMessageConverters(java.util.List)} instead. * @param converters initially an empty list of converters */ default void configureMessageConverters(List<HttpMessageConverter<?>> converters) { } /** * A hook for extending or modifying the list of converters after it has been * configured. This may be useful for example to allow default converters to * be registered and then insert a custom converter through this method. * @param converters the list of configured converters to extend. * @since 4.1.3 */ default void extendMessageConverters(List<HttpMessageConverter<?>> converters) { }
這裏貼出一段測試代碼,可調整PostMapping中的produces和consumes查看變化
經過對HttpConvert的修改,咱們的代碼能夠適配成咱們想要的入參或者出參,固然,應用場景多用json格式,能夠考慮本身封裝json對應的轉換器,去除掉springboot中默認的json轉換器
@Configuration public class MyWebMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new PropertiesToUserConverter()); } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new PropertiesToUserConverter()); } }
/** * @program: springboot * @description: * @author: mk_passby * @create: 2019-06-25 22:19 **/ public class PropertiesToUserConverter extends AbstractHttpMessageConverter<User> { public PropertiesToUserConverter() { super(MediaType.valueOf("application/properties+person")); setDefaultCharset(Charset.forName("UTF-8")); } @Override protected boolean supports(Class<?> aClass) { return aClass.isAssignableFrom(User.class); } //轉換入參 @Override protected User readInternal(Class<? extends User> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { InputStream inputStream=httpInputMessage.getBody(); Properties properties=new Properties(); //請求內容properties轉換爲User對象 properties.load(inputStream); User user=new User(); user.setName(properties.getProperty("user.name")); return user; } /*** * @param user * @param httpOutputMessage * @throws IOException * @throws HttpMessageNotWritableException */ //用properties格式寫出去 @Override protected void writeInternal(User user, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException { OutputStream outputStream=httpOutputMessage.getBody(); Properties properties=new Properties(); properties.setProperty("user.name",user.getName()); properties.store(new OutputStreamWriter(outputStream,getDefaultCharset()),"from web server"); } }
@RestController public class RestControllerDemo { @PostMapping( value = "user/properties/to/json", produces = "application/properties+person",//出參類型Accept consumes = "application/properties+person"//入參類型Content-Type ) public User userToProperties(@RequestBody User user) { return user; } }
用postman模擬請求,結果以下