Spring MVC 实践

标签 : Java与Web


Spring Web MVC

Spring-Web-MVC是一种基于请求驱动的轻量级Web-MVC设计模式框架, Spring MVC使用MVC架构思想, 对Web层进行职责解耦,使用请求-响应模型将数据业务视图进行分离, 简化开发.


MVC

MVC(模型-视图-控制器)是一个以设计界面应用程序为基础的架构模式,通过分离模型-视图-控制器在应用中的角色将业务逻辑从界面中解耦:

  • 模型负责封装应用数据和业务逻辑;
  • 视图仅负责展示数据;
  • 控制器负责接收用户请求,并调用模型(Service/Manger/DAO)来处理业务逻辑.模型可能会返回一些数据需要在视图层展示,控制器就需要整理模型数据并调用视图展示.

MVC模式的核心思想就是将业务逻辑从界面中分离出来,允许它们单独改变而不会相互影响.


Spring MVC

Spring MVC框架是基于Java语言的MVC架构具体实现.他的设计围绕DispatcherServlet展开,DispatcherServlet负责将请求分派给指定的Controller(Handler),通过可配置的HandlerMappingHandlAdapterControllerViewResolver处理请求拿到数据并填充对应的视图View:

组件 名称 描述
DispatcherServlet 调度器/前端控制器 DispatcherServlet前端控制器模式的具体实现(详细可参考Front Controller),他提供了整个Web应用的集中访问点,截获请求并将其分派给指定的Controller,相当于MVC中的C. 他的存在降低了组件之间的耦合性.
HandlerMapping 处理器映射器 HandlerMapping负责根据用户请求URI找到对应的ControllerInterceptor,并将它们封装在HandlerExecutionChain中返回给DispatcherServlet.
HandlAdapter 处理器适配器 Spring MVC通过HandlerAdapter执行Controller,这是适配器模式的应用,通过扩展适配器可以执行更多类型的Controller.
Controller 处理器 Controller(又称Handler)是继DispatcherServlet前端控制器之后的后端控制器,在DispatcherServlet的控制下Controller对具体的用户请求进行处理.
ViewResolver 视图解析器 负责将Model数据填充到View, 组合生成视图展示:他首先将逻辑视图名解析成物理视图名,生成View视图对象,最后对View进行渲染(填充数据). Spring默认提供了针对JSP/Velocity/PFD等视图的ViewResolver实现.

初识Spring MVC

需求: 用户列表查询.
  • 创建Maven Web项目
mvn archetype:generate  -DgroupId=com.fq.mvc
                        -DartifactId=MVC
                        -DarchetypeArtifactId=maven-archetype-webapp
                        -DinteractiveMode=false
                        -DarchetypeCatalog=internal
  • 依赖管理

    在pom.xml中添加SpringSpring MVCSevletVelocity依赖:
<!-- Spring -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- WEB -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servlet.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- Velocity -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>${velocity.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <version>${velocity.tools.version}</version>
</dependency>
  • 配置DispatcherServlet(web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <display-name>MVC</display-name>

    <!-- 配置SpringMVC -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- contextConfigLocation: 指定MVC配置文件位置 -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/mvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <!-- 拦截所有以.do结尾的URL -->
        <servlet-name>mvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>
  • 配置HandlerMapping/HandlerAdapter(mvc-servlet.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 处理器映射器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <!-- 处理器适配器 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
</beans>
  • Controller
/**
 * @author jifang
 * @since 16/3/15 下午4:40.
 */
public class UserController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<User> users = new ArrayList<>();
        users.add(new User(1, "翡青", new Date(), 1, "浙江-杭州"));
        users.add(new User(2, "小芳", new Date(), 2, "山东-青岛"));

        ModelAndView result = new ModelAndView();
        result.addObject("users", users);
        result.setViewName("users");
        return result;
    }
}
<!-- 装配 Controller -->
<bean name="/mvc/users.do" class="com.fq.mvc.controller.UserController"/>
  • 配置ViewResolver(mvc-servlet.xml)

    由于我们视图选用的是Velocity,因此配置VelocityViewResolver:
<!-- 配置视图解析器 -->
<bean id="viewResolver"
      class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    <property name="suffix" value=".vm"/>
    <property name="contentType" value="text/html;charset=UTF-8"/>
    <property name="dateToolAttribute" value="dateTool"/>
    <property name="numberToolAttribute" value="numberTool"/>
    <property name="exposeRequestAttributes" value="false"/>
    <property name="exposeSessionAttributes" value="true"/>
</bean>

<!-- 配置Velocity -->
<bean id="velocityConfigurer"
      class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
    <!-- 设置VM模板放置位置-->
    <property name="resourceLoaderPath" value="views"/>
    <!-- 防止乱码 -->
    <property name="velocityProperties">
        <props>
            <prop key="input.encoding">UTF-8</prop>
            <prop key="output.encoding">UTF-8</prop>
        </props>
    </property>
</bean>
  • View(/views/users.vm)
<html>
<head>
    <title>用户列表</title>
    <link rel="stylesheet" href="/css/main.css">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="global">
    <fieldset>
        <legend>用户列表:</legend>

        <table width="100%" border=0>
            <tr>
                <td>ID</td>
                <td>username</td>
                <td>birthday</td>
                <td>sex</td>
                <td>address</td>
            </tr>

            #foreach($user in $users)
                <tr>
                    <td>${user.id}</td>
                    <td>${user.name}</td>
                    <td>${user.birthday}</td>
                    #if(${user.sex} == 1)
                        <td>男</td>
                    #else
                        <td>女</td>
                    #end
                    <td>${user.address}</td>
                </tr>
            #end
        </table>
    </fieldset>
</div>
</body>
</html>

参考: main.css地址:OSChina


小结

  • 前端控制器

    Spring MVC DispatcherServleturl-pattern有两种配置方式:

    • *.do: 拦截所有以.do结尾的URI.
    • / : 拦截 所有 URI.
  • 处理器映射器

    BeanNameUrlHandlerMapping指定将Bean的Name作为URI映射; Spring还提供了SimpleUrlHandlerMapping将URI和Controller的id统一映射配置.

  • 处理器适配器

    SimpleControllerHandlerAdapter对所有实现了Controller接口的JavaBean适配.Spring还提供HttpRequestHandlerAdapter对所有实现了HttpRequestHandler接口的JavaBean适配.

  • 视图解析器

    前面使用Velocity作为视图展示,如果使用的是JSP的话, 需要配置另一种视图解析器InternalResourceViewResolver:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- "JstlView"表示可以在JSP页面JSTL标签库,所以需要在项目中添加JSTL依赖 -->
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/views"/>
    <property name="suffix" value=".jsp"/>
</bean>

注解初步

从2.5版本开始, Spring引入了注解开发:

  • 注解驱动(mvc-servlet.xml)

    • 从3.1版本开始,Spring MVC使用RequestMappingHandlerMapping 注解式处理器映射器 对标有@ResquestMapping的方法进行映射.
    • 从3.1版本开始,Spring MVC使用RequestMappingHandlerAdapter 注解式处理器适配器 对标记有@ResquestMapping的方法进行适配.
    • Spring MVC使用<mvc:annotation-driven/> 注解驱动自动加载RequestMappingHandlerMappingRequestMappingHandlerAdapter及其他相关配置. 因此在mvc-servlet.xml最简单有效的配置方式是使用<mvc:annotation-driven/>替代注解处理器和适配器:
<mvc:annotation-driven/>

尽管<mvc:annotation-driven/>很小, 但他具有足够的威力, 它注册了很多的特性, 包括对JSR-303校验、信息转换以及对域格式化的支持.

  • 组件扫描(mvc-servlet.xml):

    使用Spring的组件扫描来省去为每个Controller注册的麻烦:
<context:component-scan base-package="com.fq.mvc.controller"/>
  • 注解Controller
@Controller
@RequestMapping("/mvc")
public class UserController {

    @RequestMapping("/users.do")
    public ModelAndView users() {
        List<User> users = new ArrayList<>();
        users.add(new User(1, "翡青", new Date(), 1, "浙江-杭州"));
        users.add(new User(2, "小芳", new Date(), 2, "山东-青岛"));

        ModelAndView result = new ModelAndView();
        result.addObject("users", users);
        result.setViewName("users");
        return result;
    }
}
使用基于注解的`Controller`有以下优点:
    - 基于注解的`Controller`请求映射关系不存储在配置文件中,使用`@RequestMappeing`便可以对一个方法进行映射.
    - 一个`Controller`内可以有多个处理方法,可以响应多个动作,这样就允许将同一模块的多个操作写在同一个`Controller`里面,便于模块划分,且减少类的数量.

集成MyBatis

  • 通过Spring将各层整合:

    • 持久层DAO(MyBatis的Mapper)注册到Spring容器中.
    • 业务层Service/Manager从Spring容器中拿到DAO接口, 并注册Service服务.
    • 表现层Controller从Spring容器中拿到Service接口, 通过Spring MVC进行数据展示.

    需求: 展示所有用户数据(由于数据模型较简单, 因此从DAO到Controller只使用一个DO对象-User).
  • 依赖管理
<!-- Spring -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- AOP -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- WEB -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servlet.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- Velocity -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>${velocity.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <version>${velocity.tools.version}</version>
</dependency>

<!-- DB -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>${hikaricp.version}</version>
</dependency>

<!-- Spring DB支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>${mybatis.spring.version}</version>
</dependency>

<!-- log -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
</dependency>

集成MyBatis

  • mybatis-configuration.xml
<?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>
    <mappers>
        <mapper resource="mybatis/mapper/UserDAO.xml"/>
    </mappers>
</configuration>
  • domain: User
/**
 * @author jifang.
 * @since 2016/5/26 11:16.
 */
public class User {

    private int id;

    private String name;

    private Date birthday;

    private int sex;

    private String address;

    public User() {
    }

    public User(int id, String name, Date birthday, int sex, String address) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int 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 int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
  • UserDAO
/**
 * @author jifang
 * @since 16/3/20 下午5:47.
 */
public interface UserDAO {

    List<User> selectAllUsers();
}
<?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.fq.mvc.dao.UserDAO">

    <select id="selectAllUsers" resultType="com.fq.mvc.domain.User">
        SELECT *
        FROM user;
    </select>

</mapper>
  • applicationContext-datasource.xml(配置数据源)
<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:db.properties"/>

    <!-- 配置数据源 -->
    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
        <property name="driverClassName" value="${mysql.driver.class}"/>
        <property name="jdbcUrl" value="${mysql.url}"/>
        <property name="username" value="${mysql.user}"/>
        <property name="password" value="${mysql.password}"/>
        <property name="maximumPoolSize" value="5"/>
        <property name="maxLifetime" value="700000"/>
        <property name="idleTimeout" value="600000"/>
        <property name="connectionTimeout" value="10000"/>
        <property name="dataSourceProperties">
            <props>
                <prop key="dataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlDataSource</prop>
                <prop key="cachePrepStmts">true</prop>
                <prop key="prepStmtCacheSize">250</prop>
                <prop key="prepStmtCacheSqlLimit">2048</prop>
            </props>
        </property>
    </bean>

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg ref="hikariConfig"/>
    </bean>

    <!-- 配置SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis/mybatis-configuration.xml"/>
    </bean>

    <!-- 基于包扫描的mapper配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.fq.mvc.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  • db.properties
## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://host:port/db?characterEncoding=utf-8
mysql.user=user
mysql.password=password

集成Service

public interface UserService {

    List<User> getAllUsers();
}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO dao;

    @Override
    public List<User> getAllUsers() {
        return dao.selectAllUsers();
    }
}
  • applicationContext.xml(配置组件扫描与事务控制)
<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       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">

    <import resource="applicationContext-datasource.xml"/>

    <context:component-scan base-package="com.fq.mvc.service"/>

    <bean id="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="select*" timeout="-1" propagation="REQUIRED" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.fq.mvc.service.impl.UserServiceImpl.*(..))"/>
    </aop:config>

</beans>

集成MVC

  • Controller
@Controller
@RequestMapping("/mvc")
public class UserController {

    @Autowired
    private UserService service;

    @RequestMapping(value = "/user.do")
    public ModelAndView users() {
        List<User> users = service.getAllUsers();
        return new ModelAndView("users", "users", users);
    }
}
  • mvc-servlet.xml
<?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">

    <mvc:annotation-driven/>

    <context:component-scan base-package="com.fq.mvc.controller"/>

    <!-- 配置视图解析器 -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
        <property name="suffix" value=".vm"/>
        <property name="contentType" value="text/html;charset=UTF-8"/>
        <property name="dateToolAttribute" value="dateTool"/>
        <property name="numberToolAttribute" value="numberTool"/>
        <property name="exposeRequestAttributes" value="false"/>
        <property name="exposeSessionAttributes" value="true"/>
    </bean>

    <!-- 配置Velocity -->
    <bean id="velocityConfigurer"
          class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <!-- 设置VM模板放置位置-->
        <property name="resourceLoaderPath" value="views"/>
        <!-- 防止乱码 -->
        <property name="velocityProperties">
            <props>
                <prop key="input.encoding">UTF-8</prop>
                <prop key="output.encoding">UTF-8</prop>
            </props>
        </property>
    </bean>
</beans>
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <display-name>MVC</display-name>

    <!-- 加载Spring -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>

    <!-- 加载SpringMVC -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/mvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>
  • View视图同前

集成log

为了能够看到Spring MVC的运行时信息, 最好在应用中集成一种Log实现, 在此选用LogBack:

  • logback.xml
<configuration>

    <property name="log_root_dir" value="/data/logs/mvc/"/>
    <property name="log_pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{0} - %msg%n"/>

    <appender name="STD_OUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log_pattern}</pattern>
        </encoder>
    </appender>
    <appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_root_dir}/mvc-server.%d{YY-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log_pattern}</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="STD_OUT"/>
        <appender-ref ref="FILE_OUT"/>
    </root>

</configuration>

注解开发

需求: 实现User添加.
  • add_user.vm
<html>
<head>
    <title>用户添加</title>
    <link rel="stylesheet" href="/css/main.css">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="global">
    <fieldset>
        <legend>Add a User</legend>
        <form action="/mvc/add_user.do" method="post">
            <p>
                <label for="name">username: </label>
                <input type="text" name="name" tabindex="1">
            </p>

            <p>
                <label for="birthday">birthday: </label>
                <input type="text" name="birthday" tabindex="2"/>
            </p>

            <p>
                <label for="sex">sex: </label>
                <select name="sex" tabindex="3">
                    <option value="1">男</option>
                    <option value="2">女</option>
                </select>
            </p>
            <p>
                <label form="address">address: </label>
                <input type="text" name="address" tabindex="4">
            </p>
            <p id="buttons">
                <input type="reset" tabindex="5">
                <input type="submit" tabindex="6" value="Add User">
            </p>
        </form>
    </fieldset>
</div>
</body>
</html>
  • Controller
@RequestMapping("/add_user.do")
public String addUser(User user){
    service.addUser(user);
    return "redirect: users.do";
}
  • Service/DAO实现简单,不再赘述.

请求参数解析

在传统的Servlet编程中, 可以使用HttpServletRequestgetParameter()方法来获取请求参数值, 而在Spring MVC中, 它会调用请求参数解析组件将客户端传过来的String字符串解析为指定的Java对象并传递给Controller, 这个过程称为请求参数解析.

Spring MVC默认提供了很多参数解析组件, 因此Controller的请求处理方法默认就可以接收很多不同类型:

JavaEE JavaSE Spring
ServletRequest / HttpServletRequest InputStream / Reader WebRequest / NativeWebRequest
ServletResponse / HttpServletResponse OutputStream / Writer Model / ModelMap
HttpSession 命令或表单对象 (如String / Integer / User) RedirectAttributes
HttpEntity<?> Array / List / Map Errors / BindingResult
@PathVariable / @MatrixVariable注解的对象 @RequestParam / @RequestHeader / @RequestBody / @RequestPart
Principal / Locale SessionStatus
UriComponentsBuilder

注:

1. 如果Controller形参名与URI中name不一致, 可使用@RequestParam注解对其进行修饰.

2. Spring MVC还支持路径变量映射(路径变量类似于请求参数, 但没有key部分, 只是一个值, 详细可参考博客Spring MVC URL 路径映射).


ModelAttribute

Spring MVC会在每次调用请求处理方法时都创建一个Model对象, 若打算使用该实例, 则可以在方法的形参添加一个Model形参. 其实还可以使用@ModelAttribute注解来访问Model实例: 使用@ModelAttribute注解标注参数或方法,该方法会将其输入的或创建的参数对象添加到Model对象中:

  • @ModelAttribute标注形参
@RequestMapping("/check_id.do")
public String checkId(@ModelAttribute("id") String id, Model model){
    return "check_id";
}

String实例将以id做key添加到Model对象中(如果key未命名,则默认使用类型的名称(如string)做key).

  • @ModelAttribute标注方法

    @ModelAttribute的第二个用途是标注一个非请求处理方法: 被@ModelAttribute标注的方法会在每次调用Controller的请求处理方法时调用.该方法可以返回对象或void:

如果返回对象, 该对象会自动添加到Model中:

@ModelAttribute
private List<User> addUser() {
    return service.getAllUsers();
}

若返回void, 则必须添加一个Model类型参数, 自行将实例添加到Model中:

@ModelAttribute
private void addUser(Model model) {
    model.addAttribute("users", service.getAllUsers());
}

重定向与Flash属性

使用重定向的一个重要场景是避免用户重新加载页面再次调用相同动作. 比如add_user.do: 当用户提交表单时会将用户信息插入数据库. 但如果在提交表单后重新加载页面, add_user.do会被再次调用, 相同的用户可能会被再次添加.为了避免这种情况, 最好的办法是在提交表单后将用户重定向到一个不同的页面,这个页面任意重新加载都没有副作用.

但使用重定向的一个不便之处在于: 无法轻松的给目标页面传值. 如果采用转发, 可属性添加到Model, 使得目标视图可以轻松访问. 但用户重定向需要经过客户端, 所以Model的一切内容都会在重定向中丢失. 幸运的是, 在Spring 3.1之后,可以通过Flash属性提供重定向传值的方法:

要使用Flash属性, 必须要有<annotation-driven/>注解驱动支持, 然后在方法上添加一个新的参数类型RedirectAttributes:

@RequestMapping("/add_user.do")
public String addUser(User user, RedirectAttributes attributes){
    service.addUser(user);
    attributes.addAttribute("message", "new user has been saved to DB.");
    return "redirect: /views/message.vm";
}

Controller返回值

应用了@Controller@RequestMapping注解之后,Controller不再需要implements特定的接口, 因此Controller的返回值也变得多种多样:

返回值 描述
ModelAndView 包含视图名与Model数据
Model
Map 包含模型的属性
View
String 代表逻辑视图名
void 可以在参数中传入request/response完成对参数的解析, 对客户端的响应
HttpEntity/ResponseEntity 提供对Servlet的访问, 以响应HTTP头部和内容
Callable
DeferredResult
其他任意类型 常与响应JSON/XML结合

Spring MVC 实践 - Base的更多相关文章

  1. Spring MVC 实践 - Component

    Spring MVC 实践 标签 : Java与Web Converter Spring MVC的数据绑定并非没有任何限制, 有案例表明: Spring在如何正确绑定数据方面是杂乱无章的. 比如: S ...

  2. Spring MVC实践

    MVC 设计概述 在早期 Java Web 的开发中,统一把显示层.控制层.数据层的操作全部交给 JSP 或者 JavaBean 来进行处理,我们称之为 Model1: 出现的弊端: JSP 和 Ja ...

  3. Spring MVC 实践笔记

    1.了解 Maven 的用法:http://spring.io/guides/gs/maven/ .这篇英文非常详细的演示了 Maven 的用法,在命令行下执行.注意,运行Maven的时候,Maven ...

  4. IntelliJ IDEA:Getting Started with Spring MVC, Hibernate and JSON实践

    原文:IntelliJ IDEA:Getting Started with Spring MVC, Hibernate and JSON实践 最近把编辑器换成IntelliJ IDEA,主要是Ecli ...

  5. JSR-303 Bean Validation 介绍及 Spring MVC 服务端参数验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用很多时候同样的数据验证逻辑会出现在不同的层,这样 ...

  6. 搭建Spring4+Spring MVC web工程的最佳实践

    Spring是个非常非常非常优秀的java框架,主要是用它的IOC容器帮我们依赖注入和管理一些程序中的Bean组件,实现低耦合关联,最终提高系统可扩展性和可维护性,用它来辅助我们构建web工程将会感觉 ...

  7. JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

    任何时候,当要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情. 应用程序必须通过某种手段来确保输入参数在上下文来说是正确的. 分层的应用在很多时候,同样的数据验证逻辑会出现在不同的层, ...

  8. 基于 Spring MVC 的开源测试用例管理系统以及开发自测的实践

    早前公司领导提出让开发自测,测试么也做做开发.当然了,为了保证自测质量,测试用例仍需测试提供,所以为了提高开发自测的效率和质量,我们开发了捉虫记.捉虫记是一个完整的Spring MVC项目,现已开源, ...

  9. Spring MVC基本配置和实践(一)

    一.Spring MVC介绍 1. Spring MVC是什么? The Spring Web MVC framework和Struts2都属于表现层的框架,它是Spring框架的一部分,我们可以从S ...

随机推荐

  1. vue报错 Do not use built-in or reserved HTML elements as component id:header

    组件,不能和html标签重复 header组件,h5新标签重复 Do not use built-in or reserved HTML elements as component id:header ...

  2. [LeetCode] Split Array into Consecutive Subsequences 将数组分割成连续子序列

    You are given an integer array sorted in ascending order (may contain duplicates), you need to split ...

  3. PHPCMS v9.6.0 wap模块 SQL注入

    调试这个漏洞的时候踩了个坑,影响的版本是php5.4以后. 由于漏洞是由parse_str()函数引起的,但是这个函数在gpc开启的时候(也就是php5.4以下)会对单引号进行过滤\'  . 看这里: ...

  4. [HNOI 2014]江南乐

    Description 题库链接 给你指定一个数 \(f\) ,并给你 \(T\) 组游戏,每组有 \(n\) 堆石子, \(A,B\) 两人轮流对石子进行操作,每次你可以选择其中任意一堆数量不小于 ...

  5. [Codeforces 864B]Polycarp and Letters

    Description Polycarp loves lowercase letters and dislikes uppercase ones. Once he got a string s con ...

  6. [Vijos 2024]无向图最短路径

    Description 无向图最短路径问题,是图论中最经典也是最基础的问题之一.本题我们考虑一个有 $n$ 个结点的无向图 $G$.$G$ 是简单完全图,也就是说 $G$ 中没有自环,也没有重边,但任 ...

  7. 计蒜客NOIP模拟赛(3)D2T1 小区划分

    一条街道的两侧各连续坐落着 N 座单元楼.现在要为这些单元楼划分居民校区. 规则如下: 每个小区只能由同一侧连续的若干座单元楼组成.且两侧都恰有 K 个小区(每个小区至少有一栋楼). 两侧的小区划分规 ...

  8. Evensgn 的债务

    问题 A: Evensgn 的债务 大致题意:a欠b5元,b欠c5元,那么最小债务总额为a欠c5元,给你关系,求最小债务总额! 不想说话...一句超级大水题,我居然没读懂!!差点想到网络流了...其实 ...

  9. bzoj 3672: [Noi2014]购票

    Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的 ...

  10. bzoj 3631: [JLOI2014]松鼠的新家

    Description 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在"树&q ...