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)
<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
第六站:视图
既然 DispatcherServlet 已经知道由哪个视图渲染结果了,那请求的任务基本上也就完成了。
它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会通过响应对象传递给客户端。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<h1>${message}</h1>