Java 22:Spring 5(Spring MVC 入门)

来源:互联网 发布:js定时器setinterval 编辑:程序博客网 时间:2024/06/05 08:12

Spring MVC的流程经历流程:

1、请求带着用户请求的信息,到达DispatcherServlet。

Spring MVC所有的请求都会通过一个前端控制器Servlet。前端控制器是常用的Web应用程序模式。DispatcherServlet的任务是将请求发送给Spring MVC控制器。控制器是一个用于处理请求的spring组件。

2、DispatcherServlet查询处理器映射器以确定将请求发送给哪一个控制器

DispatcherServlet会查询一个或多个处理器映射器(handler mapping)来确定下一站到哪里。处理器映射器会根据请求所携带的URL信息进行决策。

3、请求信息交由控制器处理

控制器处理用户的请求信息,通常需要将产生的信息返回给用户并在浏览器上显示。这些信息称为模型。仅仅把原始信息返回给用户是不够的,需要以用户友好的方式进行格式化,一般是HTML,所以信息需要发送给一个视图(view),通常是JSP。

4、控制器将模型及逻辑视图名返回给DispatcherServlet,查找视图解析器匹配为特定的视图实现。

这样控制器就不会和特定的视图耦合,传递的视图名并不直接表示某个JSP,甚至不确定是否是JSP,这个名称被用来查找真正的视图,DispatcherServlet通过查询视图解析器确定真正的视图。

5、视图将模型渲染输出,输出会通过响应对象传递给客户端


下面就是Spring MVC的使用。

首先是需要的JAR包:


这些大概是Spring基本使用中最常见的包了,一开始像往常一样:右键项目——build path——Add External JARs,将这些包导进去,但是实际运行的时候,说找不到类文件,这和之前在InteliJ里的现象有点像,后来改成不显示Add External JARs,而是直接将这些包拷贝到项目WEB-INF的lib文件夹里,等到它自动识别以后,Eclipse里每个包的上面会多一个小标志,这就真正能够识别到了。


必备的文件:

1、web.xml

2、spring mvc声明的xml

3、控制器的java文件

4、视图的jsp文件


1、web.xml:

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1"><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></web-app>
DispatcherServlet继承自HttpServlet,在web.xml中声明,这里给它起名叫dispather,默认情况下会在WEB-INF下找[<servlet-name>]-servlet.xml,这里也就是dispather-servlet.xml,来查看spring-mvc的更为具体的功能配置,比如视图解析器,也可以选择自定义路径和文件名:

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:META-INF/spring/springmvc-context.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

这样就会去src/MRTA-INF/spring目录下找名为springmvc-context.xml的文件

load-on-startup:

标记声明是否容器在启动的时候加载该servlet(实例化并调用init()方法),它的值是一个整数,表示其被载入的顺序。当其大于等于0,表示容器在应用启动时就加载这个servlet,当其小于0或没有指定,表示容器在该servlet被选择时才去加载。正数的值越小代表启动时优先级越高,允许同样的优先级出现(是优先级,不是顺序,也不是启动的延时)


servlet-mapping

在说明这个之前,先介绍一下servlet,感觉在各种框架的时代,一般很少需要直接接触servlet,但是也离不开servlet的支持。servlet是“服务器端小程序”,用于处理和响应客户的请求。Servlet是一个特殊的Java类,继承自HttpServlet。客户端通常有GET和POST两种请求方式,servlet相应地重写了doGet和dePsot方法。大部分情况下,servlet对于所有请求响应是一样的,只需要重写service()方法即可响应所有客户端请求。

servlet另有init()和destroy()方法用于初始化资源和回收资源。

servlet可以在以下情况创建实例:(1)客户第一次请求某个servlet时,大部分servlet都是如此(2)web应用启动时立即创建,比如这里DispatcherServlet

终于说到servlet-mapping,它的作用也就是将URL映射到某一个servlet,也就是选择某一个servlet来处理相应的URL。URL的映射不是一个完全确定的,而是根据一条条规则进行选择。

