码迷,mamicode.com
首页 > 编程语言 > 详细

springmvc学习

时间:2021-07-02 15:48:41      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:osi   new   tab   枚举   pat   驱动   扫描器   turn   字符串   

//springmvc就是一个spring框架,它能创建对象,并将对象放到springMVC的容器中。也能使用ioc管理对象,也可以使用<bean>,@Component,@Repository,@Service,@Controller等注解。但springmvc容器中主要放的是控制器对象

 

一、控制器对象

//使用@Controller注解创建控制器对象,用该对象接收用户的请求,显示处理结果,可以看作成一个Servlet使用

//但使用@Controller注解创建的对象是一个普通对象,能将其看成一个Servlet,但其本质上不是一个Servlet,因为该对象没有继承HttpServlet类,它不能直接接收用户有浏览器发送过来的请求,只能就受中央调度器的二次转发

//在springmvc中有一个servlet:DispatcherServlet(中央调度器)它负责直接接收用户的请求,再将请求转发给控制器对象,对请求进行处理

 

 

二、第一个例子

 

  1. 在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()的内容是

  1. 创建容器,读取配置文件:WebApplicationContext ctx=new ClassPathXmlApplicationContext ("springmvc.xml")

  2. 把容器对象放到ServletContext中:getServletContext().setAttribute(key,ctx)

 

//<init-param>标签中是配置Tomcat服务器读取springmvc.xml配置文件的位置,以上配置是在类路径的根目录下读取;默认位置是:/WEB-INF/<servlet-name>-servlet.xml,<servlet-name>在这里是aaa

//<load-on-startup>标签要写在<init-param>标签后面,不然会报错

 

  1. 创建控制类

@Controller
public class MyController {
   @RequestMapping("/some.do")
   public ModelAndView doSome(){
       ModelAndView mv=new ModelAndView();
       mv.addObject("a","123");
       mv.addObject("b","456");
       mv.setViewName("/show.jsp");
       return mv;
  }
}

//@Controller:定义控制器类,接收处理用户的请求。一个控制器类中可以有多个处理器方法

//@RequestMapping:请求映射,将一个请求地址和一个方法绑定在一起

//属性:value,String类型,表请求的URL地址,唯一值

//位置:在方法上(常用);也可以在类上

//ModelAndView:springmvc中的一个类,表示既可以返回数据,也可以返回视图,如:jsp等

//setViewName()方法相当于forward操作

 

  1. 在springmvc.xml文件中声明组件扫描器

<context:component-scan base-package="com.dh.controller"/>

//base-package:控制器类所在的包名

 

 

三、springmvc请求处理过程

  1. 发起请求:.../some.do

  2. Tomcat接收到请求后,解析出其uri,然后找到对应的<url-pattern>为*.do的Servlet——DispatcherServlet

  3. DispatcherServlet通过springmvc配置文件扫描控制器包,找到@Controller注解类,找到对应的处理器方法

  4. DispatcherServlet把.../some.do请求转发给doSome()方法

  5. 框架执行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注解

在类上

