The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config)-- 来自spring官网
//首先实现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目录下的文件
实现的接口
spring 对servlet3.0规范中onStartup()方法的实现
@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项目启动application
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注解
@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注解
@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项目目录结构
@Import加载的类:AutoConfigurationImportSelector,其实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
DeferredImportSelector接口
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进行管理。