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

    xiaoxiao2024-01-30  157

    本节书摘来自异步社区《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对象,显示用户名和积分信息。这样,就完成了实例所有的开发任务。

    最新回复(0)