@Controller
@RequestMapping("test")
public class MyController {
   @RequestMapping("/some.do")
   public ModelAndView doSome(){

//此时doSome()方法的真实url为:/test/some.do

//test出现在类上,被所有的处理其方法共有,所以被称为模块名称

 

Method属性

public class MyController {
   @RequestMapping(value = "/some.do",method = RequestMethod.GET)
   public ModelAndView doSome(){
  }    

//method属性:表示接收请求方式,它的值是RequestMethod枚举值。常用的有GET,POST,PUT......

//若method=RequestMethod.GET,此时发送post请求,则报错:405

//若在@RequestMapping注解中不指定method属性,则该方法可以接收任何请求方式的请求,没有限制

 

 

六、处理器方法的参数

 

四类参数

  1. HttpServletRequest

  2. HttpServletResponse

  3. HttpSession

  4. 用户提交的参数(逐个接收,对象接收)

//前3种参数可以直接使用,在调用时由系统自动赋值

@RequestMapping(...)
public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response, HttpSession session){
   request...
   session...    
}

 

 

逐个接收

//适合参数数量较少时使用

 

<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>
@RequestMapping(value = "receive.do",method = RequestMethod.POST)
public ModelAndView doSome(String name,int age){       //这里int使用的自动类型转换
   System.out.println(name);
   System.out.println(age);           //abc 123
   return mv;
}

//处理器方法的形参名必须和请求中的参数名一致。系统自动会将同名的请求参数赋给同名的形参,不关心位置,只看名称

//底层是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

@RequestMapping(value = "receive.do",method = RequestMethod.POST)
public ModelAndView doSome(@RequestParam(value = "name",required = false) String a,
                          @RequestParam(value = "age") Integer b){
   System.out.println(a);
   System.out.println(b);
}    

 

 

对象接收数据

//适合参数数量较多时使用

 

public class Student {
   private String name;
   private Integer age;      //还要写构造,set,get等方法
  ...
}
?
@Controller
public class MyController {
@RequestMapping(value = "other.do",method = RequestMethod.POST)
   public ModelAndView doother(Student stu){
       System.out.println(stu.getName());
       System.out.println(stu.getAge());
  }    
}    

//Studnet类中的属性名必须和请求中的参数名相同

//在doother(Student stu)中,框架会自动将请求中的参数赋给同名的属性,完成stu对象的赋值

//doother(对象1,对象2,对象3,...):可同时有多个对象

 

 

七、处理器方法的返回值

ModelAndView数据+视图
String 视图,不能返回数据
void 即不返回数据,又不能跳转到其他的视图
对象Object 返回数据,但不能跳转到其他视图

 

String

@Controller
public class ReturnController {
   @RequestMapping(value = "string.do",method = RequestMethod.POST)
   public String doString(String name, Integer age) {
       System.out.println(name);
       System.out.println(age);             //abc 12
       return "show";
  }
}

//使用该返回值,依然能接受表单提交的数据,但无法将表单的数据输出到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

@RequestMapping(value="void.do",method = RequestMethod.POST)
public void doVoid(String name, Integer age) {
   System.out.println(name);
   System.out.println(age);
   PrintWriter out=response.getWriter();
   Student s=new Student();
   s.setName("ww");
   s.setAge(66);
   out.print(s);
   out.flush();
   out.close();
}

//因为没有返回值,所以不能跳转到其他页面。但在处理Ajax时,通过添加 HttpServletResponse response参数输出数据,响应Ajax请求。注:要加入Jackson依赖

 

 

Object

//若处理器方法的返回值是Object类型,则该处理器方法必须有@ResponseBody注解,否则无法访问该处理器方法

 

  1. 在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, @Nullable MediaType var2);
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)

//canWrite方法是检查处理器方法的返回值能不能转化为var2表示的数据格式。若能,返回true;MediaType是数据格式封装的类,如json,xml等

//write方法是调用Jackson中的ObjectMapper方法将处理器方法的返回值对象转化为json格式

 

 

  1. @ResponseBody注解

@Controller
public class MyController {
   @ResponseBody
   @RequestMapping("some.do")
   public Student doSome(String name,Integer age) {
       System.out.println(name);
       System.out.println(age);
       Student s=new Student();
       s.setName("ww");
       s.setAge(66);
       return s;
  }
}    

//@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区别

@ResponseBody
@RequestMapping(value = "string.do",produces = "text/plain;charset=utf-8")
public String doString(String name,Integer age) {
   System.out.println("String:"+name);
   System.out.println("String:"+age);
   return "Hello,张三";
}

//前面的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

  1. 将中央调度器DispatcherServlet的<url-pattern>改为/,不用额外增加另外的为*.do的<url-pattern>也能访问some.do为url的Servlet,或者相关的控制器类。

  2. 中央调度器DispatcherServlet的<url-pattern>改为/后,导致DispatcherServlet替代了org.apache.catalina.servlets.DefaultServlet的作用。但DispatcherServlet没有处理静态资源的能力,因为没有对应的控制器对象。所静态资源访问都是404。但不影响some.do这类的请求

  3. 所以在加入springmvc.xml中加入以下标签来处理静态资源

<mvc:default-servlet-handler/>

//加入该标签后,框架会自动创建DefaultServletHttpRequestHandler控制对象,该对象的作用就是将所有请求转发给tomcat的org.apache.catalina.servlets.DefaultServlet,让他来处理静态资源

 

  1. 但该标签与@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配置文件为辅

 

