(三)Web模块:【5】Servlet3.0 与 SpringMVC 整合


一、SpringMVC对ServletContainerInitializer 类的实现

  配置信息:

  查看SpringServletContainerInitializer

//感兴趣的类为WebApplicationInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    //webAppInitializerClasses:所有WebApplicationInitializer 类型的Class
    @Override
    public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {

        List initializers = new LinkedList<>();

        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // 如果该Class 不是接口,不是抽象类,并且是WebApplicationInitializer类型的类
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        //实例化该类,并加入到initializers集合中
                        initializers.add((WebApplicationInitializer)
                                ReflectionUtils.accessibleConstructor(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);
        //遍历initializers集合中的类
        for (WebApplicationInitializer initializer : initializers) {
            //调用其onStartup方法
            initializer.onStartup(servletContext);
        }
    }
}

二、WebApplicationInitializer 相关类详解

  1、继承关系如下

  2、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) {
            //根据根容器,创建容器监听,相当于以前在xml文件里配置ContextLoaderListener
            ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
            listener.setContextInitializers(getRootApplicationContextInitializers());
            servletContext.addListener(listener);
        }
        else {
            logger.debug("No ContextLoaderListener registered, as " +
                    "createRootApplicationContext() did not return an application context");
        }
    }
   //创建根容器方法是个抽象方法,留给子类实现
   protected abstract WebApplicationContext createRootApplicationContext();

}

  3、AbstractDispatcherServletInitializer

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        //注册一个DispatcherServlet
        registerDispatcherServlet(servletContext);
    }
    protected void registerDispatcherServlet(ServletContext servletContext) {
        String servletName = getServletName();
        Assert.hasLength(servletName, "getServletName() must not return null or empty");
        //创建一个servlet容器
        WebApplicationContext servletAppContext = createServletApplicationContext();
        Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
        //创建一个dispatcherServlet 
        FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
        Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
        dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
        //给servlet容器 添加dispatcherServlet 得到一个注册器
        ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
        if (registration == null) {
            throw new IllegalStateException("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);
    }
    // 这是一个抽象方法,留给子类实现的
    protected abstract String[] getServletMappings();
}

  4、AbstractAnnotationConfigDispatcherServletInitializer

public abstract class AbstractAnnotationConfigDispatcherServletInitializer
        extends AbstractDispatcherServletInitializer {
     //覆盖了AbstractContextLoaderInitializer的createRootApplicationContext方法,
     //用于创建根容器
     @Override
     protected WebApplicationContext createRootApplicationContext() {
        //得到根容器的Class
        Class<?>[] configClasses = getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            //创建一个web容器
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            //注册配置类
            context.register(configClasses);
            return context;
        }
        else {
            return null;
        }
    }
    
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
        Class<?>[] configClasses = getServletConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            servletAppContext.register(configClasses);
        }
        return servletAppContext;
    }
    
   //得到根容器配置类,这是一个抽象方法,留给子类实现,覆盖了AbstractContextLoaderInitializer
   protected abstract Class<?>[] getRootConfigClasses();
   //得到Servlet容器配置类,覆盖了AbstractDispatcherServletInitializer 的方法
   protected abstract Class<?>[] getServletConfigClasses();
}

  所以我们在用配置类创建springmvc应用时,只需继承AbstractAnnotationConfigDispatcherServletInitializer,覆写其对应方法即可

三、Servlet3.0 整合 SpringMVC

  1、创建父容器配置类

//Spring的容器不扫描controller;父容器
@ComponentScan(value="com.njf", excludeFilters={
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class})
})
public class RootConfig {}

  2、创建子容器配置类

//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.njf", includeFilters={
        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters=false)
public class AppConfig {}

  3、自定义初始化器,并指定配置类

//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //获取根容器的配置类;(Spring的配置文件)   父容器;
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }


    //获取web容器的配置类(SpringMVC配置文件)  子容器;
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{AppConfig.class};
    }


    //获取DispatcherServlet的映射信息
    //  /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
    //  /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }


}

  4、创建一个 Controller

@Controller
public class HelloController {

    @Autowired
    HelloService helloService;

    @ResponseBody
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        String result = helloService.sayHello("Tomcat...");
        return result;
    }
}

  5、创建一个 Service

@Service
public class HelloService {

    public String sayHello(String word) {
        System.out.println("Hello," + word);
        return "Hello" + word;
    }
}

  6、测试正常运行。

相关