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

2022-12-29,,,,

上一篇我们讲了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

最熟悉的莫过于,在spring boot在自动配置springmvc的时候,会自动注册springmvc前端控制器:dispatcherservlet,该控制器主要在dispatcherservletautoconfiguration自动配置类中进行注册的。dispatcherservlet是springmvc中的核心分发器。dispatcherservletautoconfiguration也在spring.factories中配置了

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

日常求赞

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

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

  • python如何实现多进程

    这篇文章将为大家详细讲解有关python如何实现多进程,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。 多进程的含义 进程(Process)是具有一定独立功能的程序关于某个数据集...

  • 如何实现封装

    这篇文章主要介绍如何实现封装,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! 1、封装是什么?以及为什么要进行封装? 通常情况下可以给成员变量赋值一些合法但不合理的数值,这种情况...

  • OpenCV Python如何实现图像指定区域裁剪

    这篇文章主要为大家展示了OpenCV Python如何实现图像指定区域裁剪,内容简而易懂,希望大家可以学习一下,学习完之后肯定会有收获的,下面让小编带大家一起来看看吧。 在工作中。在做数据集时,需要对图片进行处...

  • vue中如何实现后台管理系统的权限控制

    一、前言 在广告ji项目中,角色的权限管理是卡了挺久的一个难点。首先我们确定的权限控制分为两大部分,其中根据粒的大小分的更细: 接口访问的权限控制 页面的权限控制 菜单中的页面是否能被访问 页面中...

  • Redis如何实现聊天室功能?

    今天小编就为大家带来一篇介绍Redis实现聊天室功能的文章。小编觉得挺实用的,为此分享给大家做个参考。一起跟随小编过来看看吧。 首先,来介绍下关于pub/sub的几个命令。 发布消息 publish channel message 向指...

  • 详解Python如何实现读取并写入Excel文件

    小编这次要给大家分享的是详解Python如何实现读取并写入Excel文件,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。 需求是有两个Excel文件:1.xlsx,2.xlsx,比较2.xlsx...