欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?

程序员文章站 2023-11-14 09:18:52
上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet、Filter、Listener 自定义Servlet、Filter、Listener Spring容器中声明ServletRegistrationBean、Fil ......

上一篇我们讲了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方法呢?

SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?

当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方法了

SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?

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

最熟悉的莫过于,在spring boot在自动配置springmvc的时候,会自动注册springmvc前端控制器:dispatcherservlet,该控制器主要在dispatcherservletautoconfiguration自动配置类中进行注册的。dispatcherservlet是springmvc中的核心分发器。dispatcherservletautoconfiguration也在spring.factories中配置了
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?

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对象,这样就能接收到我们的请求了

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才

如果这个文章写得还不错,觉得学到了一点东西的话 求点赞

(0)
打赏 SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的? 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
验证码: SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?