springmvc是做为一个web开发的基础.负责web容器与spring容器的桥接工作。
通过在DispatcherServlet配置关联2容器。
public class SpringWebTest {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletContextHandler servletHandler = new ServletContextHandler();
server.setHandler(servletHandler);
loadDispatcherServlet(servletHandler);
configContext(servletHandler);
server.start();
server.join();
}
// 加载DispatcherServlet,并指定spring容器加载路径
private static void loadDispatcherServlet(ServletContextHandler handler){
ServletHolder holder = new ServletHolder(Source.EMBEDDED);
holder.setHeldClass(DispatcherServlet.class);
holder.setInitParameter("contextConfigLocation", "classpath:spring-web.xml");
holder.setInitOrder(1);
handler.addServlet(holder, "/");
}
// 监听器加载父容器
// Listener的加载优先于Servlet
private static void configContext(ServletContextHandler handler){
//handler.setInitParameter("contextConfigLocation", "classpath:spring-web.xml");
//handler.addEventListener(new ContextLoaderListener());
}
}
public class HelloController implements Controller, View, ViewResolver {
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView();
mav.setViewName("hello");
mav.getModel().put("msg", "eeeeeeee");
return mav;
}
@Nullable
public String getContentType() {
return null;
}
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
ServletOutputStream stream = response.getOutputStream();
Object value = model.get("msg");
stream.write(value.toString().getBytes());
stream.flush();
}
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
return this;
}
}
<bean id="userController" class="controller.HelloController">bean>
<bean name="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="order" value="1"/>
<property name="mappings">
<props>
<prop key="user.do">userControllerprop>
props>
property>
bean>
<bean id="viewresolver" class="controller.HelloController">
bean>
在《Jetty概述》一文中简述了Servlet容器的概念,具体可参考《servlet规范》。下面进行部分介绍。
做为ServletContex的配置文件,协助加载组件,其顺序
context-param -> listener -> filter -> servlet
Servlet容器的配置
public interface ServletConfig {
// 容器名称,对应web.xml display-name
public String getServletName();
// 获取容器实例
public ServletContext getServletContext();
// 容器初始化参数,对应web.xml context-param
public String getInitParameter(String name);
}
容器实例,提供一些工具性的方法及组件
public interface ServletContext {
// 当前容器对应请求的主路径
public String getContextPath();
// 根据uripath获取容器
public ServletContext getContext(String uripath);
// 根据路径查找文件,例如
// /welcome.html, /WEB-INF/web.xml
public Set<String> getResourcePaths(String path);
// 获取path转发器,使用forward方法进行容器内转发
public RequestDispatcher getRequestDispatcher(String path);
// 获取通过web.xml注册的Servlet
public Servlet getServlet(String name) throws ServletException;
// 获取服务器上的工作路径 + path
public String getRealPath(String path);
// 获取web容器的版本
public String getServerInfo();
// 获取通过web.xml context-param
public String getInitParameter(String name);
// 手动设置 context-param
public boolean setInitParameter(String name, String value);
// 获取 context中暂存的对象,相当于map.get
public Object getAttribute(String name);
// 手工设置 context中暂存的对象。 相当于map.set
public void setAttribute(String name, Object object);
// 容器名称,对应web.xml display-name
public String getServletContextName();
// 增加监听器
public void addListener(String className);
// 添加过滤器
public FilterRegistration.Dynamic addFilter(String filterName, String className);
// session的配置,具体要关注web容器
public SessionCookieConfig getSessionCookieConfig();
}
/* Servlet的抽象实现类
* @see GenericServlet 提供获取ServletConfig信息一的些访求
* @see HttpServlet 把service根据请求头拆分成 get,post,put... 遵守rest规范
*/
public interface Servlet {
// init()初始化阶段
// config 参应web.xml servlet init-param
public void init(ServletConfig config) throws ServletException;
// 客户端请求阶段
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
// 终止阶段
public void destroy();
}
对 spring 容器进行扩充打通 Servlet容器
存放于spring-web包中,spring-web 类似于spring-context, 主要架负责桥接web方面的组件
两容器相互映射的名称接口
public interface WebApplicationContext {
// 把父亲spring容器存放在servlet容器中的Bean名称
// @see org.springframework.web.context.support.WebApplicationContextUtils#getWebApplicationContext
// @see org.springframework.web.context.support.WebApplicationContextUtils#getRequiredWebApplicationContext
// @see ContextLoaderListener
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
// 向 spring 容器注册名为 “request” 的 RequestScope
// RequestScope 的功能是把 Request 作用域的bean 引向 httpServletRequest.setAttribute
// @see WebApplicationContextUtils.registerWebApplicationScopes
String SCOPE_REQUEST = "request";
// 向 spring 容器注册名为 "session" 的 SessionScope
// SessionScope 的功能是把 Session 作用域的bean 引向 httpServletSession.setAttribute
// @see WebApplicationContextUtils.registerWebApplicationScopes
String SCOPE_SESSION = "session";
// 向 spring 容器注册名为 "application" 的 ServletContextScope
// ServletContextScope 的功能是把 Application 作用域的bean 引向 ServletContext.setAttribute
// @see WebApplicationContextUtils.registerWebApplicationScopes
String SCOPE_APPLICATION = "application";
// 把servlet容器存入在 spring容器中的Bean名称
// @see WebApplicationContextUtils.registerEnvironmentBeans
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
// 把web.xml context-param 转成Map存入在spring容器中的Bean名称
// @see WebApplicationContextUtils.registerEnvironmentBeans
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
// 把ServletContext attribute 转成Map存入在spring容器中的Bean名称
// @see WebApplicationContextUtils.registerEnvironmentBeans
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
// 获取servlet容器
ServletContext getServletContext();
}
扩展WebApplicationContext,并提供配置接口
public interface ConfigurableWebApplicationContext {
// web.xml display-name + 此前缀 为 spring 容器的名字
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
// 把servletConfig存入在 spring容器中的Bean名称
// @see WebApplicationContextUtils.registerEnvironmentBeans
String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
// 获取 spring配置文件目录
String[] getConfigLocations();
}public abstract class AbstractRefreshableWebApplicationContext {
// servlet容器配置
private ServletContext servletContext;
// servlet容器配置
private ServletConfig servletConfig;
// 主题,类似于MessageSource, 用切换css样式
private ThemeSource themeSource;
// 注入spring-context postProcessBeanFactory流程
// 通过 ServletContextAwareProcessor 向 ServletContextAware, ServletConfigAware 注入相当对象
// 向spring容器注入Scope 例如 request, session , apllication
// 向spring容器注入servlet环境, servletConfig,ServletContext
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
// 注入spring-context, onRefresh流程
// 设置默认主题处理
protected void onRefresh();
// 注入spring-context, oprepareRefresh流程
// 把 servletConfig,ServletContext 放到spring Environment环境中
protected void initPropertySources();
}
对spring context 初始化流程做 servlet 容器设置
public abstract class AbstractRefreshableWebApplicationContext {
// servlet容器配置
private ServletContext servletContext;
// servlet容器配置
private ServletConfig servletConfig;
// 主题,类似于MessageSource, 用切换css样式
private ThemeSource themeSource;
// 注入spring-context postProcessBeanFactory流程
// 通过 ServletContextAwareProcessor 向 ServletContextAware, ServletConfigAware 注入相当对象
// 向spring容器注入Scope 例如 request, session , apllication
// 向spring容器注入servlet环境, servletConfig,ServletContext
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
// 注入spring-context, onRefresh流程
// 设置默认主题处理
protected void onRefresh();
// 注入spring-context, oprepareRefresh流程
// 把 servletConfig,ServletContext 放到spring Environment环境中
protected void initPropertySources();
}
spring 不容器 servlet的请求控制方式,按MVC模型Servlet基础上结合spring容器进行扩展
存放于spring-webmvc包中,专注于mvc模式部分,依赖于 spring-web打通servlet容器和spring容器.
《Jetty概述》介绍jetty-webapp内容。网上Demo都是依赖webapp的web.xml方式。笔者为了少加一个包,现在采用直接依赖jetty-servlet的方式创建。
public class SpringWebTest {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletContextHandler servletHandler = new ServletContextHandler();
server.setHandler(servletHandler);
loadDispatcherServlet(servletHandler);
configContext(servletHandler);
server.start();
server.join();
}
// 加载DispatcherServlet,并指定spring容器加载路径
private static void loadDispatcherServlet(ServletContextHandler handler){
ServletHolder holder = new ServletHolder(Source.EMBEDDED);
holder.setHeldClass(DispatcherServlet.class);
holder.setInitParameter("contextConfigLocation", "classpath:spring-web.xml");
holder.setInitOrder(1);
handler.addServlet(holder, "/");
}
// 监听器加载父容器
// Listener的加载优先于Servlet
// 2容器易于service层和controller层分离
private static void configContext(ServletContextHandler handler){
//handler.setInitParameter("contextConfigLocation", "classpath:spring-parent.xml");
//handler.addEventListener(new ContextLoaderListener());
}
}
负责接入Servlet生命周期
public abstract class HttpServletBean {
// DispatcherServlet 实现 init 周期
// 1. 把 ServletConfig 接入到 Environment
// 2. 抽象出 initServletBean 让子类扩展
public final void init() throws ServletException;
}
结合Servlet生命周期关联容器
public abstract class FrameworkServlet {
// 默认加载的spring 容器类
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
// 把spring容器存放在servlet容器中的Bean名称前缀
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";
// 把spring容器存放在servlet容器中的Bean名称
private String contextAttribute;
// 默认加载的spring 容器类
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
// 配置文件 spring-web.xml 的路径
private String contextConfigLocation;
// spring容器初始化后切入逻辑类
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
new ArrayList<>();
// 字符串ApplicationContextInitializer类名,以,分割。最终入contextInitializers
private String contextInitializerClasses;
// 是否将 spring 容器 push到servlet context
private boolean publishContext = true;
// 每个请求进来是不当做Event事件进行push
// @see publishRequestHandledEvent
private boolean publishEvents = true;
// 是否把请求中的 Locale RequestAttributes 放在上下文中爆露
// @see initContextHolders
private boolean threadContextInheritable = false;
// 是否将 option的请求,转发到service处理
// @see doOptions
private boolean dispatchOptionsRequest = false;
// 是否将 trace的请求,转发到service处理
// @see doTrace
private boolean dispatchTraceRequest = false;
// 是否打印请求明细
// @see logResult
private boolean enableLoggingRequestDetails = false;
// spring 容器
private WebApplicationContext webApplicationContext;
// 实现HttpServletBean的切口
// 1. 通过 initWebApplicationContext 初始还容器
// 2. 抽象出 initFrameworkServlet 让子类扩展
protected final void initServletBean() throws ServletException;
// 初始化spring容器
// 1. 通过 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 拿到父亲容器
// 2. 创建容器Refresh 设置父亲容器
// 3. 设置容器进 ServletContext 以 contextAttribute 为名
// @see configureAndRefreshWebApplicationContext
protected WebApplicationContext initWebApplicationContext();
// 把httpServlet 中的 doput dopost,导向 doService方法中
// 1. 设置请求上下文对象
// 2. 是否异步处理
// 3. 日志处理
protected final void processRequest(HttpServletRequest request, HttpServletResponse response);
}
专注MVC流程设计
public class DispatcherServlet {
// spring容器在 request的名字
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
// MultipartResolver在容器中的名字
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
// multipart类型http请求,multipartl转为正常的HttpServletRequest
// @see checkMultipart
private MultipartResolver multipartResolver;
// LocaleResolver在容器中的名字
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
// LocaleResolver 在 request中的名字
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
// 从request中取出请求地区,放入上下文
// @see buildLocaleContext
private LocaleResolver localeResolver;
// themeResolver在容器中的名字
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
// themeResolver 在 request中的名字
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
// ThemeSource 在 request中的名字
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
// 从request中取出主题请求,放入上下文
// @see buildLocaleContext
private ThemeResolver themeResolver;
// handlerMappings在容器中的名字
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
// 路径和执行器的关系维护
// @see getHandler
private List<HandlerMapping> handlerMappings;
// handlerAdapter在容器中的名字
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
// 为HandlerMapping中执行器,统一执行方式,返回model
// @see getHandlerAdapter
private List<HandlerAdapter> handlerAdapters;
// handlerExceptionResolver在容器中的名字
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
// 执行异常在request中的名字
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
// 解决执行器出来的异常
// @see processHandlerException
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// RequestToViewNameTranslator在容器中的名字
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
// viewResolver在容器中的名字
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
// 根据执行器返回的model 使用策略转到view
// @see resolveViewName
private List<ViewResolver> viewResolvers;
// 默认返回视图
// @see getDefaultViewName
private RequestToViewNameTranslator viewNameTranslator;
// flashMapManager在容器中的名字
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
// flashMapManager 在 request中的名字
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
// 输入的(转发前)的 FlashMap 在 request的名字
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
// 要转发 的 FlashMap 在 request的名字
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
// 解决Redirect参数保存问题,利用session的方式保存,转前存,存后取出
// @see doService
private FlashMapManager flashMapManager;
// 以上组件的默认的配置文件名
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
// 以上组件的默认配置的内存映射
// @see getDefaultStrategies
private static final Properties defaultStrategies;
// 直接根据类型从容器中取HandlerMappings,不走 HANDLER_MAPPING_BEAN_NAME
private boolean detectAllHandlerMappings = true;
// 直接根据类型从容器中取HandlerAdapter,不走 HANDLER_ADAPTER_BEAN_NAME
private boolean detectAllHandlerAdapters = true;
// 直接根据类型从容器中取ExceptionResolver,不走 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME
private boolean detectAllHandlerExceptionResolvers = true;
// 直接根据类型从容器中取ViewResolver,不走 VIEW_RESOLVER_BEAN_NAME
private boolean detectAllViewResolvers = true;
// 页面没找到以异常的方式,不返回404
// @see noHandlerFound
private boolean throwExceptionIfNoHandlerFound = false;
}
以逻辑图,简述其运行流程
《SpringMVC实现原理及详解》