springboot源码解读,我们先从springboot的应用开始。 @RestController修饰后的类里面的方法不用再使用@ResponseBody注解
源码编译Spring Boot官方建议使用./mvnw clean install或者标准的mvn clean install命令来编译源代码,如果要使用标准的mvn命令的话,Maven的版本要求在3.5.0或以上。使用下面命令 ./mvnw clean install [-DskipTests -Pfast] mvn -Dmaven.test.skip=true clean install 使用上面命令构建时,会自动下载maven,然后使用新下载的maven来完成构建。此时的本地仓库地址都变了,不再是我们本机指定的目录了。直接使用 maven install 。
spring web源码springboot源码源码不能立竿见影,需要慢慢沉淀! spring reactive 使用了非servlet的web项目在springMvc中,
在加载DispatchServlet时就会执行静态代码块。在静态代码块中会加载配置
static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } }加载的配置如下:配置文件在spring-webmvc的resources目录下 完成了配置文件的加载后,在调用DispatchServlet的init方法(父类的),最终经过一系列的调用链,执行handlerMapping的初始化。调用链下面我们会讲解到。
在springMvc中,一个请求如何到java类(我们使用自定义的请求类接受请求的参数)? 1,使用servlet技术,一个请求过来,会被servlet接受到。 2,springMvc能够将请求使用非servlet的java类接受。中间会使用一个servlet(Spring提供)接受,然后在servlet内部进行方法调用。 servlet会在方法内部使用反射,然后调用方法(方法调用一定要知道类名,方法名呀?)。将路径映射到方法,放到一个map中,在匹 配到路径时,便会利用反射创建那个类的对象,调用那个对应的方法。DispatchServlet的init()就是用于初始化controller和请求映射。
Tomcat tomcat = new Tomcat(); tomcat.setPort(9090); /** * addWebapp()表示这是一个web项目,web项目,tomcat底层会去加载jsp相关的类 * contextPath 表示tomcat的访问路径 * 第二个参数表示项目的web目录 * 这里不能这么addWebapp()(springboot也没有这么做,可以查看TomcatServerWebServletFactory) */ // tomcat.addWebapp("/", ""); // 此时会调用处理WebApplicationInitializer,会报错没有web.xml Context tomcatCtx = tomcat.addContext("/",base.getAbsolutePath()); //不会调用WebApplicationInitializer,因此没法初始化spring环境了,因此直接在这个方法里初始化spring DispatcherServlet dispatcherServlet = new DispatcherServlet(actx); Tomcat.addServlet(tomcatCtx,"eason", dispatcherServlet).setLoadOnStartup(1); tomcatCtx.addServletMapping("/","eason"); tomcat.start(); tomcat.getServer().await();setLoadOnStartup(1);就意味着在tomcat启动时就去调用DispatchServlet的init方法,而init()方法中 用来初始化controller和映射器
@Override public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like.重要的方法,其他方法无关紧要 initServletBean(); }下面走进 initServletBean();
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); }catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } }然后进入initWebApplicationContext看看:
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }onRefresh()内部是这样子,初始化
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }至此,就完成了handlerMapping的初始化。这也验证了我们之前的猜想,init()主要是完成controller和请求映射的初始化。
一个请求被DispatchServet拦截,肯定会被doService()方法处理。因此我们可以在这里打断点,然后调试,查看调用链。 doService会调用doDispatch()方法,在这个方法里mappedHandler = getHandler(processedRequest);最为重要
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }getHandler()里循环每一个handler,去获取controller的类型。mapping的类型是不一样的,实现接口的controller用BeanNameHandlerMapping来处理,而用注解实现的controller是用RequestMappingHandlerMapping去处理
上面的getHandler()会走进AbstractHandlerMapping的getHandler(),内部又会调用 getHandlerInternal(request);进入
@Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); }else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }AbstractUrlHandlerMapping的getHandlerInternal()方法如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }lookupHandler()会根据lookupPath查找到对应HandlerMapping。 当请求的mapping不是上面两种mapping时,可以自己手动写一个mapping,然后手写一个Adapter去处理它。例如,请求index.html页面,我们并没有这样的映射器呀,于是springboot底层自己写了一个映射器。
1, 使用@Controller注解修饰的类。此时的映射里存放的是(路径,方法) 2,实现Controller(springframework)接口,重写接口的方法。此时的映射里存放的是(路径,类),然后调用固定的方法handleRequest
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component("/test1") public class IndexController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { return null; } }3,实现org.springframework.web.HttpRequestHandler接口,重写handleRequest(HttpServletRequest request, HttpServletResponse response),此时映射的map里存放的也是(路径,类),然后调用固定的方法handleRequest
import org.springframework.web.HttpRequestHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component("/test") public class IndexController implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { } }只是先做个笔记,后续我会花时间排版,然后同时把逻辑理顺后展现出来的。