Spring Web:Java 替代 web.xml 原理

  • 在 Servlet 3.0 环境中,服务器的 Servlet 容器会在类路径中查找实现 javax.servlet.ServletContainerInitializer 接口的类,如果能发现的话,就调用它的 onStartup(*)
  • 这个实现类一般不需要我们写,因为 Spring 已经提供了:SpringServletContainerInitializer
//javax.servlet.annotation.HandlesTypes 指定那些需要处理的类型;
//指定了 WebApplicationInitializer 接口,所有实现了该接口的类型都会被收集到下面 onStartup() 的第一个参数
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    //WebApplicationInitializer 实现类类型都被注入到第一个参数,包括我们自定义的实现类
    //Spring 会依次调用这些类的 onStartup(ServletContext servletContext) ,无论我们定义了多少个
    //ServletContext 是服务器的容器,可以向它注册 Listener 和 Servlet
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {

        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer) waiClass.newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }

        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }

}

由此,我们需要新建一个类实现 WebApplicationInitializer 接口,并重写 onStartup(ServletContext servletContext) ,然后在里边分别创建 Spring 的 IOC 容器和 WebMVC 容器,并且向 ServletContext(服务器的容器) 注册 Spring 的 ContextLoaderListener 和 DispatcherServlet


但是!Spring 有一系列的实现类和子类帮我们做了很多工作,来看 Spring 提供的 WebApplicationInitializer 实现类:AbstractContextLoaderInitializer

public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {


    //......


    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        registerContextLoaderListener(servletContext);
    }

    protected void registerContextLoaderListener(ServletContext servletContext) {
        WebApplicationContext rootAppContext = createRootApplicationContext();
        if (rootAppContext != null) {
            //创建一个 ContextLoaderListener
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            listener.setContextInitializers(getRootApplicationContextInitializers());
            //向 Servlet 容器注册 ContextLoaderListener
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                    "createRootApplicationContext() did not return an application context");
        }
    }

    //抽象模板方法,留给子类重写:创建 IOC 容器
    protected abstract WebApplicationContext createRootApplicationContext();

    //如果子类不重写,无任何参数提供给 IOC 容器
    protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
        return null;
    }

}

可以看得出来,AbstractContextLoaderInitializer 的贡献就是定义了一套标准的创建 ContextLoaderInitializer 的流程,并以模板方法模式将细节定义开放给子类,但是这还没完!Spring 进一步提供了它的子类:AbstractDispatcherServletInitializer

public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {

    //......

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        //父类创建 Listener 的流程要走
        super.onStartup(servletContext);
        //自己创建 Servlet 的流程也要走
        registerDispatcherServlet(servletContext);
    }


    protected void registerDispatcherServlet(ServletContext servletContext) {
        String servletName = getServletName();
        Assert.hasLength(servletName, "getServletName() must not return empty or null");

        WebApplicationContext servletAppContext = createServletApplicationContext();
        Assert.notNull(servletAppContext,
                "createServletApplicationContext() did not return an application " +
                "context for servlet [" + servletName + "]");

        //创建一个 DispatcherServlet
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
        dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

        //向 Servlet 容器注册 DispatcherServlet
        ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
        Assert.notNull(registration,
                "Failed to register servlet with name '" + servletName + "'." +
                "Check if there is another servlet registered under the same name.");

        registration.setLoadOnStartup(1);
        registration.addMapping(getServletMappings());
        registration.setAsyncSupported(isAsyncSupported());

        Filter[] filters = getServletFilters();
        if (!ObjectUtils.isEmpty(filters)) {
            for (Filter filter : filters) {
                registerServletFilter(servletContext, filter);
            }
        }

        customizeRegistration(registration);
    }


    //......

    //抽象模板方法,留给子类重写:创建 WebMVC 容器
    protected abstract WebApplicationContext createServletApplicationContext();

    //......

    //如果子类不重写,无任何参数提供给 WebMVC 容器
    protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
        return null;
    }

    //抽象模板方法,留给子类重写:设置 DispatcherServlet URL映射
    protected abstract String[] getServletMappings();

    //子类可重写:可以提供多个 Filter,Spring 会将他们一一注册到服务器的 Servlet 容器中
    protected Filter[] getServletFilters() {
        return null;
    }

    //向 Servlet 容器注册 Filter
    protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
        String filterName = Conventions.getVariableName(filter);
        Dynamic registration = servletContext.addFilter(filterName, filter);
        if (registration == null) {
            int counter = -1;
            while (counter == -1 || registration == null) {
                counter++;
                registration = servletContext.addFilter(filterName + "#" + counter, filter);
                Assert.isTrue(counter < 100,
                        "Failed to register filter '" + filter + "'." +
                        "Could the same Filter instance have been registered already?");
            }
        }
        registration.setAsyncSupported(isAsyncSupported());
        registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName());
        return registration;
    }

    //......

    //子类可重写:自定义设置 DispatcherServlet 的注册表信息,比如设置初始化参数
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    }

}

一样可以看得,AbstractDispatcherServletInitializer 的贡献是定义了一套标准的创建 DispatcherServlet 的流程,并以模板方法模式将细节定义开放给子类,然而,这还没完!Spring 依旧提供了它的子类:AbstractAnnotationConfigDispatcherServletInitializer

public abstract class AbstractAnnotationConfigDispatcherServletInitializer
        extends AbstractDispatcherServletInitializer {

    //创建 AnnotationConfigWebApplicationContext 类型的 IOC 容器
    @Override
    protected WebApplicationContext createRootApplicationContext() {
        Class<?>[] configClasses = getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
            rootAppContext.register(configClasses);
            return rootAppContext;
        }
        else {
            return null;
        }
    }

    //创建 AnnotationConfigWebApplicationContext 类型的 WebMVC 容器
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
        Class<?>[] configClasses = getServletConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            servletAppContext.register(configClasses);
        }
        return servletAppContext;
    }

    //抽象模板方法,留给子类重写:提供 IOC 容器需要的 JavaConfig
    protected abstract Class<?>[] getRootConfigClasses();

    //抽象模板方法,留给子类重写:提供 WebMVC 容器需要的 JavaConfig
    protected abstract Class<?>[] getServletConfigClasses();

}

看到这里已经很清楚了,这个类将两个 Spring 容器的创建再度简化,简单到我们只需要提供 JavaConfig!所以我们开发者要做的就是,新建一个类继承 AbstractAnnotationConfigDispatcherServletInitializer,并重写以上几个类的抽象方法,以下是一个简单例子

public class GoWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // IOC 容器配置
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        // WebMvc 容器配置
        return new Class<?>[] { WebMvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        // DispatcherServlet URL映射
        return new String[] { "/" };
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值