《Spring》实战——5.SpringMVC

Day_04

1. 早期的Web架构

在早期 Java Web 的开发中,统一把显示层、控制层、数据层的操作全部交给 JSP 或者 JavaBean 来进行处理,我们称之为 Model1:

出现的弊端:

  • JSP和JavaBean之间严重耦合,Java代码和HTML代码也耦合在了一起;
  • 要求开发者不仅要掌握Java,还要有高超的前端水平;

正因为上面的种种弊端,所以很快这种方式就被 Servlet + JSP + Java Bean 所替代了,早期的 MVC 模型(Model2)就像下图这样:

首先用户的请求会到达 Servlet,然后根据请求调用相应的 Java Bean,并把所有的显示结果交给JSP去完成,这样的模式我们就称为MVC模式。

MVC

  • M代表模型(Model)

模型是什么呢?模型就是数据,就是dao,bean;

  • V代表视图(View)

视图是什么呢?就是网页,JSP,用来展示模型中的数据;

  • C代表控制器(controller)

控制器是什么?控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet扮演的就是这样的角色。

2. Spring MVC架构

为解决持久层中一直未处理好的数据库事务的编程,又为了迎合NoSQL的强势崛起,SpringMVC给出了方案:

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

*3. 跟踪请求?

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

第一站:DispatcherServlet

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

(DispatcherServlet配置在web.xml里面)

定义在:<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

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

问题: 典型的应用程序中可能会有多个控制器,这些请求到底应该发给哪一个控制器呢?

所以 DispatcherServlet 会查询一个或多个处理器映射来确定请求的下一站在哪里,处理器映射会根据请求所携带的 URL 信息来进行决策,例如上面的例子中,我们通过配置 simpleUrlHandlerMapping 来将 /hello 地址交给 helloController 处理:

第三站:控制器Controller

一旦选择了合适的控制器, DispatcherServlet 会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:(Controller调用Service,而Service持有实体类,调DAO接口的方法操作数据库+复杂逻辑)

第四站:返回 DispatcherServlet

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

控制器所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名(逻辑视图名)。它接下来会将请求连同模型和视图名发送回DispatcherServlet。返回类型可以是.jsp、ModelAndView对象、String,Spring会解析到对应的视图;(即 第五站)

第五站:视图解析器

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

DispatcherServlet将会使用视图解析器(viewresolver)来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是JSP;上面的例子是直接绑定到了index.jsp视图;

第六站:视图

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

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

4. 一个DemoSpring整合Mybatis

(1)使用注解配置 Spring MVC

上面我们已经对 Spring MVC 有了一定的了解,并且通过 XML 配置的方式创建了第一个 Spring MVC 程序,我们来看看基于注解应该怎么完成上述程序的配置:

为 HelloController 添加注解:

 简单解释一下:

@Controller 注解:很明显,这个注解是用来声明控制器的,但实际上这个注解对 Spring MVC 本身的影响并不大。

@RequestMapping 注解:很显然,这就表示路径 /hello 会映射到该方法上;

@RequestMapping 注解细节:如果 @RequestMapping 作用在类上,那么就相当于是给该类所有配置的映射地址前加上了一个地址,例如:

则访问地址: localhost/wmyskxz/hello

(2)配置视图解析器

还记得我们 Spring MVC 的请求流程吗,视图解析器负责定位视图,它接受一个由 DispaterServlet 传递过来的逻辑视图名来匹配一个特定的视图。

需求:有一些页面我们不希望用户用户直接访问到(通过url访问页面资源),例如有重要数据的页面,例如有模型数据支撑的页面。

造成的问题:我们可以在【web】根目录下放置一个【test.jsp】模拟一个重要数据的页面,我们什么都不用做,重新启动服务器,网页中输入 localhost/test.jsp 就能够直接访问到了,这会造成数据泄露...另外我们可以直接输入 localhost/index.jsp 试试,根据我们上面的程序,这会是一个空白的页面,因为并没有获取到 ${message} 参数就直接访问了,这会影响用户体验;