整合中的容器

  1. springmvc容器:管理Controller控制器对象

  2. 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写入数据库中,造成数据的中文乱码问题,但不能使数据库中已经乱码的数据复原回来,只作用于新加入的数据

 

 

 

 

十、重定向与转发

  1. 请求转发

mv.setViewName("forward:视图文件的完整路径")

//添加了forward与没添加forward,没有区别,setViewName方法本身就是forward方式

//forward特点:不和视图解析器一起使用,即使项目中有视图解析器,也当其没有。forward后依然要跟完成路径

//forward后的完成路径以webapp的根目录为起点开始算的

//forward底层是request.getRequestDispatcher ("xxx.jsp") .forward ()

 

  1. 重定向

mv.setViewName("redirect:视图文件的完整路径")

//两次请求,第二次的请求(即重定向的那次)不能访问WEB-INF中的资源

//redirect特点:不和视图解析器一起使用,即使项目中有视图解析器,也当其没有。redirect后依然要跟完成路径

//redirect底层是response.sendRedirect ("xxx.jsp")

 

 

 

十一、异常处理

//springmvc框架采用的是统一,全局的异常处理。把controller中所有的异常处理都集中到一个地方(这样就不用在每个地方中写try..catch,单独处理),把异常处理和业务逻辑代码分开,解耦合

//采用了aop思想,增强目标类的功能

 

异常处理步骤

  1. 在控制器类的处理器方法中抛出异常InsertFailException(自定义的异常)

  2. 创建普通类作为全局异常处理类

package com.dh.handler;
?
@ControllerAdvice
public class handler {
?
   @ExceptionHandler(InsertFailException.class)
   public ModelAndView doInsertFailException(Exception ex){
       ModelAndView mv = new ModelAndView();
       mv.addObject("ex",ex);
       mv.setViewName("doException");        //跳转到异常处理页面
       return mv;
  }
}

//@ControllerAdvice:控制器增强(即给控制器类增加了异常处理功能);位置:在类上

//@ExceptionHandler:定义异常处理方法。异常处理方法与处理方法一样,有4中返回值类型,即String,ModelAndView,viod,object;异常处理方法的形参为Exception,可打印指定异常的异常信息

//@ExceptionHandler的属性是value,值为异常类。当控制器类中的方法抛出指定异常时,转到对应的异常处理方法,执行该异常方法中的内容,不会再回到原控制器类中的方法

//同一个异常处理类中可有多个异常处理方法;若还有一个异常处理方法,但该方法没有指定value值,则该异常处理方法处理除了InsertFailException异常外的所有异常

 

  1. 为异常处理类所在包配置组件扫描器

<context:component-scan base-package="com.dh.handler"/>

 

 

异常处理逻辑

  1. 需要把异常记录下来,记录到数据库中,以及日志文件。记录日志发生的时间,位置,异常错误内容

  2. 发送通知,把异常信息通过邮件,短信等方式发送给相关人员

  3. 给用户友好的提示,不要404页面

 

 

 

十二、拦截器

  1. 拦截器是springmvc框架中的,需要实现HandlerInterceptor接口

  2. 拦截器与过滤器类似,但功能的侧重点不同。过滤器主要是用来过滤请求参数的,设置字符编码等工作;拦截器是拦截用户请求的,做请求的判断处理

  3. 拦截器是全局的,可以同时对多个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,则不会执行控制器类的处理器方法

 

 

拦截器与过滤器的区别

  1. 过滤器是Servlet中的对象,拦截器是springmvc框架中

  2. 过滤器是实现Filter接口,拦截器是实现HandlerInterceptor接口

  3. 过滤器主要是用来设置request,response参数的属性的,侧重数据过滤;拦截器主要是用来验证请求,截断请求的

  4. 过滤器在拦截器之前执行

  5. 过滤器只有一个执行时间点,拦截器有3个

  6. 过滤器可以处理jsp,html等静态资源;拦截器侧重拦截Controller对象,若你的请求不能被DispatcherServlet接收,则该请求不会受到拦截器的拦截

  7. 拦截器拦截普通方法执行,过滤器是过滤Servlet请求

  8. 过滤器是Tomcat服务器创建对象,拦截器是spring容器中创建对象

 

springmvc学习

标签:osi   new   tab   枚举   pat   驱动   扫描器   turn   字符串   

原文地址:https://www.cnblogs.com/zhestudy-2021/p/14961121.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有
迷上了代码!