Spring MVC 笔记

 
Spring MVC基本概念:
1、DispatcherServlet: 前端控制器
2、Controller: 调用业务逻辑生成model的地方
3、HandlerAdapter:是被dispatcherServlet使用的,dispatcherServlet通过handlerAdapter来调用controller方法,handler其实是dispatcherServlet内部使用的一个类。
4、HandlerInterceptor:拦截器 该接口提供after postHandle preHandle 三个方法,调用controller前后使用
5、HandlerMapping:负责确定DispatcherServelet与controller之间映射的类,告诉DispatcherServelet,在请求到来后,由哪个controller来响应这个请求
6、HandlerExecutionChain: preHandle->Controller method->postHandle->afterCompletion的执行链
7、ModelAndView:model的具体表现
8、viewResolver:视图解析器,决定需要用哪个视图来进行视图的呈现。
9、view:界面
 
Spring MVC的原理流程
 
ModelAndView是springmvc的封装对象,将model和view封装在一起。
 
一个中心:(不需要开发
DispatcherServlet前端控制器
三个组件:不需要开发
 处理器映射器
处理器适配器
视图解析器
 
Handler:(需要开发,上图的第5步,第6步之间的Handler处理器)
处理器,理解成action
 
View
需要开发页面:jsp
 
springmvc的框架原理
包括哪些组件:
前端控制器(中心)
处理器映射器(三大组件)
作用:根据url找到Handler.
处理器适配器(三大组件)
作用:执行Handler
视图解析器(三大组件)
作用:解析出View,根据逻辑视图名解析出真正的视图
 
开发springmvc程序的步骤
第一步:配置前端控制器
在web.xml配置DispathcherServlet前端控制器
第二步:配置springmvc的全局配置文件
配置三大组件:
处理器映射器:
         根据url查找 Handler
处理器适配器:
         执行Handler
视图解析器:
         解析出视图View,根据逻辑视图名解析出真正的视图。
 
第三步:按照处理器适配器规则开发Handler(action
第四步:将Handler配置在spring容器中。
第五步:编写视图(jsp+jstl
 
处理器映射器:
作用:根据url找到Handler.
①形式 BeanNameUrlHandlerMapping
根据url匹配bean的name 处理器映射器实现了HandlerMapping接口
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
 
<!-- 配置action -->
<bean id="hello_controller" name="/helloworld.action" class="cn.itcast.springmvc.Hello" />
 
 
②形式 SimpleUrlHandlerMapping
将url进行集中配置
实现了HandlerMapping接口,处理url到bean的映射
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello1.action">hello_controller</prop>
<prop key="/hello2.action">hello_controller</prop>
<prop key="/hello3.action">hello_controller3</prop>
</props>
</property>
</bean>
 
<!-- 配置action -->
<bean id="hello_controller" name="/helloworld.action" class="cn.itcast.springmvc.Hello" />
 
总结:
多个处理器映射器可以共存
 
③注解映射器形式
<!--注解映射器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
 
④<mvc:annotation-driven />代替注解映射器
 
 
处理器适配器
执行Handler
①形式 SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter:规则是Handler要实现Controller接口
<bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
 
public class Hello implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
//向页面显示一行提示信息
//下边的方法就相当于request.setAttribute(arg0, arg1)
modelAndView.addObject("message", "helloworldttt@$@!^&&%9966!!!!");
//指定jsp页面地址
//指定逻辑视图名,真正的视图地址:前缀+逻辑视图名+后缀
modelAndView.setViewName("hello");
return modelAndView;
}
}
 
②形式 HttpRequestHandlerAdapter
HttpRequestHandlerAdapter:规则是Handler要实现HttpRequestHandler接口。
<!-- 配置HttpRequestHandlerAdapter处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
public class Hello1 implements HttpRequestHandler {
 
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
//向页面显示一行提示信息
String message = "hellworld1";
request.setAttribute("message", message);
 
//指定转向页面,使用request指定页面完整路径
request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request, response);
 
}
 
}
 
③ 注解适配器形式
<!--注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
 
④<mvc:annotation-driven />代替注解适配器
 
mvc的注解驱动
<mvc:annotation-driven />默认注册了注解映射器和注解适配器等bean
 
mvc组件扫描
<context:component-scan base-package="springmvc.action" />
扫描@component@controller@service@repository的注解
注意:如果使用组件扫描则controller不需要在springmvc-servlet.xml中配置
 
 
 
视图解析器:
InternalResourceViewResolver
 
<!-- 视图解析器 解析jsp视图,默认使用jstl,要求classpath下有jstl的jar包 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图的前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 视图的后缀 -->
<property name="suffix" value=".jsp" />
</bean>
 
 
 
 
 
<mvc:annotation-driven />
是一种简写形式,替换掉适配器、映射器
 
