Spring MVC 注解方式使用及和Tomcat,Servlet的整合
ServletContainerInitializer该类是Servlet 提供的接口,该接口的实现类会被Tomcat 等 Web容器,在启动的时候会使用 JAR 服务API---service-api 来发现 ,并调用实现类里面的 onStartup 方法
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
Spring MVC 就是通过实现了 ServletContainerInitializer 接口 的SpringServletContainerInitializer 中的 onStartup 方法来完成Spring的容器以及相关的 MVC 相关配置的加载。
如下:
但是在该类的 onStartup 方法中并没有看到显示的初始化 Servlet 的地方,只是for循环调用 WebApplicationInitializer 接口的实现类的方法 onStartup;所以 初始化 Servlet 就是在这些实现类的 onStartup中完成的,WebApplicationInitializer的相关实现类是 @HandlesTypes 注解标签注入的,类似 Spring 中的@Autowired 注解, 会自动找到 标签里面属性的Class 的实现类,并注入到 Tomcat 容器里面(HandlesTypes注解是Servlet里面的注解)
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
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)
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);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
支持注解方式的 DispatcherServlet,可以继承 AbstractAnnotationConfigDispatcherServletInitializer 抽象类来实现我们的业务需求
如下所示:
package com.yuns.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* 实现 无 web.xml 化 的全注解的 Spring MVC 配置
*/
public class StartWebApplicationContextInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* SpringContext 中相关的类-- 根容器的配置类
*
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{SpringRootConfig.class};
}
/**
* DispatcherServlet 中上下文相关的类--子容器的配置类
*
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{MVCConfig.class};
}
/**
* Servlet 请求映射路径
*
* @return
*/
@Override
protected String[] getServletMappings() {
//所有请求都会经过 DispatcherServlet 来处理
return new String[]{"/"};
}
/**
* 拦截并处理请求的编码
*
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
//设置拦截请求后的统一编码
encodingFilter.setEncoding("UTF-8");
//强制编码属性设置为 true--对请求进行强制的 UTF-8 编码
encodingFilter.setForceEncoding(true);
return new Filter[]{encodingFilter};
}
}
package com.yuns.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 扫描除了Controller以外的基础服务层
*
* @author wsq
* @version SpringRootConfig.java 2020/7/26 上午9:59 下午
*/
@ComponentScan("com.yuns.service")
//让其作为配置Bean 注册到 root 容器中
@Configuration
public class SpringRootConfig {
}
package com.yuns.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
* 扫描 Spring MVC 相关的 Bean
*
* @author wsq
* @version MVCConfig.java 2020/7/26 上午9:59 下午
*/
@ComponentScan("com.yuns.controller")
//让其作为配置Bean 注册到 root 容器中
@Configuration
//开启容器的 MVC 功能
@EnableWebMvc
public class MVCConfig {
/**
* 内部资源视图解析器实例
* 创建并返回视图解析器实例
*
* @return
*/
//方法上加这个注解标签之后,Spring 容器在启动并读入 MVCConfig 本类的时候,
//调用 viewResolver 这个方法,将方法的返回值作为 Bean 注入到容器中
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
//解析的路径前缀和后缀进行赋值
//让视图解析器解析 Controller 指定的 View路径 的时候,自动给路径加上设置好的前缀和后缀
internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
以上完成IOC 容器的创建和DispatcherServlet实例,以上是基于注解的 IOC 容器初始化,而Web.xml的初始化则是通过下面的 ContextLoaderListener Servlet 的监听器 完成的;
注意:注解的初始化先于 web.xml方式的初始化。
注意:不管是基于注解方式还是web.xml方式,触发机制都是Tomcat容器启动的时候触发的,即SpringServletContainerInitializer 中的 onStartup 方法 或 ContextLoaderListener 的 contextInitialized 方法都是 tomcat 触发的,即 tomcat 应用本身自己维护的线程池中的线程触发的。
Root IOC 容器是什么时候刷新的???—包含非 Spring MVC Bean
ContextLoaderListener 实现了 ServletContextListener 本质上 是 一个Servlet 的监听器,Tomcat 会优先加载 Servlet 的监听器组件,以保证 在 Servlet被创建之前即 Context 实例进行初始化的时候,去调用 ServletContextListener 接口定义的contextInitialized 方法 来根据 contextConfigLocation指定的位置 去读取并解析 Spring 容器的配置,创建出容器的实例并对容器进行刷新操作,在刷新之后对 Controller和请求的映射关系进行创建。不管是web.xml的方式还是注解的方式都会走到这一步:调用 ServletContextListener 接口定义的contextInitialized 方法
MVC Bean 的加载???
在 DispatcherServlet 的初始化的时候完成的
三种类型的 HandlerMapping:
RequestMappingHandlerMapping:处理 @RequestMapping 注解标签的
AbstractHandlerMethodMapping.
BeanNameUrlHandlerMapping
RouterFunctionMapping
网友评论