前言
SpringMVC简介
SpringMVC 是一个类似于 Struts2 表现层的框架,属于 SpringFramework 的后续产品。
学习SpringMVC的原因
SpringMVC 与 Struts2 的区别 | |||
---|---|---|---|
对比项目 | SpringMVC | Struts2 | 优势 |
国内市场情况 | 有大量用户,一般新项目启动都会选用 SpringMVC。 | 有部分老用户,老项目组,由于习惯了,一直在使用。 | 国内情况,SpringMVC 的使用率已经超过了 Struts2。 |
框架入口 | 基于 Servlet。 | 基于 Filter。 | 本质上没有太大优势,只是配置方式不同。 |
框架设计思想 | 控制器基于方法级别的拦截,处理器设计为单实例。 | 控制器基于类级别的拦截,处理器设计为多实例。 | 由于设计本身原因,造成了 Struts2 通常只能设计为多实例模式,相比于 SpringMVC 设计为单实例模式,Struts2 会消耗更多内存。 |
参数传递 | 参数通过方法入参传递。 | 参数通过类的成员变量传递。 | Struts2 通过成员变量传递参数,导致了参数的线程不安全,有可能引发并发问题。 |
与 Spring 整合 | 与 Spring 属于同一家公司开发,可与 Spring 无缝整合。 | 需要整合包。 | SpringMVC 可以与 Spring 更轻松的整合。 |
SpringMVC架构图
使用
入门程序
1、创建 maven web 工程,引入如下依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zze.springmvc</groupId> <artifactId>springmvc_test1</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!--使用 tomcat7:run--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
pom.xml
2、创建 SpringMVC 核心配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置控制器扫描--> <context:component-scan base-package="com.zze.springmvc.web.controller"/> <mvc:annotation-driven/> </beans>
springmvc.xml
3、配置 SpringMVC 核心监听 Servlet 并加载核心配置文件:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--加载 SpringMVC 核心配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!--注意:这里使用 / ,如果使用 /* 会出现找不到视图的问题--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
WEB-INF/web.xml
4、创建测试用的 pojo:
package com.zze.springmvc.pojo; import java.util.Date; public class User { private Integer id; private String name; private Date birthday; private String address; public User(Integer id, String name, Date birthday, String address) { this.id = id; this.name = name; this.birthday = birthday; this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
com.zze.springmvc.pojo.User
5、创建视图:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <html> <head> <title>list</title> </head> <body> <table border="1"> <thead> <tr> <th>Id</th> <th>Name</th> <th>Birthday</th> <th>Address</th> </tr> </thead> <tbody> <c:forEach items="${list}" var="user"> <tr> <td>${user.id}</td> <td>${user.name}</td> <td><fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd"/></td> <td>${user.address}</td> </tr> </c:forEach> </tbody> </table> </body> </html>
WEB-INF/jsp/userlist.jsp
6、创建控制器:
package com.zze.springmvc.web.controller; import com.zze.springmvc.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @Controller public class TestController { @RequestMapping("list") public ModelAndView list() throws ParseException { List<User> userList = new ArrayList<>(); userList.add(new User(1, "张三", new SimpleDateFormat("yyyy-MM-dd").parse("1997-1-12"), "湖南")); userList.add(new User(2, "李四", new SimpleDateFormat("yyyy-MM-dd").parse("1995-5-23"), "湖北")); userList.add(new User(3, "王五", new SimpleDateFormat("yyyy-MM-dd").parse("1993-2-23"), "常德")); userList.add(new User(4, "赵六", new SimpleDateFormat("yyyy-MM-dd").parse("1998-5-6"), "北京")); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("list", userList); modelAndView.setViewName("/WEB-INF/jsp/userlist.jsp"); return modelAndView; } }
com.zze.springmvc.web.controller.TestController
7、测试:
浏览器:
请求 'localhost:8080/list'
配置相关
处理器适配器和映射器
Spring MVC 有许多默认配置,如下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver # 处理器映射器 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping # 处理器适配器 org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
spring-webmvc-4.2.4.RELEASE.jar!/org/springframework/web/servlet/DispatcherServlet.properties
查看处理器映射器和处理器适配器类可以发现这两个类是已过期的,如下:
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet.mvc.annotation; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Controller; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping; /** * Implementation of the {@link org.springframework.web.servlet.HandlerMapping} * interface that maps handlers based on HTTP paths expressed through the * {@link RequestMapping} annotation at the type or method level. * * <p>Registered by default in {@link org.springframework.web.servlet.DispatcherServlet} * on Java 5+. <b>NOTE:</b> If you define custom HandlerMapping beans in your * DispatcherServlet context, you need to add a DefaultAnnotationHandlerMapping bean * explicitly, since custom HandlerMapping beans replace the default mapping strategies. * Defining a DefaultAnnotationHandlerMapping also allows for registering custom * interceptors: * * <pre class="code"> * <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> * <property name="interceptors"> * ... * </property> * </bean></pre> * * Annotated controllers are usually marked with the {@link Controller} stereotype * at the type level. This is not strictly necessary when {@link RequestMapping} is * applied at the type level (since such a handler usually implements the * {@link org.springframework.web.servlet.mvc.Controller} interface). However, * {@link Controller} is required for detecting {@link RequestMapping} annotations * at the method level if {@link RequestMapping} is not present at the type level. * * <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping * expressed at the class level (if any). HTTP paths need to uniquely map onto * specific handler beans, with any given HTTP path only allowed to be mapped * onto one specific handler bean (not spread across multiple handler beans). * It is strongly recommended to co-locate related handler methods into the same bean. * * <p>The {@link AnnotationMethodHandlerAdapter} is responsible for processing * annotated handler methods, as mapped by this HandlerMapping. For * {@link RequestMapping} at the type level, specific HandlerAdapters such as * {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter} apply. * * @author Juergen Hoeller * @author Arjen Poutsma * @since 2.5 * @see RequestMapping * @see AnnotationMethodHandlerAdapter * @deprecated as of Spring 3.2, in favor of * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping RequestMappingHandlerMapping} */ @Deprecated public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping { static final String USE_DEFAULT_SUFFIX_PATTERN = DefaultAnnotationHandlerMapping.class.getName() + ".useDefaultSuffixPattern"; private boolean useDefaultSuffixPattern = true; private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>(); /** * Set whether to register paths using the default suffix pattern as well: * i.e. whether "/users" should be registered as "/users.*" and "/users/" too. * <p>Default is "true". Turn this convention off if you intend to interpret * your {@code @RequestMapping} paths strictly. * <p>Note that paths which include a ".xxx" suffix or end with "/" already will not be * transformed using the default suffix pattern in any case. */ public void setUseDefaultSuffixPattern(boolean useDefaultSuffixPattern) { this.useDefaultSuffixPattern = useDefaultSuffixPattern; } /** * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping} * annotation on the handler class and on any of its methods. */ @Override protected String[] determineUrlsForHandler(String beanName) { ApplicationContext context = getApplicationContext(); Class<?> handlerType = context.getType(beanName); RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class); if (mapping != null) { // @RequestMapping found at type level this.cachedMappings.put(handlerType, mapping); Set<String> urls = new LinkedHashSet<String>(); String[] typeLevelPatterns = mapping.value(); if (typeLevelPatterns.length > 0) { // @RequestMapping specifies paths at type level String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true); for (String typeLevelPattern : typeLevelPatterns) { if (!typeLevelPattern.startsWith("/")) { typeLevelPattern = "/" + typeLevelPattern; } boolean hasEmptyMethodLevelMappings = false; for (String methodLevelPattern : methodLevelPatterns) { if (methodLevelPattern == null) { hasEmptyMethodLevelMappings = true; } else { String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern); addUrlsForPath(urls, combinedPattern); } } if (hasEmptyMethodLevelMappings || org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) { addUrlsForPath(urls, typeLevelPattern); } } return StringUtils.toStringArray(urls); } else { // actual paths specified by @RequestMapping at method level return determineUrlsForHandlerMethods(handlerType, false); } } else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) { // @RequestMapping to be introspected at method level return determineUrlsForHandlerMethods(handlerType, false); } else { return null; } } /** * Derive URL mappings from the handler's method-level mappings. * @param handlerType the handler type to introspect * @param hasTypeLevelMapping whether the method-level mappings are nested * within a type-level mapping * @return the array of mapped URLs */ protected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final boolean hasTypeLevelMapping) { String[] subclassResult = determineUrlsForHandlerMethods(handlerType); if (subclassResult != null) { return subclassResult; } final Set<String> urls = new LinkedHashSet<String>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); handlerTypes.add(handlerType); handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces())); for (Class<?> currentHandlerType : handlerTypes) { ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) { RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (mapping != null) { String[] mappedPatterns = mapping.value(); if (mappedPatterns.length > 0) { for (String mappedPattern : mappedPatterns) { if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) { mappedPattern = "/" + mappedPattern; } addUrlsForPath(urls, mappedPattern); } } else if (hasTypeLevelMapping) { // empty method-level RequestMapping urls.add(null); } } } }, ReflectionUtils.USER_DECLARED_METHODS); } return StringUtils.toStringArray(urls); } /** * Derive URL mappings from the handler's method-level mappings. * @param handlerType the handler type to introspect * @return the array of mapped URLs */ protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) { return null; } /** * Add URLs and/or URL patterns for the given path. * @param urls the Set of URLs for the current bean * @param path the currently introspected path */ protected void addUrlsForPath(Set<String> urls, String path) { urls.add(path); if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) { urls.add(path + ".*"); urls.add(path + "/"); } } /** * Validate the given annotated handler against the current request. * @see #validateMapping */ @Override protected void validateHandler(Object handler, HttpServletRequest request) throws Exception { RequestMapping mapping = this.cachedMappings.get(handler.getClass()); if (mapping == null) { mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class); } if (mapping != null) { validateMapping(mapping, request); } request.setAttribute(USE_DEFAULT_SUFFIX_PATTERN, this.useDefaultSuffixPattern); } /** * Validate the given type-level mapping metadata against the current request, * checking HTTP request method and parameter conditions. * @param mapping the mapping metadata to validate * @param request current HTTP request * @throws Exception if validation failed */ protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception { RequestMethod[] mappedMethods = mapping.method(); if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) { String[] supportedMethods = new String[mappedMethods.length]; for (int i = 0; i < mappedMethods.length; i++) { supportedMethods[i] = mappedMethods[i].name(); } throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods); } String[] mappedParams = mapping.params(); if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) { throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap()); } String[] mappedHeaders = mapping.headers(); if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) { throw new ServletRequestBindingException("Header conditions \"" + StringUtils.arrayToDelimitedString(mappedHeaders, ", ") + "\" not met for actual request"); } } @Override protected boolean supportsTypeLevelMappings() { return true; } }
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping:处理器映射器
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet.mvc.annotation; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.lang.reflect.Method; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.transform.Source; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.Ordered; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.xml.SourceHttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.validation.support.BindingAwareModelMap; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; 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.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.support.HandlerMethodInvoker; import org.springframework.web.bind.annotation.support.HandlerMethodResolver; import org.springframework.web.bind.support.DefaultSessionAttributeStore; import org.springframework.web.bind.support.SessionAttributeStore; import org.springframework.web.bind.support.WebArgumentResolver; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestScope; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver; import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.support.WebContentGenerator; import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.WebUtils; /** * Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} interface * that maps handler methods based on HTTP paths, HTTP methods, and request parameters * expressed through the {@link RequestMapping} annotation. * * <p>Supports request parameter binding through the {@link RequestParam} annotation. * Also supports the {@link ModelAttribute} annotation for exposing model attribute * values to the view, as well as {@link InitBinder} for binder initialization methods * and {@link SessionAttributes} for automatic session management of specific attributes. * * <p>This adapter can be customized through various bean properties. * A common use case is to apply shared binder initialization logic through * a custom {@link #setWebBindingInitializer WebBindingInitializer}. * * @author Juergen Hoeller * @author Arjen Poutsma * @author Sam Brannen * @since 2.5 * @see #setPathMatcher * @see #setMethodNameResolver * @see #setWebBindingInitializer * @see #setSessionAttributeStore * @deprecated as of Spring 3.2, in favor of * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter RequestMappingHandlerAdapter} */ @Deprecated public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, Ordered, BeanFactoryAware { /** * Log category to use when no mapped handler is found for a request. * @see #pageNotFoundLogger */ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /** * Additional logger to use when no mapped handler is found for a request. * @see #PAGE_NOT_FOUND_LOG_CATEGORY */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); private MethodNameResolver methodNameResolver = new InternalPathMethodNameResolver(); private WebBindingInitializer webBindingInitializer; private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); private int cacheSecondsForSessionAttributeHandlers = 0; private boolean synchronizeOnSession = false; private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); private WebArgumentResolver[] customArgumentResolvers; private ModelAndViewResolver[] customModelAndViewResolvers; private HttpMessageConverter<?>[] messageConverters; private int order = Ordered.LOWEST_PRECEDENCE; private ConfigurableBeanFactory beanFactory; private BeanExpressionContext expressionContext; private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache = new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>(64); private final Map<Class<?>, Boolean> sessionAnnotatedClassesCache = new ConcurrentHashMap<Class<?>, Boolean>(64); public AnnotationMethodHandlerAdapter() { // no restriction of HTTP methods by default super(false); // See SPR-7316 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); this.messageConverters = new HttpMessageConverter<?>[] { new ByteArrayHttpMessageConverter(), stringHttpMessageConverter, new SourceHttpMessageConverter<Source>(), new org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter() }; } /** * Set if URL lookup should always use the full path within the current servlet * context. Else, the path within the current servlet mapping is used if applicable * (that is, in the case of a ".../*" servlet mapping in web.xml). * <p>Default is "false". * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath */ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); } /** * Set if context path and request URI should be URL-decoded. Both are returned * <i>undecoded</i> by the Servlet API, in contrast to the servlet path. * <p>Uses either the request encoding or the default encoding according * to the Servlet spec (ISO-8859-1). * @see org.springframework.web.util.UrlPathHelper#setUrlDecode */ public void setUrlDecode(boolean urlDecode) { this.urlPathHelper.setUrlDecode(urlDecode); } /** * Set the UrlPathHelper to use for resolution of lookup paths. * <p>Use this to override the default UrlPathHelper with a custom subclass, * or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters. */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); this.urlPathHelper = urlPathHelper; } /** * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. * <p>Default is {@link org.springframework.util.AntPathMatcher}. */ public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; } /** * Set the MethodNameResolver to use for resolving default handler methods * (carrying an empty {@code @RequestMapping} annotation). * <p>Will only kick in when the handler method cannot be resolved uniquely * through the annotation metadata already. */ public void setMethodNameResolver(MethodNameResolver methodNameResolver) { this.methodNameResolver = methodNameResolver; } /** * Specify a WebBindingInitializer which will apply pre-configured * configuration to every DataBinder that this controller uses. */ public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { this.webBindingInitializer = webBindingInitializer; } /** * Specify the strategy to store session attributes with. * <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, * storing session attributes in the HttpSession, using the same attribute name as in the model. */ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) { Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null"); this.sessionAttributeStore = sessionAttributeStore; } /** * Cache content produced by {@code @SessionAttributes} annotated handlers * for the given number of seconds. Default is 0, preventing caching completely. * <p>In contrast to the "cacheSeconds" property which will apply to all general handlers * (but not to {@code @SessionAttributes} annotated handlers), this setting will * apply to {@code @SessionAttributes} annotated handlers only. * @see #setCacheSeconds * @see org.springframework.web.bind.annotation.SessionAttributes */ public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) { this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers; } /** * Set if controller execution should be synchronized on the session, * to serialize parallel invocations from the same client. * <p>More specifically, the execution of the {@code handleRequestInternal} * method will get synchronized if this flag is "true". The best available * session mutex will be used for the synchronization; ideally, this will * be a mutex exposed by HttpSessionMutexListener. * <p>The session mutex is guaranteed to be the same object during * the entire lifetime of the session, available under the key defined * by the {@code SESSION_MUTEX_ATTRIBUTE} constant. It serves as a * safe reference to synchronize on for locking on the current session. * <p>In many cases, the HttpSession reference itself is a safe mutex * as well, since it will always be the same object reference for the * same active logical session. However, this is not guaranteed across * different servlet containers; the only 100% safe way is a session mutex. * @see org.springframework.web.util.HttpSessionMutexListener * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) */ public void setSynchronizeOnSession(boolean synchronizeOnSession) { this.synchronizeOnSession = synchronizeOnSession; } /** * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed * (e.g. for default attribute names). * <p>Default is a {@link org.springframework.core.DefaultParameterNameDiscoverer}. */ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } /** * Set a custom WebArgumentResolvers to use for special method parameter types. * <p>Such a custom WebArgumentResolver will kick in first, having a chance to resolve * an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) { this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver}; } /** * Set one or more custom WebArgumentResolvers to use for special method parameter types. * <p>Any such custom WebArgumentResolver will kick in first, having a chance to resolve * an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolvers(WebArgumentResolver... argumentResolvers) { this.customArgumentResolvers = argumentResolvers; } /** * Set a custom ModelAndViewResolvers to use for special method return types. * <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve * a return value before the standard ModelAndView handling kicks in. */ public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) { this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver}; } /** * Set one or more custom ModelAndViewResolvers to use for special method return types. * <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve * a return value before the standard ModelAndView handling kicks in. */ public void setCustomModelAndViewResolvers(ModelAndViewResolver... customModelAndViewResolvers) { this.customModelAndViewResolvers = customModelAndViewResolvers; } /** * Set the message body converters to use. * <p>These converters are used to convert from and to HTTP requests and responses. */ public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) { this.messageConverters = messageConverters; } /** * Return the message body converters that this adapter has been configured with. */ public HttpMessageConverter<?>[] getMessageConverters() { return messageConverters; } /** * Specify the order value for this HandlerAdapter bean. * <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered. * @see org.springframework.core.Ordered#getOrder() */ public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public void setBeanFactory(BeanFactory beanFactory) { if (beanFactory instanceof ConfigurableBeanFactory) { this.beanFactory = (ConfigurableBeanFactory) beanFactory; this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope()); } } @Override public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); } @Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Class<?> clazz = ClassUtils.getUserClass(handler); Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz); if (annotatedWithSessionAttributes == null) { annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null); this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes); } if (annotatedWithSessionAttributes) { checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); } else { checkAndPrepare(request, response, true); } // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handler); } } } return invokeHandlerMethod(request, response, handler); } protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); Method handlerMethod = methodResolver.resolveHandlerMethod(request); ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); ServletWebRequest webRequest = new ServletWebRequest(request, response); ExtendedModelMap implicitModel = new BindingAwareModelMap(); Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); return mav; } /** * This method always returns -1 since an annotated controller can have many methods, * each requiring separate lastModified calculations. Instead, an * {@link RequestMapping}-annotated method can calculate the lastModified value, call * {@link org.springframework.web.context.request.WebRequest#checkNotModified(long)} * to check it, and return {@code null} if that returns {@code true}. * @see org.springframework.web.context.request.WebRequest#checkNotModified(long) */ @Override public long getLastModified(HttpServletRequest request, Object handler) { return -1; } /** * Build a HandlerMethodResolver for the given handler type. */ private ServletHandlerMethodResolver getMethodResolver(Object handler) { Class<?> handlerClass = ClassUtils.getUserClass(handler); ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass); if (resolver == null) { synchronized (this.methodResolverCache) { resolver = this.methodResolverCache.get(handlerClass); if (resolver == null) { resolver = new ServletHandlerMethodResolver(handlerClass); this.methodResolverCache.put(handlerClass, resolver); } } } return resolver; } /** * Template method for creating a new ServletRequestDataBinder instance. * <p>The default implementation creates a standard ServletRequestDataBinder. * This can be overridden for custom ServletRequestDataBinder subclasses. * @param request current HTTP request * @param target the target object to bind onto (or {@code null} * if the binder is just used to convert a plain parameter value) * @param objectName the objectName of the target object * @return the ServletRequestDataBinder instance to use * @throws Exception in case of invalid state or arguments * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest) * @see ServletRequestDataBinder#convertIfNecessary(Object, Class, org.springframework.core.MethodParameter) */ protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName) throws Exception { return new ServletRequestDataBinder(target, objectName); } /** * Template method for creating a new HttpInputMessage instance. * <p>The default implementation creates a standard {@link ServletServerHttpRequest}. * This can be overridden for custom {@code HttpInputMessage} implementations * @param servletRequest current HTTP request * @return the HttpInputMessage instance to use * @throws Exception in case of errors */ protected HttpInputMessage createHttpInputMessage(HttpServletRequest servletRequest) throws Exception { return new ServletServerHttpRequest(servletRequest); } /** * Template method for creating a new HttpOutputMessage instance. * <p>The default implementation creates a standard {@link ServletServerHttpResponse}. * This can be overridden for custom {@code HttpOutputMessage} implementations * @param servletResponse current HTTP response * @return the HttpInputMessage instance to use * @throws Exception in case of errors */ protected HttpOutputMessage createHttpOutputMessage(HttpServletResponse servletResponse) throws Exception { return new ServletServerHttpResponse(servletResponse); } /** * Servlet-specific subclass of {@link HandlerMethodResolver}. */ private class ServletHandlerMethodResolver extends HandlerMethodResolver { private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>(); private ServletHandlerMethodResolver(Class<?> handlerType) { init(handlerType); } @Override protected boolean isHandlerMethod(Method method) { if (this.mappings.containsKey(method)) { return true; } RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class); if (mapping != null) { String[] patterns = mapping.value(); RequestMethod[] methods = new RequestMethod[0]; String[] params = new String[0]; String[] headers = new String[0]; if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) { methods = mapping.method(); } if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) { params = mapping.params(); } if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) { headers = mapping.headers(); } RequestMappingInfo mappingInfo = new RequestMappingInfo(patterns, methods, params, headers); this.mappings.put(method, mappingInfo); return true; } return false; } public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException { String lookupPath = urlPathHelper.getLookupPathForRequest(request); Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath); Map<RequestSpecificMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestSpecificMappingInfo, Method>(); Set<String> allowedMethods = new LinkedHashSet<String>(7); String resolvedMethodName = null; for (Method handlerMethod : getHandlerMethods()) { RequestSpecificMappingInfo mappingInfo = new RequestSpecificMappingInfo(this.mappings.get(handlerMethod)); boolean match = false; if (mappingInfo.hasPatterns()) { for (String pattern : mappingInfo.getPatterns()) { if (!hasTypeLevelMapping() && !pattern.startsWith("/")) { pattern = "/" + pattern; } String combinedPattern = getCombinedPattern(pattern, lookupPath, request); if (combinedPattern != null) { if (mappingInfo.matches(request)) { match = true; mappingInfo.addMatchedPattern(combinedPattern); } else { if (!mappingInfo.matchesRequestMethod(request)) { allowedMethods.addAll(mappingInfo.methodNames()); } break; } } } mappingInfo.sortMatchedPatterns(pathComparator); } else if (useTypeLevelMapping(request)) { String[] typeLevelPatterns = getTypeLevelMapping().value(); for (String typeLevelPattern : typeLevelPatterns) { if (!typeLevelPattern.startsWith("/")) { typeLevelPattern = "/" + typeLevelPattern; } boolean useSuffixPattern = useSuffixPattern(request); if (getMatchingPattern(typeLevelPattern, lookupPath, useSuffixPattern) != null) { if (mappingInfo.matches(request)) { match = true; mappingInfo.addMatchedPattern(typeLevelPattern); } else { if (!mappingInfo.matchesRequestMethod(request)) { allowedMethods.addAll(mappingInfo.methodNames()); } break; } } } mappingInfo.sortMatchedPatterns(pathComparator); } else { // No paths specified: parameter match sufficient. match = mappingInfo.matches(request); if (match && mappingInfo.getMethodCount() == 0 && mappingInfo.getParamCount() == 0 && resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) { match = false; } else { if (!mappingInfo.matchesRequestMethod(request)) { allowedMethods.addAll(mappingInfo.methodNames()); } } } if (match) { Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod); if (oldMappedMethod != null && oldMappedMethod != handlerMethod) { if (methodNameResolver != null && !mappingInfo.hasPatterns()) { if (!oldMappedMethod.getName().equals(handlerMethod.getName())) { if (resolvedMethodName == null) { resolvedMethodName = methodNameResolver.getHandlerMethodName(request); } if (!resolvedMethodName.equals(oldMappedMethod.getName())) { oldMappedMethod = null; } if (!resolvedMethodName.equals(handlerMethod.getName())) { if (oldMappedMethod != null) { targetHandlerMethods.put(mappingInfo, oldMappedMethod); oldMappedMethod = null; } else { targetHandlerMethods.remove(mappingInfo); } } } } if (oldMappedMethod != null) { throw new IllegalStateException( "Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" + oldMappedMethod + ", " + handlerMethod + "}. If you intend to handle the same path in multiple methods, then factor " + "them out into a dedicated handler class with that path mapped at the type level!"); } } } } if (!targetHandlerMethods.isEmpty()) { List<RequestSpecificMappingInfo> matches = new ArrayList<RequestSpecificMappingInfo>(targetHandlerMethods.keySet()); RequestSpecificMappingInfoComparator requestMappingInfoComparator = new RequestSpecificMappingInfoComparator(pathComparator, request); Collections.sort(matches, requestMappingInfoComparator); RequestSpecificMappingInfo bestMappingMatch = matches.get(0); String bestMatchedPath = bestMappingMatch.bestMatchedPattern(); if (bestMatchedPath != null) { extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request); } return targetHandlerMethods.get(bestMappingMatch); } else { if (!allowedMethods.isEmpty()) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), StringUtils.toStringArray(allowedMethods)); } throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap()); } } private boolean useTypeLevelMapping(HttpServletRequest request) { if (!hasTypeLevelMapping() || ObjectUtils.isEmpty(getTypeLevelMapping().value())) { return false; } Object value = request.getAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING); return (value != null) ? (Boolean) value : Boolean.TRUE; } private boolean useSuffixPattern(HttpServletRequest request) { Object value = request.getAttribute(DefaultAnnotationHandlerMapping.USE_DEFAULT_SUFFIX_PATTERN); return (value != null) ? (Boolean) value : Boolean.TRUE; } /** * Determines the combined pattern for the given methodLevelPattern and path. * <p>Uses the following algorithm: * <ol> * <li>If there is a type-level mapping with path information, it is {@linkplain * PathMatcher#combine(String, String) combined} with the method-level pattern.</li> * <li>If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} * in the request, it is combined with the method-level pattern.</li> * <li>Otherwise, the method-level pattern is returned.</li> * </ol> */ private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) { boolean useSuffixPattern = useSuffixPattern(request); if (useTypeLevelMapping(request)) { String[] typeLevelPatterns = getTypeLevelMapping().value(); for (String typeLevelPattern : typeLevelPatterns) { if (!typeLevelPattern.startsWith("/")) { typeLevelPattern = "/" + typeLevelPattern; } String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern); String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern); if (matchingPattern != null) { return matchingPattern; } } return null; } String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) { String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern); String matchingPattern = getMatchingPattern(combinedPattern, lookupPath, useSuffixPattern); if (matchingPattern != null && !matchingPattern.equals(bestMatchingPattern)) { return matchingPattern; } } return getMatchingPattern(methodLevelPattern, lookupPath, useSuffixPattern); } private String getMatchingPattern(String pattern, String lookupPath, boolean useSuffixPattern) { if (pattern.equals(lookupPath)) { return pattern; } boolean hasSuffix = pattern.indexOf('.') != -1; if (useSuffixPattern && !hasSuffix) { String patternWithSuffix = pattern + ".*"; if (pathMatcher.match(patternWithSuffix, lookupPath)) { return patternWithSuffix; } } if (pathMatcher.match(pattern, lookupPath)) { return pattern; } boolean endsWithSlash = pattern.endsWith("/"); if (useSuffixPattern && !endsWithSlash) { String patternWithSlash = pattern + "/"; if (pathMatcher.match(patternWithSlash, lookupPath)) { return patternWithSlash; } } return null; } @SuppressWarnings("unchecked") private void extractHandlerMethodUriTemplates(String mappedPattern, String lookupPath, HttpServletRequest request) { Map<String, String> variables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); int patternVariableCount = StringUtils.countOccurrencesOf(mappedPattern, "{"); if ((variables == null || patternVariableCount != variables.size()) && pathMatcher.match(mappedPattern, lookupPath)) { variables = pathMatcher.extractUriTemplateVariables(mappedPattern, lookupPath); Map<String, String> decodedVariables = urlPathHelper.decodePathVariables(request, variables); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedVariables); } } } /** * Servlet-specific subclass of {@link HandlerMethodInvoker}. */ private class ServletHandlerMethodInvoker extends HandlerMethodInvoker { private boolean responseArgumentUsed = false; private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) { super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer, customArgumentResolvers, messageConverters); } @Override protected void raiseMissingParameterException(String paramName, Class<?> paramType) throws Exception { throw new MissingServletRequestParameterException(paramName, paramType.getSimpleName()); } @Override protected void raiseSessionRequiredException(String message) throws Exception { throw new HttpSessionRequiredException(message); } @Override protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception { return AnnotationMethodHandlerAdapter.this.createBinder( webRequest.getNativeRequest(HttpServletRequest.class), target, objectName); } @Override protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception { ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder; servletBinder.bind(webRequest.getNativeRequest(ServletRequest.class)); } @Override protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); return AnnotationMethodHandlerAdapter.this.createHttpInputMessage(servletRequest); } @Override protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception { HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse(); return AnnotationMethodHandlerAdapter.this.createHttpOutputMessage(servletResponse); } @Override protected Object resolveDefaultValue(String value) { if (beanFactory == null) { return value; } String placeholdersResolved = beanFactory.resolveEmbeddedValue(value); BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver(); if (exprResolver == null) { return value; } return exprResolver.evaluate(placeholdersResolved, expressionContext); } @Override protected Object resolveCookieValue(String cookieName, Class<?> paramType, NativeWebRequest webRequest) throws Exception { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName); if (Cookie.class.isAssignableFrom(paramType)) { return cookieValue; } else if (cookieValue != null) { return urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue()); } else { return null; } } @Override @SuppressWarnings({"unchecked"}) protected String resolvePathVariable(String pathVarName, Class<?> paramType, NativeWebRequest webRequest) throws Exception { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Map<String, String> uriTemplateVariables = (Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) { throw new IllegalStateException( "Could not find @PathVariable [" + pathVarName + "] in @RequestMapping"); } return uriTemplateVariables.get(pathVarName); } @Override protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); if (ServletRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(parameterType)) { Object nativeRequest = webRequest.getNativeRequest(parameterType); if (nativeRequest == null) { throw new IllegalStateException( "Current request is not of type [" + parameterType.getName() + "]: " + request); } return nativeRequest; } else if (ServletResponse.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; Object nativeResponse = webRequest.getNativeResponse(parameterType); if (nativeResponse == null) { throw new IllegalStateException( "Current response is not of type [" + parameterType.getName() + "]: " + response); } return nativeResponse; } else if (HttpSession.class.isAssignableFrom(parameterType)) { return request.getSession(); } else if (Principal.class.isAssignableFrom(parameterType)) { return request.getUserPrincipal(); } else if (Locale.class == parameterType) { return RequestContextUtils.getLocale(request); } else if (InputStream.class.isAssignableFrom(parameterType)) { return request.getInputStream(); } else if (Reader.class.isAssignableFrom(parameterType)) { return request.getReader(); } else if (OutputStream.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; return response.getOutputStream(); } else if (Writer.class.isAssignableFrom(parameterType)) { this.responseArgumentUsed = true; return response.getWriter(); } return super.resolveStandardArgument(parameterType, webRequest); } @SuppressWarnings("unchecked") public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue, ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception { ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(handlerMethod, ResponseStatus.class); if (responseStatus != null) { HttpStatus statusCode = responseStatus.code(); String reason = responseStatus.reason(); if (!StringUtils.hasText(reason)) { webRequest.getResponse().setStatus(statusCode.value()); } else { webRequest.getResponse().sendError(statusCode.value(), reason); } // to be picked up by the RedirectView webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, statusCode); this.responseArgumentUsed = true; } // Invoke custom resolvers if present... if (customModelAndViewResolvers != null) { for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) { ModelAndView mav = mavResolver.resolveModelAndView( handlerMethod, handlerType, returnValue, implicitModel, webRequest); if (mav != ModelAndViewResolver.UNRESOLVED) { return mav; } } } if (returnValue instanceof HttpEntity) { handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest); return null; } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) { handleResponseBody(returnValue, webRequest); return null; } else if (returnValue instanceof ModelAndView) { ModelAndView mav = (ModelAndView) returnValue; mav.getModelMap().mergeAttributes(implicitModel); return mav; } else if (returnValue instanceof Model) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap()); } else if (returnValue instanceof View) { return new ModelAndView((View) returnValue).addAllObjects(implicitModel); } else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) { addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else if (returnValue instanceof Map) { return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue); } else if (returnValue instanceof String) { return new ModelAndView((String) returnValue).addAllObjects(implicitModel); } else if (returnValue == null) { // Either returned null or was 'void' return. if (this.responseArgumentUsed || webRequest.isNotModified()) { return null; } else { // Assuming view name translation... return new ModelAndView().addAllObjects(implicitModel); } } else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) { // Assume a single model attribute... addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel); return new ModelAndView().addAllObjects(implicitModel); } else { throw new IllegalArgumentException("Invalid handler method return value: " + returnValue); } } private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws Exception { if (returnValue == null) { return; } HttpInputMessage inputMessage = createHttpInputMessage(webRequest); HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest); writeWithMessageConverters(returnValue, inputMessage, outputMessage); } private void handleHttpEntityResponse(HttpEntity<?> responseEntity, ServletWebRequest webRequest) throws Exception { if (responseEntity == null) { return; } HttpInputMessage inputMessage = createHttpInputMessage(webRequest); HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest); if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) { ((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode()); } HttpHeaders entityHeaders = responseEntity.getHeaders(); if (!entityHeaders.isEmpty()) { outputMessage.getHeaders().putAll(entityHeaders); } Object body = responseEntity.getBody(); if (body != null) { writeWithMessageConverters(body, inputMessage, outputMessage); } else { // flush headers outputMessage.getBody(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage, HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept(); if (acceptedMediaTypes.isEmpty()) { acceptedMediaTypes = Collections.singletonList(MediaType.ALL); } MediaType.sortByQualityValue(acceptedMediaTypes); Class<?> returnValueType = returnValue.getClass(); List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>(); if (getMessageConverters() != null) { for (MediaType acceptedMediaType : acceptedMediaTypes) { for (HttpMessageConverter messageConverter : getMessageConverters()) { if (messageConverter.canWrite(returnValueType, acceptedMediaType)) { messageConverter.write(returnValue, acceptedMediaType, outputMessage); if (logger.isDebugEnabled()) { MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType == null) { contentType = acceptedMediaType; } logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + messageConverter + "]"); } this.responseArgumentUsed = true; return; } } } for (HttpMessageConverter messageConverter : messageConverters) { allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); } } throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); } } /** * Holder for request mapping metadata. */ static class RequestMappingInfo { private final String[] patterns; private final RequestMethod[] methods; private final String[] params; private final String[] headers; RequestMappingInfo(String[] patterns, RequestMethod[] methods, String[] params, String[] headers) { this.patterns = (patterns != null ? patterns : new String[0]); this.methods = (methods != null ? methods : new RequestMethod[0]); this.params = (params != null ? params : new String[0]); this.headers = (headers != null ? headers : new String[0]); } public boolean hasPatterns() { return (this.patterns.length > 0); } public String[] getPatterns() { return this.patterns; } public int getMethodCount() { return this.methods.length; } public int getParamCount() { return this.params.length; } public int getHeaderCount() { return this.headers.length; } public boolean matches(HttpServletRequest request) { return matchesRequestMethod(request) && matchesParameters(request) && matchesHeaders(request); } public boolean matchesHeaders(HttpServletRequest request) { return ServletAnnotationMappingUtils.checkHeaders(this.headers, request); } public boolean matchesParameters(HttpServletRequest request) { return ServletAnnotationMappingUtils.checkParameters(this.params, request); } public boolean matchesRequestMethod(HttpServletRequest request) { return ServletAnnotationMappingUtils.checkRequestMethod(this.methods, request); } public Set<String> methodNames() { Set<String> methodNames = new LinkedHashSet<String>(this.methods.length); for (RequestMethod method : this.methods) { methodNames.add(method.name()); } return methodNames; } @Override public boolean equals(Object obj) { RequestMappingInfo other = (RequestMappingInfo) obj; return (Arrays.equals(this.patterns, other.patterns) && Arrays.equals(this.methods, other.methods) && Arrays.equals(this.params, other.params) && Arrays.equals(this.headers, other.headers)); } @Override public int hashCode() { return (Arrays.hashCode(this.patterns) * 23 + Arrays.hashCode(this.methods) * 29 + Arrays.hashCode(this.params) * 31 + Arrays.hashCode(this.headers)); } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(Arrays.asList(this.patterns)); if (this.methods.length > 0) { builder.append(','); builder.append(Arrays.asList(this.methods)); } if (this.headers.length > 0) { builder.append(','); builder.append(Arrays.asList(this.headers)); } if (this.params.length > 0) { builder.append(','); builder.append(Arrays.asList(this.params)); } return builder.toString(); } } /** * Subclass of {@link RequestMappingInfo} that holds request-specific data. */ static class RequestSpecificMappingInfo extends RequestMappingInfo { private final List<String> matchedPatterns = new ArrayList<String>(); RequestSpecificMappingInfo(String[] patterns, RequestMethod[] methods, String[] params, String[] headers) { super(patterns, methods, params, headers); } RequestSpecificMappingInfo(RequestMappingInfo other) { super(other.patterns, other.methods, other.params, other.headers); } public void addMatchedPattern(String matchedPattern) { matchedPatterns.add(matchedPattern); } public void sortMatchedPatterns(Comparator<String> pathComparator) { Collections.sort(matchedPatterns, pathComparator); } public String bestMatchedPattern() { return (!this.matchedPatterns.isEmpty() ? this.matchedPatterns.get(0) : null); } } /** * Comparator capable of sorting {@link RequestSpecificMappingInfo}s (RHIs) so that * sorting a list with this comparator will result in: * <ul> * <li>RHIs with {@linkplain AnnotationMethodHandlerAdapter.RequestSpecificMappingInfo#matchedPatterns better matched paths} * take precedence over those with a weaker match (as expressed by the {@linkplain PathMatcher#getPatternComparator(String) * path pattern comparator}.) Typically, this means that patterns without wild cards and uri templates * will be ordered before those without.</li> * <li>RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be * ordered before those without a method, or with more than one method.</li> * <li>RHIs with more {@linkplain RequestMappingInfo#params request parameters} will be ordered * before those with less parameters</li> * </ol> */ static class RequestSpecificMappingInfoComparator implements Comparator<RequestSpecificMappingInfo> { private final Comparator<String> pathComparator; private final ServerHttpRequest request; RequestSpecificMappingInfoComparator(Comparator<String> pathComparator, HttpServletRequest request) { this.pathComparator = pathComparator; this.request = new ServletServerHttpRequest(request); } @Override public int compare(RequestSpecificMappingInfo info1, RequestSpecificMappingInfo info2) { int pathComparison = pathComparator.compare(info1.bestMatchedPattern(), info2.bestMatchedPattern()); if (pathComparison != 0) { return pathComparison; } int info1ParamCount = info1.getParamCount(); int info2ParamCount = info2.getParamCount(); if (info1ParamCount != info2ParamCount) { return info2ParamCount - info1ParamCount; } int info1HeaderCount = info1.getHeaderCount(); int info2HeaderCount = info2.getHeaderCount(); if (info1HeaderCount != info2HeaderCount) { return info2HeaderCount - info1HeaderCount; } int acceptComparison = compareAcceptHeaders(info1, info2); if (acceptComparison != 0) { return acceptComparison; } int info1MethodCount = info1.getMethodCount(); int info2MethodCount = info2.getMethodCount(); if (info1MethodCount == 0 && info2MethodCount > 0) { return 1; } else if (info2MethodCount == 0 && info1MethodCount > 0) { return -1; } else if (info1MethodCount == 1 & info2MethodCount > 1) { return -1; } else if (info2MethodCount == 1 & info1MethodCount > 1) { return 1; } return 0; } private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) { List<MediaType> requestAccepts = request.getHeaders().getAccept(); MediaType.sortByQualityValue(requestAccepts); List<MediaType> info1Accepts = getAcceptHeaderValue(info1); List<MediaType> info2Accepts = getAcceptHeaderValue(info2); for (MediaType requestAccept : requestAccepts) { int pos1 = indexOfIncluded(info1Accepts, requestAccept); int pos2 = indexOfIncluded(info2Accepts, requestAccept); if (pos1 != pos2) { return pos2 - pos1; } } return 0; } private int indexOfIncluded(List<MediaType> infoAccepts, MediaType requestAccept) { for (int i = 0; i < infoAccepts.size(); i++) { MediaType info1Accept = infoAccepts.get(i); if (requestAccept.includes(info1Accept)) { return i; } } return -1; } private List<MediaType> getAcceptHeaderValue(RequestMappingInfo info) { for (String header : info.headers) { int separator = header.indexOf('='); if (separator != -1) { String key = header.substring(0, separator); String value = header.substring(separator + 1); if ("Accept".equalsIgnoreCase(key)) { return MediaType.parseMediaTypes(value); } } } return Collections.emptyList(); } } }
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter:处理器适配器
从注释中可以看到让我们使用新的处理器映射器和处理器适配器类,而我们只需要将这两个类的实例交给 Spring 管理,Spring MVC 就会默认使用上这两个类,配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置控制器扫描--> <context:component-scan base-package="com.zze.springmvc.web.controller"/> <!--配置处理器映射器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!--配置处理器适配器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> </beans>
springmvc.xml
而这样配置显然不够简洁,Spring MVC 还为我们提供了一个更简洁的配置方式,如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置控制器扫描--> <context:component-scan base-package="com.zze.springmvc.web.controller"/> <!--配置注解驱动,相当于同时使用最新处理器映射器和处理器适配器--> <mvc:annotation-driven/> </beans>
springmvc.xml
从 spring3.1 版本开始,废除了 DefaultAnnotationHandlerMapping 的使用,推荐使用 RequestMappingHandlerMapping 完成注解式处理器映射。
从 spring3.1 版本开始,废除了 AnnotationMethodHandlerAdapter 的使用,推荐使用 RequestMappingHandlerAdapter 完成注解式处理器适配。
映射器与适配器必需配套使用,如果映射器使用了推荐的 RequestMappingHandlerMapping,适配器也必需使用推荐的 RequestMappingHandlerAdapter。
视图解析器
在 Spring 核心配置文件中配置上如下视图解析器:
<!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>
此时 Controller 返回的 viewName 如果是 'userlist',那么它实际上会转发到 '/WEB-INF/jsp/userlist.jsp'。
默认支持的入参
package com.zze.springmvc.web.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller public class TestController { @RequestMapping("test") public String test(Model model, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response){ // ModelAndView modelAndView = new ModelAndView(); // 此时的入参 model 及 modelMap,就对应于 modelAndView 中的 model 部分,而 String 返回值,就相当于 modelAndView 中的 view 部分 // modelAndView.addObject() 对应 model.addAttribute()、 modelMap.addAttribute() // modelAndView.setViewName("list") 对应 return "list" // 默认还支持直接传入 HttpServletRequest、HttpServletResponse 实例,这样就可以直接访问到原生 Servlet 相关 API return null; } }
参数绑定
package com.zze.springmvc.web.controller; import com.zze.springmvc.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class TestController { /** * 简单参数绑定 * 请求参数名必须和请求方法入参名一致 */ @RequestMapping("test") public void test(Integer id,String name) { System.out.println(name); System.out.println(id); /* zhangsan 123 */ } }
例:简单参数绑定
package com.zze.springmvc.web.controller; import com.zze.springmvc.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class TestController { /** * pojo 参数绑定 * 请求参数名必须和 pojo 属性名一致 */ @RequestMapping("test") public void test(User user) { System.out.println(user.getName()); System.out.println(user.getId()); /* zhangsan 123 */ } }
例:pojo 参数绑定
package com.zze.springmvc.web.controller; import com.zze.springmvc.pojo.QueryVo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class TestController { /** * 包装 pojo 参数绑定 */ @RequestMapping("test") public void test(QueryVo queryVo) { System.out.println(queryVo.getUser().getName()); System.out.println(queryVo.getUser().getId()); /* zhangsan 123 */ } }
例:包装 pojo 参数绑定
与MyBatis整合
1、创建 maven web 项目,引入如下依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zze.springmvc</groupId> <artifactId>springmvc_mybatis</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.18</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.11.0.GA</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.0-rc1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.0-rc1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.7</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!--使用 tomcat7:run--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
pom.xml
2、使用逆向工程生成 MyBatis 相关类及映射文件。
package com.zze.springmvc.pojo; import java.util.Date; public class User { private Integer id; private String name; private Date birthday; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address == null ? null : address.trim(); } }
com.zze.springmvc.pojo.User
package com.zze.springmvc.pojo; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; public class UserExample { protected String orderByClause; protected boolean distinct; protected List<Criteria> oredCriteria; public UserExample() { oredCriteria = new ArrayList<Criteria>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List<Criteria> getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List<Criterion> criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<Criterion>(); } public boolean isValid() { return criteria.size() > 0; } public List<Criterion> getAllCriteria() { return criteria; } public List<Criterion> getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } protected void addCriterionForJDBCDate(String condition, Date value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } addCriterion(condition, new java.sql.Date(value.getTime()), property); } protected void addCriterionForJDBCDate(String condition, List<Date> values, String property) { if (values == null || values.size() == 0) { throw new RuntimeException("Value list for " + property + " cannot be null or empty"); } List<java.sql.Date> dateList = new ArrayList<java.sql.Date>(); Iterator<Date> iter = values.iterator(); while (iter.hasNext()) { dateList.add(new java.sql.Date(iter.next().getTime())); } addCriterion(condition, dateList, property); } protected void addCriterionForJDBCDate(String condition, Date value1, Date value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } addCriterion(condition, new java.sql.Date(value1.getTime()), new java.sql.Date(value2.getTime()), property); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Integer value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Integer value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Integer value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Integer value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Integer value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Integer value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List<Integer> values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List<Integer> values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Integer value1, Integer value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Integer value1, Integer value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List<String> values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List<String> values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } public Criteria andBirthdayIsNull() { addCriterion("birthday is null"); return (Criteria) this; } public Criteria andBirthdayIsNotNull() { addCriterion("birthday is not null"); return (Criteria) this; } public Criteria andBirthdayEqualTo(Date value) { addCriterionForJDBCDate("birthday =", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayNotEqualTo(Date value) { addCriterionForJDBCDate("birthday <>", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayGreaterThan(Date value) { addCriterionForJDBCDate("birthday >", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayGreaterThanOrEqualTo(Date value) { addCriterionForJDBCDate("birthday >=", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayLessThan(Date value) { addCriterionForJDBCDate("birthday <", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayLessThanOrEqualTo(Date value) { addCriterionForJDBCDate("birthday <=", value, "birthday"); return (Criteria) this; } public Criteria andBirthdayIn(List<Date> values) { addCriterionForJDBCDate("birthday in", values, "birthday"); return (Criteria) this; } public Criteria andBirthdayNotIn(List<Date> values) { addCriterionForJDBCDate("birthday not in", values, "birthday"); return (Criteria) this; } public Criteria andBirthdayBetween(Date value1, Date value2) { addCriterionForJDBCDate("birthday between", value1, value2, "birthday"); return (Criteria) this; } public Criteria andBirthdayNotBetween(Date value1, Date value2) { addCriterionForJDBCDate("birthday not between", value1, value2, "birthday"); return (Criteria) this; } public Criteria andAddressIsNull() { addCriterion("address is null"); return (Criteria) this; } public Criteria andAddressIsNotNull() { addCriterion("address is not null"); return (Criteria) this; } public Criteria andAddressEqualTo(String value) { addCriterion("address =", value, "address"); return (Criteria) this; } public Criteria andAddressNotEqualTo(String value) { addCriterion("address <>", value, "address"); return (Criteria) this; } public Criteria andAddressGreaterThan(String value) { addCriterion("address >", value, "address"); return (Criteria) this; } public Criteria andAddressGreaterThanOrEqualTo(String value) { addCriterion("address >=", value, "address"); return (Criteria) this; } public Criteria andAddressLessThan(String value) { addCriterion("address <", value, "address"); return (Criteria) this; } public Criteria andAddressLessThanOrEqualTo(String value) { addCriterion("address <=", value, "address"); return (Criteria) this; } public Criteria andAddressLike(String value) { addCriterion("address like", value, "address"); return (Criteria) this; } public Criteria andAddressNotLike(String value) { addCriterion("address not like", value, "address"); return (Criteria) this; } public Criteria andAddressIn(List<String> values) { addCriterion("address in", values, "address"); return (Criteria) this; } public Criteria andAddressNotIn(List<String> values) { addCriterion("address not in", values, "address"); return (Criteria) this; } public Criteria andAddressBetween(String value1, String value2) { addCriterion("address between", value1, value2, "address"); return (Criteria) this; } public Criteria andAddressNotBetween(String value1, String value2) { addCriterion("address not between", value1, value2, "address"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List<?>) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } }
com.zze.springmvc.pojo.UserExample
package com.zze.springmvc.mapper; import com.zze.springmvc.pojo.User; import com.zze.springmvc.pojo.UserExample; import java.util.List; import org.apache.ibatis.annotations.Param; public interface UserMapper { long countByExample(UserExample example); int deleteByExample(UserExample example); int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); List<User> selectByExample(UserExample example); User selectByPrimaryKey(Integer id); int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example); int updateByExample(@Param("record") User record, @Param("example") UserExample example); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); }
com.zze.springmvc.mapper.UserMapper
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zze.springmvc.mapper.UserMapper"> <resultMap id="BaseResultMap" type="com.zze.springmvc.pojo.User"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="birthday" jdbcType="DATE" property="birthday" /> <result column="address" jdbcType="VARCHAR" property="address" /> </resultMap> <sql id="Example_Where_Clause"> <where> <foreach collection="oredCriteria" item="criteria" separator="or"> <if test="criteria.valid"> <trim prefix="(" prefixOverrides="and" suffix=")"> <foreach collection="criteria.criteria" item="criterion"> <choose> <when test="criterion.noValue"> and ${criterion.condition} </when> <when test="criterion.singleValue"> and ${criterion.condition} #{criterion.value} </when> <when test="criterion.betweenValue"> and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} </when> <when test="criterion.listValue"> and ${criterion.condition} <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> #{listItem} </foreach> </when> </choose> </foreach> </trim> </if> </foreach> </where> </sql> <sql id="Update_By_Example_Where_Clause"> <where> <foreach collection="example.oredCriteria" item="criteria" separator="or"> <if test="criteria.valid"> <trim prefix="(" prefixOverrides="and" suffix=")"> <foreach collection="criteria.criteria" item="criterion"> <choose> <when test="criterion.noValue"> and ${criterion.condition} </when> <when test="criterion.singleValue"> and ${criterion.condition} #{criterion.value} </when> <when test="criterion.betweenValue"> and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} </when> <when test="criterion.listValue"> and ${criterion.condition} <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> #{listItem} </foreach> </when> </choose> </foreach> </trim> </if> </foreach> </where> </sql> <sql id="Base_Column_List"> id, name, birthday, address </sql> <select id="selectByExample" parameterType="com.zze.springmvc.pojo.UserExample" resultMap="BaseResultMap"> select <if test="distinct"> distinct </if> <include refid="Base_Column_List" /> from user <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null"> order by ${orderByClause} </if> </select> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from user where id = #{id,jdbcType=INTEGER} </delete> <delete id="deleteByExample" parameterType="com.zze.springmvc.pojo.UserExample"> delete from user <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> </delete> <insert id="insert" parameterType="com.zze.springmvc.pojo.User"> insert into user (id, name, birthday, address) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{birthday,jdbcType=DATE}, #{address,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" parameterType="com.zze.springmvc.pojo.User"> insert into user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null"> id, </if> <if test="name != null"> name, </if> <if test="birthday != null"> birthday, </if> <if test="address != null"> address, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null"> #{id,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="birthday != null"> #{birthday,jdbcType=DATE}, </if> <if test="address != null"> #{address,jdbcType=VARCHAR}, </if> </trim> </insert> <select id="countByExample" parameterType="com.zze.springmvc.pojo.UserExample" resultType="java.lang.Long"> select count(*) from user <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> </select> <update id="updateByExampleSelective" parameterType="map"> update user <set> <if test="record.id != null"> id = #{record.id,jdbcType=INTEGER}, </if> <if test="record.name != null"> name = #{record.name,jdbcType=VARCHAR}, </if> <if test="record.birthday != null"> birthday = #{record.birthday,jdbcType=DATE}, </if> <if test="record.address != null"> address = #{record.address,jdbcType=VARCHAR}, </if> </set> <if test="_parameter != null"> <include refid="Update_By_Example_Where_Clause" /> </if> </update> <update id="updateByExample" parameterType="map"> update user set id = #{record.id,jdbcType=INTEGER}, name = #{record.name,jdbcType=VARCHAR}, birthday = #{record.birthday,jdbcType=DATE}, address = #{record.address,jdbcType=VARCHAR} <if test="_parameter != null"> <include refid="Update_By_Example_Where_Clause" /> </if> </update> <update id="updateByPrimaryKeySelective" parameterType="com.zze.springmvc.pojo.User"> update user <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> <if test="birthday != null"> birthday = #{birthday,jdbcType=DATE}, </if> <if test="address != null"> address = #{address,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.zze.springmvc.pojo.User"> update user set name = #{name,jdbcType=VARCHAR}, birthday = #{birthday,jdbcType=DATE}, address = #{address,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper>
com/zze/springmvc/mapper/UserMapper.xml
3、引入 MyBatis 核心配置文件,包含文件头的空文件即可:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>
config/SqlMapConfig.xml
4、引入 jdbc 属性文件:
jdbc.url=jdbc:mysql://192.168.208.192:3306/test?characterEncoding=utf-8 jdbc.driver=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=root
config/jdbc.properties
5、dao 层,在 Spring 中配置数据库连接及事务:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--读取属性文件--> <context:property-placeholder location="classpath:config/jdbc.properties"/> <!--连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="maxActive" value="20"/> <property name="initialSize" value="1"/> <property name="minIdle" value="1"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000"/> </bean> <!--配置 SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--配置数据源--> <property name="dataSource" ref="dataSource"/> <!--配置核心配置文件路径--> <property name="configLocation" value="classpath:config/SqlMapConfig.xml"/> <!--配置别名扫描包--> <property name="typeAliasesPackage" value="com.zze.springmvc.pojo"/> </bean> <!--动态代理配置方式二:配置 mapper 包扫描--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.zze.springmvc.mapper"/> </bean> </beans>
config/spring/applicationContext-dao.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置事务管理器--> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事务的通知/增强--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--事务管理规则--> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="add*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="modify*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="remove*" propagation="REQUIRED"/> <tx:method name="find*" read-only="true"/> <tx:method name="get*" read-only="true"/> <tx:method name="select*" read-only="true"/> <!-- name : 匹配方法名 read-only : 为 true 时表示只做只读操作 propagation : 事务的传播行为 timeout : 事务过期时间,为 -1 时不会过期 isolation : 事务的隔离级别 --> <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/> </tx:attributes> </tx:advice> <!--AOP 配置--> <aop:config> <aop:pointcut id="pc_service" expression="execution(* com.zze.springmvc.service.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pc_service"/> </aop:config> </beans>
config/spring/applicationContext-trans.xml
6、service 层,在 Spring 中配置包扫描:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置控制器扫描--> <context:component-scan base-package="com.zze.springmvc.service"/> </beans>
config/spring/applicationContext-service.xml
7、web 层,SpringMVC 的核心配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置控制器扫描--> <context:component-scan base-package="com.zze.springmvc.web.controller"/> <!--注解驱动--> <mvc:annotation-driven/> <!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
config/spring/springmvc.xml
8、引入 log4j 属性文件:
log4j.rootLogger = debug,stdout ### 输出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 输出DEBUG 级别以上的日志到=E://logs/error.log ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = E://logs/log.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 输出ERROR 级别以上的日志到=E://logs/error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =E://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.properties
9、配置 Spring 核心监听器及加载 SpringMVC 核心配置文件:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/spring/applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--加载 SpringMVC 核心配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!--注意:这里使用 / ,如果使用 /* 会出现找不到视图的问题--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
WEB-INF/web.xml
9、编写 service 层测试代码:
package com.zze.springmvc.service; import com.zze.springmvc.pojo.User; import java.util.List; public interface UserService { List<User> getAll(); }
com.zze.springmvc.service.UserService
package com.zze.springmvc.service.impl; import com.zze.springmvc.mapper.UserMapper; import com.zze.springmvc.pojo.User; import com.zze.springmvc.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; public List<User> getAll() { return userMapper.selectByExample(null); } }
com.zze.springmvc.service.impl.UserServiceImpl
10、编写 web 层测试代码:
package com.zze.springmvc.web.controller; import com.zze.springmvc.pojo.User; import com.zze.springmvc.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import java.text.ParseException; import java.util.List; @Controller @RequestMapping("user") public class UserController { @Autowired private UserService userService; @RequestMapping("list") public String list(Model model) throws ParseException { List<User> all = userService.getAll(); model.addAttribute("list", all); return "userlist"; } }
com.zze.springmvc.web.controller.UserController
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <html> <head> <title>list</title> </head> <body> <table border="1"> <thead> <tr> <th>Id</th> <th>Name</th> <th>Birthday</th> <th>Address</th> </tr> </thead> <tbody> <c:forEach items="${list}" var="user"> <tr> <td>${user.id}</td> <td>${user.name}</td> <td><fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd"/></td> <td>${user.address}</td> </tr> </c:forEach> </tbody> </table> </body> </html>
WEB-INF/jsp/userlist.jsp
11、启动项目,测试:
浏览器:
访问 'http://localhost:8080/user/list'
java框架之SpringMVC(1)-入门&整合MyBatis的更多相关文章
-
【java框架】SpringBoot(7) -- SpringBoot整合MyBatis
1.整合MyBatis操作 前面一篇提到了SpringBoot整合基础的数据源JDBC.Druid操作,实际项目中更常用的还是MyBatis框架,而SpringBoot整合MyBatis进行CRUD也 ...
-
JAVA 框架 / SSM / SSM SPRING+SPING MVC + MYBATIS 三大框架整合详细步骤
http://how2j.cn/k/ssm/ssm-tutorial/1137.html
-
【Java框架型项目从入门到装逼】第七节 - 学生管理系统项目搭建
本次的教程是打算用Spring,SpringMVC以及传统的jdbc技术来制作一个简单的增删改查项目,对用户信息进行增删改查,就这么简单. 1.新建项目 首先,打开eclipse,新建一个web项目. ...
-
java框架之springmvc
一.HelloWorld程序 (1)导包:四个spring 核心包(core.beans.context.expression).一个aop包.两个 web 包和一个logging 包: (2)配置 ...
-
java框架之Quartz-任务调度&;整合Spring
准备 介绍 定时任务,无论是互联网公司还是传统的软件行业都是必不可少的.Quartz,它是好多优秀的定时任务开源框架的基础,使用它,我们可以使用最简单基础的配置来轻松的使用定时任务. Quartz 是 ...
-
java框架之Spring(1)-入门
介绍 概述 Spring 是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring 是于 2003 年兴起的一个轻量级的 J ...
-
java框架之Spring(4)-Spring整合Hibernate和Struts2
准备 导包 Struts2 导入 Struts2 zip 包解压目录下 'apps/struts-blank.war' 中所有 jar 包,如下: asm-3.3.jar asm-commons-3. ...
-
java框架之SpringBoot(1)-入门
简介 Spring Boot 用来简化 Spring 应用开发,约定大于配置,去繁从简,just run 就能创建一个独立的.产品级别的应用. 背景: J2EE 笨重的开发.繁多的配置.低下的开发效率 ...
-
微信公众号开发java框架:wx4j(入门篇)
导航 入门 http://www.cnblogs.com/2333/p/6617819.html WxServlet介绍 MaterialUtils 素材工具类使用说明 http://www.cnbl ...
随机推荐
-
CSS3与页面布局学习总结(六)——CSS3新特性(阴影、动画、渐变、变形、伪元素等)
CSS3在CSS2.1的基础上新增加了许多属性,这里选择了较常用的一些功能与大家分享,帮助文档中有很详细的描述,可以在本文的示例中获得帮助文档. 一.阴影 1.1.文字阴影 text-shadow&l ...
-
[POJ1151]Atlantis
[POJ1151]Atlantis 试题描述 There are several ancient Greek texts that contain descriptions of the fabled ...
-
3,SFDC 管理员篇 - 区域划分
1,销售区域划分 Setup | Administrator | Manage Territory Territory Type : 帮助用户建立大的销售区域分类,分类顺序按照Priority进行显示 ...
-
node.js应用Redis数据库
node.js下使用Redis,首先: 1.有一台安装了Redis的服务器,当然,安装在本机也行 2.本机,也就是客户端,要装node.js 3.项目要安装nodejs_redis模块 注意第 3 点 ...
-
Eclipse下建立geoserver源码工程
摘要:本文详细阐述,如何基于geoserver源码构建eclipse工程文件,操作过程中除用到jdk.eclipse以外,还有git和maven,操作系统为windows8. 1安装Git 从(htt ...
-
ZooKeeper 应用场景
ZooKeeper典型应用场景一览 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新 ...
-
NET Core,你必须了解无处不在的“依赖注入”
NET Core,你必须了解无处不在的“依赖注入” ASP.NET Core的核心是通过一个Server和若干注册的Middleware构成的管道,不论是管道自身的构建,还是Server和Middle ...
-
JS ES6的变量的结构赋值
变量的结构赋值用户很多 1.交换变量的值 let x = 1; let y = 2; [x,y] = [y,x] 上面的代码交换变量x和变量y的值,这样的写法不仅简洁,易读,语义非常清晰 2.从函数返 ...
-
Golang实现杨辉三角
杨辉三角,也算是一个经典的题目了.就简单的说说. 写代码之前,先分析要做的东西的特点,找到规律,再把这个规律描述一下. 然后把这个描述翻译成编程语言,就可以说是编程了. 那么杨辉三角有什么特点? 首先 ...
-
Controller层aop
利用@Around通知修改Controller的返回值 自定义一个注解@OperationBtn 在切入点Controller上加上自定义注解 接下来就是重点了,AspectJ写切面类,对该Contr ...