<context:component-scan base-package="com.roof" />
自动扫描bean
例子:
<!-- 扫描所有的controller 但是不扫描service -->
<context:component-scan base-package="cn.itcast">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service" />
</context:component-scan>
 
org.springframework.context.support.ResourceBundleMessageSource
注册到bean.xml中,这个类的作用是获取资源文件的内容,注册到IoC的bean.xml文件中是为了自动获得此类的对象
 
org.springframework.web.multipart.commons.CommonsMultipartResolver
 
@Entity
标注该类为实体类。
 
@Table(name = "sys_user")
@Table注释指定了Entity所要映射带的据库表
 
 
Controller方法通过形参接收页面传递的参数。
如下:
@RequestMapping("/userlist")
public String userlist(HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Model model
){
}
 
默认支持的参数类型
HttpServletRequest
通过request对象获取请求信息
HttpServletResponse
通过response处理响应信息
HttpSession
通过session对象得到session中存放的对象
Model
通过model向页面传递数据,如下:
 
model.addAttribute("user", new User("李四"));
 
页面通过${user.XXXX}获取user对象的属性值。
 
springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。简而言之就是,从把jsp页面的数据,以对象的形式可以在后台获取到。前提是jsp的中的名称和model的名称是一致的。页面上以pojo对象中属性名称命名
 
 
 
 
    表单对象-pojo
1.1.1      使用pojo属性名传递
页面定义:
 
Action方法的定义:
 
在形参使用pojo接收上边的参数。
 
1.1.2      使用pojo点属性名传递
页面定义:
 
Action方法的定义:
在形参不能直接使用pojo接收上边的参数。
应该使用包装对象接收上边的参数。
 
 
Action形参的定义:
 
字符串数组绑定
需求:在学生列表上多选,删除学生信息。
 
