教你搭建SpringMVC框架( 附源码)
一、项目目录结构
二、SpringMVC需要使用的jar包
- commons-logging-1.2.jar
- junit-4.10.jar
- log4j-api-2.0.2.jar
- log4j-core-2.0.2.jar
- log4j-web-2.0.2.jar
- spring-beans-3.2.4.RELEASE.jar
- spring-context-3.2.4.RELEASE.jar
- spring-core-4.0.5.RELEASE.jar
- spring-expression-3.2.4.RELEASE.jar
- spring-web-4.0.5.RELEASE.jar
- spring-webmvc-3.2.9.RELEASE.jar
三、在Web.xml配置SpringMVC控制器
在不设置contextConfigLocation参数的情况下,SpringMVC框架默认在WEB-INF路径下查找文件名为XXX-servlet.xml(XXX是web.xml中配置的SpringMVC框架控制器的名称)。可以通过设置contextConfigLocation参数,自定义SpringMVC-servlet.xml的路径。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>SpringMVC</display-name> <!-- 添加Spring控制器及映射规则 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 不设置contextConfigLocation,SpringMVC 会在WEB-INF文件夹下查找<servlet-name/>-servlet.xml,设置 contextConfigLocation,可自定义SpringMVC-servlet.xml的位置 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/SpringMVC-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 首页文件列表 --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
四、配置SpringMVC-servlet.xml
在路径/SpringMVCDemo/src/comm/config/配置SpringMVC-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd "> <!-- 自动扫描的包名 --> <context:component-scan base-package="com.candy.dao"></context:component-scan> <context:component-scan base-package="com.candy.service"></context:component-scan> <context:component-scan base-package="com.candy.web"></context:component-scan> <!-- 添加注解驱动 --> <mvc:annotation-driven/> <!-- 模型视图名称解析 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value></value> </property> </bean> <!-- 对静态资源文件的访问 方案 --> <mvc:default-servlet-handler /> </beans>
五、编写Controller
BaseController:
package com.candy.web; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.propertyeditors.CustomCollectionEditor; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; @Controller public class BaseController { protected final Logger log = LogManager.getLogger(); /** * 处理页面参数序列化后数据类型的问题 * * @author Candy * @date 2014-11-3 * @version V1.0 * @param binder */ @InitBinder public void initBinder(WebDataBinder binder) { log.debug("initBinder"); SimpleDateFormat dataSdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat dataTimesdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); binder.registerCustomEditor(Date.class, new CustomDateEditor(dataSdf, true)); binder.registerCustomEditor(Timestamp.class, new CustomDateEditor( dataTimesdf, true)); binder.registerCustomEditor(List.class, new CustomCollectionEditor( List.class, true)); } /** * 获取客户端真实IP地址 * * @author Candy * @date 2014-11-3 * @version V1.0 * @param request * @return */ public String getIpAddr(HttpServletRequest request) { log.debug("getIpAddr"); if (request == null) { return null; } String ip = request.getRemoteAddr(); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("x-forwarded-for"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } return ip; } }
TestController:
/** * @projectName SpringMVC * @package com.candy.web.IndexController.java * @Copyright Copyright(c) 2014 Candy工会 * @author Candy * @date 2014-11-3 * @version V1.0 */ package com.candy.web.test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.candy.service.TestService; import com.candy.web.BaseController; /** * 测试Controller * * @author Candy * @date 2014-11-3 * @version V1.0 */ @Controller public class TestController extends BaseController { @Autowired private TestService testService; /** * 测试跳转 * * @author Candy * @date 2014-11-3 * @return */ @RequestMapping("/transfer") public ModelAndView testTransfer() { log.debug("控制器查找到请求的路径!"); String viewName =testService.testTransfer(); return new ModelAndView(viewName); } }
六、编写测试页面
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html lang="zh"> <head> <base href="<%=basePath%>"> <title>SpringMVCDemo-首页</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/styles.css"> </head> <body> <div class="container"> <div class="header"></div> <div class="content"> <a href="${pageContext.request.contextPath}/transfer">发起请求</a>,后台响应,跳转到指定页面! </div> <div class="footer"></div> </div> </body> </html>
在路径/SpringMVCDemo/WebContent/WEB-INF/jsp/下编写success.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html lang="zh"> <head> <base href="<%=basePath%>"> <title>SpringMVCDemo-成功页面</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/styles.css"> </head> <body> <div class="container"> <div class="header"></div> <div class="content"> <h3>跳转成功!</h3> <p>当你看到这些文字时,说明SpringMVC配置成功了!</p> </div> <div class="footer"></div> </div> </body> </html>
七、访问测试
在浏览器中访问http://localhost:8080/SpringMVCDemo/,根据Web.xml中的配置默认显示index.jsp
<!-- 首页文件列表 --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
点击index.jsp中的“发起请求”链接,根据index.jsp代码,此时,浏览器会向服务器发出请求访问路径为“/SpringMVCDemo/transfer"的资源。
<a href="${pageContext.request.contextPath}/transfer">发起请求</a>,后台响应,跳转到指定页面!
SpringMVC框架通过Web.xml中定义的控制器及映射规则,读取SpringMVC-servlet中的配置。
<!-- 添加Spring3控制器及映射规则 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 不设置contextConfigLocation,SpringMVC 会在WEB-INF文件夹下查找<servlet-name/>-servlet.xml,设置 contextConfigLocation,可自定义SpringMVC-servlet.xml的位置 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/SpringMVC-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
SpringMVC框架扫描各个包,查找注解为“@Controller”的类,并扫描注解为"@RequestMapping("/transfer")"对应的方法。
<!-- 自动扫描的包名 --> <context:component-scan base-package="com.candy.dao"></context:component-scan> <context:component-scan base-package="com.candy.service"></context:component-scan> <context:component-scan base-package="com.candy.web"></context:component-scan> <!-- 添加注解驱动 --> <mvc:annotation-driven/>
此时SpringMVC框架已经定位到了com.candy.web.test.TestController.testTransfer()方法。
在TestController的testTransfer()方法中,SpringMVC框架根据注解和自动扫描配置,查找类名为“TestService”,类注解为“@Component”的类,完成自动注入(Autowired)。
此时,TestController的testTransfer()方法返回一个ModelAndView的对象。SpringMVC框架根据SpringMVC-servlet.xml中的配置,在路径/WEB-INF-jsp中查找success.jsp文件,并返回给客户端。
<!-- 模型视图名称解析 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/jsp/</value> </property> <property name="suffix"> <value></value> </property> </bean>
此时,你就可以看到成功访问的信息了。
注:以上博文中,简单的贴了下配置,并在第七步“访问测试”中,按照自己的理解,通过简单易懂的文字,描述了框架的执行顺序。
其实在项目启动时,SpringMVC框架就完成了以下工作:
- 读取web.xml,获取到contextConfigLocation中配置的SpringMVC-servlet.xml路径。
- 读取SpringMVC-servlet.xml,扫描配置的各个包。
- 根据注解,加载所有的Controller、Service等组件,并完成自动注入。
- 根据注解,计算可能多的访问路径。
源码下载地址:http://pan.baidu.com/s/1c0gZUXa
以下是搭建环境时,遇到的各种问题笔记。
- 搭建JUnit环境时,测试Controller时,提示“Caused by: javax.validation.ValidationException: Unable to instantiate Configuration.”
2014-11-3 18:06:46 org.springframework.context.support.FileSystemXmlApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@f81843: startup date [Mon Nov 03 18:06:46 CST 2014]; root of context hierarchy 2014-11-3 18:06:46 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from file [D:\Workspaces\SpringMVCDemo\WebContent\WEB-INF\SpringMVC-servlet.xml] 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-330 'javax.inject.Named' annotation found and supported for component scanning 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-330 'javax.inject.Named' annotation found and supported for component scanning 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning 2014-11-3 18:06:46 org.springframework.context.annotation.ClassPathBeanDefinitionScanner registerDefaultFilters 信息: JSR-330 'javax.inject.Named' annotation found and supported for component scanning 2014-11-3 18:06:46 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init> 信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 2014-11-3 18:06:46 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@dda25b: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,baseController,testController,mvcContentNegotiationManager,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.validation.beanvalidation.LocalValidatorFactoryBean#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,viewResolver,org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0,org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console... 2014-11-3 18:06:46 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod 信息: Mapped "{[/transfer],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView com.candy.web.test.TestController.referLogin() 2014-11-3 18:06:46 org.hibernate.validator.util.Version <clinit> 信息: Hibernate Validator bean-validator-3.0-JBoss-4.0.2 2014-11-3 18:06:46 org.springframework.beans.factory.support.DefaultListableBeanFactory destroySingletons 信息: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@dda25b: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,baseController,testController,mvcContentNegotiationManager,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.validation.beanvalidation.LocalValidatorFactoryBean#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,viewResolver,org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0,org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy java.lang.ExceptionInInitializerError Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.validation.beanvalidation.LocalValidatorFactoryBean#0': Invocation of init method failed; nested exception is javax.validation.ValidationException: Unable to instantiate Configuration. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1482) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:140) at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:84) at com.candy.web.BaseController.<clinit>(BaseController.java:25) Caused by: javax.validation.ValidationException: Unable to instantiate Configuration. at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:272) at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:188) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1541) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1479) ... 12 more Caused by: java.lang.NullPointerException at java.util.ResourceBundle.getBundle(ResourceBundle.java:950) at org.hibernate.validator.engine.ResourceBundleMessageInterpolator.loadBundle(ResourceBundleMessageInterpolator.java:202) at org.hibernate.validator.engine.ResourceBundleMessageInterpolator.getFileBasedResourceBundle(ResourceBundleMessageInterpolator.java:182) at org.hibernate.validator.engine.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:81) at org.hibernate.validator.engine.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:73) at org.hibernate.validator.engine.ConfigurationImpl.<init>(ConfigurationImpl.java:57) at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:43) at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:269) ... 15 more Exception in thread "main"
解决方案:删除JavaEE6中自带的jar包:bean-validator.jar
- 如何删除JavaEE6中自带的jar包?
在MyEclipse的菜单:Window-->Properties-->MyEclipse-->Java Enterprise Project-->Library Sets中,选择到Java EE6.0选项卡(默认选中),然后选择bean-validator.jar,点击右侧的“Remove”按钮,然后点“OK”按钮即可。 - Log4j2输出到控制台的log是乱码。
解决方案:在使用的PatternLayout中加入属性:charset="GBK"即可。 - ……