SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
上一篇我们讲了springboot中tomcat的启动过程,本篇我们接着讲在springboot中如何向tomcat中添加servlet、filter、listener
自定义servlet、filter、listener
spring容器中声明servletregistrationbean、filterregistrationbean、servletlistenerregistrationbean
@bean public servletregistrationbean customservlet() { return new servletregistrationbean(new customservlet(), "/custom"); } private static class customservlet extends httpservlet { @override protected void service(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception { resp.getwriter().write("receive by custom servlet"); } }
先自定义一个servlet,重写service实现自己的业务逻辑,然后通过@bean注解往spring容器中注入一个servletregistrationbean类型的bean实例,并且实例化一个自定义的servlet作为参数,这样就将自定义的servlet加入tomcat中了。
@servletcomponentscan注解和@webservlet、@webfilter以及@weblistener注解配合使用
@servletcomponentscan注解启用importservletcomponentscanregistrar类,是个importbeandefinitionregistrar接口的实现类,会被spring容器所解析。servletcomponentscanregistrar内部会解析@servletcomponentscan注解,然后会在spring容器中注册servletcomponentregisteringpostprocessor,是个beanfactorypostprocessor,会去解析扫描出来的类是不是有@webservlet、@weblistener、@webfilter这3种注解,有的话把这3种类型的类转换成servletregistrationbean、filterregistrationbean或者servletlistenerregistrationbean,然后让spring容器去解析:
@springbootapplication @servletcomponentscan public class embeddedservletapplication { ... } @webservlet(urlpatterns = "/simple") public class simpleservlet extends httpservlet { @override protected void service(httpservletrequest req, httpservletresponse resp) throws servletexception, ioexception { resp.getwriter().write("receive by simpleservlet"); } }
在spring容器中声明servlet、filter或者listener
@bean(name = "dispatcherservlet") public dispatcherservlet mydispatcherservlet() { return new dispatcherservlet(); }
我们发现往tomcat中添加servlet、filter或者listener还是挺容易的,大家还记得以前springmvc是怎么配置dispatcherservlet的吗?在web.xml中:
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <init-param> <param-name>contextconfiglocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
和我们springboot中配置servlet相比是不是复杂很多,虽然springboot中自定义servlet很简单,但是其底层却不简单,下面我们来分析一下其原理
servletregistrationbean、filterregistrationbean、servletlistenerregistrationbean
我们来看看这几个特殊的类:
servletregistrationbean
public class servletregistrationbean extends registrationbean { //存放目标servlet实例 private servlet servlet; //存放servlet的urlmapping private set<string> urlmappings; private boolean alwaysmapurl; private int loadonstartup; private multipartconfigelement multipartconfig; public servletregistrationbean(servlet servlet, string... urlmappings) { this(servlet, true, urlmappings); } public servletregistrationbean(servlet servlet, boolean alwaysmapurl, string... urlmappings) { this.urlmappings = new linkedhashset(); this.alwaysmapurl = true; this.loadonstartup = -1; assert.notnull(servlet, "servlet must not be null"); assert.notnull(urlmappings, "urlmappings must not be null"); this.servlet = servlet; this.alwaysmapurl = alwaysmapurl; this.urlmappings.addall(arrays.aslist(urlmappings)); } public void onstartup(servletcontext servletcontext) throws servletexception { assert.notnull(this.servlet, "servlet must not be null"); string name = this.getservletname(); if (!this.isenabled()) { logger.info("servlet " + name + " was not registered (disabled)"); } else { logger.info("mapping servlet: '" + name + "' to " + this.urlmappings); dynamic added = servletcontext.addservlet(name, this.servlet); if (added == null) { logger.info("servlet " + name + " was not registered (possibly already registered?)"); } else { this.configure(added); } } } //略 }
在我们例子中我们通过return new servletregistrationbean(new customservlet(), "/custom");就知道,servletregistrationbean里会存放目标servlet实例和urlmapping,并且继承registrationbean这个类
filterregistrationbean
public class filterregistrationbean extends abstractfilterregistrationbean { //存放目标filter对象 private filter filter; public filterregistrationbean() { super(new servletregistrationbean[0]); } public filterregistrationbean(filter filter, servletregistrationbean... servletregistrationbeans) { super(servletregistrationbeans); assert.notnull(filter, "filter must not be null"); this.filter = filter; } public filter getfilter() { return this.filter; } public void setfilter(filter filter) { assert.notnull(filter, "filter must not be null"); this.filter = filter; } } abstract class abstractfilterregistrationbean extends registrationbean { private static final enumset<dispatchertype> async_dispatcher_types; private static final enumset<dispatchertype> non_async_dispatcher_types; private static final string[] default_url_mappings; private set<servletregistrationbean> servletregistrationbeans = new linkedhashset(); private set<string> servletnames = new linkedhashset(); private set<string> urlpatterns = new linkedhashset(); //重写onstartup方法 public void onstartup(servletcontext servletcontext) throws servletexception { filter filter = this.getfilter(); assert.notnull(filter, "filter must not be null"); string name = this.getordeducename(filter); if (!this.isenabled()) { this.logger.info("filter " + name + " was not registered (disabled)"); } else { dynamic added = servletcontext.addfilter(name, filter); if (added == null) { this.logger.info("filter " + name + " was not registered (possibly already registered?)"); } else { this.configure(added); } } } //略... }
我们看到filterregistrationbean 中也保存了目标filter对象,并且继承了registrationbean
servletlistenerregistrationbean
public class servletlistenerregistrationbean<t extends eventlistener> extends registrationbean { //存放了目标listener private t listener; public servletlistenerregistrationbean() { } public servletlistenerregistrationbean(t listener) { assert.notnull(listener, "listener must not be null"); assert.istrue(issupportedtype(listener), "listener is not of a supported type"); this.listener = listener; } public void setlistener(t listener) { assert.notnull(listener, "listener must not be null"); assert.istrue(issupportedtype(listener), "listener is not of a supported type"); this.listener = listener; } public void onstartup(servletcontext servletcontext) throws servletexception { if (!this.isenabled()) { logger.info("listener " + this.listener + " was not registered (disabled)"); } else { try { servletcontext.addlistener(this.listener); } catch (runtimeexception var3) { throw new illegalstateexception("failed to add listener '" + this.listener + "' to servlet context", var3); } } } //略... }
servletlistenerregistrationbean也是一样,那我们来看看registrationbean这个类
public abstract class registrationbean implements servletcontextinitializer, ordered { ... } public interface servletcontextinitializer { void onstartup(servletcontext var1) throws servletexception; }
我们发现registrationbean 实现了servletcontextinitializer这个接口,并且有一个onstartup方法,servletregistrationbean、filterregistrationbean、servletlistenerregistrationbean都实现了onstartup方法。
servletcontextinitializer
是 servlet 容器初始化的时候,提供的初始化接口。所以,servlet 容器初始化会获取并触发所有的filterregistrationbean、filterregistrationbean、servletlistenerregistrationbean实例中
onstartup方法
那到底是何时触发这些类的onstartup方法呢?
当tomcat容器启动时,会执行callinitializers
,然后获取所有的servletcontextinitializer,循环执行
onstartup
方法触发回调方法。那filterregistrationbean、filterregistrationbean、servletlistenerregistrationbean实例是何时加入到
initializers集合的呢?这要回顾一下我们上一篇文章tomcat的启动过程
servlet容器的启动
大家可以看看我上一篇文章,我这里简单的复制一下代码
embeddedwebapplicationcontext
1 @override 2 protected void onrefresh() { 3 super.onrefresh(); 4 try { 5 //核心方法:会获取嵌入式的servlet容器工厂,并通过工厂来获取servlet容器 6 createembeddedservletcontainer(); 7 } 8 catch (throwable ex) { 9 throw new applicationcontextexception("unable to start embedded container", ex); 10 } 11 } 12 13 private void createembeddedservletcontainer() { 14 embeddedservletcontainer localcontainer = this.embeddedservletcontainer; 15 servletcontext localservletcontext = getservletcontext(); 16 if (localcontainer == null && localservletcontext == null) { 17 //先获取嵌入式servlet容器工厂 18 embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory(); 19 //根据容器工厂来获取对应的嵌入式servlet容器 20 this.embeddedservletcontainer = containerfactory.getembeddedservletcontainer(getselfinitializer()); 21 } 22 else if (localservletcontext != null) { 23 try { 24 getselfinitializer().onstartup(localservletcontext); 25 } 26 catch (servletexception ex) { 27 throw new applicationcontextexception("cannot initialize servlet context",ex); 28 } 29 } 30 initpropertysources(); 31 }
关键代码在第20行,先通过getselfinitializer()获取到所有的initializer,传入servlet容器中,那核心就在getselfinitializer()方法:
1 private servletcontextinitializer getselfinitializer() { 2 //只是创建了一个servletcontextinitializer实例返回 3 //所以servlet容器启动的时候,会调用这个对象的onstartup方法 4 return new servletcontextinitializer() { 5 public void onstartup(servletcontext servletcontext) throws servletexception { 6 embeddedwebapplicationcontext.this.selfinitialize(servletcontext); 7 } 8 }; 9 }
我们看到只是创建了一个servletcontextinitializer实例返回,所以servlet容器启动的时候,会调用这个对象的onstartup方法,那我们来分析其onstartup中的逻辑,也就是selfinitialize方法,并将servlet上下文对象传进去了
selfinitialize
1 private void selfinitialize(servletcontext servletcontext) throws servletexception { 2 preparewebapplicationcontext(servletcontext); 3 configurablelistablebeanfactory beanfactory = getbeanfactory(); 4 existingwebapplicationscopes existingscopes = new existingwebapplicationscopes(beanfactory); 5 webapplicationcontextutils.registerwebapplicationscopes(beanfactory,getservletcontext()); 6 existingscopes.restore(); 7 webapplicationcontextutils.registerenvironmentbeans(beanfactory,getservletcontext()); 8 //这里便是获取所有的 servletcontextinitializer 实现类,会获取所有的注册组件 9 for (servletcontextinitializer beans : getservletcontextinitializerbeans()) { 10 //执行所有servletcontextinitializer的onstartup方法 11 beans.onstartup(servletcontext); 12 } 13 }
关键代码在第9和第11行,先获取所有的servletcontextinitializer 实现类,然后遍历执行所有servletcontextinitializer的onstartup方法
获取所有的servletcontextinitializer
我们来看看getservletcontextinitializerbeans方法
protected collection<servletcontextinitializer> getservletcontextinitializerbeans() { return new servletcontextinitializerbeans(getbeanfactory()); }
servletcontextinitializerbeans对象是对servletcontextinitializer
的一种包装:
1 public class servletcontextinitializerbeans extends abstractcollection<servletcontextinitializer> { 2 private final multivaluemap<class<?>, servletcontextinitializer> initializers = new linkedmultivaluemap(); 3 //存放所有的servletcontextinitializer 4 private list<servletcontextinitializer> sortedlist; 5 6 public servletcontextinitializerbeans(listablebeanfactory beanfactory) { 7 //执行addservletcontextinitializerbeans 8 this.addservletcontextinitializerbeans(beanfactory); 9 //执行addadaptablebeans 10 this.addadaptablebeans(beanfactory); 11 list<servletcontextinitializer> sortedinitializers = new arraylist(); 12 iterator var3 = this.initializers.entryset().iterator(); 13 14 while(var3.hasnext()) { 15 entry<?, list<servletcontextinitializer>> entry = (entry)var3.next(); 16 annotationawareordercomparator.sort((list)entry.getvalue()); 17 sortedinitializers.addall((collection)entry.getvalue()); 18 } 19 this.sortedlist = collections.unmodifiablelist(sortedinitializers); 20 } 21 22 private void addservletcontextinitializerbeans(listablebeanfactory beanfactory) { 23 iterator var2 = this.getorderedbeansoftype(beanfactory, servletcontextinitializer.class).iterator(); 24 25 while(var2.hasnext()) { 26 entry<string, servletcontextinitializer> initializerbean = (entry)var2.next(); 27 this.addservletcontextinitializerbean((string)initializerbean.getkey(), (servletcontextinitializer)initializerbean.getvalue(), beanfactory); 28 } 29 30 } 31 32 private void addservletcontextinitializerbean(string beanname, servletcontextinitializer initializer, listablebeanfactory beanfactory) { 33 if (initializer instanceof servletregistrationbean) { 34 servlet source = ((servletregistrationbean)initializer).getservlet(); 35 this.addservletcontextinitializerbean(servlet.class, beanname, initializer, beanfactory, source); 36 } else if (initializer instanceof filterregistrationbean) { 37 filter source = ((filterregistrationbean)initializer).getfilter(); 38 this.addservletcontextinitializerbean(filter.class, beanname, initializer, beanfactory, source); 39 } else if (initializer instanceof delegatingfilterproxyregistrationbean) { 40 string source = ((delegatingfilterproxyregistrationbean)initializer).gettargetbeanname(); 41 this.addservletcontextinitializerbean(filter.class, beanname, initializer, beanfactory, source); 42 } else if (initializer instanceof servletlistenerregistrationbean) { 43 eventlistener source = ((servletlistenerregistrationbean)initializer).getlistener(); 44 this.addservletcontextinitializerbean(eventlistener.class, beanname, initializer, beanfactory, source); 45 } else { 46 this.addservletcontextinitializerbean(servletcontextinitializer.class, beanname, initializer, beanfactory, initializer); 47 } 48 49 } 50 51 private void addservletcontextinitializerbean(class<?> type, string beanname, servletcontextinitializer initializer, listablebeanfactory beanfactory, object source) { 52 this.initializers.add(type, initializer); 53 if (source != null) { 54 this.seen.add(source); 55 } 56 57 if (logger.isdebugenabled()) { 58 string resourcedescription = this.getresourcedescription(beanname, beanfactory); 59 int order = this.getorder(initializer); 60 logger.debug("added existing " + type.getsimplename() + " initializer bean '" + beanname + "'; order=" + order + ", resource=" + resourcedescription); 61 } 62 63 } 64 65 private void addadaptablebeans(listablebeanfactory beanfactory) { 66 multipartconfigelement multipartconfig = this.getmultipartconfig(beanfactory); 67 this.addasregistrationbean(beanfactory, servlet.class, new servletcontextinitializerbeans.servletregistrationbeanadapter(multipartconfig)); 68 this.addasregistrationbean(beanfactory, filter.class, new servletcontextinitializerbeans.filterregistrationbeanadapter(null)); 69 iterator var3 = servletlistenerregistrationbean.getsupportedtypes().iterator(); 70 71 while(var3.hasnext()) { 72 class<?> listenertype = (class)var3.next(); 73 this.addasregistrationbean(beanfactory, eventlistener.class, listenertype, new servletcontextinitializerbeans.servletlistenerregistrationbeanadapter(null)); 74 } 75 76 } 77 78 public iterator<servletcontextinitializer> iterator() { 79 //返回所有的servletcontextinitializer 80 return this.sortedlist.iterator(); 81 } 82 83 //略... 84 }
我们看到servletcontextinitializerbeans 中有一个存放所有servletcontextinitializer的集合sortedlist,就是在其构造方法中获取所有的servletcontextinitializer,并放入sortedlist集合中,那我们来看看其构造方法的逻辑,看到第8行先调用
addservletcontextinitializerbeans方法:
1 private void addservletcontextinitializerbeans(listablebeanfactory beanfactory) { 2 //从spring容器中获取所有servletcontextinitializer.class 类型的bean 3 for (entry<string, servletcontextinitializer> initializerbean : getorderedbeansoftype(beanfactory, servletcontextinitializer.class)) { 4 //添加到具体的集合中 5 addservletcontextinitializerbean(initializerbean.getkey(),initializerbean.getvalue(), beanfactory); 6 } 7 }
我们看到先从spring容器中获取所有servletcontextinitializer.class 类型的bean,这里我们自定义的servletregistrationbean、filterregistrationbean、servletlistenerregistrationbean就被获取到了,然后调用addservletcontextinitializerbean方法:
1 private void addservletcontextinitializerbean(string beanname, servletcontextinitializer initializer, listablebeanfactory beanfactory) { 2 //判断servletregistrationbean类型 3 if (initializer instanceof servletregistrationbean) { 4 servlet source = ((servletregistrationbean)initializer).getservlet(); 5 //将servletregistrationbean加入到集合中 6 this.addservletcontextinitializerbean(servlet.class, beanname, initializer, beanfactory, source); 7 //判断filterregistrationbean类型 8 } else if (initializer instanceof filterregistrationbean) { 9 filter source = ((filterregistrationbean)initializer).getfilter(); 10 //将servletregistrationbean加入到集合中 11 this.addservletcontextinitializerbean(filter.class, beanname, initializer, beanfactory, source); 12 } else if (initializer instanceof delegatingfilterproxyregistrationbean) { 13 string source = ((delegatingfilterproxyregistrationbean)initializer).gettargetbeanname(); 14 this.addservletcontextinitializerbean(filter.class, beanname, initializer, beanfactory, source); 15 } else if (initializer instanceof servletlistenerregistrationbean) { 16 eventlistener source = ((servletlistenerregistrationbean)initializer).getlistener(); 17 this.addservletcontextinitializerbean(eventlistener.class, beanname, initializer, beanfactory, source); 18 } else { 19 this.addservletcontextinitializerbean(servletcontextinitializer.class, beanname, initializer, beanfactory, initializer); 20 } 21 22 } 23 24 private void addservletcontextinitializerbean(class<?> type, string beanname, 25 servletcontextinitializer initializer, listablebeanfactory beanfactory, object source) { 26 //加入到initializers中 27 this.initializers.add(type, initializer); 28 }
很明显,判断从spring容器中获取的servletcontextinitializer类型,如servletregistrationbean、filterregistrationbean、servletlistenerregistrationbean,并加入到initializers集合中去,我们再来看构造器中的另外一个方法addadaptablebeans(beanfactory):
1 private void addadaptablebeans(listablebeanfactory beanfactory) { 2 //从beanfactory获取所有servlet.class和filter.class类型的bean,并封装成registrationbean对象,加入到集合中 3 this.addasregistrationbean(beanfactory, servlet.class, new servletcontextinitializerbeans.servletregistrationbeanadapter(multipartconfig)); 4 this.addasregistrationbean(beanfactory, filter.class, new servletcontextinitializerbeans.filterregistrationbeanadapter(null)); 5 } 6 7 private <t, b extends t> void addasregistrationbean(listablebeanfactory beanfactory, class<t> type, class<b> beantype, servletcontextinitializerbeans.registrationbeanadapter<t> adapter) { 8 //从spring容器中获取所有的servlet.class和filter.class类型的bean 9 list<entry<string, b>> beans = this.getorderedbeansoftype(beanfactory, beantype, this.seen); 10 iterator var6 = beans.iterator(); 11 12 while(var6.hasnext()) { 13 entry<string, b> bean = (entry)var6.next(); 14 if (this.seen.add(bean.getvalue())) { 15 int order = this.getorder(bean.getvalue()); 16 string beanname = (string)bean.getkey(); 17 //创建servlet.class和filter.class包装成registrationbean对象 18 registrationbean registration = adapter.createregistrationbean(beanname, bean.getvalue(), beans.size()); 19 registration.setname(beanname); 20 registration.setorder(order); 21 this.initializers.add(type, registration); 22 if (logger.isdebugenabled()) { 23 logger.debug("created " + type.getsimplename() + " initializer for bean '" + beanname + "'; order=" + order + ", resource=" + this.getresourcedescription(beanname, beanfactory)); 24 } 25 } 26 } 27 28 }
我们看到先从beanfactory获取所有servlet.class和filter.class类型的bean,然后通过servletregistrationbeanadapter和filterregistrationbeanadapter两个适配器将servlet.class和filter.class封装成registrationbean
private static class servletregistrationbeanadapter implements servletcontextinitializerbeans.registrationbeanadapter<servlet> { private final multipartconfigelement multipartconfig; servletregistrationbeanadapter(multipartconfigelement multipartconfig) { this.multipartconfig = multipartconfig; } public registrationbean createregistrationbean(string name, servlet source, int totalnumberofsourcebeans) { string url = totalnumberofsourcebeans == 1 ? "/" : "/" + name + "/"; if (name.equals("dispatcherservlet")) { url = "/"; } //还是将servlet.class实例封装成servletregistrationbean对象 //这和我们自己创建servletregistrationbean对象是一模一样的 servletregistrationbean bean = new servletregistrationbean(source, new string[]{url}); bean.setmultipartconfig(this.multipartconfig); return bean; } } private static class filterregistrationbeanadapter implements servletcontextinitializerbeans.registrationbeanadapter<filter> { private filterregistrationbeanadapter() { } public registrationbean createregistrationbean(string name, filter source, int totalnumberofsourcebeans) { //filter.class实例封装成filterregistrationbean对象 return new filterregistrationbean(source, new servletregistrationbean[0]); } }
代码中注释很清楚了还是将servlet.class实例封装成servletregistrationbean对象,将filter.class实例封装成filterregistrationbean对象,这和我们自己定义servletregistrationbean对象是一模一样的,现在所有的servletregistrationbean、filterregistrationbean
servlet.class、filter.class都添加到list<servletcontextinitializer> sortedlist这个集合中去了,接着就是遍历这个集合,执行其onstartup方法了
servletcontextinitializer的onstartup方法
servletregistrationbean
public class servletregistrationbean extends registrationbean { private static final log logger = logfactory.getlog(servletregistrationbean.class); private static final string[] default_mappings = new string[]{"/*"}; private servlet servlet; public void onstartup(servletcontext servletcontext) throws servletexception { assert.notnull(this.servlet, "servlet must not be null"); string name = this.getservletname(); //调用servletcontext的addservlet dynamic added = servletcontext.addservlet(name, this.servlet); } //略... } private javax.servlet.servletregistration.dynamic addservlet(string servletname, string servletclass, servlet servlet, map<string, string> initparams) throws illegalstateexception { if (servletname != null && !servletname.equals("")) { if (!this.context.getstate().equals(lifecyclestate.starting_prep)) { throw new illegalstateexception(sm.getstring("applicationcontext.addservlet.ise", new object[]{this.getcontextpath()})); } else { wrapper wrapper = (wrapper)this.context.findchild(servletname); if (wrapper == null) { wrapper = this.context.createwrapper(); wrapper.setname(servletname); this.context.addchild(wrapper); } else if (wrapper.getname() != null && wrapper.getservletclass() != null) { if (!wrapper.isoverridable()) { return null; } wrapper.setoverridable(false); } if (servlet == null) { wrapper.setservletclass(servletclass); } else { wrapper.setservletclass(servlet.getclass().getname()); wrapper.setservlet(servlet); } if (initparams != null) { iterator i$ = initparams.entryset().iterator(); while(i$.hasnext()) { entry<string, string> initparam = (entry)i$.next(); wrapper.addinitparameter((string)initparam.getkey(), (string)initparam.getvalue()); } } return this.context.dynamicservletadded(wrapper); } } else { throw new illegalargumentexception(sm.getstring("applicationcontext.invalidservletname", new object[]{servletname})); } }
看到没,servletregistrationbean 中的 onstartup先获取servlet的name,然后调用servletcontext的addservlet将servlet加入到tomcat中,这样我们就能发请求给这个servlet了。
abstractfilterregistrationbean
public void onstartup(servletcontext servletcontext) throws servletexception { filter filter = this.getfilter(); assert.notnull(filter, "filter must not be null"); string name = this.getordeducename(filter); //调用servletcontext的addfilter dynamic added = servletcontext.addfilter(name, filter); }
abstractfilterregistrationbean也是同样的原理,先获取目标filter,然后调用servletcontext的addfilter将filter加入到tomcat中,这样filter就能拦截我们请求了。
dispatcherservletautoconfiguration
dispatcherservletconfiguration
1 @configuration 2 @conditionalonwebapplication 3 // 先看下classpath下是否有dispatcherservlet.class字节码 4 // 我们引入了spring-boot-starter-web,同时引入了tomcat和springmvc,肯定会存在dispatcherservlet.class字节码 5 @conditionalonclass({dispatcherservlet.class}) 6 // 这个配置类的执行要在embeddedservletcontainerautoconfiguration配置类生效之后执行 7 // 毕竟要等tomcat启动后才能往其中注入dispatcherservlet 8 @autoconfigureafter({embeddedservletcontainerautoconfiguration.class}) 9 protected static class dispatcherservletconfiguration { 10 public static final string default_dispatcher_servlet_bean_name = "dispatcherservlet"; 11 public static final string default_dispatcher_servlet_registration_bean_name = "dispatcherservletregistration"; 12 @autowired 13 private serverproperties server; 14 15 @autowired 16 private webmvcproperties webmvcproperties; 17 18 @autowired(required = false) 19 private multipartconfigelement multipartconfig; 20 21 // spring容器注册dispatcherservlet 22 @bean(name = default_dispatcher_servlet_bean_name) 23 public dispatcherservlet dispatcherservlet() { 24 // 直接构造dispatcherservlet,并设置webmvcproperties中的一些配置 25 dispatcherservlet dispatcherservlet = new dispatcherservlet(); 26 dispatcherservlet.setdispatchoptionsrequest(this.webmvcproperties.isdispatchoptionsrequest()); 27 dispatcherservlet.setdispatchtracerequest(this.webmvcproperties.isdispatchtracerequest()); 28 dispatcherservlet.setthrowexceptionifnohandlerfound(this.webmvcproperties.isthrowexceptionifnohandlerfound()); 29 return dispatcherservlet; 30 } 31 32 @bean(name = default_dispatcher_servlet_registration_bean_name) 33 public servletregistrationbean dispatcherservletregistration() { 34 // 直接使用dispatcherservlet和server配置中的servletpath路径构造servletregistrationbean 35 // servletregistrationbean实现了servletcontextinitializer接口,在onstartup方法中对应的servlet注册到servlet容器中 36 // 所以这里dispatcherservlet会被注册到servlet容器中,对应的urlmapping为server.servletpath配置 37 servletregistrationbean registration = new servletregistrationbean(dispatcherservlet(), this.server.getservletmapping()); 38 registration.setname(default_dispatcher_servlet_bean_name); 39 if (this.multipartconfig != null) { 40 registration.setmultipartconfig(this.multipartconfig); 41 } 42 return registration; 43 } 44 45 @bean // 构造文件上传相关的bean 46 @conditionalonbean(multipartresolver.class) 47 @conditionalonmissingbean(name = dispatcherservlet.multipart_resolver_bean_name) 48 public multipartresolver multipartresolver(multipartresolver resolver) { 49 return resolver; 50 } 51 52 }
先看下classpath下是否有dispatcherservlet.class字节码, 我们引入了spring-boot-starter-web,同时引入了tomcat和springmvc,肯定会存在dispatcherservlet.class字节码,如果没有导入spring-boot-starter-web,则这个配置类将不会生效
然后往spring容器中注册dispatcherservlet实例,接着又加入servletregistrationbean实例,并把dispatcherservlet实例作为参数,上面我们已经学过了servletregistrationbean的逻辑,在tomcat启动的时候,会获取所有的servletregistrationbean,并执行其中的onstartup方法,将dispatcherservlet注册到servlet容器中,这样就类似原来的web.xml中配置的dispatcherservlet。
<servlet> <servlet-name>dispatcherservlet</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
所以只要导入了spring-boot-starter-web这个starter,springboot就有了tomcat容器,并且往tomcat容器中注册了dispatcherservlet对象,这样就能接收到我们的请求了
日常求赞
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
如果这个文章写得还不错,觉得学到了一点东西的话 求点赞
相关文章:
-
-
一到周末就开始放荡自我,这不带着女朋友去万达电影院看电影(其实是由于整天呆在家敲代码硬是 被女朋友强行拖拽去看电影,作为一个有理想的程序员,我想各位... [阅读全文]
-
using System;using System.Collections.Generic;using System.Collections;usi... [阅读全文]
-
由于我们开发的辅助工具Database2Sharp需要支持多种数据库,虽然我们一般使用SQLServer来开发应用较多,但是Oracle等其他数据库... [阅读全文]
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet、Filte... [阅读全文] -
'try(A a = new A())' VS 'try finally'
实现了AutoCloseable接口的类,可以在try的时候直接实例化对象。try代码块完成之后,自动调用close方法,相当于在finally里主... [阅读全文] -
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
推荐阅读
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析
-
SpringBoot 源码解析 (九)----- Spring Boot的核心能力 - 整合Mybatis
发表评论