springboot源码(1)
说到springboot,应该就不自觉地想起一个词“自动装配”;这就跟spring对应的“依赖注入”、“控制反转”一样哈;
而要了解自动装配,其实我们还是要先回归到spring源码,先去了解什么是java config技术;
这里我以纯java来替代web.xml来做例子,其实config技术你简单理解为我们用代码来替代配置类就可以了;
这里的SpringmvcConfig.class其实就是另一个用纯java来配置spring mvc的一个类,我们把它注册到web.xml,也就是这个类中;其实上面比较难以理解的就是为什么要继承WebApplicationInitializer这个类,其实这是为了保证我们调用tomcat容器运行run的时候可以调用这里的onStartup方法,因为Servlet3.0Api的一个新规范SPI,协议好了如果你有类或方法需要在容器启动时被调用的话,可以在项目根目录下的一个指定目录META-INF/services下先配置好就行:
我们打开这个类:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { /** * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer} * implementations present on the application classpath. * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Spring's {@code WebApplicationInitializer} interface and provide the set of all * such types to the {@code webAppInitializerClasses} parameter of this method. * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath, * this method is effectively a no-op. An INFO-level log message will be issued notifying * the user that the {@code ServletContainerInitializer} has indeed been invoked but that * no {@code WebApplicationInitializer} implementations were found. * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and <em>sorted</em> if the @{@link * org.springframework.core.annotation.Order @Order} annotation is present or * the {@link org.springframework.core.Ordered Ordered} interface has been * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)} * method will be invoked on each instance, delegating the {@code ServletContext} such * that each instance may register and configure servlets such as Spring's * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}, * or any other Servlet API componentry such as filters. * @param webAppInitializerClasses all implementations of * {@link WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized * @see WebApplicationInitializer#onStartup(ServletContext) * @see AnnotationAwareOrderComparator */ @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = Collections.emptyList(); if (webAppInitializerClasses != null) { initializers = new ArrayList<>(webAppInitializerClasses.size()); 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 (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
简单地解释下:其实就是重写了onStartup方法,把项目里所有实现了WebApplicationInitializer的实现类在tomcat容器运行时都实例化好并运行里面各自的onStartup方法,这样我们就不用把每个想跟随容器启动调用的类都写在目录META-INF/services下了,而只需要让他继承WebApplicationInitializer这个接口就好;
至此,java config技术就讲解完毕了,是不是觉得也没什么;
而springboot的自动装配原理,其实就是springboot帮你准备好了一堆这样的纯java配置类,同样的,我们来看源码做了些什么:
可以看到,其实新建一个springboot项目其实里面就这一个SpringBootApplication的注解,我们打开它:
我们重点看这个EnableAutoConfiguration注解里做了什么:
对应那个返回路径字符串数组的方法:
至于要了解是怎么扫描到的,我们就要进去getAutoConfigurationEntry这个方法了:
getCandidateConfigurations方法:
loadFactoryNames方法:
loadSpringFactories方法:
这个首先会先在cache里取,因为我们不会在每次用到配置类的时候再去提取记录了配置类的文件,而是在一开始就把这个文件加载到内存,在内存没找到了,再去FACTORIES_RESOURCE_LOCATION这个位置去提取出来,而这个变量所对应的路径就是:
也就是最终扫描springboot为我们准备好的配置类是在这个位置:
所以,学完这个自动装配原理后,我们如果想让自己的配置类能被springboot自动扫描到容器中,其实也可以在自己项目里新建一个META-INF/spring.factories的文件,里面写好了对应的路径,注意路径前的前缀要跟springboot给我提供的自动装配文件一样,因为在扫描时springboot是会根据这个前缀来扫描下面的字符串路径的: