《Spring 3.0就这么简单》——1.6 展现层-阿里云开发者社区

开发者社区> 开发与运维> 正文

《Spring 3.0就这么简单》——1.6 展现层

简介: 业务层和持久层的开发任务已经完成,该是为程序提供界面的时候了。Struts MVC框架由于抢尽天时地利,成为当下最流行的展现层框架。但也有很多人认为Spring MVC相比较于Struts更简单、更强大、更优雅。

本节书摘来自异步社区《Spring 3.0就这么简单》一书中的第1章,第1.6节,作者: 陈雄华 , 林开雄著,更多章节内容可以访问云栖社区“异步社区”公众号查看

1.6 展现层

业务层和持久层的开发任务已经完成,该是为程序提供界面的时候了。Struts MVC框架由于抢尽天时地利,成为当下最流行的展现层框架。但也有很多人认为Spring MVC相比较于Struts更简单、更强大、更优雅。此外,由于Spring MVC出自于Spring之手,因此和Spring容器没有任何不兼容性,显得天衣无缝。

Spring 1.5新增了基于注解的MVC,而且Spring 3.1还提供了REST风格的MVC,Spring MVC已经变得轻便、强大、易用。我们将会在本书的第8章中学习Spring MVC的详细内容。

1.6.1 配置Spring MVC框架
首先需要对web.xml文件进行配置,以便Web容器启动时能够自动启动Spring容器,如代码清单1-13所示。

代码清单1-13 自动启动Spring容器的配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="1.5" 
  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_2_5.xsd">

<!--①从类路径下加载Spring配置文件,classpath关键字特指在类路径下加载_-->
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
   classpath:applicationContext.xml
      </param-value>
  </context-param>
_<!--②负责启动Spring容器的监听器,它将引用①处的上下文参数获得Spring配置文件地址-->_
  <listener>
   <listener-class>
    org.springframework.web.context.ContextLoaderListener
   </listener-class>
  </listener>
     …
</web-app>

首先,通过Web容器上下文参数指定Spring配置文件的地址,如①所示。多个配置文件可用逗号或空格分隔,建议采用逗号分隔的方式。在②处指定Spring所提供的ContextLoaderListener的Web容器监听器,该监听器在Web容器启动时自动运行,它会根据contextConfigLocation Web容器参数获取Spring配置文件,并启动Spring容器。注意需要将log4J.propertis日志配置文件放置在类路径下,以便日志引擎自动生效。

接下来,需要配置Spring MVC相关的信息,Spring MVC像Struts一样,也通过一个Servlet截获URL请求,然后再进行相关的处理,如代码清单1-14所示。

代码清单1-14 Spring MVC地址映射

…
<!-- Spring MVC的主控Servlet -->
<servlet> ①
  <servlet-name>viewspace</servlet-name>
   <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
   </servlet-class>
   <load-on-startup>2</load-on-startup>
  </servlet>
<!-- Spring MVC处理的URL -->
  <servlet-mapping>② 
   <servlet-name>viewspace</servlet-name>
   <url-pattern>*.html</url-pattern>
</servlet-mapping>

在①处声明了一个Servlet,Spring MVC也拥有一个Spring配置文件(稍后会涉及),该配置文件的文件名和此处定义的Servlet名有一个契约:即采用-servlet.xml的形式。在这里,因为Servlet名为viewspace,所以在/WEB-INF目录下必须提供一个viewspace- servlet.xml的Spring MVC配置文件,但这个配置文件无须通过web.xml的contextConfigLocation上下文参数进行声明,因为Spring MVC的Servlet会自动将viewspace -servlet.xml和Spring其他的配置文件进行拼装。

在②处对这个Servlet的URL路径映射进行定义,在这里让所有以.html为后缀的URL都能被viewspace Servlet截获,进而转由Spring MVC框架进行处理。我们知道,在Struts框架中,一般将URL后缀配置为.do,在Webwork中一般配置为.action,其实,框架本身和URL模式没有任何关系,用户大可使用喜欢的任何后缀。使用.html后缀,一方面,用户不能通过URL直接知道开发者采用了何种服务端技术;另一方面,.html是静态网页的后缀,可以骗过搜索引擎,增加被收录的概率,所以推荐采用这种后缀。对于那些真正的无须任何动态处理的静态网页,则可以使用.htm后缀加以区分,以避免被框架截获。

请求被Spring MVC截获后,首先根据请求的URL查找到目标的处理控制器,并将请求参数封装成一个“命令”对象一起传给控制器处理,控制器调用Spring容器中的业务Bean完成业务处理工作并返回结果视图。

1.6.2 处理登录请求
POJO控制器类
首先要编写的是LoginController,它负责处理登录请求,完成登录业务,并根据登录成功与否转向欢迎页面或失败页面,如代码清单1-15所示。

代码清单1-15 LoginController.java

package com.smart.web;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.smart.domain.User;
import com.smart.service.UserService;
@Controller①
@RequestMapping(value = "/admin"
public class LoginController{

  @Autowired
  private UserService userService;


@RequestMapping(value = "/login.html")②
  public String loginPage(){
   return "login";
  }


@RequestMapping(value = "/loginCheck.html")③
  public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){
   boolean isValidUser = 
        userService.hasMatchUser(loginCommand.getUserName(),
                               loginCommand.getPassword());
   if (!isValidUser) {
    return new ModelAndView("login", "error", "用户名或密码错误。");
   } else {
    User user = userService.findUserByUserName(loginCommand
      .getUserName());
    user.setLastIp(request.getRemoteAddr());
    user.setLastVisit(new Date());
    userService.loginSuccess(user);
    request.getSession().setAttribute("user", user);
    return new ModelAndView("main");
   }
  }
}

