springMVC笔记系列(19)——控制器实现详解(上)

本文通过一个课程查询的示例介绍了springMVC的工作原理,包括MVC模式的实现方式,控制器、视图和模型的配置过程,以及前端控制器dispatcherServlet的配置。

说明:本文章的内容转载至:https://my.oschina.net/happyBKs/blog/420310
如有侵权的地方,请联系本人,本人将会立即删除!

本文我们在前文基础上引入一个例子,边做边聊控制器。

我们想实现一个课程查询的功能。

首先大脑应该条件反射——如何定义MVC。

M:课程(包含章节)——Course(包含Chapter)

V:课程页面——course_overview.jsp

C:编写一个控制器接收springMVC前端控制器dispatcherServlet的请求转发——CourseController

另外,Service——一些业务逻辑调用——业务接口CourseService,业务实现CourseServiceImpl

然后,在思考如何在springMVC下组织上述各个部分。

C控制器是M、V、Service的中间桥梁,如何组织都是围绕着C展开的。

请求URL由springMVC前端控制器dispatcherServlet转发给控制器C——在web.xml配置dispatcherServlet,给出前端控制器配置文件路径

如何标记控制器C——配置前端控制器配置文件,定义搜索包范围空间、激活注解;在类定义的地方为类添加控制器注解

如何将URL请求与控制器之间建立映射——RequestMapping注解控制器类和方法

控制器C调用Service业务逻辑——在控制器类相应方法中调用Service接口方法;Service被控制器C所关联,控制器C中的Service属性引用需要用spring的依赖注入的方式完成初始化,所以要配置spring容器(一是要在web.xml配置spring,指明spring容器配置文件路径;二是在spring容器配置文件中配置好这些bean,本文的示例采用注解方法)

控制器C与视图V建立映射——前端控制器配置文件配置ViewResolver(定义前缀和后缀);控制器类的对应的接收请求的方法返回字符串;拼接出webapp下的资源路径字符串。

以上我通过两两关系描述了几部分是如何组织起来的,各种配置分别解决哪一个两两关系。好,我们直接进入项目来看。

首先是定义M、V、C、Service几个部分:
这里写图片描述

M部分:

Course:

package com.happyBKs.model;

import java.util.List;

public class Course {

    // 课程Id
    private Integer courseId;
    // 课程名称
    private String title;
    // 图片路径
    private String imgPath;
    // 学习人数
    private Integer learningNum;
    // 课程时长
    private Long duration;
    // 课程难度
    private Integer level;
    // 课程难度描述
    private String levelDesc;
    // 课程介绍
    private String descr;
    // 课程提纲
    private List chapterList;

    public Integer getCourseId() {
        return courseId;
    }

    public void setCourseId(Integer courseId) {
        this.courseId = courseId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Integer getLearningNum() {
        return learningNum;
    }

    public void setLearningNum(Integer learningNum) {
        this.learningNum = learningNum;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        this.imgPath = imgPath;
    }

    public List getChapterList() {
        return chapterList;
    }

    public void setChapterList(List chapterList) {
        this.chapterList = chapterList;
    }

    public Long getDuration() {
        return duration;
    }

    public void setDuration(Long duration) {
        this.duration = duration;
    }

    public String getDescr() {
        return descr;
    }

    public void setDescr(String descr) {
        this.descr = descr;
    }

    public String getLevelDesc() {
        return levelDesc;
    }

    public void setLevelDesc(String levelDesc) {
        this.levelDesc = levelDesc;
    }

}

Chapter:

package com.happyBKs.model;


public class Chapter {

    private Integer id;
    private Integer courseId;
    private Integer order;
    private String title;
    private String descr;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getCourseId() {
        return courseId;
    }

    public void setCourseId(Integer courseId) {
        this.courseId = courseId;
    }

    public Integer getOrder() {
        return order;
    }

    public void setOrder(Integer order) {
        this.order = order;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }


    public String getDescr() {
        return descr;
    }

    public void setDescr(String descr) {
        this.descr = descr;
    }




}

V:

\src\main\webapp\WEB-INF\jsps\course_overview.jsp

<%@ page language="java" 
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>HappyBKs喜欢原先的osc博客页面</title>

<link rel="stylesheet"
    href="<%=request.getContextPath()%>/resources/css/main.css"
    type="text/css" />
</head>
<body>


    <div id="main">

        <div class="newcontainer" id="course_intro">
            <div class="course-title">${course.title}</div>
            <div class="course_info">
                <div class="course-embed l">
                    <div id="js-course-img" class="img-wrap">
                        <img width="600" height="340" alt=""
                            src="<%=request.getContextPath()%>/${course.imgPath}"
                            class="course_video" />
                    </div>
                    <div id="js-video-wrap" class="video" style="display: none">
                        <div class="video_box" id="js-video"></div>
                    </div>
                </div>
                <div class="course_state">
                    <ul>
                        <li><span>学习人数</span> <em>${course.learningNum }</em></li>
                        <li class="course_hour"><span>课程时长</span> <em
                            class="ft-adjust"><span>${course.duration }</span></em></li>
                        <li><span>课程难度</span> <em>${course.levelDesc }</em></li>
                    </ul>
                </div>

