网站建设昆明网络公司,icp备案号查询平台官网,班级优化大师app下载学生版,wordpress 4.8正式版Spring MVC的初始化和流程
MVC理念的发展 SpringMVC是Spring提供给Web应用领域的框架设计#xff0c;MVC分别是Model-View-Controller的缩写#xff0c;它是一个设计理念#xff0c;不仅仅存在于Java中#xff0c;各类语言及开发均可用#xff0c;其运转流程和各组件的应…Spring MVC的初始化和流程
MVC理念的发展 SpringMVC是Spring提供给Web应用领域的框架设计MVC分别是Model-View-Controller的缩写它是一个设计理念不仅仅存在于Java中各类语言及开发均可用其运转流程和各组件的应用是MVC的根本 在早期的Java Web开发中主要采用JSPJavaBean模式其设计理念在于解耦各个模块 很快就发现JSP和JavaBean之间出现了严重的耦合Java和HTML也耦合在了一起这样开发者不仅需要掌握Java还需要掌握前端技术更严重的是在软件工程过程中出现了前端需要等后端后端需要等前端才能完成有效地测试乃至后续流程而且每个过程几乎无法复制使用甚至JSP完成了很多业务逻辑混杂着许多页面逻辑功能很快这种方式就被ServletJSPJavaBean代替了就出现了新的模型 模型多了一个Servlet组件主要作用是控制器接受请求并调度JavaBean工作然后结果返回给JSP展示给用户使用了这种模式之后前后端得到了一定的分离控制器和模型层的分离也使得大量的Java代码可以重复使用但后端代码仍存在一定的耦合此时Struts1/2和作为模型层的Hibernate出现了
随着互联网的发展页面大部分采用Ajax请求他们之间的交互只需要JSON数据对于JSP的依赖大大降低而无论Struts1还是2和SJP都有比较紧密的联系有大量关于JSP的jar包加上Struts2出现的漏洞开发者慢慢转向了SpringMVC
在互联网应用中对于性能和灵活性的高要求又使得Hibernate无法强有力的支撑并且数据库的迁移几率很小Hibernate用于移植数据库的特性也难发挥MyBatis就变得更受开发者拥护而随着NoSQL的强势和在互联网应用的发挥已经不是传统的持久层框架能够完全处理的了于是SpringMVC也给出了方案 在Service下可以用Spring的声明式事务操作数据访问层处理数据在业务层上还允许拥护访问NoSQL从而大大的提升互联网应用的性能并且Spring MVC最大的特色是流程和组件是松散的可以在Spring MVC中使用各类视图包括JSON、JSP、XML、PDF等等能够满足手机端、平板端、PC端各类请求
Spring MVC组件与流程 Spring MVC的核心在于其流程和各类组件这是Spring MVC的基础它是一种基于Servlet的技术提供了核心控制器DispatcherServlet和相关的组件并制定了松散的结构从而适应灵活的需要Spring MVC框架是围绕着DispatcherServlet工作的它是个Servlet可以拦截HTTP发送过来的请求在Servlet初始化(调用init方法)时Spring MVC会根据配置获取信息从而得到URI(Uniform Resource Identifier)和处理器(Handler)之间的映射关系为了更加灵活并增强功能SpringMVC还给处理器加入拦截器所以还可以在处理器执行官前后加入自己的代码这样就构成了一个处理器的执行链(HandlerExecutionChain)根据上下文初始化视图解析器等内容当处理器返回时可以通过视图解析器定位视图将数据模型渲染到视图中用来响应用户的请求 当一个请求到来时DispatcherServlet通过请求和事前解析好的HandlerMapping配置找到对应的处理器(Handler)准备开始运行处理器和拦截器组成的执行链而运行处理器需要一个对应的环境这样它就有了一个处理器的适配器(HandlerAdaper)通过这个适配器能运行对应的处理器和拦截器这里的处理器包含了控制器的内容和其他增强的功能在处理器返回模型和视图给DispatcherServlet后DispatcherServlet就会把对应的视图信息传递给视图解析器(ViewResolver)而视图解析器不是必需的取决于是否使用逻辑视图如果使用逻辑视图那么视图解析器就会解析它把模型渲染到视图中去响应用户的请求如果不适用逻辑视图则不会进行处理直接通过视图渲染数据库模型这就是Spring MVC完整的流程SpringMVC也提供了大量的类库用于支撑这个体系大部分组件不需要开发者自己实现 Spring MVC实例
SpringMVC的体系既可以使用XML配置的方式也可以使用注解的方式首先引入所需要的依赖包 !-- 引入Spring Web和MVC框架的依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-web/artifactIdversion5.2.1.RELEASE/version/dependencydependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion5.2.1.RELEASE/version/dependency!-- 引入Jackson JSON处理依赖用于Spring MVC的模型绑定和JSON序列化/反序列化 --dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-core/artifactIdversion2.10.1/version/dependencydependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.10.1/version/dependencydependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-annotations/artifactIdversion2.10.1/version/dependency配置WEB项目的web.xml文件
?xml version1.0 encodingUTF-8?
web-app version3.1xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd idWebApp_ID!-- 配置Spring IoCInversion of Control容器的配置文件路径 --!-- 默认值是/WEB-INF/applicationContext.xml文件 --context-paramparam-namecontextConfigLocation/param-nameparam-value/WEB-INF/applicationContext.xml/param-value/context-param!-- 配置ContextLoaderListener监听器用于初始化Spring IoC容器 --!-- 当Web应用启动时该监听器会读取在context-param中设置的配置文件 --listenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class/listener!-- 配置Spring MVC的核心组件DispatcherServlet --!-- 它负责处理HTTP请求并将请求分发到相应的控制器 --servlet!-- servlet名称用于在web.xml中引用和在URL中匹配 --servlet-namedispatcher/servlet-name!-- 指定Servlet实现类 --servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 指定servlet在服务器启动时加载的优先级 --!-- 数字越小优先级越高。默认值是-1表示不自动加载 --!-- 这里设置为2意味着在ContextLoaderListener之后加载 --load-on-startup2/load-on-startup/servlet!-- 配置Servlet的映射规则 --!-- 所有以/mvc/开头的URL请求都将被DispatcherServlet处理 --servlet-mappingservlet-namedispatcher/servlet-nameurl-pattern/mvc/*/url-pattern/servlet-mapping!-- 欢迎文件列表配置 --!-- 当用户访问没有明确资源的目录时服务器会尝试显示这些欢迎文件 --welcome-file-listwelcome-fileindex.html/welcome-filewelcome-fileindex.htm/welcome-filewelcome-fileindex.jsp/welcome-filewelcome-filedefault.html/welcome-filewelcome-filedefault.htm/welcome-filewelcome-filedefault.jsp/welcome-file/welcome-file-list
/web-app系统变量contextConfigLocation会告诉Spring MVC其Spring IoC的配置文件位置Spring根据位置加载如果是多个文件用逗号隔开也可以使用正则式进行模糊匹配默认值是/WEB-INF/applicationContext.xmlContextLoaderListener实现了接口ServletContextListenerServletContextListener的作用是在整个Web项目前后加入自定义代码从而可以在Web项目初始化之前完成对SpringIoC容器的初始化可以在Web项目关闭的时候完成对SpringIoC容器资源的释放在配置DispatcherServlet时设置servlet-name为dispatcher在SpringMVC中需要一个/WEB-INF/dispatcher-servlet.xml文件(注意servlet-name和文件名的对应关系)与之对应并且设置在服务器启动期间就初始化它配置DispatcherServlet并且拦截匹配正则式/mvc/*的请求这样可以限定拦截范围applicationContext.xml的配置代码中未进行配置这样SpringIoC就不会装载自己的类dispatcher-servlet.xml文件内容如下其中定义了视图解析器解析器定义了前缀和后缀这样视图就知道找到什么样的文件作为视图响应用户请求了
?xml version1.0 encodingUTF-8 ?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxmlns:txhttp://www.springframework.org/schema/txxmlns:contexthttp://www.springframework.org/schema/contextxmlns:mvchttp://www.springframework.org/schema/mvcxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd!-- 使用注解驱动 --mvc:annotation-driven /!-- 定义扫描装载的包 --!-- 会自动搜索com包及其子包下所有的组件 --context:component-scan base-packagecom.* /!-- 定义视图解析器 --!-- 解析请求并返回对应的WEB-INF/jsp下的JSP页面 --bean idviewResolver classorg.springframework.web.servlet.view.InternalResourceViewResolver p:prefix/WEB-INF/jsp/ p:suffix.jsp /
/beans定义一个Controller代码如下
package com.ssmvc.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;/*** DemoController类用于处理与演示相关的HTTP请求。* 通过Controller注解该类被标识为一个Spring MVC控制器。* Controller(demoController)注解为该控制器指定了一个别名便于在配置文件中引用。*/
Controller(demoController)
RequestMapping(/HelloFresh)
public class MyController {/*** 处理访问/index路径的HTTP请求。* 该方法返回一个ModelAndView对象其中viewName属性设置为index。* 这意味着请求将被重定向到名为index的视图。** return ModelAndView对象其中包含用于渲染视图的信息。*/RequestMapping(/index)public ModelAndView index() {ModelAndView mv new ModelAndView();mv.setViewName(index);return mv;}}
Controller指明当前类为控制器SpringMVC扫描的时候就会把它作为控制器加载进来RequestMapping指定了所匹配请求的URISpringMVC在初始化的时候会将这些信息解析保存于是便有了HandlerMapping当发生请求时DispatcherServlet在拦截请求后可以通过请求的URL和相关信息在HandlerMapping机制中找到对应的控制器和方法方法定义返回ModelAndView在方法中把视图名称定义为index由于配置的前缀是/WEB-INF/jsp/后缀是.jsp,因此它会选择使用/WEB-INF/jsp/index.jsp作为最后的响应,jsp代码如下
%page contentTypetext/html pageEncodingUTF-8%
!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.01 Transitional//ENhttp://www.w3.org/TR/html4/loose.dtd
htmlheadmeta http-equivContent-Type contenttext/html; charsetUTF-8titleChapter15/title/headbodyh1Hello, Spring MVC/h1/body
/htmlIDEA启动Tomcat并访问链接http://localhost:8080/SSMVC_war/mvc/HelloFresh/indexTomat9JDK1.8Spring5.2.1.RELEASE其他的环境会有兼容性问题再次不多赘述各组件版本之间兼容性Tomcat服务器配置如下 如图所示图中展示了实例的组件和流程Spring MVC启动的时候就会去解析MyController的注解生成对应URI和请求的映射关系并注册对应的方法当请求来了的时候SpringMVC首先根据URI找到对应的HandlerMapping然后组织为一个执行链(HandlerExecutionChain)通过请求类型找到RequestMappingHandlerAdaper实例该实例是在DispatcherServlet初始化的时候创建的接下来通过它去执行HandlerExecutionChain的内容最终在MyController的方法中将index视图返回DispatcherServlet由于配置的视图解析器(InternalResourceViewResolver)前缀为/WEB-INF/jsp/后缀为.jsp视图名为index所以最后它会找到/WEB-INF/jsp/index.jsp文件作为视图响应最终的请求 以上就完成了一个Spring MVC最简单的一个实例 Spring MVC初始化
整个Spring MVC的流程中配置了DispatcherServlet和ContextLoaderListener在初始化时它们要初始化Spring IoC容器上下文和映射请求上下文其中映射请求上下文是基于Spring IoC上下文扩展出来的以适应Java Web项目的需要
初始化Spring IoC上下文
在Web容器的规范中存在一个ServletContextListener接口它允许我们在Web容器初始化和结束期执行一定的逻辑换句话说就是通过实现它可以在DispatcherServlet初始化之前完成SpringIoC容器的初始化可以再结束期销毁Spring IoC容器 package org.springframework.web.context;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;/*** ContextLoaderListener 类实现了 ServletContextListener 接口* 用于在 Servlet 应用上下文中初始化和销毁 Spring 的 WebApplicationContext。* 它是 Spring Web 应用程序的核心组件之一负责在 Servlet 应用程序启动时创建和配置ApplicationContext* 并在应用程序停止时关闭ApplicationContext以管理应用程序的生命周期。*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {/*** 默认构造函数用于创建一个没有关联 WebApplicationContext 的 ContextLoaderListener。* 在实例化后通常会通过 ServletContext 的 init 参数来指定要关联的 ApplicationContext。*/public ContextLoaderListener() {}/*** 带有 WebApplicationContext 参数的构造函数用于明确指定要与此 ContextLoaderListener 关联的 ApplicationContext。** param context 要关联的 WebApplicationContext。*/public ContextLoaderListener(WebApplicationContext context) {super(context);}/*** ServletContextListener 接口的实现方法。* 当 ServletContext 初始化时调用此方法用于初始化 WebApplicationContext。** param event ServletContextEvent包含 ServletContext 相关的信息。*/public void contextInitialized(ServletContextEvent event) {this.initWebApplicationContext(event.getServletContext());}/*** ServletContextListener 接口的实现方法。* 当 ServletContext 销毁时调用此方法用于关闭 WebApplicationContext 并进行清理工作。** param event ServletContextEvent包含 ServletContext 相关的信息。*/public void contextDestroyed(ServletContextEvent event) {this.closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}通过这个类可以在Web项目的上下文使用Spring IoC容器管理整个Web项目的资源 初始化映射请求上下文
映射请求上下文是通过DispatcherServlet初始化的它和普通的Servlet是一样的可以根据自己的需要设置它在启动时初始化或者在等待用户第一次请求时候初始化如果在Web项目中并没有注册ContextLoaderListener这个时候DispatcherServlet就会在其初始化的时候对Spring IoC容器进行初始化 由于初始化Spring IoC容器是一个耗时的操作这个工作不应该放到用户请求上在大部分场景下都应该让DispatcherServlet在服务器启动期间完成Spring IoC容器的初始化可以是Web容器启动时也可以在Web容器载入DispatcherServlet的时候进行初始化最好是在Web容器刚启动的时候对其进行初始化因为在整个Web项目的初始化过程中不只DispatcherServlet需要使用到Spring IoC的资源在开始时初始化可以让Web中的各个组件共享资源当然可以指定Web容器中组件得初始化顺序让DispatcherServlet第一个初始化只是这样就增加了配置的复杂度因此在绝大多数情况下使用ContextLoaderListener初始化Spring IoC容器更佳 从类的关系图中可以看出DispatcherServlet的父类是FrameworkServletFrameworkServlet的父类是HttpServletBeanHttpServletBean继承了Web容器提供的HttpServlet
Web容器初始化会调用其init方法对于DispatcherServlet也是如此这个方法位于其父类HttpServletBean中源码如下 /*** 创建一个标准的Servlet环境。* * return ConfigurableEnvironment 一个可配置的环境实例用于Servlet环境配置。*/protected ConfigurableEnvironment createEnvironment() {return new StandardServletEnvironment();}/*** 初始化Servlet。* 此方法用于初始化Servlet实例包括设置Servlet属性、初始化BeanWrapper以及初始化Servlet bean。* * throws ServletException 如果初始化过程中出现错误。*/public final void init() throws ServletException {// 从Servlet配置中创建属性值集合PropertyValues pvs new ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);// 如果属性值不为空则进行属性设置if (!pvs.isEmpty()) {try {// 创建BeanWrapper用于属性设置和bean访问BeanWrapper bw PropertyAccessorFactory.forBeanPropertyAccess(this);// 创建ServletContextResourceLoader用于加载资源ResourceLoader resourceLoader new ServletContextResourceLoader(this.getServletContext());// 注册自定义编辑器用于处理Resource类型的属性bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));// 初始化BeanWrapper可以被子类重写以提供额外的初始化逻辑this.initBeanWrapper(bw);// 设置属性值到BeanWrapper中强制覆盖现有值bw.setPropertyValues(pvs, true);} catch (BeansException var4) {// 如果在设置bean属性过程中出现BeansException则记录错误并重新抛出异常if (this.logger.isErrorEnabled()) {this.logger.error(Failed to set bean properties on servlet this.getServletName() , var4);}throw var4;}}// 初始化Servlet bean此方法可以被子类重写以提供特定的初始化逻辑this.initServletBean();}/*** 初始化BeanWrapper。* 此方法为保护类型的抽象方法用于子类提供BeanWrapper的初始化逻辑。* * param bw 要初始化的BeanWrapper实例。* throws BeansException 如果初始化过程中出现错误。*/protected void initBeanWrapper(BeanWrapper bw) throws BeansException {}/*** 初始化Servlet bean。* 此方法为保护类型的抽象方法用于子类进行特定的Servlet bean初始化。* * throws ServletException 如果初始化过程中出现错误。*/protected void initServletBean() throws ServletException {}
在HttpServletBean中可以看到initServletBean方法在FrameworkServlet中也可以看到子类方法会覆盖父类的方法FrameworkServlet的initServletBean()源码如下 /*** 初始化Servlet Bean。* 这个方法负责Spring Servlet的初始化过程包括日志记录、初始化WebApplicationContext和FrameworkServlet。* 如果在初始化过程中遇到RuntimeException或ServletException将记录错误并重新抛出异常。* throws ServletException 如果在初始化Servlet时发生错误。*/protected final void initServletBean() throws ServletException {// 记录Servlet初始化开始的日志。this.getServletContext().log(Initializing Spring this.getClass().getSimpleName() this.getServletName() );if (this.logger.isInfoEnabled()) {this.logger.info(Initializing Servlet this.getServletName() );}// 记录初始化开始时间。long startTime System.currentTimeMillis();try {// 初始化WebApplicationContext和FrameworkServlet。this.webApplicationContext this.initWebApplicationContext();this.initFrameworkServlet();} catch (RuntimeException | ServletException var4) {// 记录初始化失败的日志并抛出异常。this.logger.error(Context initialization failed, var4);throw var4;}// 根据enableLoggingRequestDetails的值记录关于请求参数和头信息日志的详细程度。if (this.logger.isDebugEnabled()) {String value this.enableLoggingRequestDetails ? shown which may lead to unsafe logging of potentially sensitive data : masked to prevent unsafe logging of potentially sensitive data;this.logger.debug(enableLoggingRequestDetails this.enableLoggingRequestDetails : request parameters and headers will be value);}// 记录初始化完成的日志。if (this.logger.isInfoEnabled()) {this.logger.info(Completed initialization in (System.currentTimeMillis() - startTime) ms);}}/*** 初始化Web应用程序上下文。* 此方法负责获取或创建WebApplicationContext并根据需要配置和刷新它。* 如果已经存在webApplicationContext则检查它是否是一个可配置的上下文并且是否尚未激活。* 如果尚未激活它将被配置并刷新。如果不存在webApplicationContext则尝试查找或创建一个新的上下文。* 最后如果配置了发布上下文属性则将上下文发布到Servlet上下文中。** return WebApplicationContext 应用程序上下文实例*/protected WebApplicationContext initWebApplicationContext() {// 尝试从当前Servlet上下文中获取根应用程序上下文WebApplicationContext rootContext WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());WebApplicationContext wac null;// 如果已经存在webApplicationContext则进行进一步的配置和检查if (this.webApplicationContext ! null) {wac this.webApplicationContext;// 如果当前上下文是可配置的并且尚未激活if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac (ConfigurableWebApplicationContext)wac;// 如果当前上下文没有父上下文则设置根上下文作为其父上下文if (!cwac.isActive()) {if (cwac.getParent() null) {cwac.setParent(rootContext);}// 配置并刷新当前上下文this.configureAndRefreshWebApplicationContext(cwac);}}}// 如果当前上下文为null则尝试查找或创建一个新的上下文if (wac null) {wac this.findWebApplicationContext();}if (wac null) {wac this.createWebApplicationContext(rootContext);}// 如果尚未接收到刷新事件则同步刷新上下文if (!this.refreshEventReceived) {synchronized(this.onRefreshMonitor) {this.onRefresh(wac);}}// 如果配置了发布上下文属性则将其发布到Servlet上下文中if (this.publishContext) {String attrName this.getServletContextAttributeName();this.getServletContext().setAttribute(attrName, wac);}// 返回最终配置和刷新的应用程序上下文return wac;}
当IoC容器没有对应的初始化时DispatcherServlet会常识去初始化它调度onRefresh方法该方法将初始化Spring MVC各个组件该方法源码如下 /*** 在上下文刷新时初始化策略。* 此方法在应用程序上下文被刷新时被调用用于初始化一系列的策略对象这些对象是处理Web请求所必需的。* param context 应用程序上下文提供对应用程序范围内的Bean和其他服务的访问。*/protected void onRefresh(ApplicationContext context) {this.initStrategies(context);}/*** 初始化各种策略实例。* 此方法负责在应用程序上下文加载后初始化处理请求、解析请求参数、处理异常、解析视图等各个环节所需的策略实例。* 这些策略包括但不限于多部分解析器、地域解析器、主题解析器、处理器映射、处理器适配器、异常解析器、请求到视图名的翻译器、视图解析器和闪现映射管理器。* 通过这种方式框架可以灵活地根据配置来调整其行为提供高度可定制的服务。** param context 应用程序上下文用于获取配置和服务实例。*/protected void initStrategies(ApplicationContext context) {// 初始化多部分解析器用于处理上传文件和表单数据。this.initMultipartResolver(context);// 初始化地域解析器用于处理地域相关的请求和响应。this.initLocaleResolver(context);// 初始化主题解析器用于处理主题相关的请求和响应。this.initThemeResolver(context);// 初始化处理器映射用于映射请求到特定的处理器。this.initHandlerMappings(context);// 初始化处理器适配器用于适配和调用各种类型的处理器。this.initHandlerAdapters(context);// 初始化异常解析器用于处理处理器执行过程中抛出的异常。this.initHandlerExceptionResolvers(context);// 初始化请求到视图名的翻译器用于将处理结果翻译成对应的视图名。this.initRequestToViewNameTranslator(context);// 初始化视图解析器用于解析视图名并渲染相应的视图。this.initViewResolvers(context);// 初始化闪现映射管理器用于管理闪现消息和数据。this.initFlashMapManager(context);}事实上对于这些组件DispatcherServlet会根据其配置文件DispatcherServlet.properties进行初始化
# Default implementation classes for DispatcherServlets strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
# 为DispatcherServlet的策略接口提供默认的实现类。
# 当在DispatcherServlet的应用上下文中找不到匹配的bean时这些配置用作备用选项。
# 不建议应用程序开发者对此进行自定义。# 配置默认的LocaleResolver实现用于解析请求中的地域信息。
# 默认使用AcceptHeaderLocaleResolver根据请求头中的Accept-Language字段解析地域信息。
org.springframework.web.servlet.LocaleResolverorg.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver# 配置默认的ThemeResolver实现用于解析请求中的主题信息。
# 默认使用FixedThemeResolver固定的主题解析策略。
org.springframework.web.servlet.ThemeResolverorg.springframework.web.servlet.theme.FixedThemeResolver# 配置默认的HandlerMapping实现用于映射请求到处理器。
# 列出了多个实现按照优先级从高到低排列DispatcherServlet会按顺序尝试解析请求。
org.springframework.web.servlet.HandlerMappingorg.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\org.springframework.web.servlet.function.support.RouterFunctionMapping# 配置默认的HandlerAdapter实现用于适配不同的处理器并执行它们。
# 列出了多个实现支持处理不同类型的操作包括HttpRequestHandler、Controller、HandlerFunction等。
org.springframework.web.servlet.HandlerAdapterorg.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\org.springframework.web.servlet.function.support.HandlerFunctionAdapter# 配置默认的HandlerExceptionResolver实现用于处理处理器执行过程中抛出的异常。
# 列出了多个实现按照优先级从高到低排列用于映射不同的异常到对应的处理逻辑。
org.springframework.web.servlet.HandlerExceptionResolverorg.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver# 配置默认的RequestToViewNameTranslator实现用于将请求映射到视图名称。
# 默认使用DefaultRequestToViewNameTranslator根据请求路径规则推断视图名称。
org.springframework.web.servlet.RequestToViewNameTranslatororg.springframework.web.servlet.view.DefaultRequestToViewNameTranslator# 配置默认的ViewResolver实现用于解析视图。
# 默认使用InternalResourceViewResolver支持JSP和其他内部资源的解析。
org.springframework.web.servlet.ViewResolverorg.springframework.web.servlet.view.InternalResourceViewResolver# 配置默认的FlashMapManager实现用于管理Flash属性。
# 默认使用SessionFlashMapManager将Flash属性存储在会话中以支持请求之间的数据传递。
org.springframework.web.servlet.FlashMapManagerorg.springframework.web.servlet.support.SessionFlashMapManager 这便解释了在启动期间DispatcherServlet会加载这些配置的组件进行初始化不需要很多配置就能够使用Spring MVC的原因 使用注解配置方式初始化
Servlet3.0之后的规范允许取消web.xml配置只使用注解方式Spring3.1之后的版本也提供了注解方式的配置使用注解方式很简单先继承一个抽象类AbstractAnnotationConfigDispatcherServletInitializer,再实现它所以定义的方法使用起来非常方便如下代码所示
package com.ssmvc.config;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;import com.ssmvc.backend.config.BackendConfig;
import com.ssmvc.web.config.WebConfig;
/*** 自定义Web应用程序初始化器继承自Spring框架的AbstractAnnotationConfigDispatcherServletInitializer类。* 该类用于配置Spring MVC的DispatcherServlet初始化参数以及应用程序的上下文配置。*/
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {/*** 配置根应用程序上下文的Java配置类。* 根上下文通常用于配置共享于整个应用程序的bean如数据源、事务管理器等。** return Java配置类的数组。*/Overrideprotected Class?[] getRootConfigClasses() {// 可以返回Spring的Java配置文件数组return new Class?[] { BackendConfig.class };}/*** 配置DispatcherServlet实例的应用程序上下文的Java配置类。* Servlet上下文通常用于配置与Web层相关的bean如控制器、视图解析器等。* * return Java配置类的数组。*/Overrideprotected Class?[] getServletConfigClasses() {// 可以返回Spring的Java配置文件数组return new Class?[] { WebConfig.class };}/*** 配置DispatcherServlet的URL映射。* 该方法定义了哪些URL路径应该由DispatcherServlet处理。* * return URL模式的数组。*/Overrideprotected String[] getServletMappings() {return new String[] { /mvc/* };}
}
继承了AbstractAnnotationConfigDispatcherServletInitializerSpring MVC就会加载这个类文件Servlet3.0之后的版本允许扫描加载Servlet只需要按照规范实现ServletContainerInitializer接口Web容器就会把对应的Servlet加载进来于是Spring MVC框架在自己的包内实现了一个类SpringServletContainerInitializer它实现了ServletContainerInitializer接口可以通过它去加载用户提供的MyWebAppInitializer看一下源码
package org.springframework.web;import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;/*** 实现{link ServletContainerInitializer}以便在Servlet 3.0容器中自动检测和初始化Spring的{link WebApplicationInitializer}。* p* 该类通过反射实例化找到的所有WebApplicationInitializer实现并按照它们的顺序调用{link WebApplicationInitializer#onStartup(ServletContext)}方法。* 如果没有找到任何实现则不执行任何操作。*/
HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {/*** 默认构造函数。*/public SpringServletContainerInitializer() {}/*** 当Servlet容器启动时调用此方法。* p* 它负责检测并实例化所有的{link WebApplicationInitializer}实现然后按照它们的顺序初始化Spring应用程序。** param webAppInitializerClasses Servlet容器检测到的WebApplicationInitializer类的集合。* param servletContext Servlet上下文对象用于初始化Spring应用程序。* throws ServletException 如果在实例化WebApplicationInitializer实现时发生错误。*/public void onStartup(Nullable SetClass? webAppInitializerClasses, ServletContext servletContext) throws ServletException {ListWebApplicationInitializer initializers new LinkedList();// 如果提供了WebApplicationInitializer类的集合则遍历该集合if (webAppInitializerClasses ! null) {for (Class? waiClass : webAppInitializerClasses) {// 检查类是否为接口或抽象类以及是否实现WebApplicationInitializer接口if (!waiClass.isInterface() !Modifier.isAbstract(waiClass.getModifiers()) WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {// 通过无参构造函数实例化WebApplicationInitializer实现initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());} catch (Throwable var7) {// 如果实例化失败抛出ServletExceptionthrow new ServletException(Failed to instantiate WebApplicationInitializer class, var7);}}}}// 如果没有找到任何WebApplicationInitializer实现记录日志if (initializers.isEmpty()) {servletContext.log(No Spring WebApplicationInitializer types detected on classpath);} else {// 如果找到了实现记录日志并按排序顺序调用每个initializer的onStartup方法servletContext.log(initializers.size() Spring WebApplicationInitializers detected on classpath);AnnotationAwareOrderComparator.sort(initializers);for (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}}}
}
从源码中可以看到只要实现了WebApplicationInitializer接口的onStartup方法Spring MVC就会把类当作一个初始化器加载进来 图中可以看到有5个类实现了WebApplicationInitializer接口该接口是一个通用的Spring Web初始化接口
AbstractContextLoaderInitializer抽象类用于初始化Spring IoC容器AbstractDispatcherServletInitializer用于初始化DispatcherServlet映射(HandlerMapping)关系AbstractAnnotationConfigDispatcherServletInitializer暴露给用户使用的Spring初始化器方法AbstractReactiveWebInitializer是一个响应式编程的初始化器 除了AbstractReactiveWebInitializer另外的抽象类之间也是继承的关系 代码中定义的MyWebAppInitializer类继承了抽象类AbstractAnnotationConfigDispatcherServletInitializer,并实现了三个方法其中
getRootConfigClasses()用于获取Spring IoC容器的Java配置类用以装载各类Spring BeangetServletConfigClasses()用于获取各类Spring MVC的URI和控制器的配置关系类用于生成Web请求的上下getServletMappings()定义DispatcherServlet拦截请求的范围比如通过正则式/mvc/*限制拦截
getRootConfigClasses方法返回配置BackendConfig类用于Spring应用程序的配置包括数据源、MyBatis、事务管理器等的初始化,BackendConfig如下所示
package com.ssmvc.backend.config;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import org.springframework.web.bind.annotation.RestController;Configuration
// 扫描所有com.ssmvc包下的组件但排除Controller和RestController注解的类
ComponentScan(basePackages com.ssmvc.*, excludeFilters Filter(type FilterType.ANNOTATION, classes {Controller.class, RestController.class}))
EnableTransactionManagement // 开启事务管理
MapperScan(basePackages com.ssmvc, annotationClass Mapper.class) // 扫描Mapper接口
public class BackendConfig implements TransactionManagementConfigurer {// 数据源实例// 数据源private DataSource dataSource null;/*** 初始化数据源。* 使用Apache Commons DBCP2创建数据源实例。** return DataSource 数据源实例*/Bean(name dataSource)public DataSource initDataSource() {if (dataSource ! null) {return dataSource;}Properties props new Properties();// 配置数据库连接信息和连接池参数props.setProperty(driverClassName, com.mysql.jdbc.Driver);props.setProperty(url, jdbc:mysql://localhost:3306/ssm);props.setProperty(username, root);props.setProperty(password, 123456);props.setProperty(maxActive, 200);props.setProperty(maxIdle, 20);props.setProperty(maxWait, 30000);try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}/*** 初始化JdbcTemplate。* 为Spring提供的简化JDBC操作的工具类配置数据源。** param dataSource 数据源* return JdbcTemplate 实例*/Bean(name jdbcTemplate)public JdbcTemplate initjdbcTemplate(Autowired DataSource dataSource) {JdbcTemplate jdbcTemplate new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}/*** 配置基于注解的事务管理器。* 使用DataSourceTransactionManager作为事务管理器并配置数据源。** return PlatformTransactionManager 事务管理器实例*/OverrideBean(name transactionManager)public PlatformTransactionManager annotationDrivenTransactionManager() {DataSourceTransactionManager transactionManager new DataSourceTransactionManager();transactionManager.setDataSource(initDataSource());return transactionManager;}/*** 创建SqlSessionFactory。* 配置MyBatis的SqlSessionFactoryBean并指定数据源和配置文件位置。** param dataSource 数据源* return SqlSessionFactory 实例* throws Exception*/Bean(sqlSessionFactory)public SqlSessionFactory createSqlSessionFactoryBean(Autowired DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);// 指定MyBatis配置文件位置String cfgFile mybatis-config.xml;Resource configLocation new ClassPathResource(cfgFile);sqlSessionFactoryBean.setConfigLocation(configLocation);return sqlSessionFactoryBean.getObject();}}
getServletConfigClasses方法返回配置WebConfig类如下是WebConfig代码它是一个URI和控制器的映射关系类由此产生Web请求的上下文代码如下
package com.ssmvc.web.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;/*** Spring MVC的配置类* 通过Configuration注解标记这个类是一个配置类等同于Spring XML配置文件* 使用EnableWebMvc注解开启Spring MVC的功能* 使用ComponentScan注解指定Spring要扫描的组件包这些包中的组件会被自动注册为Spring Bean*/
Configuration
ComponentScan(com.ssmvc.controller)
EnableWebMvc
public class WebConfig {/*** 配置InternalResourceViewResolver作为Spring MVC的视图解析器* 通过Bean注解标记这个方法会返回一个Bean该Bean会被注册到Spring的ApplicationContext中* 方法名称viewResolver和Bean注解中的name属性一起定义了这个Bean的名称* return 返回一个配置好的InternalResourceViewResolver实例*/Bean(name viewResolver)public ViewResolver initViewResolver() {InternalResourceViewResolver viewResolver new InternalResourceViewResolver();viewResolver.setPrefix(/WEB-INF/jsp/);viewResolver.setSuffix(.jsp);return viewResolver;}
}
这样就通过Java配置的方式完成了之前在XML(web.xml和Spring IoC容器所需要的XML文件)里的配置执行效果一样注意不能共存如果使用Java配置那之前的xml配置文件要删掉(web.xmldispatcher-servlet.xmlapplicationContext.xml)
WebMvcConfigurer接口
上面代码中配置了视图解析器实际上为了方便配置各类组件Spring MVC提供了接口WebMvcConfigurer这是一个允许定制SpringMVC组件得接口有时候使用它会更佳方便和简便接口源码如下:
package org.springframework.web.servlet.config.annotation;/*** 配置Spring MVC的接口。* 该接口提供了多种方法用于定制Spring MVC的各个方面的行为例如路径匹配、内容协商、异步支持等。* 通过实现这个接口开发者可以精细地控制Spring MVC的应用程序行为。*/
public interface WebMvcConfigurer {/*** 配置路径匹配的设置。* 使用{link PathMatchConfigurer}可以定制路径匹配的行为例如启用Ant风格的路径模式、配置路径变量的解析等。** param configurer 用于配置路径匹配的工具类。*/default void configurePathMatch(PathMatchConfigurer configurer) {}/*** 配置内容协商的行为。* 内容协商允许服务器根据客户端的请求头如Accept来选择合适的响应格式。通过这个方法可以定制内容协商的策略和配置。** param configurer 用于配置内容协商的工具类。*/default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}/*** 配置异步请求的支持。* 使用{link AsyncSupportConfigurer}可以配置Spring MVC如何处理异步请求例如设置异步请求的超时时间、配置异步请求的处理器等。** param configurer 用于配置异步支持的工具类。*/default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}/*** 配置默认Servlet的处理行为。* 通过这个方法可以决定Spring MVC如何处理静态资源请求或者将某些请求代理给默认的Servlet处理。** param configurer 用于配置默认Servlet处理的工具类。*/default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}/*** 注册格式化器Formatter。* 格式化器用于将字符串转换为对象或者将对象转换为字符串。通过这个方法可以向Spring MVC添加自定义的格式化器。** param registry 用于注册格式化器的工具类。*/default void addFormatters(FormatterRegistry registry) {}/*** 注册拦截器。* 拦截器可以在请求处理之前、之后或者发生异常时执行自定义逻辑。通过这个方法可以向Spring MVC添加自定义的拦截器。** param registry 用于注册拦截器的工具类。*/default void addInterceptors(InterceptorRegistry registry) {}/*** 注册资源处理器。* 资源处理器用于处理静态资源请求例如CSS、JavaScript文件。通过这个方法可以向Spring MVC添加自定义的资源处理器。** param registry 用于注册资源处理器的工具类。*/default void addResourceHandlers(ResourceHandlerRegistry registry) {}/*** 注册CORS跨域资源共享映射。* CORS允许Web应用程序从不同的域访问资源。通过这个方法可以向Spring MVC添加自定义的CORS映射规则。** param registry 用于注册CORS映射的工具类。*/default void addCorsMappings(CorsRegistry registry) {}/*** 注册ViewController。* ViewController将特定的URL路径直接映射到视图而不需要通过控制器处理。通过这个方法可以向Spring MVC添加自定义的ViewController。** param registry 用于注册ViewController的工具类。*/default void addViewControllers(ViewControllerRegistry registry) {}/*** 配置视图解析器。* 视图解析器负责将逻辑视图名解析为实际的视图对象。通过这个方法可以向Spring MVC添加自定义的视图解析器或者配置已有的视图解析器。** param registry 用于配置视图解析器的工具类。*/default void configureViewResolvers(ViewResolverRegistry registry) {}/*** 注册方法参数解析器。* 方法参数解析器用于解析控制器方法的参数。通过这个方法可以向Spring MVC添加自定义的方法参数解析器。** param resolvers 方法参数解析器的列表。*/default void addArgumentResolvers(ListHandlerMethodArgumentResolver resolvers) {}/*** 注册方法返回值处理器。* 方法返回值处理器用于处理控制器方法的返回值。通过这个方法可以向Spring MVC添加自定义的方法返回值处理器。** param handlers 方法返回值处理器的列表。*/default void addReturnValueHandlers(ListHandlerMethodReturnValueHandler handlers) {}/*** 配置HTTP消息转换器。* HTTP消息转换器用于将HTTP请求体中的数据转换为Java对象或将Java对象转换为HTTP响应体中的数据。通过这个方法可以向Spring MVC添加自定义的HTTP消息转换器或者配置已有的消息转换器。** param converters HTTP消息转换器的列表。*/default void configureMessageConverters(ListHttpMessageConverter? converters) {}/*** 扩展HTTP消息转换器。* 这个方法与configureMessageConverters类似但是它允许在已有的消息转换器列表上进行扩展而不是替换。** param converters HTTP消息转换器的列表。*/default void extendMessageConverters(ListHttpMessageConverter? converters) {}/*** 配置异常处理器。* 异常处理器用于处理控制器方法抛出的异常。通过这个方法可以向Spring MVC添加自定义的异常处理器。** param resolvers 异常处理器的列表。*/default void configureHandlerExceptionResolvers(ListHandlerExceptionResolver resolvers) {}/*** 扩展异常处理器。* 这个方法与configureHandlerExceptionResolvers类似但是它允许在已有的异常处理器列表上进行扩展而不是替换。** param resolvers 异常处理器的列表。*/default void extendHandlerExceptionResolvers(ListHandlerExceptionResolver resolvers) {}/*** 获取验证器。* 这个方法允许返回一个自定义的验证器用于验证控制器方法的参数或者模型对象。如果不需要自定义验证器可以返回null。** return 验证器实例或者null。*/Nullabledefault Validator getValidator() {return null;}/*** 获取消息代码解析器。* 消息代码解析器用于解析验证错误的消息代码。通过这个方法可以返回一个自定义的消息代码解析器或者null。** return 消息代码解析器实例或者null。*/Nullabledefault MessageCodesResolver getMessageCodesResolver() {return null;}
}
其中有两个比较常用的方法分别是configureViewResolvers和addViewControllers, 据此改造一下WebConfig类代码如下
package com.ssmvc.web.config;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.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;/*** Spring MVC的配置类* 通过Configuration注解标记这个类是一个配置类等同于Spring XML配置文件* 使用EnableWebMvc注解开启Spring MVC的功能* 使用ComponentScan注解指定Spring要扫描的组件包这些包中的组件会被自动注册为Spring Bean*/
Configuration
ComponentScan(com.ssmvc.controller)
EnableWebMvc
public class WebConfigII implements WebMvcConfigurer {/*** 配置视图解析器* 本方法用于设置Spring MVC中视图解析的前缀和后缀指定视图文件在项目中的位置和格式。* 使用InternalResourceViewResolver作为视图解析器它能够处理JSP视图。* 设置前缀为/WEB-INF/jsp/确保视图文件位于WEB-INF目录下的jsp子目录中。* 设置后缀为.jsp指明视图文件的格式为JSP。* param registry ViewResolverRegistry实例用于注册和配置视图解析器*/Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {InternalResourceViewResolver viewResolver new InternalResourceViewResolver();viewResolver.setPrefix(/WEB-INF/jsp/);viewResolver.setSuffix(.jsp);registry.viewResolver(viewResolver);}/*** 添加ViewController* 本方法用于配置默认的控制器即无需处理业务逻辑的简单页面跳转。* 通过ViewControllerRegistry添加一个控制器将/config/index路径映射到index视图。* 这样当请求/config/index时会直接展示对应的index.jsp页面无需额外的控制器逻辑。* param registry ViewControllerRegistry实例用于注册和配置ViewController*/Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController(/config/index).setViewName(index);}
}
Spring MVC开发流程
开发Spring MVC的核心是控制器的开发在Spring MVC中会将控制器的方法封装为一个处理器(Handler), 然后增强它的功能主要包括解析控制器的方法参数和结果等等控制器(Controller)是通过注解Controller标注的只要把它配置在IoC容器的扫描路径中就可以通过扫描装配了控制器往往要结合注解RequestMapping使用注解RequestMapping可以配置在类或方法上它的作用是将指定的URI和类或者方法进行绑定这样请求对应的URI就可以找到对应的方法进行处理了 在SpringMVC4.3之后增加了RestController、GetMapping和PostMapping等注解通过这些注解用户能够方便的构建REST风格的Spring MVC Spring MVC还给处理器添加了拦截器机制当启动SpringMVC的时候首先会解析控制器中注解RequestMapping的配置将方法绑定为一个处理器然后结合所配置的拦截器存放到一个执行链(HandlerExecutionChain)中最后将注解RequestMappint定义的路径和执行链映射这样当请求到达服务器时通过请求路径找到对应的执行链并运行它就可以运行拦截器和处理器了
RequestMapping
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;/*** 注解用于映射HTTP请求到处理方法。* * Target({ElementType.TYPE, ElementType.METHOD}) 指定该注解可以用于类或方法上。* Retention(RetentionPolicy.RUNTIME) 指定该注解在运行时可被读取。* Documented 指示Javadoc工具应包含此注释。* Mapping 表明这是一个映射注解用于Spring MVC中。*/
Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
Mapping
public interface RequestMapping {/*** 映射的名称可选。* * return 映射的名称默认为空字符串。*/String name() default ;/*** 映射的URL路径可以使用通配符。* * return 映射的URL路径数组默认为空数组。*/AliasFor(path)String[] value() default {};/*** 映射的URL路径与value()相同。* * return 映射的URL路径数组默认为空数组。*/AliasFor(value)String[] path() default {};/*** 映射支持的HTTP请求方法。* * return 支持的HTTP请求方法数组默认为空数组。*/RequestMethod[] method() default {};/*** 映射方法参数用于指定请求参数的条件。* * return 请求参数数组默认为空数组。*/String[] params() default {};/*** 映射请求头用于指定请求头的条件。* * return 请求头数组默认为空数组。*/String[] headers() default {};/*** 映射消费的内容类型用于指定请求体的MIME类型。* * return 消费的内容类型数组默认为空数组。*/String[] consumes() default {};/*** 映射产生的内容类型用于指定响应体的MIME类型。* * return 产生的内容类型数组默认为空数组。*/String[] produces() default {};
}
例如如下代码使用了value和method RequestMapping(value /index2, method RequestMethod.GET)public ModelAndView index2() {// 创建 ModelAndView 对象ModelAndView mv new ModelAndView();// 设置视图名称为 indexmv.setViewName(index);// 返回 ModelAndView 对象return mv;}这样就可以对/mvc/HelloFresh/index2的HTTP GET请求提供响应了如果没有配置method那么就会响应所有的请求
控制器开发 控制器开发是SpringMVC的核心大致分为三个步骤获取请求参数处理业务逻辑绑定模型和视图 首先看一下获取请求参数在SpringMVC中接收参数的方法很多建议不要使用Servlet容器给与的API避免控制器依赖Servlet容器例如如下代码 /*** 通过参数 获取Servlet规范中的HttpServletRequest和HttpSession对象** param session -- 会话对象* param request -- 请求对象* return*/RequestMapping(value /index3)public ModelAndView index3(HttpSession session, HttpServletRequest request) {ModelAndView mv new ModelAndView();mv.setViewName(index);return mv;}SpringMVC会自动解析代码中的方法参数session和request传递关于Servlet容器的API参数所以是可以获取到的通过request或者session都可以很容易的获取HTTP的请求参数但并非一个好的方法这样做index3方法就和Servlet容器耦合在了一起不利于扩展和测试
SpringMVC提供了更多的方法和注解用来获取参数例如要获取一个HTTP请求的参数id(长整型)可以使用注解RequestParamindex方法写成如下所示代码 /*** 通过RequestParam获取参数** param id 参数长整型* return ModelAndView*/RequestMapping(value /index4, method RequestMethod.GET)public ModelAndView index4(RequestParam(id) Long id) {System.out.println(params[id] id);ModelAndView mv new ModelAndView();mv.setViewName(index);return mv;}RequestParam源码如下
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;/*** 用于标记方法参数指示该参数应作为HTTP请求参数加以处理。* p* 此注解用于说明方法参数与HTTP请求参数之间的映射关系。它允许指定参数的名称、是否必需以及默认值。* 参数的名称和值可以通过注解的属性来定义提供了一种灵活的方式来处理不同的请求场景。* * Target({ElementType.PARAMETER}) 指定注解可以应用于方法参数上* Retention(RetentionPolicy.RUNTIME) 指定注解在运行时可见* Documented 指示Javadoc应该包含这个注解*/
Target({ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface RequestParam {/*** 参数的名称用于映射到HTTP请求参数。* p* 此属性与value属性具有相同的作用提供了不同的命名选项以增强代码的可读性和灵活性。* 默认情况下如果未显式指定名称则会使用方法参数的名称。* * return 参数的名称*/AliasFor(name)String value() default ;/*** 参数的名称用于映射到HTTP请求参数。* p* 此属性与name属性具有相同的作用提供了不同的命名选项以增强代码的可读性和灵活性。* 默认情况下如果未显式指定名称则会使用方法参数的名称。* * return 参数的名称*/AliasFor(value)String name() default ;/*** 指示参数是否必需。* p* 如果设置为true则在请求中必须存在对应的参数否则将导致错误。* 如果设置为false则可以省略参数此时方法可以使用默认值。* * return 是否必需的布尔值默认为true*/boolean required() default true;/*** 指定参数的默认值。* p* 当请求中未提供参数时将使用此默认值。此属性仅在required属性设置为false时有效。* 默认值的特殊字符串表示是为了在文档生成和处理中提供更好的可读性和区分度。* * return 参数的默认值默认为一个特殊的空白字符串用于指示没有提供默认值*/String defaultValue() default \n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n;
}
假设一个登陆系统已经再Session中设置了userName那么应该如何获取Session中内容呢Spring MVC提供了注解SessionAttribute获取Session中的数据 /*** 通过注解SessionAttribute获得会话属性** param userName 用户名* return ModelAndView*/RequestMapping(value /index5, method RequestMethod.GET)public ModelAndView index5(SessionAttribute(userName) String userName) {System.out.println(session[userName] userName);ModelAndView mv new ModelAndView();mv.setViewName(index);return mv;}获取参数之后便是处理业务逻辑和绑定视图一般而言实现的逻辑和数据库有关既可以在applicationContext.xml中配置关于数据库的部分也可以使用Java配置的方式例如在BackendConfig中加入Bean如下代码所示
package com.ssmvc.backend.config;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import org.springframework.web.bind.annotation.RestController;/*** 后端配置类用于Spring应用程序的配置包括数据源、MyBatis、事务管理器等的初始化。*/
Configuration
// 扫描所有com.ssmvc包下的组件但排除Controller和RestController注解的类
ComponentScan(basePackages com.ssmvc.*, excludeFilters Filter(type FilterType.ANNOTATION, classes {Controller.class, RestController.class}))
EnableTransactionManagement // 开启事务管理
MapperScan(basePackages com.ssmvc, annotationClass Mapper.class) // 扫描Mapper接口
public class BackendConfig implements TransactionManagementConfigurer {// 数据源实例private DataSource dataSource null;/*** 初始化数据源。* 使用Apache Commons DBCP2创建数据源实例。** return DataSource 数据源实例*/Bean(name dataSource)public DataSource initDataSource() {if (dataSource ! null) {return dataSource;}Properties props new Properties();// 配置数据库连接信息和连接池参数props.setProperty(driverClassName, com.mysql.jdbc.Driver);props.setProperty(url, jdbc:mysql://localhost:3306/ssm);props.setProperty(username, root);props.setProperty(password, 123456);props.setProperty(maxActive, 200);props.setProperty(maxIdle, 20);props.setProperty(maxWait, 30000);try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}/*** 初始化JdbcTemplate。* 为Spring提供的简化JDBC操作的工具类配置数据源。** param dataSource 数据源* return JdbcTemplate 实例*/Bean(name jdbcTemplate)public JdbcTemplate initjdbcTemplate(Autowired DataSource dataSource) {JdbcTemplate jdbcTemplate new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}/*** 配置基于注解的事务管理器。* 使用DataSourceTransactionManager作为事务管理器并配置数据源。** return PlatformTransactionManager 事务管理器实例*/OverrideBean(name transactionManager)public PlatformTransactionManager annotationDrivenTransactionManager() {DataSourceTransactionManager transactionManager new DataSourceTransactionManager();transactionManager.setDataSource(initDataSource());return transactionManager;}/*** 创建SqlSessionFactory。* 配置MyBatis的SqlSessionFactoryBean并指定数据源和配置文件位置。** param dataSource 数据源* return SqlSessionFactory 实例* throws Exception*/Bean(sqlSessionFactory)public SqlSessionFactory createSqlSessionFactoryBean(Autowired DataSource dataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);// 指定MyBatis配置文件位置String cfgFile mybatis-config.xml;Resource configLocation new ClassPathResource(cfgFile);sqlSessionFactoryBean.setConfigLocation(configLocation);return sqlSessionFactoryBean.getObject();}}
package com.ssmvc.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;import com.ssmvc.pojo.Role;
import com.ssmvc.service.RoleService;/*** 角色控制器负责处理与角色相关的请求。*/
Controller
RequestMapping(/role)
public class RoleController {// 自动注入角色服务用于处理角色相关的业务逻辑// 注入角色服务类Autowiredprivate RoleService roleService null;/*** 获取角色详情返回角色信息的ModelAndView。** param id 角色ID用于查询特定的角色。* return ModelAndView包含角色详情的视图和模型。*/RequestMapping(value /details, method RequestMethod.GET)public ModelAndView getRole(RequestParam(id) Long id) {Role role roleService.getRole(id);ModelAndView mv new ModelAndView();mv.setViewName(role_details);// 给数据模型添加一个角色对象mv.addObject(role, role);return mv;}/*** 获取角色详情返回以JSON格式表示的角色信息。** param id 角色ID用于查询特定的角色。* return ModelAndView包含以JSON格式表示的角色详情。*///获取角色RequestMapping(value/details/json, methodRequestMethod.GET)public ModelAndView getRoleJson(RequestParam(id) Long id) {Role role roleService.getRole(id);ModelAndView mv new ModelAndView();mv.addObject(role, role);//指定视图类型mv.setView(new MappingJackson2JsonView());return mv;}
}
例如有这样的一个角色控制器在代码中注入RoleService就可以通过这个服务类使用传递的参数id获取角色最后把查询出来的角色添加给模型和视图以便将来使用至此只是完成了业务逻辑并没有实现视图渲染还需要把从数据库查询出来的数据通过某种方式渲染到视图中展示给用户
视图渲染
一般而言可以把视图定义为一个JSP文件实际上SpringMVC存在大量的的视图模式例如JSP、JSTL、JSON、Thymeleaf等等通过这些可以方便的渲染数据响应用户请求例如上边的例子根据配置它需要一个/WEB-INF/jsp/role_details.jsp文件来渲染数据代码如下
% page pageEncodingutf-8%
% page importcom.ssmvc.pojo.Role%
htmlheadtitle角色详情/title/head%// 获得数据模型Role role (Role)request.getAttribute(role);%bodycentertable border1trtd标签/tdtd值/td/trtrtd角色编号/tdtd%role.getId() %/td/trtrtd角色名称/tdtd%role.getRoleName()%/td/trtrtd角色备注/tdtd%role.getNote()%/td/tr/table/center/body
/html启动Tomcat然后用浏览器请求地址http://localhost:8080/SSMVC_war/mvc/role/details?id2将会得到如下页面 在很多使用Ajax技术的环境中后台往往返回JSON数据给前端SpringMVC在模型和视图上也支持如下代码所示 /*** 获取角色详情返回以JSON格式表示的角色信息。** param id 角色ID用于查询特定的角色。* return ModelAndView包含以JSON格式表示的角色详情。*///获取角色RequestMapping(value/details/json, methodRequestMethod.GET)public ModelAndView getRoleJson(RequestParam(id) Long id) {Role role roleService.getRole(id);ModelAndView mv new ModelAndView();mv.addObject(role, role);//指定视图类型mv.setView(new MappingJackson2JsonView());return mv;}代码中视图类型为MappingJackson2JsonView, 然后请求地址http://localhost:8080/SSMVC_war/mvc/role/details/json?id2,便会得到如下页面 这样就返回JSON数据提供给Ajax异步请求使用MappingJackson2JsonView是一个非逻辑视图因此不需要视图解析器定位MappingJackson2JsonView会直接把ModelAndView中的数据模型通过JSON视图转换出来就得到了JSON数据 这不是将控制器返回的结果变为JSON的唯一方式使用注解ResponseBody或者RestController是更简单和广泛的方法