Spring MVC概述

1.MVC发展

1.1 Model1 (JSP + Java Bean)

Model1产生的弊端:

  • 如果有海量的jsp, 这些jsp互相调用, 那么最终它们之间的关系将及其复杂
  • JSP和Java Bean之间严重耦合, Java代码和HTML代码严重耦合
  • 前后端相互依赖太严重, 代码难以复用

1.2 Model2 (Servlet + JavaBean + JSP)

Model2出现了Servlet, 担当了一个控制器的角色, 这个时候才真正的算早期的MVC。

各个组件的含义以及MVC的组成

  • M - Model, 模型代表数据和处理数据的逻辑, 比如JavaBean, DAO等。比如定义数据Bean来表示需要显示给用户的结果, 定义业务Bean来封装业务逻辑,DAO
  • V - View, 就是视图层, 主要是网页, HTML, JSP等, 用来展示Model层中获取的数据
  • C - Controller, 就是控制层, 来控制整个流程, 决定哪个请求交给哪个JavaBean来处理, 然后最终的数据去渲染哪个View.

1.3 EJB(Enterprise Java bean)

这里不过多介绍了, 有关Spring和EJB的区别介绍 https://www.cnblogs.com/yjmyzz/p/3518386.html

 

1.4 Spring MVC

Spring MVC架构, 其核心思想:

  • 依赖注入(dependency injection)
  • 面向切面编程(AOP) 

与Model2不同的是, 传统的模型层被拆成了业务层+数据访问层(DAO, Data Access Object), 在 Service 下可以通过 Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL ,这样就能够满足异军突起的 NoSQL 的使用了,它可以大大提高互联网系统的性能。 

1.Spring MVC

1.新建项目

勾选download自动下载依赖的jar包

新项目模板中主要有这四个文件

 

2.配置详解

web.xml(加了很多注释和配置, 模板中不是这样)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
 5          version="4.0">
 6     <!--2、部署applicationContext的xml文件-->
 7     <!--如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,
 8     在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。
 9     如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
10     在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。
11     也可以这样applicationContext-*.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,
12     applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。
13     在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。-->
14     <context-param>
15         <param-name>contextConfigLocation</param-name>
16         <param-value>/WEB-INF/applicationContext.xml</param-value>
17     </context-param>
18     <!--1、在web.xml配置监听器ContextLoaderListener-->
19     <!--ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
20     在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。 -->
21     <listener>
22         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
23     </listener>
24 
25     <!--如果你的DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了。-->
26     <!--方案一:激活Tomcat的defaultServlet来处理静态文件-->
27     <!--要写在DispatcherServlet的前面, 让 defaultServlet先拦截请求,这样请求就不会进入Spring了,我想性能是最好的吧。-->
28     <servlet-mapping>
29         <servlet-name>default</servlet-name>
30         <url-pattern>*.css</url-pattern>
31     </servlet-mapping>
32 
33     <!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一个Servlet,,所以可以配置多个DispatcherServlet-->
34     <!--DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。-->
35     <servlet>
36         <!--在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean。-->
37         <servlet-name>dispatcher</servlet-name>
38         <!--指明了配置文件的文件名,不使用默认配置文件名,而使用dispatcher-servlet.xml配置文件。-->
39         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
40         <init-param>
41             <param-name>contextConfigLocation</param-name>
42             <!--其中<param-value>**.xml</param-value> 这里可以使用多种写法-->
43             <!--1、不写,使用默认值:/WEB-INF/<servlet-name>-servlet.xml-->
44             <!--2、<param-value>/WEB-INF/classes/dispatcher-servlet.xml</param-value>-->
45             <!--3、<param-value>classpath*:dispatcher-servlet.xml</param-value>-->
46             <param-value/>
47         </init-param>
48         <!--是启动顺序,让这个Servlet随Servletp容器一起启动。-->
49         <load-on-startup>1</load-on-startup>
50     </servlet>
51 
52     <servlet-mapping>
53         <!--这个Servlet的名字是dispatcher,可以有多个DispatcherServlet,是通过名字来区分的。每一个DispatcherServlet有自己的WebApplicationContext上下文对象。同时保存的ServletContext中和Request对象中.-->
54         <!--ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了:P,Spring把Bean放在这个容器中,在需要的时候,用getBean方法取出-->
55         <servlet-name>dispatcher</servlet-name>
56         <!--Servlet拦截匹配规则可以自已定义,当映射为@RequestMapping("/user/add")时,为例,拦截哪种URL合适?-->
57         <!--1、拦截*.do、*.htm, 例如:/user/add.do,这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。-->
58         <!--2、拦截/,例如:/user/add,可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。 -->
59         <url-pattern>/</url-pattern> <!--会拦截URL中带“/”的请求。-->
60     </servlet-mapping>
61 
62     <welcome-file-list><!--指定欢迎页面-->
63         <welcome-file>login.html</welcome-file>
64     </welcome-file-list>
65     <error-page> <!--当系统出现404错误,跳转到页面nopage.html-->
66         <error-code>404</error-code>
67         <location>/nopage.html</location>
68     </error-page>
69     <error-page> <!--当系统出现java.lang.NullPointerException,跳转到页面error.html-->
70         <exception-type>java.lang.NullPointerException</exception-type>
71         <location>/error.html</location>
72     </error-page>
73     <session-config><!--会话超时配置,单位分钟-->
74         <session-timeout>360</session-timeout>
75     </session-config>
76 
77 </web-app>

