SpringMVC
一、SpringMVC是什么
• 是基于spring的一个框架,实际上就是spring的一个模块,专门做web开发的
• 可以理解为Servlet的升级
• web开发底层就是Servlet,框架是在Servlet基础上面添加一些功能,做web项目更方便
• SpringMVC能够创建对象,放入到容器中(SpringMVC的容器) SpringMVC里面放的是控制器对象
• 我们要做的是使用@Controller注解创建控制器对象,把对象放入到SpringMVC容器中,把创建的对象作为控制器使用
• 这个控制器对象能接收用户的请求,显示处理结果,就当作是一个servlet使用【并不是servlet,就是普通对象】
• 使用@Controller创建的对象,其实就是个普通的对象,springMVC赋予了他控制器的能力
• Springmvc底层访问依然是DispatcherServlet 他的两大作用:
• 创建WebApplicationContext对象,读取配置文件,进而控制控制器对象
• 本身就是一个Servlet,要接收请求,显示结果
二、springMVC的工作模式
• web开发底层时servlet,springmvc中有一个对象是Servlet:DispatcherServlet(中央调度器) 负责接收用户所有请求,然后把请求转发给Controller对象,最后是Controller对象处理请求
• Index.jsp------>DispatherServlet------>转发,分配给----->Controller对象(@Controller注解创建的对象)
完整的工作流程
1.用户发送请求至 前端控制器DispatcherServlet。
2.前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping。
3.处理器映射器HandlerMapping根据请求的Url找到具体的处理器,生成处理器对象Handler及处理器拦截器HandlerIntercepter(如果有则生成)一并返回给前端控制器DispatcherServlet。
4.前端控制器DispatcherServlet通过处理器适配器HandlerAdapter调用处理器Controller。
5.执行处理器(Controller,也叫后端控制器)
6.处理器Controller执行完后返回ModelAnView。
7.处理器映射器HandlerAdapter将处理器Controller执行返回的结果ModelAndView返回给前端控制器DispatcherServlet。
8.前端控制器DispatcherServlet将ModelAnView传给视图解析器ViewResolver。
9.视图解析器ViewResolver解析后返回具体的视图View。
10.前端控制器DispatcherServlet对视图View进行渲染视图(即:将模型数据填充至视图中)
11.前端控制器DispatcherServlet响应用户。
三、SpringMVC入门案例
1、创建maven项目,webapp
因为maven创建的web项目中的 web.xml文件是 2.3版本的,需要重新创建一下
<?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_4_0.xsd"
version="4.0">
</web-app>
2、加入依赖 spring-mvc 、servlet、
<!--springMVC的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
会间接的把spring的依赖都加入到项目中,
3、在web.mxl文件中,注册springmvc框架的核心对象 DispatcherServlet
• DispatcherServlet :中央调度器【父类继承Servlet,本身就是Servlet,具备所有servlet的功能】
• DispatcherServlet:也叫前端控制器
• 负责去接收用户提交的请求,调用其他的控制器对象 ,并把请求的处理结果显示给用户
<servlet>
<!--springmvc在创建容器对象时,读取的配置文件默认是 /WEB-INF/<servlet-name>-servlet.xml
也就是说 读取的就是下面这个name 后面加 -servlet.xml,
因为这个规则不方便,所以就不用这个默认的规则,去自定义文件所在的位置-->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置在服务器启动时,马上创建DispatcherServlet对象-->
<!--load-on-startup:在启动时进行加载,
数字表示tomcat启动后创建的顺序,数制越小创建的对象时机越早 >=0的整数-->
<!--自定义文件所在位置-->
<init-param>
<!--指定SpringMVC的配置文件的属性,必须是contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--自定义文件的位置 classpath:-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--设置servlet-mapping-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--可以使用两种值,
1.使用扩展名的方式,语法*.xxxxxx是自定义的拓展名常用的有*.do*.action*.mvc
表示以.do结尾的请求,都会被该servlet-mapping处理 (没有/)
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用"/"的方式
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
4、创建发起请求的页面
5、创建控制器类/后端控制器 @Controller
1. 在类的上面加入@Controller注解,创建对象,放入到SpringMVC容器中
2. 在中的方法上加入@RequestMapping注解
• 方法是自定义的,可以有多种返回值,多种参数,方法名称自定义
• 被@RequestMapping注解修饰的方法叫处理器方法
@RequestMapping("/login.do")
public ModelAndView doSome(){
ModelAndView wv = new ModelAndView();
return wv;
}
6、创建springMVC的配置文件
(和spring的配置文件一样)
1. 声明组件扫描器,指定controller注解所在的包名
• 在resources目录下创建springmvc配置文件,和spring配置文件一样
• 声明组件扫描器 <context:component-scan base-package="com.yixuexi"/>
2. 声明视图解析器,帮助开发人员设置视图文件的路径
• 为了防止用户恶意访问show.jsp, 把show.jsp放到 WEB-INF目录下,这样用户不可能在地址框搜索到
• 配置视图解析器
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀,视图文件的拓展名-->
<property name="suffix" value=".jsp"/>
</bean>
• 在WEB-INF目录下有一个 view目录,里面放着 show.jsp
3. 注意:springmvc配置文件的名字要和web.xml文件中的这个属性中的值一致
为什么要在tomcat启动后,创建DispatcherServlet对象实例
• 因为,DispatcherServlet在他创建的过程中,会同时创建springmvc容器对象,
• 读取springmvc的配置文件,把配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了
总结
1. 先导入依赖
2. 然后在web.xml文件中,创建DispachterServlet对象
3. 然后创建类 在类上添加@Controller注解,
4. 然后创建方法,方法返回值类型为ModelAndView, 方法名随意
5. 方法上添加@RequestMapping注解 value属性写 请求的uri地址,就是/login.do 或者 /register.do
6. 在springmvc的配置文件中,声明组件扫描器,和视图解析器
7. 然后处理完之后,可以在mv那个对象上,进行添加数据 addObject(key,value); setViewName("视图文件")
四、返回值ModelAndView对象使用
ModelAndView mv = new ModelAndView();
//指定数据,添加数据,框架在最后把数据放到了request作用域中
//相当于 request.setAttribute("key","value"); 框架帮你做了
mv.addObject("key","欢迎使用mvc做web开发");
mv.addObject("function","执行的时doSome()方法");
//指定视图,指定视图的完整路径
//框架对视图执行的是forward操作,(请求转发)//request.getRequestDispatcher().forward() 框架帮你做了
mv.setViewName("/show.jsp"); //没有配置视图解析器的时候,show.jsp还在webapp目录下
//配置了视图解析器的时候,show.jsp在WEB-INF/view下,后缀也省了
//直接使用文件的名称就可以了,框架自己去/WEB-INF/view/下去找show.jsp
mv.setViewName("show")
说明:
使用@RequestMapping修饰的方法叫做处理器方法/通知器方法
使用@RequestMapping修饰的方法是可以处理请求的,类似于Servlet中的doGet() doPost()
返回值:ModelAndView : 本次请求的处理结果
Model:数据,请求处理完成后,要显示给用户的数据
View:视图,比如jsp/html等等的
五、SpringMVC web.xml 固定配置
web.xml 固定代码
<!--web.xml固定代码-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置在服务器启动时,马上拆功能键DispatcherServlet对象-->
<!--load-on-startup:在启动时进行加载,
数字表示tomcat启动后创建的顺序,数制越小创建的对象时机越早>=0的整数-->
<init-param>
<!--指定SpringMVC的配置文件的属性,必须是contextConfigLocation-->
<param-name>contextConfigLocation</param-name>
<!--自定义文件的位置classpath:-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!--设置servlet-mapping-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--可以使用两种值,
1.使用扩展名的方式,语法*.xxxxxx是自定义的拓展名常用的有*.do*.action*.mvc
表示以.do结尾的请求,都会被该servlet-mapping处理(没有/)
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2.使用"/"的方式
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--注册过滤器,解决post请求中文乱码问题 web.xml文件配置-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<!--CharacterEncodingFilter-->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置项目中使用的字符编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<!--强制请求对象,使用encoding编码的值-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制响应对象,使用encoding编码的值-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--表示强制所有的请求先通过过滤器处理-->
<url-pattern>/*</url-pattern>
</filter-mapping>
springmvc.xml 固定代码
<!--组件扫描器,用来扫描注解创建对象的-->
<context:component-scan base-package="com.yixuexi"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀,视图文件的拓展名-->
<property name="suffix" value=".jsp"/>
</bean>
<!--springmvc响应AJAX 请求返回一个json数据的对象 添加注解驱动-->
<mvc:annotation-driven/>
<!--url-pattern是/时,配置访问静态文件,目录要求:静态文件都在static文件下-->
<!--同时也需要上面那一句 <mvc:annotation-driven>-->
<mvc:resources mapping="/static/**" location="/static/"/>
拦截器
<!--声明拦截器,在框架中拦截器可以有0或多个-->
<mvc:interceptors>
<!--声明第一个-->
<mvc:interceptor>
<!--指定拦截的请求uri地址的
path:就是uri地址,可以使用通配符 **
**:表示任意的字符,文件,或者多级目录和目录中的文件
/** : 表示所有的请求都会被拦截
-->
<mvc:mapping path="/**"/>
<!--声明拦截器对象,表示访问上面那个地址的时候都由下面这个拦截器处理-->
<bean class="com.yixuexi.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
六、@RequestMapping注解
准备使用doSome()方法处理前端页面发来的login.do的请求
@RequestMapping:请求映射,作用是把一个请求地址和一个方法绑定在一起
一个请求指定一个方法处理
1、属性:
1.value: 表示请求的uri地址 唯一值【login.do】
• 在使用时,推荐以“/”开头
• 可以是数组,{"/xxx","/xxx","/xxx"} (多个请求一个方法处理)
2.method:表示请求的方式,他的值是RequestMethod的枚举值
• 如果没有指定的话,所有的请求方式都可以访问
• 例如表示get请求方式, RequestMethod.GET 或者.POST
• 如果该方法用的post请求,那么请求必须是post才能访问该方法【表单可以发post请求】 不按照对应的请求方式 报错 405
• 前端发送请求 开头不需要 /
2、位置
1. 在方法的上面【表示这个方法处理这个请求】
2. 在类的上面 【集中的定义,把公用的提取出来】
a. value: 所有请求地址的公共部分 ,也叫模块名称
b. 如果请求是这样的 test/login.do test/register.do 的话就可以在类的上面添加@RequestMapping("/test") 注解,那么他的方法就不用加 "/test" 了 直接 /login.do就行了
七、请求的处理流程
1、springmvc请求的处理流程
第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步:Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对 象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果
2、springmvc执行过程分析 init源码分析
2.1 tomcat启动,创建容器的过程
1. tomcat启动,先执行web.xml文件,然后通过load-on-startup标签中的1创建DispatcherServlet对象
2. DispatcherServlet他的父类是继承HttpSerlvet的,他是一个Serlvet,在被创建时,会执行init方法。
3. 在inut方法中会执行 new ClassPathWebApplicationContext("文件地址"); 并把它放到全局作用域中
4. 创建了容器,容器同时也创建了@Controller注解所在的对象,
5. 这个controller对象放入到了springmvc的容器中,
3、组件说明
1. DispatcherServlet
前端控制器
用户请求到达前端控制器,它就相当于mvc中的c,DispatcherServlet是整个流程控制的中心,由他调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合度
2. HandlerMapping
处理器映射器
HandlerMapping 负责根据用户发送的请求 uri 来找到对应的controller中的方法
springmvc提供了不同的映射器实现不同的映射方式,HandlerMapping会把找到映射返回给DispatcherServlet
3. Handler / controller
后端控制器
在DispatcherServlet的控制下,Handler对具体的用户请求进行处理
4. HandlerAdapter
通过HandlerAdapter对处理器进行执行,处理器适配器
5. ViewResolver
View Resolver 负责将结果转换成view 视图
6. View
SpringMVC框架提供了很多的View视图类型的支持
包括:jstlView,freemarkerView,pdfView,我们最常用的视图就是JSP
八、处理器方法的形参
处理器方法可以包含以下4类参数,这些参数springmvc会给自动赋值,直接使用就行
直接在处理器方法里面定义形参即可
1. HttpServletRequest 请求
2. HttpServletResponse 响应
3. HttpSession 会话
4. 请求中锁携带的请求参数
@RequestMapping(value="/login.do",method=RequestMethod.POST)
//直接在这里声明 上请求对象,响应对象,session对象就行
Public ModelAndView test(HttpSession session,
HttpServletRequest request,
HttpServletResponse response){
//从请求中得到数据 getParameter()
String username = request.getParameter("username");
ModelAndView mv=new ModelAndView();
//添加到mv对象中,也就是请求作用域对象
mv.addObject("key",username);
//请求转发,到login.jsp, 这里配置了视图解析器,本来应该写 /WEB-INF/view/login.jsp的
mv.setViewName("login");
returnmv;
}
1、获取请求中携带的请求参数
1.1 逐个获取
要求:
• 处理其方法的形参名,和请求中参数的name必须一致。
• 同名的请求参数会自动赋值给同名的形参
@RequestMapping(value="/login.do",method=RequestMethod.POST)
//逐个接收,形参名必须和参数名一致才行
public ModelAndView login(String username,String password){
ModelAndView mv=new ModelAndView();
mv.addObject("key",username);
mv.setViewName("login");
return mv;
}
• 框架内部完成了 String username = request.getParameter("username");
• 框架会提供类型转换的功能,能把String转换成int,long,float,double 等类型和他的包装类型。
• 注意:当提交的是空字符串 "",或者是"abc"时,无法进行int类型转换,所以会报错400
• 状态码400:所有4xx报错都是客户端问题,400表示提交请求参数的时候,发生了问题。
• 建议使用包装类,不要使用基本数据类型,容易出错(但还是不能转换adc这种的,"" 不会报错)
1.2 对象接收
(参数多的话,使用对象接收会比较好)
创建一个类,类中的属性名和参数名一致,提供get set 方法和无参构造。
框架自动调用无参构造创建对象,再调用set方法 设置值。
前端发来的参数名称是name,框架自动调用 setName()方法
形参中也可以有多个对象,比如Student, School,Address
2、解决请求中参数和方法形参不一致
前端发来的请求,参数名是 xingming,mima
只能用在逐个接收的方案中
2.1 @RequsetParam注解:
解决请求中参数名形参名不一致的问题
属性:
value:请求中参数的名称。
required:是要给布尔类型 [必须的]
true:表示这个请求中必须包含此参数。
false:如果没有这个参数也可以
位置:
在处理器方法的形参定义前面
@RequestMapping("/login")
public String login(@RequestParam("xingming") String username
@RequestParam("mima") String password
){
}
九、处理器方法的返回值
1、返回ModelAndView
1. ModelAndView mv = new ModelAndView();
2. 返回数据部分 mv.addObject("key","zhangsan"); 最后是放到了request作用域中
3. 返回视图部分 mv.setViewName("show");前提是SpringMvc配置了视图解析器。
4. 如果页面发送的是AJAX请求,页面不会进行跳转,那么setViewName()方法就显得很多余,所以这里使用ModeAndView作为返回值就不在合适。
5. 同时需要保存数据,同时需要跳转页面,这样的话就使用ModelAndView
2、返回String表示视图部分
如果本次请求,仅仅是要做的一个页面跳转的功能,那么使用String作为返回值最好
可以使用逻辑名称【文件名】,也可以是完整路径。
程序内部执行的是 forward() 请求 请求转发
使用逻辑名称:
• return “doSome” 【配置了视图解析器,拼接后的完整路径为/WEB-INF/view/doSome.jsp】
使用过完整视图路径:
• 项目中不能配置视图解析器
• return “/WEB-INF/view/doSome.jsp”
请求转发:
• return “redirect:/doSome”; 【调转到doSome路由】
3、返回值void
既不能表示数据,也不能表示视图
在处理AJAX时,可以使用void,通过HttpServletResponse来输出数据,响应AJAX请求。
AJAX请求服务器端返回的就是数据,和视图无关。
当接受到ajax请求时,需要返回数据,只需要往HttpServletResponse的print();方法里写就行。
修改响应字符集
response.setContentType("text/html,charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("响应AJAX请求");
4、返回值Object 【处理AJAX】
返回:String、Integer、Map、List、Student 等等都是对象。
对象有属性,属性就是数据,所以返回Object表示返回数据,和视图无关
可以使用对象表示的数据,来响应AJAX请求
现在做AJAX,主要是使用JSON的数据格式,处理器返回Json数据实现步骤:
①、加入Json
springmvc默认使用的是jackson 【一个自动生成json的jar包】
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
②、把java对象转换成json
在springmvc配置文件中加入<mvc:annotation-driven> 注解驱动。这样就好了
在加入到springmvc中,会自动创建HttpMessageConverter接口,和他的7个实现类对象
③、在处理器方法的上面加入 @ResponseBody注解
放在处理器方法的上面,通过HttpServletResponse来输出数据,响应AJAX请求的
处理器方法返回一个Student,通过框架转为json,响应ajax请求
@ResponseBody
• 作用:把处理器方法返回的对象转换为json数据后,通过HttpServletResponse输出给浏览器
• 位置:在方法定义的上面
例如:处理器方法返回一个Student对象,通过框架转为json,响应ajax请求
@ResponseBody
@RequestMapping("/test")
public Student test(){
return new Student();
}
例如:返回一个List集合,里面是Student
4、处理器方法返回一个String,并不是视图信息,而是数据
• 区分返回值字符是数据还是视图,看有没有@ResponseBody注解
• 如果有就是数据,如果没有就是视图
@RequestMapping("处理地址")
@ResponseBody
public String returnString(){
return "登录失败";
} //有@ResponseBody注解,是数据,不是视图
出现的问题: 返回中文的时候出现乱码问题 “???”
解决方案:给@RequestMapping注解增加一个属性
@RequestMapping(value = “/returnStudentJson.do”,produces = “text/html;charset=utf-8”)
十、解读url-pattern
在servlet-Mapping中的url-pattern中使用 /
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern> 【这样就可以不用some.do了,就是some了】
</servlet-mapping>
• 导致所有的静态资源请求都交给了DispatcherSerlvet处理,默认情况下DispatcherServlet没有处理静态资源的能力。没有控制器对象能对静态资源的访问,
• 所有静态资源都是404【图片,js,html,css】
(当中央调度器的url-pattern设置为/ 之后 静态资源访问会失败 404 ,动态资源可以正常访问)
1、第一种处理静态资源无法访问的方式
需要在springmvc的配置文件中加入 <mvc:default-servlet-handler>
• 加入这个标签后,框架会创建一个default对象,他像是一个监察员,对请求中的uri进行筛查,如果是访问静态资源,那么 他做处理。【注意 这里要加入 <mvc:annotation-driven/> 注解驱动,要不动态资源无法访问,有冲突,至于什么冲突,负负得正吧…】
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
2、第二种处理静态资源无法访问的方式【常用】
在springmvc的配置文件中添加该标签
<mvc:resources mapping="/static/**" location="/static/"/>
• 属性:
• Mapping: 访问静态资源的uri,使用统配符 ** [两个星星]
• Location:静态资源在项目中的目录
• 作用:
• 加入该标签后,框架会创建 ResourceHttpRequestHandler对象,让这个对象去处理静态资源的访问。
• 注意:还需要添加上json的注解驱动标签 不然有冲突
• <mvc:annotation-driven/>
• 常用
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:annotation-driven/>
十一、路径问题
在jsp/html中路径是使用 /开头 还是不用 / 开头 ?
1、地址分类:
相对地址:除绝对地址其余都是相对地址 user/some.do /user/some.do
相对地址不能独立使用,必须要有一个参考地址,通过参考地址+相对地址=访问地址
绝对地址:带有协议名称的。 http://www.baidu.com
2、处理方案
Jsp/html文件上有一个 user/some.do 的请求,链接
当你的地址没有斜杠 / 开头,点击链接时,访问地址是当前页面的地址 加上 链接的地址 【不建议使用】
• 参考地址 :Http://localhost:8080/myWeb/ + user/some.do
会出现的问题:
• 当网页路径是 Http://localhost:8080/myWeb/user/some.do时 在去访问 user/some.do时,路径就会变成 Http://localhost:8080/myWeb/user/user/some.do, 此时就会出现404问题【找不到】
解决方案:
• 添加EL表达式
<a href="${pageContext.request.contextPath}/user/some.do"> 链接</a>
• 加入一个base标签
表示当前页面中访问地址中的基地址 【你的页面中所有 没有以“/” 开头的地址,都是以base中的地址为参考地址】
使用base中的地址 + user/some.do 组成访问地址
<base href=”http://localhost:8080/myWeb/"/>
当你的地址有斜杠 / 开头,点击链接时,访问的是服务器地址 加上 链接的地址
• 参考地址 :Http://localhost:8080 + /user/some.do
• 解决方案:在访问的链接上 加上项目名 <a href="/myWeb/user/some.do">链接</a>
3、在项目中遇到了 / 开头的路径时,可加入${pageContext.request.contextPath}
<a href="${pageContext.request.contextPath}/user/some.do"> 链接</a>
4、通过代码获取项目的路径,base可以直接使用获取到的
<%
//获取项目路径,到myWeb项目那里
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<title>Title</title>
<base href="<%=basePath%>">
<a href="user/doSome">aaaaaaaaaaa</a>
//在使用了base之后,就不需要添加/开头了,因为base会将没有以斜杠开头的地址,把参考地址改为base地址
十二、请求转发和重定向
• springmvc对请求转发和重定向操作进行了封装,现在可以使用简单的方式实现请求转发和重定向
• forward:表示请求转发,实现request.getRequestDispatcher("xx.jsp").forward();
• redirect:表示重定向,实现response.sendRedirect("xxx".jsp)
• forwar和redirect都是框架的关键字,有一个共同的特点,他们都不和视图解析器一同工作。
1、forward
处理器方法返回ModelAndView,实现请求转发 forward
语法:mv.setViewName(“forward:/视图文件完整路径”)
特点:不和视图解析器一同使用,就当项目中没有视图解析器
就算有视图解析器,也得写上前缀 后缀
显式转发,明显的说明这是转发,同时setViewName(“show”) 这也是转发
作用:项目中存在视图解析器,但是要转发的页面没在view目录下,那么就转发不到
例如:转发和index.jsp同级目录的jsp文件 setViewName(“forward:/hello.jsp”)
2、redirect
处理器方法返回ModelAndView,实现重定向 redirect
语法:mv.setViewName(“redirect:/路由”)
特点:和上面的一样
• 框架对重定向的操作:框架会把ModelAndView中的简单类型的数据,转为字符串使用,作为hello.jsp的get请求参数使用,
• 在jsp中使用el表达式 ${param.xxxx} 来获取get请求参数值
• 目的:为了在重定向的过程中能够传递数据
缺点:
• 因为是重新从浏览器发出的请求,所以不能访问 WEB-INF 下的文件
• 重定向取不到请求作用域的数据。【不是同一个请求包了】
十三、统一全局异常处理
• 为了避免代码中,出现冗长的try..catch的语句。
• 统一全局异常处理:把controller中所有的异常都集中到一个地方,采用的是aop的思想,把业务逻辑和异常处理分开,解耦合。
1、使用两个注解
1. @ExceptionHandler
2. @ControllerAdvice
2、异常处理步骤
1. 新建一个maven web项目
2. 加入依赖
3. 新建一个自定义异常类,MyUserException ,在定义子类 NameException,AgeException
4. 在MyController中抛出异常 NameException,AgeException
5. 创建一个普通类,作为全局异常处理类
• 在类的上面加入注解,@ControllerAdvice
• 在类中定义方法,方法的上面加入 @ExceptionHandler
6. 创建处理异常的视图页面 【当发生异常了,让用户知道的页面】
7. 创建springmvc的配置文件
• 组件扫描器,【注解创建对象 @Controller】
• <context:component-scan base-package="com.yixuexi.controller"/>
• 组件扫描器,【扫描@ControllerAdvice所在的包名】(直接扫描他的父包也行,那上面那个就免了)
• <context:component-scan base-package="com.yixuexi.handler"/>
• 声明注解驱动
• <mvc:annotation-driven/>
不用写try catch,直接在方法的定义上抛出他的父类异常,让统一全局异常处理类代替你去处理异常
3、@ControllerAdvice
• 控制器增强(给控制器增加功能,---->异常处理功能)
• 特点:必须让框架知道这个注解所在的包名,需要在springmvc声明组件扫描器
• 位置:在类的上面定义
@ControllerAdvice
public class HandlerException {
// 在这个统一全局异常处理类中写方法 来处理对应的异常
}
4、@ExceptionHandler(异常类.class)
• value参数表示该异常类由该方法进行处理,一个异常类的class
• 位置:方法定义的上面
@ExceptionHandler(NameException.class)
public ModelAndView doNameException(Exception e){}
统一全局异常处理类中的方法定义:和处理器方法定义是一样的,可以有ModelAndView,String void,等等返回值,也可以有多个参数
参数Exception e 表示controller类中抛出的异常对象,可以通过该形参获取详细的异常信息
@ExceptionHandler(NameException.class)
public ModelAndView doNameException(Exception e){
ModelAndView mv = new ModelAndView();
//给请求作用域中添加数据
mv.addObject("msg","姓名必须是张三,其他不能显示");
mv.addObject("ex",e);
//转发给nameError页面【已经配置了视图解析器】
mv.setViewName("nameError"); // 转发给nameError.jsp显示错误信息
return mv;
}
5、发生异常之后的处理逻辑:
1. 需要把异常记录下来,记录到数据库,日志文件记录发生的时间,那个方法发生的,异常错误内容
2. 发送通知,把异常的信息通过邮件,短息,微信发送给相关人员
3. 给用户友好的提示
6、定义一个处理其他异常的方法(只能有一个该方法)
【其实统一都交给他处理就行,别的方法不需要】
• 因为处理的是Name和Age的异常,还会有许多不知道会发生什么的异常,比如空指针,类没找到,除数为0的异常….
• 方法的定义和上面的一样,特殊的是方法上定义的注解 value属性没有值
@ExceptionHandler(空的)
public ModelAndView doOtherException(Exception e){
//抛出非 age 非 name异常时,在这里执行
ModelAndView mv = new ModelAndView();
mv.addObject("msg","未知异常");
mv.addObject("ex",e);
//转发给默认的defaultError页面
mv.setViewName("defaultError");
return mv;
}
十四、拦截器
1、拦截器是什么
1. 拦截器是springmvc的一种,需要实现HandlerInterceptor接口
2. 和过滤器类似,功能的方向侧重点不同:
a. 过滤器是用来过滤请求参数,设置编码字符集等功能
b. 拦截器是拦截用户的请求,对请求做判断处理的。
3. 拦截器特点:
a. 全局的,可以对多个controller进行拦截
b. 一个项目中可以有一个或者多个拦截器,他们在一起来拦截用户的请求
4. 拦截器常用在:
a. 用户登陆处理,权限的检查,记录日志
2、拦截器的使用步骤
1. 定义类,实现HandlerInterceptor接口
a. 实现里面的3个方法
2. 需要在springmvc配置文件中中声明拦截器,让框架知道拦截器的存在
a. 指定拦截的请求的uri地址
拦截器的执行时间
• 在请求处理之前,controller类中的方法执行之前,先被拦截
• 在控制器方法执行之后,也会执行拦截器
• 在请求处理完成后也会执行拦截器
拦截器看作是多个Controller中公用的功能,集中到拦截器统一处理,使用的aop的思想
3、拦截器和过滤器的区别
1. 过滤器是Servlet中的对象,拦截器是框架中的对象
2. 过滤器是实现Filter接口,拦截器是实现HandlerInterceptor
3. 过滤器是用来设置request response的参数,属性的,侧重对数据的过滤的
拦截器是用来验证请求的,能截断请求【返回false】
4. 过滤器是在拦截器之前 先执行的
5. 过滤器是tomcat服务器创建的对象,拦截器是springmvc创建的对象
6. 过滤器是一个执行时间点,拦截器有三个执行时间点
7. 过滤器可以处理jsp,js,html等等,
拦截器是侧重拦截对controller的请求,如果你的请求不能被中央调度器【DispatcherServlet】接收,那么他不会执行拦截器的内容
4、HandlerInterceptor接口中的三个方法
4.1、第一个 preHandle()
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
计算的业务逻辑,根据计算结果 返回true还是false
return false;
}
preHandle:预处理方法,整个项目的入口,重要
形参:
• 请求对象
• 响应对象
• handler:被拦截的控制器对象,controller对象
返回值boolean
• true: 请求通过了拦截器的验证,可以执行处理器的方法
• 同时拦截器的另外两个方法都会执行
• false: 请求没有通过拦截器的验证,请求到达拦截器后,就截至了,请求不会被处理
特点:
1. 方法在控制器方法(MyController的doSome()方法)之前先执行的
• 用户的请求首先到达此方法
2. 在这个方法中可以获取请求的信息,验证请求是否符合要求。
• 可以验证用户是否登录,验证用户是否有权限访问某个链接地址(url)
• 如果验证失败可以截断请求,请求不会被处理
• 验证成功可以放行请求,此时控制器方法才能执行
3. 可以想象成电影院的售票员,这个preHandle方法就是入口
4.2、第二个方法 postHandle()
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
可以在里面修正结果 modelAndView这个对象的方法
}
postHandle() 后处理方法
形参:
• Handler:被拦截的处理器对象 MyController
• modelAndView:处理方法的返回值
特点:
• 在处理器方法之后执行的
• 能够获取到处理器方法的返回值,ModelAndView
• 可以修改ModelAndView中的数据和视图,影响最后的结果
作用:
• 对原来的执行结果做二次修正
4.3、第三个方法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
afterCompletion():最后执行的方法
形参:
• Handler 被拦截的处理器对象
• Exception ex:程序中发生的异常
特点:
• 在请求处理完成后执行的。框架中规定当视图处理完成后【对视图进行了forward,就认为请求处理完成】
作用:
• 一般做资源回收功能
• 在程序中创建了一些对象,在这里进行删除。把占用的内存回收
十四、多个拦截器
声明拦截器:先在springmvc配置文件里声明的先执行,后声明的后执行(底层就是一个ArrayList)按照先后顺序放入到ArrayList
1、执行顺序:
两个拦截器的preHandle()方法返回值都是true
1111111111111-拦截器的 preHandle()方法执行
2222222222222-拦截器的 preHandle()方法执行
2222222222222-拦截器的 postHandle()方法执行
1111111111111-拦截器的 postHandle()方法执行
2222222222222-拦截器的afterCompletion()方法执行
1111111111111-拦截器的afterCompletion()方法执行
两个拦截器的preHandler()方法返回值 1是true 2是false
1111111111111-拦截器的 preHandle()方法执行
2222222222222-拦截器的 preHandle()方法执行
1111111111111-拦截器的 afterCompletion()方法执行
1返回是真的话 他的第三个方法 afterCompletion肯定会执行到
只要任意个返回false,那么controller里的doSome()方法不会执行
两个拦截器的preHandler()方法返回值 1是false 2是true
1111111111111-拦截器的 preHandle()方法执行
因为这个1在ArrayList集合中是第一个,他是false就直接是false了 后面的链条直接就截止了
十五、拦截器登陆验证
1. maven项目,加依赖
2. Web.xml注册中央调度器
3. 新建index.jsp发起请求
4. 新建MyController类处理请求
5. 创建MyController处理请求
6. 创建结果show.jsp
7. 创建login.jsp模拟登录(把用户的信息放到session中)
a. 创建logout.jsp模拟退出信息(把用户信息从session中删除)
8. 创建拦截器,从session中来获取用户的登录数据,验证能否访问系统
9. 创建一个验证的jsp,如果验证失败,给出提示
10. 创建springmvc的配置文件