spring mvc基于编码配置的原理

时间:2022-04-25
本文章向大家介绍spring mvc基于编码配置的原理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

使用spring mvc的时候需要注册DispatcherServlet,DispatcherServlet是一个前端控制器,主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。我们配置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>/WEB-INF/spring/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>

但spring3.1引入了WebApplicationInitializer接口,有了它,我们不需要配置web.xml初始化web应用,只需要继承该接口,通过编码实现相应的配置:

public class Initializer implements WebApplicationInitializer {
@Override
 public void onStartup(ServletContext servletContext) throws ServletException {
 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
  ctx.register(WebAppConfig.class);
  servletContext.addListener(new ContextLoaderListener(ctx));
  ctx.setServletContext(servletContext);
  Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
  servlet.addMapping("/");
  servlet.setLoadOnStartup(1);
}
}

那通过编码方式有什么好处呢?它可以充分利用编译器,在编译期间检查出配置中的错误,另外,这增强了配置的灵活性和可控性,你可以在启动过程中自定义需要的检查验证条件。

Spring mvc是跟着更底层的接口标准servlet走的,servlet3+以后引入ServletContainerInitializer接口,这为去web.xml,基于代码配置提供了一种途径:

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

Spring的SpringServletContainerInitializer 类实现了该接口:

//HandlesTypes注解标识SpringServletContainerInitializer 类启动时需要处理的类,此处专门标识了WebApplicationInitializer,正如前面所展示的,我们正是通过实现WebApplicationInitializer接口.来作一些配置工作的。

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
 public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
  List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
 //以下代码对前面我们实现的WebApplicationInitializer作了循环,实例化,调用onStartUp 等   操作。
  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;
  }
  Collections.sort(initializers, new AnnotationAwareOrderComparator());
  servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
  for (WebApplicationInitializer initializer : initializers) {
   initializer.onStartup(servletContext);
  }
 }
}  

tomcat7以后采用了servlet3标准,我们启动tomcat7(servlet3容器)时,容器通过JAR Services API 机制,探测到spring-web包下的一个文件META-INF/services/javax.servlet.ServletContainerInitializer,文件内声明实现类,进而调用其onStartup方法,如果以后我们要自己实现ServletContainerInitializer ,也需要在相应jar包的 META-INF/services目录下放置这样一个以接口的全限定名命名的文件。

程序员局限于框架的使用,将始终停留于浅层次的水平,只有深入框架,理解原理,甚至阅读优化其中的源码,才是升级打怪的王道,大家有空可以去看看源码,我们随时讨论。