页面定义:
 
 
Action方法定义:
使用字符串数组接收。
批量删除学生信息方法,接收页面复选框的值(学生id
 
 
List绑定
 
页面向action传递复杂的批量数据,比如学生的成绩信息(课程名称、成绩)
页面定义:
 
 
 
Scores:包装对象中list属性的名称
Coursename:包装对象中list中pojo的属性名称。
Score:包装对象中list中pojo的属性名称。
如果上边下标相同的Coursename和Score设置在一个pojo中。
 
Action方法定义:
使用List<pojo>接收上边的接收,pojo中包括上边课程名称(coursename)和成绩(score)
List通过包装对象接收。
 
在UserVo包装中定义属性:List<>
 
Action方法形参使用包装对象接收list
 
 
@RequestMapping
URL路径映射
@RequestMapping(value="/user")或@RequestMapping("/user")
根路径+子路径
根路径:
@RequestMapping放在类名上边,如下:
@Controller
@RequestMapping("/user")
子路径:
@RequestMapping放在方法名上边,如下:
@RequestMapping("/useradd")
public String useradd(….
 
URI 模板模式映射
@RequestMapping(value="/useredit/{userId}"):{×××}占位符,请求的URL可以是“/useredit/001”或“/useredit/abc”,通过在方法中使用@PathVariable获取{×××}中的×××变量。
 
例如:
// 修改学生信息页面
// @RequestMapping指定url
@RequestMapping(value="/editstudent/{id}",method = RequestMethod.GET)
public String editstudent(HttpServletRequest request,Model model,@PathVariable String id) throws Exception {
System.out.println("id="+id);
......
jsp页面上
<td><a href="editstudent/${stu.id }.action">修改</a></td>
 
实现restFul,所有的url都是一个资源的链接,有利于搜索引擎对网址收录。
 
多个占位符:
 
@RequestMapping("/useredit/{groupid}/{userid}")
public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
//方法中使用@PathVariable获取useried的值,使用model传回页面
model.addAttribute("groupid", groupid);
model.addAttribute("userid", userid);
return"/user/useredit";
}
 
请求方法限定
限定GET方法
@RequestMapping(method = RequestMethod.GET)
 
如果通过Post访问则报错:
HTTP Status 405 - Request method 'POST' not supported
 
例如:
@RequestMapping(value="/useredit/{userid}",method=RequestMethod.GET)
限定POST方法
 
@RequestMapping(method = RequestMethod.POST)
 
如果通过Post访问则报错:
HTTP Status 405 - Request method 'GET' not supported
 
GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
 
 
@RequestParam绑定单个请求参数
 
value:参数名字,即入参的请求参数名字,如value=“studentid”表示请求的参数区中的名字为studentid的参数的值将传入;
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
 
定义如下:
public String userlist( @RequestParam(defaultValue="2",value="group",required=true) String groupid) {=
}
形参名称为groupid,但是这里使用value="group"限定参数名为group,所以页面传递参数的名必须为group。
这里通过required=true限定groupid参数为必需传递,如果不传递则报400错误,由于使用了defaultvalue=”2”默认值即使不传group参数它的值为”2”,所以页面不传递group也不会报错,如果去掉defaultvalue=”2”且定义required=true则如果页面不传递group则会报错。
 
结果转发
Redirect
//请求重定向
Contrller方法返回结果重定向到一个url地址,如果方式:
return "redirect:/user/userlist.action";
 
 
redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/user/userlist.action后边加参数,如下:
/user/userlist.action?groupid=2&…..
 
forward
//页面转发
controller方法执行后继续执行另一个controller方法。
return "forward:/user/userlist.action";
 
forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
 
如下例子:
 
@RequestMapping("/c")
public String c(String groupid,UserVo userVo)throws Exception{
 
System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
return "forward:/to/d.action";
}
 
@RequestMapping("/d")
public String d(String groupid,UserVo userVo)throws Exception{
 
System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
return "success";
}
 
@RequestBody @ResponseBody实现json数据交互
可以使用@RequestBody将请求的json串转成java对象。
使用@ResponseBody将action方法返回java对象转成json输出。
 
请求json响应json 
 
<!--注解适配器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
json转换器
<property name="messageConverters">
<list>
<bean
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
 
</bean>
 
如果使用 了<mvc:annotation-driven />则替换上边定义的处理器映射器和适配器 -->
 
 
 
1       拦截器
 
拦截器是针对handlerMapping的拦截器,由handlerMapping查找Handler后,将拦截器返回给前端控制器。
 
1.1      配置拦截器
 
针对某一个handlerMapping配置拦截器
<bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
       <list>
           <ref bean="handlerInterceptor1"/>
           <ref bean="handlerInterceptor2"/>
       </list>
    </property>
</bean>
    <bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
 
 
 
 
间接配置全局拦截器:让springmvc框架自动向每个handlerMapping中注册拦截器
<!--拦截器 -->
<mvc:interceptors>
    <!--多个拦截器,顺序执行 -->
    <mvc:interceptor>
       <mvc:mapping path="/**"/>//拦截所有请求
       <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
       <mvc:mapping path="/**"/>
       <bean class="cn.itcast.springmvc.interceptor.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>
 
 
 
1.2      定义拦截器
实现HandlerInterceptor接口。
 
public class HandlerInterceptor1 implements HandlerInterceptor {
 
   //handlerspringmvc根据url找到Handler(只有一个方法)
   //执行时机:进入Handler方法之前执行,如果返回false表示拦截,如果返回true表示放行
   //使用场景:用于用户身份校验,用户权限拦截校验
   @Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
     
      System.out.println("HandlerInterceptor1..preHandle");
     
      return false;
   }
   //执行时机:进入Handler方法之后 ,在返回modelAndView之前
   //使用场景:使用modelAndView,向页面传递通用数据,使用统一的view
   @Override
   public void postHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler,
         ModelAndView modelAndView) throws Exception {
      System.out.println("HandlerInterceptor1..postHandle");
     
   }
   //执行时机:Handler方法执行完成,(modelAndView已经返回)
   //使用场景:统一异常处理,统一记录系统日志,用于action方法执行监控(在preHandle记录一个时间点,在afterCompletion记录执行结束时间点,将结束时间点减去开始执行时间点,得到执行时长)
   @Override
   public void afterCompletion(HttpServletRequest request,
         HttpServletResponse response, Object handler, Exception ex)
         throws Exception {
      System.out.println("HandlerInterceptor1..afterCompletion");
     
   }
 
 
1.3      测试
 
1.3.1      两个拦截器都放行
 
HandlerInterceptor1..preHandle
HandlerInterceptor2..preHandle
HandlerInterceptor2..postHandle
HandlerInterceptor1..postHandle
HandlerInterceptor2..afterCompletion
HandlerInterceptor1..afterCompletion
 
结论:
preHandle是按照拦截器定义顺序执行,
postHandleafterCompletion是按照拦截器定义逆向执行。
 
 
1.3.1      第一个放行第二个不放行
HandlerInterceptor1..preHandle
HandlerInterceptor2..preHandle
HandlerInterceptor1..afterCompletion
 
结论:
只要有一个拦截器不放行,action方法无法完成。
如果拦截器放行,afterCompletion才会执行。
只要有一个拦截器不放行,postHandle不执行。
 
1.3.2      两个都不放行
 
HandlerInterceptor1..preHandle
 
结论:
只要有一个拦截器不放行,action方法无法完成。
只要有一个拦截器不放行,postHandle不执行。
 
 
 
1.4      拦截器应用
 
案例:
用户身份认证拦截,用户登陆成功后,系统记录session(用户身份信息),用户去操作url时,拦截器需要校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
 
 
1.4.1      action
@Controller
public class LoginAction {
  
   //登陆页面
   @RequestMapping("/login")
   public String login(Model model)throws Exception{
     
      return "login";
   }
  
   //登陆提交
   //userid:用户账号,pwd:密码
   @RequestMapping("/loginsubmit")
   public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
     
      //session记录用户身份信息
      session.setAttribute("activeUser", userid);
     
      return "redirect:stu/querystudent.action";
   }
  
   //退出
   public String logout(HttpSession session)throws Exception{
     
      //session过期
      session.invalidate();
     
      return "redirect:stu/querystudent.action";
   }
  
 
}
 
