SpringMVC的註解配置原理

META-INF\services\javax.servlet.ServletContainerInitializer

web容器啓動的時候,會掃描每個jar包中META-INF\services\javax.servlet.ServletContainerInitializer文件, 加載其中指定的ServletContainerInitializer的實現類,並調用onStartup方法。@HandlesTypes(value = {HelloServlet.class})用於指定一個接口,所有該接口的實現類會傳給onStartup方法的第一個參數。

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

在這裏插入圖片描述

package com.example.demo3

// HelloServlet是個接口,我例子中的實現類就不貼出來了
@HandlesTypes(value = {HelloServlet.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * 應用啓動的時候,會運行onStartup
     * @param c @HandlesTypes註解中配置的接口的所有實現類
     * @param ctx 可以用來註冊三大組件
     * @throws ServletException
     */
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        System.out.println("onStartup");
        for (Class<?> aClass : c) {
            System.out.println(aClass);
        }
    }
}

在這裏插入圖片描述

配置web三大組件

@HandlesTypes(value = {HelloServlet.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        System.out.println("onStartup");
        for (Class<?> aClass : c) {
            System.out.println(aClass);
        }

        //形式註冊三大組件
        
        //註冊組件  ServletRegistration
        ServletRegistration.Dynamic servlet = ctx.addServlet("servletDemo4", new ServletDemo4());
        //配置servlet的映射信息
        servlet.addMapping("/demo4");

        //註冊Listener
        ctx.addListener(MyServletContextListener.class);

        //註冊Filter  FilterRegistration
        FilterRegistration.Dynamic filter = ctx.addFilter("filterDemo1", FilterDemo1.class);
        //配置Filter的映射信息
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
    }
}

過濾器:

public class FilterDemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter-init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Filter-doFilter");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        System.out.println("Filter-destroy");
    }
}

監聽器:

public class MyServletContextListener implements ServletContextListener {

    /**
     * 監聽ServletContext對象創建
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("MyServletContextListener-contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroyed");
    }
}

Servlet程序:

public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo4");
    }
}

Spring整合SpringMVC

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.2.RELEASE</version>
</dependency>

在這裏插入圖片描述
從上面加載的spring-web的jar包中可以看到,也配置了javax.servlet.ServletContainerInitializer,內容是org.springframework.web.SpringServletContainerInitializer,所以,Spring啓動的時候,會加載org.springframework.web.SpringServletContainerInitializer類,並調用onStartup方法,這個類的源碼如下:

// 會加載所有WebApplicationInitializer的實現類給onStartup方法的webAppInitializerClasses參數
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(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)waiClass.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);
            }

        }
    }
}

現在來看看WebApplicationInitializer的實現類有哪些:
在這裏插入圖片描述

  • AbstractAnnotationConfigDispatcherServletInitializer:註解方式配置的DispatcherServlet初始化器
  • AbstractContextLoaderInitializer :創建 根容器
  • AbstractDispatcherServletInitializer
    創建web ioc容器
    創建DispatcherServlet
    將創建的DispatcherServlet添加到ServletContext中

所以,如果我們希望以註解的方式來啓動SpringMVC,就繼承 AbstractAnnotationConfigDispatcherServletInitializer類,實現抽象方法,具體如何配置請看官網(https://docs.spring.io/spring/docs/),這裏只是討論了註解配置的基本原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章