精确匹配::比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。 
最长路径匹配。例子:servletA的url-pattern为/test/*,而servletB的url-pattern为/test/a/*,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。 
扩展匹配,如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action 
如果前面三条规则都没有找到servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet。

总之,其实可以定义多个DispatcherServlet来处理不同的URL请求。


ContextLoaderListener

在看别人的例子时,有的包含这个配置,有的没有,查了一下,发现Springmvc中确实可以省略不用,它的作用是创建了一个WebApplicationContext,如果不使用该上下文就可以不配置。正常情况下,还是需要配置,因为spring IOC的两种实现,不常用的BeanFactory和常用的ApplicationContext。WebApplicationContext是ApplicationContext的一种高级实现。

同时,很多地方也提到了ContextLoaderListener和ApplicationContext的关系,说ContextLoaderListener的作用是自动装配ApplicationContext的配置信息。如果web.xml没有配置相关的ApplicationContext信息,那么默认的路径是“WEB-INF/applicationContext.xml”,如果要自定义路径,则要在web.xml中加入“contextConfigLocation”这个参数。

《Spring实战》里说,“我们希望DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射器,而ContextLoaderListener加载其他应用的bean,比如驱动应用后端的中间层和数据层组件”


web.xml的其他作用包括:配置欢迎页、servlet、filter、指定错误处理页面



2、dispatcher-servlet.xml

因为前面设置的DispatcherServlet的名字是dispatcher,所以相应的Spring MVC的配置要去dispatcher-servlet.xml里去找。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:context="http://www.springframework.org/schema/context"     xmlns:p="http://www.springframework.org/schema/p"    xmlns:mvc="http://www.springframework.org/schema/mvc"     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-4.0.xsd          http://www.springframework.org/schema/context          http://www.springframework.org/schema/context/spring-context-4.0.xsd          http://www.springframework.org/schema/mvc          http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">    <!-- 支持注解 -->    <mvc:annotation-driven />    <!-- 设置自动扫描的路径,用于自动注入bean 这里的路径与自己的项目目录对应 -->    <context:component-scan base-package="controller" />        <!-- 这两个类用来启动基于Spring MVC的注解功能,将控制器与方法映射加入到容器中 --><bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />    <!-- 这个类用于Spring MVC视图解析 --><bean id="viewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean></beans>

后面我们的控制器尽量采取注解的方式,并且把需要扫描的包含的bean的包配置进去。

HandlerMapping:处理器映射器,就和前面的servlet-mapping一样,将一个请求的URL指定到对应的controller上,Spring内置了很多处理器映射策略。首先需要将Handler注册到HandlerMapping中,然后根据规则从已注册的Handler中匹配对应的Handler,及Controller。也可以设置多个处理器映射器,DispatcherServlet根据优先级,依次询问HandlerMapping,直到获取一个可用的Handler为止。

HandlerAdapter:处理器适配器,定位到Handler以后,DispatcherServlet将得到的Handler告诉HandlerAdapter,HandlerAdapter根据请求去定位具体的方法。

可能是因为,DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter是默认的配置,这里把这两行去除,也不会对程序有影响。

ViewResolver:视图解析器,首先View接口表示一个响应给用户的视图,例如jsp、html等。ViewResolver接口定义了如何通过view名称来解析对应View实例的行为,该接口只有一个resolveViewName方法,通过viewName解析出View。常用的

InternalResourceViewResolver:就是在试图逻辑名前面加上prefix,后面加上suffix,注意如果prefix和suffix的配置顺序调换,可能发生路径错误

ResourceBundleViewResolver:将逻辑视图名和真实文件的映射关系放在配置文件中。
由于可以存在多个ViewResolver,所以resolver里有一个配置项order,表明其优先级。

我们代码里的配置,说明我们在WEB-INF下建立了一个名为jsp的文件夹,专门存放jsp文件


3、HelloController.java控制器代码

控制器相关的内容,在之前21章已经讲了一些,这里先放一个最简单的

@Controllerpublic class HelloController {@RequestMapping("")public String Home(Model model){return "home";}@RequestMapping("/a")public String Home2(Model model){return "home2";}}
我们在dispatcher-servlet.xml里设置的自动扫描的包是controller,因此,该类文件要放在名为controller的package里才能扫描到。

两个函数分别处理后缀为空和后缀为“/a”的请求,比如在我们的项目中,分别是:http://localhost:8321/mvclearn/和http://localhost:8321/mvclearn/a

分别访问home.jsp和home2.jsp


4、jsp

这里随便写一个jsp和controller里名称匹配即可,路径要和xml里一样,及前面配置的WEB-INF/jsp/home.jsp


5、返回model给view

前面为止已经是一个SpringMVC完成的简单例子了,但是controller仅提供了jsp的名称,没有给界面返回任何值。我们添加一个User类,类很简单,也就是name和id两个字段,并且将其声明为@Component,也就是Bean,然后再来看HelloController.java

HelloController.java

@Controllerpublic class HelloController {private User user;@Autowiredpublic HelloController(User user){this.user=user;}@RequestMapping("")public String home2(Model model){model.addAttribute("user", user);return "home2";}}
我们用自动注解的方式,装配了user进来,然后把它作为model的attribute的value,传递给view。

home2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><title>Spring MVC</title></head><body>姓名:${user.name}<br>id:${user.id} </body></html>
这里要注意,不论是User的public或是private字段,都可以由前端获取,但前提是他们都设置了对应的getter和setter方法。

addAttribute有多个重载函数,其中有只包含一个Object而没有String(键)的。Model的本质就是一个Map的键值对,当addAttribute不指定key的时候,他会根据值的对象类型来推断,比如我们改写:

@Controllerpublic class HelloController {private User a;@Autowiredpublic HelloController(User a){this.a=a;}@RequestMapping("")public String home2(Model model){model.addAttribute(a);return "home2";}}
我们只在model了添加了User类型的变量a,但是home2.jsp调用时还是一样的用${user.name},这里之所以将private User user改成了User a,是避免混淆,默认的键是根据类型User产生的小写user,而不是根据a,所以这个User叫“user”还是“a”,它的key都是“user”。

再次修改HelloController,把User不声明为bean,返回给前端一个List<User>

@Controllerpublic class HelloController {private List<User> users;public HelloController(){users=new ArrayList<>();}@RequestMapping("")public String home2(Model model){User a=new User("a",1);User b=new User("b",2);users.add(a);users.add(b);model.addAttribute("users",users);return "home2";}}

此时对应的jsp是:

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><title>Spring MVC</title></head><body><c:forEach items="${users}" var="user">姓名:<c:out value="${user.name}"/> --id:<c:out value="${user.id}"/><br/></c:forEach></body></html>

jsp里用到了jstl表达式,需要相应的JAR,并且需要使用前声明,<forEach>循环打印出User信息。这里传过来的是List<User>,但它本身不是bean,也不是一个类,Use中依然需要声明getter和setter,虽然这一次的User不是Bean,但只要有了getter、setter,依然能访问。

将model的键省略:

model.addAttribute(users);
根据users的类型List<User>,对应的key变成了“userList”。

更进一步可以省略视图名:

@RequestMapping("/home2")public List<User> home2(Model model){User a=new User("a",1);User b=new User("b",2);users.add(a);users.add(b);return users;}
model的键还是根据List<User>生成的对应userList,视图名则根据请求路径得到,前面我们的请求路径都是空,得到的就是“.jsp”,就出了问题,所以加上“/home2”,这样默认的视图名和前面一样还是home2.jsp


到这里相当于,已经把最开始说的Spring MVC的一整套流程走通了:

-web.xml配置了DispatcherServlet,拦截、分发一切请求

-controller上声明的RequestMapping接收相应的URL

-具体的方法来处理请求,返回model,指定展示的view

-最终以一个jsp呈现结果














原创粉丝点击