解决方案:我们将我们的 JSP 文件配置在【WEB-INF】文件夹中的【page】文件夹下,【WEB-INF】是 Java Web 中默认的安全目录,是不允许用户直接访问的(也就是你说你通过 localhost/WEB-INF/ 这样的方式是永远访问不到的)

但是我们需要将这告诉给视图解析器,我们在 dispatcher-servlet.xml 文件中做如下配置:

这里配置了一个 Spring MVC 内置的一个视图解析器,该解析器是遵循着一种约定:会在视图名上添加前缀和后缀,进而确定一个 Web 应用中视图资源的物理路径的。让我们实际来看看效果:

第一步:修改 HelloController

我们将代码修改一下:

第二步:配置视图解析器

按照上述的配置,完成:

第三步:剪贴 index.jsp 文件

在【WEB-INF】文件夹下新建一个【page】文件夹,并将【index.jsp】文件剪贴到里面:

原理:

我们传入的逻辑视图名为 index ,再加上 “/WEB-INF/page/” 前缀和 “.jsp” 后缀,就能确定物理视图的路径了,这样我们以后就可以将所有的视图放入【page】文件夹下了!

注意:此时的配置仅是 dispatcher-servlet.xml 下的

关于Controller的方法的方法参数:(参数是从JSP传来的)

  1. 使用Http的request/response;
  2. 使用同名匹配规则,即post过来的form表单里面的值得name与方法参数名匹配;
  3. 使用模型传参,即定义一个实体,它的属性名与表单里面的值得name匹配;

示例:控制器接收请求数据

使用控制器接收参数往往是 Spring MVC 开发业务逻辑的第一步,为探索 Spring MVC 的传参方式,为此我们先来创建一个简单的表单用于提交数据:

(1)使用 Servlet 原生 API 实现

我们很容易知道,表单会提交到 /param 这个目录,我们先来使用 Servlet 原生的 API 来看看能不能获取到数据:

(2)使用同名匹配规则:

我们可以把方法定义的形参名字设置成和前台传入参数名一样的方法,来获取到数据(同名匹配规则):

问题: 这样又会和前台产生很强的耦合,这是我们不希望的

解决: 使用 @RequestParam("前台参数名") 来注入:

 (3)使用模型(对象)传参:

要求: 前台参数名字必须和模型中的字段名一样

让我们先来为我们的表单创建一个 User 模型:

中文乱码问题:

注意: 跟 Servlet 中的一样,该方法只对 POST 方法有效(因为是直接处理的 request)

我们可以通过配置 Spring MVC 字符编码过滤器来完成,在 web.xml 中添加:

关于Controller的方法的方法参数:(控制器回显数据)

通过上面,我们知道了怎么接受请求数据,并能解决 POST 乱码的问题,那么我们怎么回显数据呢?为此我们在【page】下创建一个【test2.jsp】:

同样的,3种方式:

  1. 原生的http request/response
  2. 使用 Spring MVC 所提供的 ModelAndView 对象
  3. 使用String返回

(1)使用 Servlet 原生 API 来实现

我们先来测试一下 Servlet 原生的 API 是否能完成这个任务:(setAttribute方法)

(2)使用 Spring MVC 所提供的 ModelAndView 对象(addObject方法)

(3)在 Spring MVC 中,我们通常都是使用这样的方式来绑定数据:

也可以拆成这样:

这样写就会在访问控制器方法 handleRequest() 时,会首先调用 model() 方法将 message 添加进页面参数中去,在视图中可以直接调用,但是这样写会导致该控制器所有的方法都会首先调用 model() 方法,但同样的也很方便,因为可以加入各种各样的数据。

关于Controller里面设置页面跳转?

前面不管是地址 /hello 跳转到 index.jsp 还是 /test 跳转到 test.jsp,这些都是服务端的跳转,也就是 request.getRequestDispatcher("地址").forward(request, response);

那我们如何进行客户端跳转呢?我们继续在 HelloController 中编写:

也可以这样用:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值