SpringMvc零配置的原理

在servlet3.0之前,我们必须将listener、filter、servlet声明在web.xml中。在servlet3.0之后为什么我们启动springmvc就可以不需要web.xml了呢?

我们一直在说servlet3.0,但是你真的知道3.0之后有哪些改动让我们可以不需要用到web.xml了吗?


首先这是servlet3.0定义的接口,它定义的规则就是:

  1. 实现了这个接口的类要加在META-INF/services/javax.servlet.ServletContainerInitializer文件中才会生效
  2. 实现了这个接口的类,这个类上被加了@HandlesTypes注解的类会被注入到这个类的onStartup方法的第一个参数中,也就是Set集合
  3. servlet容器在启动的时候要调用这个类的onStartup方法
package javax.servlet;

import java.util.Set;

/**
 * 定义在META-INF/services/javax.servlet.ServletContainerInitializer文件中
 * 
 * @since Servlet 3.0
 */
public interface ServletContainerInitializer {

    
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

来看看spring中是怎样做的

 spring中SpringServletContainerInitializer实现了ServletContainerInitializer接口,@HandlesTypes传入的是WebApplicationInitializer

在onStartup方法最后循环调用了WebApplicationInitializer的onStartup方法

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

        }
    }
}

接下来看看spring官网,官方推荐我们正确打开springmvc的方式如下,现在就知道为什么要这么用了吧

https://docs.spring.io/spring-framework/docs/5.1.18.RELEASE/spring-framework-reference/web.html#spring-web

最后还有一个点就是,凭什么启动servlet容器的时候会进到我的SpringServletContainerInitializer

需要清楚的一点就是,servlet只是定义了规则,实现是servlet容器供应商来实现的,如tomcat jetty 等

以tomcat为例子,tomcat在启动Context之前会读取classpath路径下的META-INF/services/javax.servlet.ServletContainerInitializer文件,然后将读取到ServletContainerInitializer对象作为key,@HandlesType上的类做为value存入StandardContext中的initializers

然后在初始化StandardContext的时候遍历这个set,逐个执行他们的onStartup方法,这样就可以无需web.xml,将DispatcherServlet添加到servlet容器中


public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {


    private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
        new LinkedHashMap<ServletContainerInitializer,Set<Class<?>>>();

    


    @Override
    protected synchronized void startInternal() throws LifecycleException {
            //*******此处省略*********

            // Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

             //******此处省略******
    }

}

附上github的demo

https://github.com/liu149/mvc

通过maven插件运行

需要注意的是运行后的base url

访问localhost:8080/mvc/index,又来到了我们熟悉的世界“hello world"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值