            </div>
            <div class="course_list">
                <div class="outline">
                    <h3 class="chapter_introduces">课程介绍</h3>
                    <div class="course_shortdecription">${course.descr}</div>

                    <h3 class="chapter_catalog">课程提纲</h3>
                    <ul id="couList">
                        <c:forEach items="${course.chapterList}" var="chapter">
                            <li class="clearfix open"><a href="#">
                                    <div class="openicon"></div>
                                    <div class="outline_list l">
                                        <!-- <em class="outline_zt"></em> -->
                                        <h5 class="outline_name">${chapter.title }</h5>
                                        <p class="outline_descr">${chapter.descr }</p>
                                    </div>
                            </a></li>
                        </c:forEach>
                    </ul>
                </div>

            </div>
        </div>

    </div>


</body>
</html>

值得注意的是,这里的href=”<%=request.getContextPath()%>/resources/css/main.css”这里url资源路径中的resources/**会在前端控制器配置文件中被配置映射到webapp项目资源路径。

这个后面会有详细的配置文件。

另外,jsp页面中的EL表达式中course,其名称也与控制器类中的相关参数和配置相对应。

Service:

业务模块部分都是讲接口调用和业务实现分类的。一个接口对应一个实现。
这里写图片描述
CourseService

package com.happyBKs.service;

import com.happyBKs.model.Course;
import org.springframework.stereotype.Service;

@Service
public interface CourseService {

    Course getCoursebyId(Integer courseId);
}

CourseServiceImpl

package com.happyBKs.service.impl;

import java.util.ArrayList;
import java.util.List;

import com.happyBKs.model.Chapter;
import com.happyBKs.model.Course;
import com.happyBKs.service.CourseService;
import org.springframework.stereotype.Service;



@Service("courseService")
public class CourseServiceImpl implements CourseService {

    public Course getCoursebyId(Integer courseId) {

        Course course = new Course();

        course.setCourseId(courseId);
        course.setTitle("springMVC笔记系列");
        course.setImgPath("resources/imgs/course-img.jpg");
        course.setLearningNum(12345);
        course.setLevel(2);
        course.setLevelDesc("中级");
        course.setDuration(7200l);
        course.setDescr("springMVC哈哈哈哈。");

        List<Chapter> chapterList = new ArrayList<Chapter>();

        warpChapterList(courseId,chapterList);

        course.setChapterList(chapterList);

        return course;
    }

    private void warpChapterList(Integer courseId,List<Chapter> chapterList) {
        Chapter chapter = new Chapter();
        chapter.setId(1);
        chapter.setCourseId(courseId);
        chapter.setOrder(1);
        chapter.setTitle("第1章 什么springMVC");
        chapter.setDescr("此处略去1个字");
        chapterList.add(chapter);

        chapter = new Chapter();
        chapter.setId(2);
        chapter.setCourseId(courseId);
        chapter.setOrder(2);
        chapter.setTitle("第2章 环境搭建");
        chapter.setDescr("此处略去2个字");
        chapterList.add(chapter);

        chapter = new Chapter();
        chapter.setId(3);
        chapter.setCourseId(courseId);
        chapter.setOrder(3);
        chapter.setTitle("第3章 前端控制器");
        chapter.setDescr("此处略去3个字");
        chapterList.add(chapter);

        chapter = new Chapter();
        chapter.setId(4);
        chapter.setCourseId(courseId);
        chapter.setOrder(4);
        chapter.setTitle("第4章 spring上下文环境配置");
        chapter.setDescr("此处略去4个字");
        chapterList.add(chapter);

        chapter = new Chapter();
        chapter.setId(5);
        chapter.setCourseId(courseId);
        chapter.setOrder(5);
        chapter.setTitle("第5章 示例");
        chapter.setDescr("此处略去5个字");
        chapterList.add(chapter);

    }




}

控制器C:

好好好,终归到了几个部分的中心环节了。CourseController类是控制器类,用@Controller注解。其类和方法注解@RequestMapping,实现请求url到控制器类的映射。当然控制器类的相应方法会返回相关字符串,与前端控制器配置文件\WEB-INF\configs\spring\mvc-dispatcher-servlet.xml中的项目视图资源映射,即这里映射到\WEB-INF\jsps\course_overview.jsp

CourseController

package com.happyBKs.controller;

import com.happyBKs.model.Course;
import com.happyBKs.service.CourseService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Created by happyBKs on 2016/6/15.
 */

@Controller
@RequestMapping("/courses")
// /courses/**
public class CourseController {


    //完成日志信息
    private static Logger log= LoggerFactory.getLogger(CourseController.class);

    private CourseService courseService;