在①处通过Spring MVC的@Controller注解可以将任何一个POJO的类标注为Spring MVC的控制器,处理HTTP的请求。当然标注了@Controller的类首先会是一个Bean,所以可以使用@Autowired进行Bean的注入。

一个控制器可以拥有多个对应不同HTTP请求路径的处理方法,通过@RequestMapping指定方法如何映射请求路径,如②和③所示。

请求的参数会根据参数名称默认契约自动绑定到响应方法的入参中,在③处的loginCheck(HttpServletRequest request,LoginCommand loginCommand)方法中,请求参数会按名称匹配绑定到loginCommand的入参中。

请求响应方法可以返回一个ModelAndView,或直接返回一个字符串,Spring MVC会解析之并转向目标响应页面。

ModelAndView对象既包括了视图信息又包括了视图渲染所需的模型数据信息,在这里用户仅需要了解它代表一个视图就可以了,在后面的内容中,读者将了解到Spring MVC如何根据这个对象转向真正的页面。

前面使用到的LoginCommand对象是一个POJO,它没有继承于特定的父类或实现特定的接口。LoginCommand类仅包括用户/密码这两个属性(和请求的用户/密码参数名称一样),如代码清单1-16所示。

代码清单1-16 LoginCommand

package com.smart.web;
public class LoginCommand {
  private String userName;
  private String password;
  //省略get/setter方法
}

Spring MVC配置文件
编写好LoginCommand后,需要在viewspace-servlet.xml中声明该控制器,扫描Web路径,指定Spring MVC的视图解析器,如代码清单1-17所示。

代码清单1-17 viewspace-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:p="http://www.springframework. org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<!--①扫描web包,应用Spring的注解 -->
  <context:component-scan base-package="com.smart.web"/>

<!--②配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
  <bean
   class="org.springframework.web.servlet.view.InternalResourceViewResolver"
   p:viewClass="org.springframework.web.servlet.view.JstlView" 
   p:prefix="/WEB-INF/jsp/"
   p:suffix=".jsp" />
</beans>

ModelAndView的解析配置
在代码清单1-15 的③处,控制器根据登录处理结果分别返回 ModelAndView ("login", "error", "用户名或密码错误。")和ModelAndView("main")。ModelAndView的第一个参数代表视图的逻辑名,第二个和第三个参数分别为数据模型名称和数据模型对象,数据模型对象将以数据模型名称为参数名放置到request的属性中。

Spring MVC如何将视图逻辑名解析为具体的视图页面呢?解决的思路也和上面的方法类似,需要在viewspace-servlet.xml中提供一个定义解析规则的Bean,如代码清单1-18所示。

代码清单1-18 viewspace-servlet.xml视图解析规则

…
<!--通过prefix指定在视图名前所添加的前缀,通过suffix指定在视图名后添加的后缀-->
<bean   class="org.springframework.web.servlet.view.InternalResourceViewResolver"
       p:viewClass="org.springframework.web.servlet.view.JstlView" 
       p:prefix="/WEB-INF/jsp/"
       p:suffix=".jsp" />

Spring MVC为视图名到具体视图的映射提供了许多可供选择的方法。在这里,使用了InternalResourceViewResolver,它通过为视图逻辑名添加前后缀的方式进行解析。如视图逻辑名“login”将解析为/WEB-INF/jsp/login.jsp;视图逻辑名“main”将解析为/WEB-INF/jsp/main.jsp。

1.6.3 JSP视图页面
景区网站登录模块共包括两个JSP页面,分别是登录页面login.jsp和管理主页面main.jsp,下面将完成这两个页面的开发工作。

登录页面login.jsp
登录页面login.jsp的代码如代码清单1-19所示。

代码清单1-19 login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
  <head>
   <title>景区网站登录</title>
  </head>
  <body>
   <c:if test="${!empty error}"> ①
             <font color="red"><c:out value="${error}" /></font>
   </c:if>        
   <form action="<c:url value="/ loginCheck.html "/>" method= "post">②
    用户名:
    <input type="text" name="userName">
    <br>
    密 码:
    <input type="password" name="password">
    <br>
    <input type="submit" value="登录" />
    <input type="reset" value="重置" />
   </form>
  </body>
</html>

login.jsp页面既作为登录页面又作为登录失败后的响应页面。因此在 ①处使用JSTL标签对登录错误返回的信息进行处理。JSTL标签中引用了error变量,这个变量正是LoginController中返回的ModelAndView("login", "error", "用户名或密码错误。") 对象所声明的error参数。

login.jsp的登录表单提交到/loginController.html,如②所示。的JSTL标签会在URL前自动加上应用程序部署根目录,假设应用部署在网站的viewspace目录下,标签将输出/viewspace/loginController.html。通过标签很好地解决了开发和应用部署目录不一致的问题。

由于 login.jsp 放置在 WEB-INF/jsp 目录下,无法直接通过 URL 进行调用,它由LoginController 控制类中标注了@RequestMapping(value = "/login.html")的loginPage()进行转发,见代码清单1-15。

景区管理主页面main.jsp
登录成功的欢迎页面很简单,仅使用JSTL标签显示一条欢迎信息即可,如代码清单1-20所示。

代码清单1-20 main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      <title>景区后台管理主页面</title>
  </head>
  <body>
      ${user.userName},欢迎您进入景区后台管理! ①
  </body>
</html>

①处访问Session域中的user对象,显示用户名和积分信息。这样,就完成了实例所有的开发任务。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章