1.4.2      页面
 
@Controller
public class LoginAction {
  
   //登陆页面
   @RequestMapping("/login")
   public String login(Model model)throws Exception{
     
      return "login";
   }
  
   //登陆提交
   //userid:用户账号,pwd:密码
   @RequestMapping("/loginsubmit")
   public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
     
      //session记录用户身份信息
      session.setAttribute("activeUser", userid);
     
      return "redirect:stu/querystudent.action";
   }
  
   //退出
@RequestMapping("/logout")
   public String logout(HttpSession session)throws Exception{
     
      //session过期
      session.invalidate();
     
      return "redirect:stu/querystudent.action";
   }
  
 
}
 
1.4.3      拦截器
 
拦截所有url(将公开地址排除(无需登陆即可操作的url)),校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
 
@Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
     
      //判断请求的url是否公开 地址(无需登陆即可操作url)
      //正常开发时,需要将公开地址配置在配置文件中。
      //取出请求的url
      String url = request.getRequestURI();
      if(url.indexOf("loginsubmit.action")>=0){
         //说明 公开地址
         //放行
         return true;
      }
     
     
      //得到session
      HttpSession session = request.getSession();
     
      // session取出用户身份信息
      String userid = (String) session.getAttribute("activeUser");
     
      if(userid!=null){
         //说明 用户已登陆(用户身份合法)
         //放行
         return true;
        
      }
     
      //执行到这里说明 用户身份不合法,拦截,跳转到登陆页面
      request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
 
      return false;
   }
 
 
1.1      拦截器应用
 
案例:
用户身份认证拦截,用户登陆成功后,系统记录session(用户身份信息),用户去操作url时,拦截器需要校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
 
 
1.1.1      action
@Controller
public class LoginAction {
  
   //登陆页面
   @RequestMapping("/login")
   public String login(Model model)throws Exception{
     
      return "login";
   }
  
   //登陆提交
   //userid:用户账号,pwd:密码
   @RequestMapping("/loginsubmit")
   public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
     
      //session记录用户身份信息
      session.setAttribute("activeUser", userid);
     
      return "redirect:stu/querystudent.action";
   }
  
   //退出
   public String logout(HttpSession session)throws Exception{
     
      //session过期
      session.invalidate();
     
      return "redirect:stu/querystudent.action";
   }
  
 
}
 
1.1.2      页面
 
@Controller
public class LoginAction {
  
   //登陆页面
   @RequestMapping("/login")
   public String login(Model model)throws Exception{
     
      return "login";
   }
  
   //登陆提交
   //userid:用户账号,pwd:密码
   @RequestMapping("/loginsubmit")
   public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
     
      //session记录用户身份信息
      session.setAttribute("activeUser", userid);
     
      return "redirect:stu/querystudent.action";
   }
  
   //退出
@RequestMapping("/logout")
   public String logout(HttpSession session)throws Exception{
     
      //session过期
      session.invalidate();
     
      return "redirect:stu/querystudent.action";
   }
  
 
}
 
1.1.3      拦截器
 
拦截所有url(将公开地址排除(无需登陆即可操作的url)),校验用户身份是否合法(查看session中是否有用户身份信息,如果没有说明用户身份不合法,不合法重新登陆)
 
@Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
     
      //判断请求的url是否公开 地址(无需登陆即可操作url)
      //正常开发时,需要将公开地址配置在配置文件中。
      //取出请求的url
      String url = request.getRequestURI();
      if(url.indexOf("loginsubmit.action")>=0){
         //说明 公开地址
         //放行
         return true;
      }
     
     
      //得到session
      HttpSession session = request.getSession();
     
      // session取出用户身份信息
      String userid = (String) session.getAttribute("activeUser");
     
      if(userid!=null){
         //说明 用户已登陆(用户身份合法)
         //放行
         return true;
        
      }
     
      //执行到这里说明 用户身份不合法,拦截,跳转到登陆页面
      request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
 
      return false;
   }
posted @ 2018-04-28 10:48  杨斌_济南  阅读(217)  评论(0编辑  收藏  举报