本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. SpringMVC的引言
为了使Spring可插入MVC架构,SpringFrameWork 在Spring基础上开发了SpringMVC框架 ,从而在使用Spring进行WEB开发时可以选择使用Spring的 SpringMVC框架作为WEB开发的控制器框架 。
# springmvc的引言
1. springmvc 由来 诞生
由来:基于spring框架基础之上开发的一个全新的框架 springmvc
作用:SpringMVC 可以作为Web开发时的控制器框架 用来替换现有项目中的Struts2控制器框架
目的:为了使现有项目中使用spring框架在mvc架构中存在自己的位置 因此开发了一个springmvc框架
MVC:编程步骤 三层编程
M:model模型 service+dao+eneity Jdbc|Mybatis
V:view视图 webapp 页面 jsp(动态页面)|html
C:Controller 控制器 action servlet|Struts2 ======> SpringMVC 控制器框架
2. SpringMVC 引言
SpringMVC 典型mvc框架 控制器框架 它是在spring基础之上进行二次开发 用来替换原有项目中struts2框架
3. 为什么是SpringMVC(优势)
1). Spring框架流行程度非常之高 90% ====> SpringMVC框架可以与Spring框架进行无缝整合 spring springmvc
2). SpringMVC 运行效率 高于struts2运行效率 action 创建一个新的Action SpringMVC默认单例处理请求
3). SpringMVC 推荐使用注解式 其注解式开发更高效更灵活
springmvc运行流程与struts2运行流程对比:
# springmvc与struts2运行流程对比
控制器做的三件事:
接收请求
调用业务对象
处理请求
struts2的执行流程:
首先要去接收请求,客户端得在浏览器上发起一个请求,它会优先被项目中web.xml中所配的
StrutsPrepareAndExecuteFitler给拦截掉,Fitler为什么要拦截这些请求呢,是因为
它要把这些请求拿到之后全部交给Struts2框架去处理请求,Fitler拿到这个请求之后,它会
去找在项目中配置的叫struts.xml的配置文件,在struts.xml的配置文件中,我们配置了
package <package name="" extends="" namespace="">,它做的第一件事会去解析
请求,那怎么解析这个请求呢,拦截到这个请求之后,我是不是就可以去解析你的url啊,在
url中取出对应的namespace,去找一个名字叫做namespace这样的一个package,找到这个
package之后再去解析url最后的这块内容,比如访问的是findAll,那我在这里去找action
标签名字叫findAll的<action name="findAll" class="com.baizhi.action.xxAction method="find">,
类和类中执行的方法都找到了,你是不是就可以进入你自己的控制器xxxAction了呀,
xxxAction
public String find..(){
1. 收集数据 用的是成员变量
2. 调用业务对象
3. 处理响应
return "ok";
}
到<action name="findAll" class="com.baizhi.action.xxAction method="find">这步的话其实它就可以
直接进入我们的action去对应去执行相应的方法,走到这个方法的执行之后,方法执行的时候做三件事:收集数据、
调用业务对象、处理响应,处理响应的时候最终返回了一个字符串,这个字符串呢它还会回到当前的这个请求的
<action name="findAll" class="com.baizhi.action.xxAction method="find"> (action)里面去找,
找一个<result name="ok">/index.jsp</result>,做对应的视图,那所对应的视图找到了其实整个请求就开始返回了,
我告诉Fitler说我当前这个请求请求的是xxAction下的find方法,这个find方法最终处理完业务之后返回的是/index.jsp,
那我们在响应回来的请求还要经过Fitler啊,最终Fitler给我们一个响应,响应给我们index.jsp,如果有数据的话展示我们的
数据,当然要展示这个数据在调用业务这块要把数据放入作用域中啊,
springmvc的运行流程:
springmvc作为一个控制器,它也要去处理请求,所以客户端同样得发起一个请求,其实这个请求它就是一个url,这个url
一定包含url:/user/findAll这样的一个路径,springmvc它作为一个控制器框架,它如果想要把客户端的这些请求都拿到,
拿到之后才能自己去处理这些请求,所有的控制器框架在处理的时候都是这样,它得先把所有的请求都拿到它才能去处理,所以
springmvc也需要我们在web.xml中配置这样的一个东西DispatacherServlet,通过这个类名字的最后你可以发现它是一个
Servlet,日后配置的时候得用Servlet便签,servletmapping有一个url-pattern/test代表日后只拦test请求,写一个
url-pattern/aa代表只拦aa,写一个url-pattern/user/findAll才能拦findAll这个请求啊,那问题来了,我日后总不能
一个请求我配一次DispatacherServlet吧,servletmapping我得配多少个啊,请求那么多,业务那么复杂,这是不是就没人
用了啊,所以这里注意,servlet我想让它拦到所有请求该怎么拦呢,这个url-pattern这我们不要写url-pattern /*,为什么
呢,因为根据servlet的机制你的第一级路径是不允许有*的,有*代表着其他的servlet执行不了了,servlet一级不能有*,但是
二级可以有*,url-pattern /aa/* 因为你这个aa啊你日后访问所有的aa开头的请求时都去让这个servlet去处理,那日后aa
会影响/login这些请求吗,它不会,但是如果我在这里配一个url-pattern /aa/* 也不行啊,我日后是不是基于aa的请求才能去..
有的同学说可以url-pattern /user/*, url-pattern /order/* 是不是也麻烦啊,所以这里请注意啊,如果想让我们的
DispatacherServlet去拦截到所有请求,这里面我们就得耍点小心思了,什么小心思呢,你看我们所有的路径都有 / 啊,所以
如果咱们想拦截到所有请求的话,那这个url-pattern这块索性就配一个url-pattern:/ 就完了,配 / 的目的就是为了拦截所有
请求,没有别的意思,就是想把所有请求拦到,拦到之后交给springmvc的DispatacherServlet处理,说白了也是交给springmvc
去处理。回过头来我们来看这个类DispatacherServlet,DispatacherServlet翻译过来叫转发吧,叫转发servlet,说白了
这个类是真正处理请求的类吗,不是,它要把拦截到的请求转发,转发给谁呢,struts2有属于struts2的配置文件,springmvc
也有属于springmvc的配置文件,springmvc的配置文件就是spring的配置文件,叫springmvc.xml,这里来解释一下,日后还有
new一个spring,你起个名字叫springmvc就行了,因为是一家公司的产品,一个团队开发的,所以springmvc的配置文件和spring
的配置文件一致,就是一个把名字命名为springmvc.xml,一个叫spring.xml,让它有一个区分就行了,你知道一个是springmvc,
一个是spring,这两个文件都是一样的,没有什么新颖的,因为是一家公司的嘛。好了,往下看,拦截到请求之后,之后要转发请求对吧,
我自己不处理,我就是为了拦截,拦截到之后谁处理呢,springmvc就出场了,这里面要找配置文件中的三个东西
1. RequestMappingHandlerMapping 处理器映射器 解析url
2. RequestMappingHandlerAdapter 处理器适配器 用来绑定参数
3. InternalResourceViewResolver
prefix 前缀 /
suffix 后缀 .jsp 解析结果:前缀+页面逻辑名+后缀=/ok.jsp
先来解释一下这三个是干嘛的,我们说我们的请求路径中,是不是一定包含着访问哪个控制器中的哪个方法对不对,所以日后拦截到
请求之后它肯定要去解析我们的请求啊,谁去解析呢,第一个组件就出场了RequestMappingHandlerMapping,这个组件呢叫
处理器映射器,处理器映射器它的核心作用是用来干嘛的呢,解析url,日后你到后台的哪个xxxController中的哪个方法,那就是说,
这个请求一经过RequestMappingHandlerMapping解析之后,它一定可以找到当前这个请求访问的是哪个xxxController中的哪个
方法,这个xxxController中的方法怎么写啊,springmvc为了减轻我们程序员的学习成本,原来struts2怎么写,它这里面还怎么
写,
xxxController{
public String find(){
// 1. 收集数据
// 2. 调用业务对象
// 3. 处理响应
return "ok"; // 页面逻辑名
}
}
第二个RequestMappingHandlerAdapter叫处理器适配器,适配器的作用是用来完成参数绑定,啥意思,你的请求有时候可能有参数,
有时候可能没参数,那日后你是不是要在方法里完成收集数据,完成收集数据的话我就得给你绑定参数,怎么去绑定,绑定给谁,给哪个
属性,怎么做类型转换,是由我们的第二大组件完成的,叫RequestMappingHandlerAdapter,它用来完成参数的绑定,那你看,请求
解析完了,参数也绑定完了,是不是就可以进入这个方法执行了啊,处理响应的时候同样返回了一个字符串,但是这个字符串在springmvc
中有一个别的名字了,叫页面逻辑名,为啥叫页面逻辑名呢,为啥叫页面逻辑名呢,当我们在这return这个"ok"时,这个控制器还会回到
配置文件中去找这样的一个东西,叫InternalResourceViewResolver这个翻译过来叫视图解释器,说白了就是用来解析视图的,怎么
解析呢,这个视图解析器我们在用的时候得给它注入两个东西,一个叫 prefix 前缀,还有一个叫suffix 后缀,前缀我们会习惯性的
给它注入一个 "/"" ,都是String类型的,后缀会习惯性的给它注入.jsp ,所以当这个控制器执行完成之后它会经过这个视图解析器
去解析,如果返回的是"ok",那它就是前缀+页面逻辑名+后缀就等价于 /ok.jsp ,也就是说大家日后在控制器里就不能随便写了,如果
想跳转到主页index,那你return就要return "index" , 这是不是就直接跳转到/index.jsp了,那如果你日后有个目录,那怎么
跳转呢,比如uesr下有个login,那你就得这样写 return "user/login"; user代表你的目录,代表你的文件夹,拼出来的就是
/user/login.jsp
走到视图解析器这,我们说响应的时候没必要再经过servlet了对不对,那直接走到视图解释器这,是不是最终就能看到index.jsp ,
当然如果要展示数据的话我们肯定也得在处理业务这块去把数据对应存到作用域,
2. SpringMVC的第一个环境搭建
输入的值为:
Name : archetypeCatalog
Value: internal
另外补充一下如何 移除模块 :
引入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!--springmvc核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
web.xml 文件中内容:
<!--配置springmvc的核心servlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springmvc配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern> <!-- / 代表拦截所有请求,交给springmvc处理-->
</servlet-mapping>
springmvc.xml 文件中的内容:
<!--开启注解扫描-->
<context:component-scan base-package="com.baizhi.controller"/>
<!--配置处理器映射器 and 配置处理器适配器 参数类型跳转 响应请求-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--注入前缀和后缀-->
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
开发控制器 :
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller // 这个是一个控制器组件,@Controller能够用来创建HelloController这个对象
// 有一个value属性,可以设置在工厂中的唯一标识,默认类名首字母小写
public class HelloController {
/**
* RequestMapping 修饰范围:类上 或 方法上
* 1. 用在方法上用来给当前方法加入指定的请求路径
* @return
*/
@RequestMapping() // 有一个value属性,用来指定该方法的请求路径
public String hello(){
// 1. 收集数据
// 2. 调用业务方法
System.out.println("hello spring mvc");
// 3. 处理响应
return "index"; // 页面逻辑名 日后就会跳转到根(webapp)下面的index.jsp
}
}
因为老师有一节课忘记录了,里面应该包含tomcat的下载和配置,接下来我来演示一下 tomcat相关的操作 :
首先进入镜像网站进行下载:点击进行镜像网站下载
下载完成之后打开下载好的压缩包进行解压
解压完成之后,运行时在cmd窗口会出现乱码问题,这是因为 cmd 默认编码格式为 GBK , Tomcat 默认输出格式为 UTF-8 ,下面我们提供两种解决方案:
-
解决命令行窗口cmd的乱码问题:
-
方法一: 修改cmd窗口编码格式为UTF-8
-
方法二:( 不推荐 )修改conf文件夹下的logging.properties文件,将Tomcat编码格式个位GBK,但是这种方式不建议使用,
因为如果我们修改Tomcat编码格式为 GBK ,因为idea编码为 UTF-8 ,所以在idea中输出时会出现乱码问题。
下面的操作我只是演示一下,在演示过后我又把Tomcat改回为 UTF-8 了,这种方式不建议使用
-
因为我将tomcat安装在了D盘,所以我需要打开D:\apache-tomcat-8.5.76\conf下的
我们来解决一下在新版本2021版idea中运行时post提交的参数乱码
新版本idea,不再认可直接从文件中编辑参数
因此我们这样修改编码格式:
测试一下tomcat是否安装成功
出现上面这个控制台之后在浏览器中输入http://localhost:8080,看到下面这个页面表示安装成功
之后我们在idea中配置tomcat
web文件的部署:
并会弹出这个窗口
另外:index.jsp的源码
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
说明在地址栏输入helllo,它最终进入了上面的方面,输出 hello spring mvc 这句话,最后跳转到了 index.jsp
本来 springmvc.xml 文件中的内容是:
<!--开启注解扫描-->
<context:component-scan base-package="com.baizhi.controller"/>
<!--配置处理器映射器 and 配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--注入前缀和后缀-->
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
但是由于有一个 更加强大的标签 , 替代了映射器和适配器的创建 ,而且还有更加强大的功能
<!--开启注解扫描-->
<context:component-scan base-package="com.baizhi.controller"/>
<!--配置处理器映射器 and 配置处理器适配器-->
<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
<!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->
<!--开启springmvc的注解式驱动,这个注解式驱动底层所完成的功能就包含了处理器适配器、处理器映射器的注册,同时它还会
帮我们做一些参数在绑定过程中的类型转换,以及响应的一些处理,这句话就替代了配置处理器映射器和配置处理器适配器
也就是说,有了这个标签,上面两行直接可以不写。下面mvc比上面的两句话还要高级,除了注册映射器、适配器,它还帮我们
做了一些参数的类型转换,包括跳转时的一些处理 还有一些响应的处理
-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--注入前缀和后缀-->
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
开发的Controller组件:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping(value = "/hello") //value可以省略不写直接写"hello", / 也可以不写
@Controller // 这个是一个控制器组件,@Controller能够用来创建HelloController这个对象
// 有一个value属性,可以设置在工厂中的唯一标识,默认类名首字母小写
public class HelloController {
/**
* RequestMapping 修饰范围:类上 或 方法上
* 1. 用在方法上用来给当前方法加入指定的请求路径
*
* 2. 用在类上用来给类中所有方法加入一个统一请求路径在方法访问之前需要加上类上@RequestMapping的路径
*
* 注意:一旦类上和方法上同时加入@RequestMapping访问时必须
* /项目名/类上@RequestMapping的路径/访问方法上@RequestMapping的路径
*
*
* @return
*/
@RequestMapping(value = "/hello") // 有一个value属性,用来指定该方法的请求路径
public String hello(){
// 1. 收集数据
// 2. 调用业务方法
System.out.println("hello spring mvc");
// 3. 处理响应
return "index"; // 页面逻辑名 日后就会跳转到根(webapp)下面的index.jsp
}
@RequestMapping("save")
public String save(){
// 1. 收集数据
// 2. 调用业务方法
System.out.println("save method ....");
// 3. 处理响应
return "index";
}
}
如果 只在方法上加@RequestMapping标签 ,在访问时直接输入方法上的@RequestMapping中的value即可( /模块/方法@RequestMapping标签上的value ),但是如果 在类上也加了@RequestMapping标签 ,输入时得**/模块/类中@RequestMapping的value/方法上@RequestMapping的value**
另外,有 其他方法 时,可以 输入其他方法的@RequestMapping的value进行访问
只有方法上有@RequestMapping标签 :
类上和方法上都有@RequestMapping标签 :
类上和方法上都有@RequestMapping标签,并且访问其他方法 :