springmvc学习
//springmvc就是一个spring框架,它能创建对象,并将对象放到springMVC的容器中。也能使用ioc管理对象,也可以使用<bean>,@Component,@Repository,@Service,@Controller等注解。但springmvc容器中主要放的是控制器对象
一、控制器对象
//使用@Controller注解创建控制器对象,用该对象接收用户的请求,显示处理结果,可以看作成一个Servlet使用
//但使用@Controller注解创建的对象是一个普通对象,能将其看成一个Servlet,但其本质上不是一个Servlet,因为该对象没有继承HttpServlet类,它不能直接接收用户有浏览器发送过来的请求,只能就受中央调度器的二次转发
//在springmvc中有一个servlet:DispatcherServlet(中央调度器)它负责直接接收用户的请求,再将请求转发给控制器对象,对请求进行处理
二、第一个例子
-
在web.xml文件中对DispatcherServlect对象进行注册
<servlet>
<servlet-name>aaa</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name> //contextConfigLocation固定写法
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
?
<servlet-mapping>
<servlet-name>aaa</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
//DispatcherServlet对象需要在服务器启动时被创建,因为该对象被创建的同时,也同时会创建springmvc的容器对象,并读取springmvc的配置文件,将springmvc配置文件中的对象都创建好,当用户发起请求时,可直接使用对象,节约时间。所以得加入<load-on-startup>标签
//DispatcherServlet对象被创建后,会执行初始化方法init(),init()的内容是
-
创建容器,读取配置文件:WebApplicationContext ctx=new ClassPathXmlApplicationContext ("springmvc.xml")
-
把容器对象放到ServletContext中:getServletContext().setAttribute(key,ctx)
//<init-param>标签中是配置Tomcat服务器读取springmvc.xml配置文件的位置,以上配置是在类路径的根目录下读取;默认位置是:/WEB-INF/<servlet-name>-servlet.xml,<servlet-name>在这里是aaa
//<load-on-startup>标签要写在<init-param>标签后面,不然会报错
-
创建控制类
//@Controller:定义控制器类,接收处理用户的请求。一个控制器类中可以有多个处理器方法
//@RequestMapping:请求映射,将一个请求地址和一个方法绑定在一起
//属性:value,String类型,表请求的URL地址,唯一值
//位置:在方法上(常用);也可以在类上
//ModelAndView:springmvc中的一个类,表示既可以返回数据,也可以返回视图,如:jsp等
//setViewName()方法相当于forward操作
-
在springmvc.xml文件中声明组件扫描器
<context:component-scan base-package="com.dh.controller"/>
//base-package:控制器类所在的包名
三、springmvc请求处理过程
-
发起请求:.../some.do
-
Tomcat接收到请求后,解析出其uri,然后找到对应的<url-pattern>为*.do的Servlet——DispatcherServlet
-
DispatcherServlet通过springmvc配置文件扫描控制器包,找到@Controller注解类,找到对应的处理器方法
-
DispatcherServlet把.../some.do请求转发给doSome()方法
-
框架执行doSome方法,把得到的ModelAndView进行处理,转发到show.jsp中
四、视图解析器
//当show.jsp在webapp的根目录下是,用户是可以直接通过网址访问的
//我们可以将show.jsp放在WEB-INF目录下,该目录下的文件是受保护的,无法直接由网页地址访问
//setViewName()方法中的路径改为
mv.setViewName("/WEB-INF/show.jsp");
//配置视图解析器在springmvc.xml之中
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/> //前缀:视图文件路径
<property name="suffix" value=".jsp"/> //后缀:视图文件扩展名
</bean>
//setViewName()方法中的路径改为
mv.setViewName("show");
五、@RequestMapping注解
在类上
//此时doSome()方法的真实url为:/test/some.do
//test出现在类上,被所有的处理其方法共有,所以被称为模块名称
Method属性
public class MyController {
//method属性:表示接收请求方式,它的值是RequestMethod枚举值。常用的有GET,POST,PUT......
//若method=RequestMethod.GET,此时发送post请求,则报错:405
//若在@RequestMapping注解中不指定method属性,则该方法可以接收任何请求方式的请求,没有限制
六、处理器方法的参数
四类参数
-
HttpServletRequest
-
HttpServletResponse
-
HttpSession
-
用户提交的参数(逐个接收,对象接收)
//前3种参数可以直接使用,在调用时由系统自动赋值
逐个接收
//适合参数数量较少时使用
<form action="receive.do" method="post">
姓名:<input type="text" name="name"/> <br> //abc
年龄:<input type="text" name="age"/> <br> //123
<input type="submit" value="提交">
</form>
//处理器方法的形参名必须和请求中的参数名一致。系统自动会将同名的请求参数赋给同名的形参,不关心位置,只看名称
//底层是String age=request.getParameter("age"),然后框架提供了自动转换功能,能将String转换为int,float等类型
//若年龄一栏不写,空着提交。则网页:400;控制台:不会抛异常,但会出现在服务器日志上。原因是空串无法转化为int类型。解决办法:doSome(String name,integer age),这样空串就转化为null,就不会报错了(实验证明:没啥用,依然会报错400:Required Integer parameter ‘age‘ is not present)。若年龄一栏填12.9或abc这样的,依然报错
中文乱码问题
//在提交请求参数时,get请求中没有中文乱码,使用post请求提交请求时,中文有乱码问题,需使用过滤器处理乱码
//过滤器可以自定义,也可以使用springmvc提供的过滤器:CharacterEncodingFilter
//在web.xml中添加
<filter>
<filter-name>abc</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value> //强制HttpServletRequest对象使用上面encoding中的编码方式
</init-param>
<init-param>
<param-name>foreResponseEncoding</param-name>
<param-value>true</param-value> //强制HttpServletResponse对象使用上面encoding中的编码方式
</init-param>
</filter>
<filter-mapping>
<filter-name>abc</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
//forceRequestEncoding,foreResponseEncoding的默认值都是false
@RequestParam注解
//在逐个接收请求中,解决请求中的参数名与处理器方法的形参名不一样的问题
//位置:在处理器方法的形参前面
//属性:value:请求中的参数名称
required:boolean类型,默认值为true。为true时,表示请求中必须包含该参数,即使在文本框中什么都不写,该参数的值为空串,依然会提交上来,不会报错。它其实指的是哪些没有name属性,无法提交的参数,若又设置了它的required值为true,就会报错:400。若不写required属性,默认为true
对象接收数据
//适合参数数量较多时使用
public class Student {
private String name;
private Integer age; //还要写构造,set,get等方法
...
}
?
//Studnet类中的属性名必须和请求中的参数名相同
//在doother(Student stu)中,框架会自动将请求中的参数赋给同名的属性,完成stu对象的赋值
//doother(对象1,对象2,对象3,...):可同时有多个对象
七、处理器方法的返回值
ModelAndView | 数据+视图 |
---|---|
String | 视图,不能返回数据 |
void | 即不返回数据,又不能跳转到其他的视图 |
对象Object | 返回数据,但不能跳转到其他视图 |
String
//使用该返回值,依然能接受表单提交的数据,但无法将表单的数据输出到show.jsp中
// return "show";语句就相当于forward,跳转到show.jsp文件,但由于其无法携带数据,所以此时的show.jsp是一个静态页面
//若想携带数据跳转,则doString(HttpServletRequest request,String name, Integer age)——request.setAttribute("a",name),加入参数request,将数据装进request域中,再在jsp文件中通过El表达式获取
//假设show.jsp文件在WEB-INF目录下,当有视图解析器时,只需返回逻辑名称即可:return "show"; 当没有视图解析器时,就需要返回完整视图路径:return "WEB-INF/show.jsp";
void
//因为没有返回值,所以不能跳转到其他页面。但在处理Ajax时,通过添加 HttpServletResponse response参数输出数据,响应Ajax请求。注:要加入Jackson依赖
Object
//若处理器方法的返回值是Object类型,则该处理器方法必须有@ResponseBody注解,否则无法访问该处理器方法
-
在springmvc.xml加入注解驱动
<context:component-scan base-package="com.dh.Controller"/>
?
<mvc:annotation-driven/>
//注解驱动的作用是:完成java对象到json,xml,text,二进制等数据格式的转化
//其底层是通过HttpMessageConveter接口(消息转化器)实现的。该接口有很多实现类,完成数据的转化,我们需要记住的是:
StringHttpMessageConverter //转为字符串格式
MappingJackson2HttpMessageConverter //转为接送格式
//该接口有5个方法:以下两个是控制器类把结果输出给浏览器时使用的
boolean canWrite(Class<?> var1,
//canWrite方法是检查处理器方法的返回值能不能转化为var2表示的数据格式。若能,返回true;MediaType是数据格式封装的类,如json,xml等
//write方法是调用Jackson中的ObjectMapper方法将处理器方法的返回值对象转化为json格式
-
@ResponseBody注解
//@ResponseBody注解是放在处理器方法上。底层是通过HttpServletResponse输出数据,响应Ajax的请求
PrintWriter out=response.getWriter();
out.print(s);
out.flush();
out.close(); //@ResponseBody注解代替的代码
//若处理器方法的返回值是Object类型,则该处理器方法必须有@ResponseBody注解,否则无法访问该处理器方法
//@ResponseBody注解是无法用于其他的返回值类型,比如:void,ModelAndView等
//返回对象的处理过程
-
框架会先调用HttpMessageConveter接口中的每个实现类的canWrite方法,检查那个实现类能处理Student类型的数据。在这里选择的是:MappingJackson2HttpMessageConverter,转为json
-
之后调用MappingJackson2HttpMessageConverter实现类的write方法,把Student的对象转化为json格式,底层是Jackson中的ObjectMapper方法。并且规定了字符集utf-8
-
最后调用@ResponseBody注解,将转化后的数据输出到浏览器中,完成Ajax的请求
Sting与Object的String区别
//前面的Sring是返回视图,这里的String是返回的数据——String对象,属于Object中的。区分两者就是看有没有@ResponseBody注解
//produces = "text/plain;charset=utf-8"语句是解决输出数据的中文字符乱码问题的;若Ajax是使用jQuery写的,解决中文乱码和String类型无法转化为json问题,请看笔记P39页
八、静态资源处理
//springmvc框架中中央调度器DispatcherServlet的<url-pattern>有两种值
(1) *.xxx
(2) / (只是单独一个斜杠,后面不能跟其他东西)
//斜杠是专门处理未映射到其他Servlet的请求。AServlet的url-pattern为/a,BServlet的url-pattern为/b,此时在网页上访问http://localhost:8080/c,由于/c没有 与其对应的Servlet,所以就属于未映射的到其他的Servlet请求。这种请求主要包含静态资源,如:html,图片,js等文件都属于
//Tomcat服务器中有一个内置的Servlet:org.apache.catalina.servlets.DefaultServlet,专门是用来处理静态资源的
处理静态资源方式1
-
将中央调度器DispatcherServlet的<url-pattern>改为/,不用额外增加另外的为*.do的<url-pattern>也能访问some.do为url的Servlet,或者相关的控制器类。
-
中央调度器DispatcherServlet的<url-pattern>改为/后,导致DispatcherServlet替代了org.apache.catalina.servlets.DefaultServlet的作用。但DispatcherServlet没有处理静态资源的能力,因为没有对应的控制器对象。所静态资源访问都是404。但不影响some.do这类的请求
-
所以在加入springmvc.xml中加入以下标签来处理静态资源
<mvc:default-servlet-handler/>
//加入该标签后,框架会自动创建DefaultServletHttpRequestHandler控制对象,该对象的作用就是将所有请求转发给tomcat的org.apache.catalina.servlets.DefaultServlet,让他来处理静态资源
-
但该标签与@RequestMapping注解有冲突,静态资源可以正常访问,但some.do不行。原因可能是DefaultServletHttpRequestHandler把所有请求都转给Tomcat处理了,而org.apache.catalina.servlets.DefaultServlet不能处理控制器对象相关的请求,由转不出来,所以就不能访问。所以需要在springmvc.xml中加入注解驱动
<mvc:annotation-driven/>
//该方式的优点:简单,方便;缺点:必须依赖服务器,而且该服务器必须有org.apache.catalina.servlets.DefaultServlet才行
处理静态资源方式2(常用)
//假设有a.html文件在webapp/html/目录下,则在springmvc.xml中添加
<mvc:resources mapping="/html/**" location="/html/"/>
//mapping:访问静态资源的url地址,可以使用通配符:**
//location:静态资源在你项目中的目录位置
//<mvc:resources>标签:该标签加入springmvc.xml中后,会创建ResourceHttpRequestHandler处理器应用,它是springmvc内置的,专门用来处理静态资源的。之后就不需要依赖其他服务器,在框架内部即可完成
//<mvc:resources>标签与@RequestMapping注解有一定的冲突,所以需要加入注解驱动
<mvc:annotation-driven/>
绝对路径获取
<%
String basePath=request.getScheme()+"://"+
request.getServerName()+":"+
request.getServerPort()+
request.getContextPath()+"/"; //basePath表示的网址:http://localhost:8080/myweb/
?
%>
<base href="basePath"/>
<a href="/some.do">跳转</a> //这里的真实href为:http://localhost:8080/myweb/some.do,简写了
//加上<base href="basePath"/>后,以下所有标签中的url都有这个前缀:http://localhost:8080/myweb/,相当于简写了
九、SSM整合开发
//整合开发以注解方式为主,xml配置文件为辅
整合中的容器
-
springmvc容器:管理Controller控制器对象
-
spring容器:管理Servlet,Dao。工具类对象
//两个容器都是独立存在的;springmvc容器是spring容器的子容器,类似java中的继承关系:子可以访问父的内容。在子容器中的Controller控制器对象可以访问父容器中的Service对象。
解决Controller到数据库的乱码
?
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
//添加 ?useUnicode=true&characterEncoding=UTF-8 这一段是解决从service写入数据库中,造成数据的中文乱码问题,但不能使数据库中已经乱码的数据复原回来,只作用于新加入的数据
十、重定向与转发
-
请求转发
mv.setViewName("forward:视图文件的完整路径")
//添加了forward与没添加forward,没有区别,setViewName方法本身就是forward方式
//forward特点:不和视图解析器一起使用,即使项目中有视图解析器,也当其没有。forward后依然要跟完成路径
//forward后的完成路径以webapp的根目录为起点开始算的
//forward底层是request.getRequestDispatcher ("xxx.jsp") .forward ()
-
重定向
mv.setViewName("redirect:视图文件的完整路径")
//两次请求,第二次的请求(即重定向的那次)不能访问WEB-INF中的资源
//redirect特点:不和视图解析器一起使用,即使项目中有视图解析器,也当其没有。redirect后依然要跟完成路径
//redirect底层是response.sendRedirect ("xxx.jsp")
十一、异常处理
//springmvc框架采用的是统一,全局的异常处理。把controller中所有的异常处理都集中到一个地方(这样就不用在每个地方中写try..catch,单独处理),把异常处理和业务逻辑代码分开,解耦合
//采用了aop思想,增强目标类的功能
异常处理步骤
-
在控制器类的处理器方法中抛出异常InsertFailException(自定义的异常)
-
创建普通类作为全局异常处理类
package com.dh.handler;
?
//@ControllerAdvice:控制器增强(即给控制器类增加了异常处理功能);位置:在类上
//@ExceptionHandler:定义异常处理方法。异常处理方法与处理方法一样,有4中返回值类型,即String,ModelAndView,viod,object;异常处理方法的形参为Exception,可打印指定异常的异常信息
//@ExceptionHandler的属性是value,值为异常类。当控制器类中的方法抛出指定异常时,转到对应的异常处理方法,执行该异常方法中的内容,不会再回到原控制器类中的方法
//同一个异常处理类中可有多个异常处理方法;若还有一个异常处理方法,但该方法没有指定value值,则该异常处理方法处理除了InsertFailException异常外的所有异常
-
为异常处理类所在包配置组件扫描器
<context:component-scan base-package="com.dh.handler"/>
异常处理逻辑
-
需要把异常记录下来,记录到数据库中,以及日志文件。记录日志发生的时间,位置,异常错误内容
-
发送通知,把异常信息通过邮件,短信等方式发送给相关人员
-
给用户友好的提示,不要404页面
十二、拦截器
-
拦截器是springmvc框架中的,需要实现HandlerInterceptor接口
-
拦截器与过滤器类似,但功能的侧重点不同。过滤器主要是用来过滤请求参数的,设置字符编码等工作;拦截器是拦截用户请求的,做请求的判断处理
-
拦截器是全局的,可以同时对多个Controller做拦截。一个项目中可以有多个拦截器,它们同时起作用
HandlerInterceptor中方法
预处理方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
//参数Object handler为被拦截的控制器对象
//该方法在控制器类中的处理器方法执行之前执行;该方法可以验证用户信息是否符合,验证失败,返回false,截断请求
后处理方法
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView)
//该方法在处理器方法之后执行
//通过参数ModelAndView modelAndView可获取处理器方法的返回值,再在后处理方法中对视图,数据进行二次修改,可以影响最后的输出结果
最后执行的方法
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex)
//在请求处理完成后执行,即当处理器方法中的跳转到指定视图中的语句执行之后,就认为该请求处理完成
//该方法一般做资源回收工作,即删除一些创建的对象,回收占有的内存
多个拦截器的执行顺序
//框架中的拦截器按照声明的先后顺序放到ArrayList集合中,越先声明,该拦截器就越先执行
//若有a,b两个拦截器,a先声明
1. a.preHandler()
2. b.preHandler()
3. MyController.doSome()
4. b.postHandler()
5. a.postHandler()
6. b.afterCompletion()
7. a.afterCompletion()
//若a,b的preHandler()都返回true,则按上述顺序执行
//若a.preHandler()返回true,b.preHandler()返回false,则只会执行1,2,7三个方法
//若a.b的preHandler()都返回false,则只执行1方法
//无论有多少个拦截器,只要其中一个拦截器的preHandler()方法返回false,则不会执行控制器类的处理器方法
拦截器与过滤器的区别
-
过滤器是Servlet中的对象,拦截器是springmvc框架中
-
过滤器是实现Filter接口,拦截器是实现HandlerInterceptor接口
-
过滤器主要是用来设置request,response参数的属性的,侧重数据过滤;拦截器主要是用来验证请求,截断请求的
-
过滤器在拦截器之前执行
-
过滤器只有一个执行时间点,拦截器有3个
-
过滤器可以处理jsp,html等静态资源;拦截器侧重拦截Controller对象,若你的请求不能被DispatcherServlet接收,则该请求不会受到拦截器的拦截
-
拦截器拦截普通方法执行,过滤器是过滤Servlet请求
-
过滤器是Tomcat服务器创建对象,拦截器是spring容器中创建对象