    //使用spring容器管理里了对应的依赖关系
    @Autowired
    public void setCourseService(CourseService courseService) {
        this.courseService = courseService;
    }

    //提供完成一个业务的方法:根据课程ID查询课程内容。
    //本方法将处理 /courses/view?courseId=123 形式的URL
    @RequestMapping(value="/view", method= RequestMethod.GET)
    public String viewCourse(@RequestParam("courseId") Integer courseId,
                             Model model) {
        //日志输出,查看请求的courseId是不是我们的courseId
        log.debug("In viewCourse, courseId = {}", courseId);
        Course course = courseService.getCoursebyId(courseId);
        model.addAttribute(course);
        return "course_overview";
    }
}

在这个控制器类中,我们注入了一个Service。Service和控制器C之间存在依赖,我们使用spring依赖注入Service的实现类对象,使用的自动化注入成员变量引用某个具体的bean,用@Autowired注解,相应的被注解的业务实现类需要标注@Service,并且,spring配置文件的搜索包空间必须包含它。

另外,本控制器类中增加了一个日志工具,帮助我们记录下执行步骤和信息。因为需要用slf4j中的log相关类型。所以我们需要重新对pom.xml增加一些jar的依赖坐标。

pom.xml

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>testIdea.happBKs.mvc</groupId>
  <artifactId>springMVC</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>springMVC Maven Webapp</name>
  <url>http://maven.apache.org</url>


  <properties>
    <commons-lang.version>2.6</commons-lang.version>
    <slf4j.version>1.7.21</slf4j.version>
    <spring.version>4.2.6.RELEASE</spring.version>
    <jackson.version>2.7.4</jackson.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-framework-bom</artifactId>
        <version>${spring.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
    </dependency>

    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>${commons-lang.version}</version>
    </dependency>


    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
      <exclusions>
        <exclusion>
          <artifactId>slf4j-api</artifactId>
          <groupId>org.slf4j</groupId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
    </dependency>


    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>

    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>


  </dependencies>


  <build>
    <finalName>springMVC</finalName>
  </build>
</project>

还需要一log的配置文件:
这里写图片描述
注意,这个配置文件属于源代码的配置文件,与webapp的配置文件不同,所以这里所处的resouces文件夹其实不同的一个,特别注意!!


log4j.appender.Cons=org.apache.log4j.ConsoleAppender
log4j.appender.Cons.layout=org.apache.log4j.PatternLayout
log4j.appender.Cons.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


# Root logger set to DEBUG using the A2 appender defined above.
log4j.rootLogger=info, Cons 
log4j.additivity=false

#Application Logger+
log4j.logger.com.imooc.mvcdemo=debug, Cons
log4j.logger.org.springframework=debug, Cons
log4j.additivity.com=false

springMVC的配置文件\WEB-INF\configs\spring\mvc-dispatcher-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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用, 提供其相关的Spring MVC配置 -->

    <!-- 启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required
        @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注 -->
    <context:annotation-config />

    <!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
    <context:component-scan base-package="com.happyBKs.controller">
        <context:include-filter type="annotation"  expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping
        annotation-driven HandlerMapping -->

    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven />

    <!-- 静态资源处理, css, js, imgs -->
    <mvc:resources mapping="/resources/**" location="/resources/" />


    <!-- 配置ViewResolver。 可以用多个ViewResolver。 使用order属性排序。 InternalResourceViewResolver放在最后。 -->
    <!--<bean-->
    <!--class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">-->
    <!--<property name="order" value="1" />-->
    <!--<property name="mediaTypes">-->
    <!--<map>-->
    <!--<entry key="json" value="application/json" />-->
    <!--<entry key="xml" value="application/xml" />-->
    <!--<entry key="htm" value="text/html" />-->
    <!--</map>-->
    <!--</property>-->

    <!--<property name="defaultViews">-->
    <!--<list>-->
    <!--&lt;!&ndash; JSON View &ndash;&gt;-->
    <!--<bean-->
    <!--class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">-->
    <!--</bean>-->
    <!--</list>-->
    <!--</property>-->
    <!--<property name="ignoreAcceptHeader" value="true" />-->
    <!--</bean>-->

    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsps/" />
        <property name="suffix" value=".jsp" />
    </bean>


    <!--200*1024*1024即200M resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常 -->
    <!--<bean id="multipartResolver"-->
    <!--class="org.springframework.web.multipart.commons.CommonsMultipartResolver">-->
    <!--<property name="maxUploadSize" value="209715200" />-->
    <!--<property name="defaultEncoding" value="UTF-8" />-->
    <!--<property name="resolveLazily" value="true" />-->
    <!--</bean>-->

</beans>

spring容器配置文件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:mvc="http://www.springframework.org/schema/mvc"
    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/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:annotation-config />

    <context:component-scan base-package="com.happyBKs">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
</beans>

这里写图片描述

另外需要一些css文件和js文件,这个不细说。
这里写图片描述
最后请求的结果如下:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值