个人感觉《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》这本书对spring的解读要优于《Spring源码深度解析(第2版)》这本书的,后者感觉就是再陈述一些代码,没有自己的理解,有点呆板!下面是《Spring技术内幕》中的springMVC的描述:
在部署描述中,为这个DispatcherServlet定义了对应的URL映射,这些URL映射为这个servlet指定了需要处理的HTTP请求。context-param 参数的配置用来指定spring IoC容器读取Bean定义的XML文件的路径,作为springMVC启动类,ContextLoaderListener被定义一个监听器,这个监听器是与web服务器的生命周期相关联的,由ContextLoaderListener监听器负责完成IoC容器
在web环境中的启动工作,DispatcherServlet和ContextLoaderListener提供了在web容器中对spring的接口,也就是说,这些接口与web容器耦合是通过ServletContext来实现的,这个ServletContext为spring的IoC容器体系提供了一个宿主环境,在宿主环境中,springMVC建立起一个IoC容器的体系。这个IoC容器体系是通过ContextLoaderListener初始化来建立起来的,在建立IoC容器的体系后,把DispatcherServlet作为spring MVC处理web请求的转发器建立起来,从而完成响应HTTP请求的准备,有了这些基本的配置,建立在IOC容器基础上的spring MVC就可以正常的发挥作用了。
《Spring源码深度解析(第2版)》中的内容:
spring框架提供了构建完整的Web应用程序的全功能MVC模块,通过策略接口,spring框架是高度可配置的,而且支持多种视图技术,springMVC框架并不知道使用的视图,
所以不会强迫您只使用JSP技术,springMVC分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让他们更容易进行定制
spring的NVC是基于Servlet功能实现的,通过实现servlet接口DispatcherServlet来封装其核心功能实现,通过将请求分配给程序处理,同时带有可配置的处理程序映射,
视图解析、本地语言、主体解析以及下载文件支持。默认的处理程序是非常简单的Controller接口,只有一个方法ModelAndView handleRequest(request, response);
spring提供了一个控制器层次结构,可以派生子类。
springMVC解决的问题主要就是以下几点:
将web页面的请求传给服务器
根据不同的请求处理不同的逻辑单元
返回处理结果数据并跳转至结果页面
一、ContextLoaderListener
对于springMVC功能实现的分析,我们首先从web.xml开始,在web.xml 文件中我们首先配置的就是ContextLoaderListener,那么它所提供的功能有哪些,又是
如何实现的呢?
当使用编程方式的时候我们可以直接将spring配置信息作为参数注入spring容器中,如ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
但是在web下,我们需要更多的是与web环境相互结合,通常的办法是将路径以context-param的方式注册并使用ContextLoaderListener进行监听读取
ContextLoaderListener的作用就是启动web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,
启动容器时,就会默认执行它实现的方法,使用ServletContextListener接口,开发者能够在为客户端请求提供服务之前向ServletContext中添加任意的对象,这个对象在
ServletContext启动的时候被初始化,然后在ServletContext整个运行期间都是可用的
每一个web应用都有一个ServletContext与之相关联。ServletContext对象在应用启动时被创建,在应用关闭的时候被销毁。ServletContext在全局范围内有效,
类似于应用中的一个全局变量
在ServletContextListener中核心逻辑便是初始化WebApplicationContext实例并存放至ServletContext中
1、ServletContextListener的使用
(1)创建自定义的ServletContextListener
首先我们创建ServletContextListener,目标是在系统启动时添加自定义属性,以便于在全局范围内可以随时调用。系统启动的时候会调用ServletContextListener实现类的
contextInitialized方法,所以需要实现在这个方法中实现我们的初始化逻辑。
1 public class MyDataContextListener implements ServletContextListener{ 2 private ServletContext context = null; 3 public MyDataContextListener(){ 4 5 } 6 // 该方法在ServletContext启动之后调用,并准备好处理客户端请求 7 public void contextInitialized (SevrvletContextEvent event){ 8 this.context = event.getServletContext(); 9 // 实现自己的逻辑并将结果记录在属性中 10 context = setAttribute("myData","this is myData"); 11 } 12 // 这个方法在ServletContext关闭时调用 13 public void contextDestroyed(SevrvletContextEvent event){ 14 this.context = null; 15 } 16 }
(2)注册监听器
1 <listener> 2 <listener-class>com.test.MyDataContextListener</listener-class> 3 </listener>
(3)测试
一旦web应用启动的时候,我们就鞥在任意的servlet或者jsp中通过下面方式获取我们初始化的参数:
String myData = (String) getServletContext().getAttribute("myData");
2、spring中的contextLoaderListener
ServletContext启动之后会调用ServletContextListener的contextInitialized方法,可以从这个方法开始:
org.springframework.web.context.ContextLoaderListener类中的contextInitialized
1 @Override 2 public void contextInitialized(ServletContextEvent event) { 3 // 初始化WebApplicationContext 4 initWebApplicationContext(event.getServletContext()); 5 }
这里有一个WebApplicationContext,WebApplicationContext继承自ApplicationContext,在ApplicationContext的基础上又追加了一些特定于web的操作与特性,看一下这个类的初始化源码:
1 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 2 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { 3 // web.xml 中存在多次ContextLoader定义 4 throw new IllegalStateException( 5 "Cannot initialize context because there is already a root application context present - " + 6 "check whether you have multiple ContextLoader* definitions in your web.xml!"); 7 } 8 9 Log logger = LogFactory.getLog(ContextLoader.class); 10 servletContext.log("Initializing Spring root WebApplicationContext"); 11 if (logger.isInfoEnabled()) { 12 logger.info("Root WebApplicationContext: initialization started"); 13 } 14 long startTime = System.currentTimeMillis(); 15 16 try { 17 // Store context in local instance variable, to guarantee that 18 // it is available on ServletContext shutdown. 19 if (this.context == null) { 20 // 初始化WebApplicationContext 21 this.context = createWebApplicationContext(servletContext); 22 } 23 if (this.context instanceof ConfigurableWebApplicationContext) { 24 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; 25 if (!cwac.isActive()) { 26 // The context has not yet been refreshed -> provide services such as 27 // setting the parent context, setting the application context id, etc 28 if (cwac.getParent() == null) { 29 // The context instance was injected without an explicit parent -> 30 // determine parent for root web application context, if any. 31 ApplicationContext parent = loadParentContext(servletContext); 32 cwac.setParent(parent); 33 } 34 configureAndRefreshWebApplicationContext(cwac, servletContext); 35 } 36 } 37 // 记录在servletContext中 38 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 39 40 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 41 if (ccl == ContextLoader.class.getClassLoader()) { 42 currentContext = this.context; 43 } 44 else if (ccl != null) { 45 currentContextPerThread.put(ccl, this.context); 46 } 47 48 if (logger.isDebugEnabled()) { 49 logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + 50 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); 51 } 52 if (logger.isInfoEnabled()) { 53 long elapsedTime = System.currentTimeMillis() - startTime; 54 logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); 55 } 56 57 return this.context; 58 } 59 catch (RuntimeException ex) { 60 logger.error("Context initialization failed", ex); 61 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); 62 throw ex; 63 } 64 catch (Error err) { 65 logger.error("Context initialization failed", err); 66 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); 67 throw err; 68 } 69 }
步骤如下:
(1)WebApplicationContext存在性的验证
(2)创建WebApplicationContext
主要就是创建WebApplicationContext类,看一下创建过程的源码,在createWebApplicationContext方法中:
org.springframework.web.context.ContextLoader类中的createWebApplicationContext
1 protected WebApplicationContext createWebApplicationContext(ServletContext sc) { 2 Class<?> contextClass = determineContextClass(sc); 3 // 这里判断使用什么样的类在web容器中作为IoC容器 4 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 5 throw new ApplicationContextException("Custom context class [" + contextClass.getName() + 6 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); 7 } 8 // 直接实例化需要产生的IoC容器,并设置IoC容器的各个参数,然后通过refresh启动容器的初始化 9 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 10 } 11 12 protected Class<?> determineContextClass(ServletContext servletContext) { 13 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 14 if (contextClassName != null) { 15 try { 16 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); 17 } 18 catch (ClassNotFoundException ex) { 19 throw new ApplicationContextException( 20 "Failed to load custom context class [" + contextClassName + "]", ex); 21 } 22 } 23 else { 24 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 25 try { 26 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); 27 } 28 catch (ClassNotFoundException ex) { 29 throw new ApplicationContextException( 30 "Failed to load default context class [" + contextClassName + "]", ex); 31 } 32 } 33 } 34 // 其中ContextLoader类中有这样的静态代码块: 35 static { 36 // Load default strategy implementations from properties file. 37 // This is currently strictly internal and not meant to be customized 38 // by application developers. 39 try { 40 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); 41 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 42 } 43 catch (IOException ex) { 44 throw new IllegalStateException("Could not load ‘ContextLoader.properties‘: " + ex.getMessage()); 45 } 46 }
根据以上代码,肯定会存在当前目录下的属性文件ContextLoader.properties,你当然可以在源码中看到这个配置:
spring-web下的resources文件夹下的org.springframework.web.context.ContextLoader.properties
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
综合代码来看,在初始化过程中,程序首先会读取ContextLoader类的同目录下的属性文件ContextLoader.properties,并根据其中的配置提取将要实现的WebApplicationContext类的
实现类,并且根据反射方式进行实例的创建
(3)将实例记录在servletContext中
(4)映射当前的类加载器与创建的实例到全局变量currentContextPerThread中
二、DispatcherServlet
1、DispatcherServlet的初始化
在servlet初始化阶段,我们知道会首先调用其init方法,在DispatcherServlet中查找init方法的实现,查找方法直接查看该类的调用关系,在其父类中:
org.springframework.web.servlet.HttpServletBean中的init方法:
1 @Override 2 public final void init() throws ServletException { 3 if (logger.isDebugEnabled()) { 4 logger.debug("Initializing servlet ‘" + getServletName() + "‘"); 5 } 6 7 // Set bean properties from init parameters. 8 // 解析init-param 并封装到pvs中 9 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 10 if (!pvs.isEmpty()) { 11 try { 12 // 将当前的这个servlet类转换成BeanWrapper,从而能够以spring的方式对init-param的值进行注入 13 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); 14 ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); 15 // 注册自定义的属性编辑器,一旦使用Resource类型的属性将会使用ResourceEditor进行解析 16 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); 17 // 空实现,留给子类覆盖 18 initBeanWrapper(bw); 19 // 属性注入 20 bw.setPropertyValues(pvs, true); 21 } 22 catch (BeansException ex) { 23 if (logger.isErrorEnabled()) { 24 logger.error("Failed to set bean properties on servlet ‘" + getServletName() + "‘", ex); 25 } 26 throw ex; 27 } 28 } 29 30 // Let subclasses do whatever initialization they like. 31 // 留给子类扩展 32 initServletBean(); 33 34 if (logger.isDebugEnabled()) { 35 logger.debug("Servlet ‘" + getServletName() + "‘ configured successfully"); 36 } 37 }
DispatcherServlet的初始化过程是通过将当前servlet类型实例转换为BeanWrapper类型实例,以便使用spring中提供的注入功能进行对应属性的注入,这些属性如contextAttribute、
contextClass、nameSpace等,都可以在web.xml文件中以初始化参数的方式。DispatcherServlet继承自FrameworkServlet,FrameworkServlet类上包含同名属性,spring会保证这些
这些参数被注入到对应的值中。属性注入主要包含以下几个步骤:
(1)封装以及验证初始化参数
ServletConfigPropertyValues除了封装属性还有对属性的验证功能
org.springframework.web.servlet.HttpServletBean.ServletConfigPropertyValues类中
1 public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) 2 throws ServletException { 3 4 Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ? 5 new HashSet<>(requiredProperties) : null); 6 7 Enumeration<String> paramNames = config.getInitParameterNames(); 8 while (paramNames.hasMoreElements()) { 9 String property = paramNames.nextElement(); 10 Object value = config.getInitParameter(property); 11 addPropertyValue(new PropertyValue(property, value)); 12 if (missingProps != null) { 13 missingProps.remove(property); 14 } 15 } 16 17 // Fail if we are still missing properties. 18 if (!CollectionUtils.isEmpty(missingProps)) { 19 throw new ServletException( 20 "Initialization from ServletConfig for servlet ‘" + config.getServletName() + 21 "‘ failed; the following required properties were missing: " + 22 StringUtils.collectionToDelimitedString(missingProps, ", ")); 23 } 24 } 25 26 // 从代码中得知,封装属性主要是对初始化的参数进行封装,也就是servlet中配置的<init-param>中配置的封装。
(2)将当前的servlet实例转化成BeanWrapper实例
PropertyAccessorFactory.forBeanPropertyAccess(this);是spring中提供的方法,主要作用就是将制定实例转化为spring中可以处理的BeanWrapper实例
(3)注册相对于Resource的属性编辑器
在当前实例的属性注入过程中一旦遇到Resource类型的属性就会使用ResourceEditor去解析
(4)属性注入
BeanWrapper为spring中的方法,支持spring的自动注入。其实我们最常用的属性注入无非是contextAttribute、contextClass、nameSpace等
(5)servletBean实例化
在ContextLoaderListener加载的时候已经创建了WebApplicationContext实例,而在这个函数中最重要的就是对这个类的进一步补充实例化
看一下initServletBean源码:
org.springframework.web.servlet.FrameworkServlet中
1 @Override 2 protected final void initServletBean() throws ServletException { 3 getServletContext().log("Initializing Spring FrameworkServlet ‘" + getServletName() + "‘"); 4 if (logger.isInfoEnabled()) { 5 logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization started"); 6 } 7 long startTime = System.currentTimeMillis(); 8 9 try { 10 this.webApplicationContext = initWebApplicationContext(); 11 // 设计为子类覆盖 12 initFrameworkServlet(); 13 } 14 catch (ServletException | RuntimeException ex) { 15 logger.error("Context initialization failed", ex); 16 throw ex; 17 } 18 19 if (logger.isInfoEnabled()) { 20 long elapsedTime = System.currentTimeMillis() - startTime; 21 logger.info("FrameworkServlet ‘" + getServletName() + "‘: initialization completed in " + 22 elapsedTime + " ms"); 23 } 24 }
最为关键的初始化逻辑其实由initWebApplicationContext方法实现:
2、WebApplicationContext的初始化
initWebApplicationContext方法的主要工作就是创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
org.springframework.web.servlet.FrameworkServlet类中
1 protected WebApplicationContext initWebApplicationContext() { 2 WebApplicationContext rootContext = 3 WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 4 WebApplicationContext wac = null; 5 6 if (this.webApplicationContext != null) { 7 // A context instance was injected at construction time -> use it 8 // context实例在构造函数中被注入 9 wac = this.webApplicationContext; 10 if (wac instanceof ConfigurableWebApplicationContext) { 11 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 12 if (!cwac.isActive()) { 13 // The context has not yet been refreshed -> provide services such as 14 // setting the parent context, setting the application context id, etc 15 if (cwac.getParent() == null) { 16 // The context instance was injected without an explicit parent -> set 17 // the root application context (if any; may be null) as the parent 18 cwac.setParent(rootContext); 19 } 20 // 刷新上下文环境 21 configureAndRefreshWebApplicationContext(cwac); 22 } 23 } 24 } 25 if (wac == null) { 26 // No context instance was injected at construction time -> see if one 27 // has been registered in the servlet context. If one exists, it is assumed 28 // that the parent context (if any) has already been set and that the 29 // user has performed any initialization such as setting the context id 30 // 根据contextAttribute属性加载WebApplicationContext 31 wac = findWebApplicationContext(); 32 } 33 if (wac == null) { 34 // No context instance is defined for this servlet -> create a local one 35 wac = createWebApplicationContext(rootContext); 36 } 37 38 if (!this.refreshEventReceived) { 39 // Either the context is not a ConfigurableApplicationContext with refresh 40 // support or the context injected at construction time had already been 41 // refreshed -> trigger initial onRefresh manually here. 42 synchronized (this.onRefreshMonitor) { 43 onRefresh(wac); 44 } 45 } 46 47 if (this.publishContext) { 48 // Publish the context as a servlet context attribute. 49 String attrName = getServletContextAttributeName(); 50 getServletContext().setAttribute(attrName, wac); 51 if (this.logger.isDebugEnabled()) { 52 this.logger.debug("Published WebApplicationContext of servlet ‘" + getServletName() + 53 "‘ as ServletContext attribute with name [" + attrName + "]"); 54 } 55 } 56 57 return wac; 58 }
对于本函数的初始化主要分为这几个部分:
(1)寻找和创建对应的WebApplicationContext实例
寻找和创建对应的WebApplicationContext实例主要分为以下几个步骤:
1.1 通过构造函数的注入进行初始化
1.2 通过contextAttribute进行初始化
org.springframework.web.servlet.FrameworkServlet中的findWebApplicationContext方法
1 @Nullable 2 protected WebApplicationContext findWebApplicationContext() { 3 String attrName = getContextAttribute(); 4 if (attrName == null) { 5 return null; 6 } 7 WebApplicationContext wac = 8 WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); 9 if (wac == null) { 10 throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); 11 } 12 return wac; 13 }
1.3 重新创建WebApplicationContext实例
org.springframework.web.servlet.FrameworkServlet类中createWebApplicationContext
1 protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { 2 // 获取servlet的初始化参数contextClass,如果没有默认匹配XmlWebApplicationContext.class 3 Class<?> contextClass = getContextClass(); 4 if (this.logger.isDebugEnabled()) { 5 this.logger.debug("Servlet with name ‘" + getServletName() + 6 "‘ will try to create custom WebApplicationContext context of class ‘" + 7 contextClass.getName() + "‘" + ", using parent context [" + parent + "]"); 8 } 9 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 10 throw new ApplicationContextException( 11 "Fatal initialization error in servlet with name ‘" + getServletName() + 12 "‘: custom WebApplicationContext class [" + contextClass.getName() + 13 "] is not of type ConfigurableWebApplicationContext"); 14 } 15 // 通过反射方式实例化contextClass 16 ConfigurableWebApplicationContext wac = 17 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 18 19 wac.setEnvironment(getEnvironment()); 20 // parent为在ContextLoaderListener中创建的实例,在ContextLoaderListener加载的时候初始化的WebApplicationContext类型实例 21 wac.setParent(parent); 22 // 获取ContextConfigLocation属性,配置在servlet初始化参数中 23 String configLocation = getContextConfigLocation(); 24 if (configLocation != null) { 25 wac.setConfigLocation(configLocation); 26 } 27 // 初始化spring环境包括加载配置文件等 28 configureAndRefreshWebApplicationContext(wac); 29 30 return wac; 31 }
(2)configureAndRefreshWebApplicationContext刷新上下文环境
org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext方法中
1 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { 2 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { 3 // The application context id is still set to its original default value 4 // -> assign a more useful id based on available information 5 if (this.contextId != null) { 6 wac.setId(this.contextId); 7 } 8 else { 9 // Generate default id... 10 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 11 ObjectUtils.getDisplayString(getServletContext().getContextPath()) + ‘/‘ + getServletName()); 12 } 13 } 14 15 wac.setServletContext(getServletContext()); 16 wac.setServletConfig(getServletConfig()); 17 wac.setNamespace(getNamespace()); 18 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); 19 20 // The wac environment‘s #initPropertySources will be called in any case when the context 21 // is refreshed; do it eagerly here to ensure servlet property sources are in place for 22 // use in any post-processing or initialization that occurs below prior to #refresh 23 ConfigurableEnvironment env = wac.getEnvironment(); 24 if (env instanceof ConfigurableWebEnvironment) { 25 ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); 26 } 27 28 postProcessWebApplicationContext(wac); 29 applyInitializers(wac); 30 // 加载配置文件及整合parent到wac 31 wac.refresh(); 32 } 33 // 无论调用方式如何变化,只要使用ApplicationContext所提供的的功能最后都免不了使用公共父类AbstractApplicationContext提供的refresh()方法
(3)刷新
onRefresh是在FrameworkServlet类中提供的模板方法,在其子类DispatcherServlet中进行重写,主要用于刷新在web功能实现中所必须使用的全局变量。
1 @Override 2 protected void onRefresh(ApplicationContext context) { 3 initStrategies(context); 4 } 5 6 protected void initStrategies(ApplicationContext context) { 7 // 初始化MultipartResolver 8 initMultipartResolver(context); 9 // 初始化LocaleResolver 10 initLocaleResolver(context); 11 // 初始化ThemeResolver 12 initThemeResolver(context); 13 // 初始化HandlerMappings 14 initHandlerMappings(context); 15 // 初始化HandlerAdapters 16 initHandlerAdapters(context); 17 // 初始化HandlerExceptionResolvers 18 initHandlerExceptionResolvers(context); 19 // 初始化RequestToViewNameTranslator 20 initRequestToViewNameTranslator(context); 21 // 初始化ViewResolvers 22 initViewResolvers(context); 23 // 初始化FlashMapManager 24 initFlashMapManager(context); 25 }
3.1 初始化MultipartResolver
在spring中,MultipartResolver主要用来处理文件上传。常用的配置如下:
1 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 2 <property name="maxmunFileSize"> 3 <value>1000</value> 4 </property> 5 </bean> 6 7 org.springframework.web.servlet.DispatcherServlet类中: 8 private void initMultipartResolver(ApplicationContext context) { 9 try { 10 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); 11 if (logger.isDebugEnabled()) { 12 logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); 13 } 14 } 15 catch (NoSuchBeanDefinitionException ex) { 16 // Default is no multipart resolver. 17 this.multipartResolver = null; 18 if (logger.isDebugEnabled()) { 19 logger.debug("Unable to locate MultipartResolver with name ‘" + MULTIPART_RESOLVER_BEAN_NAME + 20 "‘: no multipart request handling provided"); 21 } 22 } 23 }
就写一个吧,其他的基本类似!
原文地址:https://www.cnblogs.com/ssh-html/p/11329828.html