dispatcher-servlet.xml (xxxx - servlet.xml是对应web.xml中servlet-mapping配置的)

这是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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--这里指定是哪一种bean-->
    <bean id="simpleUrlHandlerMapping"
    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <!-- /hello 路径的请求交给 id 为 helloController 的控制器处理-->
                <prop key="/hello">helloController</prop>
            </props>
        </property>
    </bean>
    <!--这里的id对应上面prop中的内容-->
    <bean id="helloController" class="controller.HelloController"></bean>
</beans>

编写对应的HelloController

新建一个controller包,然后新建HelloController.java, 总之是跟上面配置的bean class是对应的

package controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
 * @author yangyiqing <yangyiqing@kuaishou.com>
 * Created on 2019-08-09
 */
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest,
            javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        return null;
    }
}

这个时候发现serlet找不到, 先右键, Add framework support添加maven支持, 然后看一下有哪些servlet的包可以添加

太多了, 先选第二个javax的再说.. 最终的java代码如下

package controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
 * @author yangyiqing <yangyiqing@kuaishou.com>
 * Created on 2019-08-09
 */
public class HelloController implements Controller {

    @Override
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest,
            javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        // 这里返回的是ModelAndView, Spring MVC 通过ModelAndView把Model和视图结合在一起
        ModelAndView mav = new ModelAndView("index.jsp");
        mav.addObject("message", "Hello Spring MVC"); // (String, Object)
        return mav;
    }
}

准备index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <h1>${message}</h1> //这里插入message
  $END$
  </body>
</html>

然后部署Tomcat以及相关环境

Tomcat服务器是java servlet和jsp的容器, 需要先去官网下载一下压缩包然后解压, 然后Configure里找一下目录就好。指定了Application server以后, 还需要在DeployMent里+一下artifact.

点击右上角启动, IDEA Tomcat启动报错Error:java: Compilation failed: internal java compiler error

解决方法

仍然有问题 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

修复了这个问题以后, 仍然不能启动, 进入Tomcat文件夹conf -> server.xml 中将8080都修改为80

然后点启动, 报错 [Address localhost:80 is already in use]

可以通过lsof -i tcp:port来找到对应的进程然后杀死, 不过最好还是把80换一个端口。

好了 不占用端口了 仍然不行

报错No Spring WebApplicationInitializer types detected on classpath

【暂时弄不好了】

 

跟踪 Spring MVC 的请求

每当用户在 Web 浏览器中点击链接或者提交表单的时候,请求就开始工作了,像是邮递员一样,从离开浏览器开始到获取响应返回,它会经历很多站点,在每一个站点都会留下一些信息同时也会带上其他信息,下图为 Spring MVC 的请求流程:

 

 

第一站:DispatcherServlet

从请求离开浏览器以后,第一站到达的就是 DispatcherServlet,看名字这是一个 Servlet,通过 J2EE 的学习,我们知道 Servlet 可以拦截并处理 HTTP 请求,DispatcherServlet 会拦截所有的请求,并且将这些请求发送给 Spring MVC 控制器。

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <!-- 拦截所有的请求 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

第二站:处理器映射(HandlerMapping)

所以 DispatcherServlet 会查询一个或多个处理器映射来确定请求的下一站在哪里,处理器映射会根据请求所携带的 URL 信息来进行决策,例如上面的例子中,我们通过配置 simpleUrlHandlerMapping 来将 /hello 地址交给 helloController 处理
<bean id="simpleUrlHandlerMapping"
      class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <!-- /hello 路径的请求交给 id 为 helloController 的控制器处理-->
            <prop key="/hello">helloController</prop>
        </props>
    </property>
</bean>
<bean id="helloController" class="controller.HelloController"></bean>

第三站:控制器

一旦选择了合适的控制器, DispatcherServlet 会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:

public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
    // 处理逻辑
    ....
}

第四站:返回 DispatcherServlet

当控制器在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是 HTML,所以,信息需要发送给一个视图(view),通常会是 JSP。

控制器所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名(逻辑视图名)。它接下来会将请求连同模型和视图名发送回 DispatcherServlet。

public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
    // 处理逻辑
    ....
    // 返回给 DispatcherServlet
    return mav;
}

第五站:视图解析器

这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。(实际上,它甚至不能确定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图。

DispatcherServlet 将会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是 JSP

上面的例子是直接绑定到了 index.jsp 视图

第六站:视图

既然 DispatcherServlet 已经知道由哪个视图渲染结果了,那请求的任务基本上也就完成了。

它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会通过响应对象传递给客户端。

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" isELIgnored="false"%>

<h1>${message}</h1>

 

 

posted @ 2019-08-09 10:32  batch-norm  阅读(282)  评论(0编辑  收藏  举报