The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config)--來自spring官網html
//首先實現WebApplicationInitializer接口中onStartUp()方法,容器啓動時候會調用該方法 public class MyWebApplicationInitializer implements WebApplicationInitializer { //容器爲何會調用onStartup()方法 //servlet 3.0 提出了spi規範,一個項目自定義的WebApplicationInitializer若是想被容器調用(tomcat), //只須要在項目中meta-inf/service目錄下定義一個文件, //文件名必須爲javax.servlet.ServletContainerInitializer, //該名字爲一個接口,文件內容爲實現該接口的類, //Spring實現該接口的類的名稱爲org.springframework.web.SpringServletContainerInitializer,spring項目中該文件內容也爲該實現類的全路徑。 //該接口爲servlet中定義的,容器實現該接口,Spring也實現該接口,以此達到容器(tomcat)啓動, //可以調用到自定義的WebApplicationInitializer @Override public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration //spring 容器對象 AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); //AppConfig類爲配置 ac.register(AppConfig.class); ac.refresh(); //spring容器配置完成 // Create and register the DispatcherServlet //設置spring mvc DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); //設置爲優先啓動 registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
spring實現servlet3.0規範中,meta-inf/services目錄下的文件java
實現的接口web
spring 對servlet3.0規範中onStartup()方法的實現spring
@HandlesTypes(WebApplicationInitializer.class) //該註解將保證容器(tomcat)首先掃描WebApplicationInitializer接口(該接口能夠自定義,spring使用的是WebApplicationInitializer接口)的全部實現類,而後賦值到onStartup()方法參數的webAppInitializerClasses set集合中。 public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); //存放new 出來的對象 if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); //for循環,依次調用new 出來的對象,調用其onStartup()方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
Springboot項目啓動applicationtomcat
package com.xiayu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @SpringBootApplication//Springboot項目啓動註解 @EnableConfigurationProperties public class XiayuRoutingDatasourceApplication { public static void main(String[] args) { SpringApplication.run(XiayuRoutingDatasourceApplication.class); } }
@SpringbootApplication註解springboot
@Target(ElementType.TYPE)//定義該註解做用於什麼上面,是類,接口,方法,方法參數等等上面,ElementType.TYPE定義在類和接口上面 @Retention(RetentionPolicy.RUNTIME)//註解在運行時生效 @Documented 聲明該註解會被在javadoc文檔信息中展現 @Inherited 該註解保證註解聲明在一個父類上後,子類也會有相同的屬性 //上述都是java自帶的註解 @SpringBootConfiguration//該註解僅僅聲明配置類 @EnableAutoConfiguration//Springboot的核心配置註解 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
@EnableAutoConfiguration註解mvc
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage //配置自動掃描包,掃描當前註解所在的目錄,若是當前配置在com.xiayu包下,就至關於 //配置了@ComponentScan("com.xiayu") //這也是爲何Springboot項目啓動類與各個模塊同級 @Import(AutoConfigurationImportSelector.class) //真正的核心自動配置類,加載AutoConfigurationImportSelector類到spring容器中,並做爲bean管理 public @interface EnableAutoConfiguration {
典型的Springboot項目目錄結構
app
@Import加載的類:AutoConfigurationImportSelector,其實現了DeferredImportSelector接口ide
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
DeferredImportSelector接口this
package org.springframework.context.annotation; public interface DeferredImportSelector extends ImportSelector { } package org.springframework.context.annotation; ------------------------------------------------------- import org.springframework.core.type.AnnotationMetadata; public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); //該接口在AutoConfigurationImportSelector類中有具體的實現 }
AutoConfigurationImportSelector類中 selectImports方法的具體實現
//返回自動配置類的路徑,交由spring管理,完成整個bean生命週期 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); //從meta-inf/spring.factories文件中加載已經定義的配置類的全路徑 List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes); configurations = removeDuplicates(configurations); configurations = sort(configurations, autoConfigurationMetadata); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } catch (IOException ex) { throw new IllegalStateException(ex); } }
getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); //返回List<String>,list中包含自動配置類的全路徑 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類中定義了自動配置類全路徑保存的文件
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();
meta-inf/spring.factories文件,若是存在多個該文件,也會被掃描到,掃描到後就會將這些類經過@Import註解加載到spring容器中,被Spring進行管理。
springboot中Rabbitmq的配置文件路徑爲:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
RabbitAutoConfiguration類
RabbitProperties類
@ConfigurationProperties(prefix = "spring.rabbitmq") //獲取配置文件中spring.rabbitmq開頭的全部配置, //映射到RabbitProperties類中的屬性中 public class RabbitProperties { /** * RabbitMQ host. */ private String host = "localhost"; //默認的host /** * RabbitMQ port. */ private int port = 5672; //默認的port /** * Login user to authenticate to the broker. */ private String username = "guest"; //默認的用戶名 /** * Login to authenticate against the broker. */ private String password = "guest";//默認的密碼 /** * SSL configuration. */ private final Ssl ssl = new Ssl(); /** * Virtual host to use when connecting to the broker. */ private String virtualHost; /** * Comma-separated list of addresses to which the client should connect. */ private String addresses; /** * Requested heartbeat timeout; zero for none. If a duration suffix is not specified, * seconds will be used. */ @DurationUnit(ChronoUnit.SECONDS) private Duration requestedHeartbeat; /** * Whether to enable publisher confirms. */ private boolean publisherConfirms; /** * Whether to enable publisher returns. */ private boolean publisherReturns; /** * Connection timeout. Set it to zero to wait forever. */ private Duration connectionTimeout; /** * Cache configuration. */ private final Cache cache = new Cache(); /** * Listener container configuration. */ private final Listener listener = new Listener(); private final Template template = new Template(); private List<Address> parsedAddresses; public String getHost() { return this.host; } }