1.Controller接口及其实现类
Controller是控制器/处理器接口,只有一个方法handleRequest,用于进行请求的功能处理(功能处理方法),处理完请求后返回ModelAndView对象(Model模型数据部分 和 View视图部分)。

如果想直接在处理器/控制器里使用response向客户端写回数据,可以通过返回null来告诉DispatcherServlet我们已经写出响应了,不需要它进行视图解析

Spring默认提供了一些Controller接口的实现类以方便我们使用,在Eclipse中选择Controller接口然后右键open type Hierarchy即可查看该接口的实现类,每个实现类都有自己特殊的功能,这里以实现类AbstractController为例简单介绍下。
查看AbstractController类中代码可知,我们写一个Controller的时候可以继承AbstractController然后实现handleRequestInternal方法即可。

提供了【可选】的会话(session)的串行化访问功能,例如:
即同一会话,线程同步

public class HelloWorldController extends AbstractController{
  @Override
  protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)throws Exception {   String name = request.getParameter("name");   //ModelAndView对象中包括了要返回的逻辑视图,以及数据模型
  ModelAndView mv = new ModelAndView();
  //设置视图名称,可以是字符串 也可以是视图对象
  mv.setViewName("hello");
  //设置数据模型
  mv.addObject("name", name);   return mv;
  }
} <bean name="/hello" class="com.briup.web.controller.HelloWorldController">
  <property name="synchronizeOnSession" value="true"></property>
</bean>

直接通过response写响应,例如:

public class HelloWorldController extends AbstractController{
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception { response.getWriter().write("Hello World!!");
//如果想直接在该处理器/控制器写响应 可以通过返回null告诉DispatcherServlet自己已经写出响应了,不需要它进行视图解析
return null;
}
}

强制请求方法类型,例如:
只支持post和get方法

<bean name="/hello" class="com.briup.web.controller.HelloWorldController">
<property name="supportedMethods" value="POST,GET"></property>
</bean>

当前请求的session前置条件检查,如果当前请求无session将抛出HttpSessionRequiredException异常,例如:
在进入该控制器时,一定要有session存在,否则抛出HttpSessionRequiredException异常。

<bean name="/hello" class="com.briup.web.controller.HelloWorldController">
<property name="requireSession" value="true"/>
</bean>

2.SpringMvc中的拦截器

SpringMVC的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。拦截器的作用有局限性只可以作用于处理器

1)常见应用场景
1、日志记录
2、权限检查
3、性能监控
4、通用行为 例如读取用户cookie等

2)拦截器接口

public interface HandlerInterceptor {
  boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;   void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;   void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}

preHandle方法
  预处理回调方法,实现处理器的预处理,第三个参数为的处理器(本次请求要访问的那个Controller)
  返回值:true表示继续流程(如调有下一个拦截器或处理器)
  false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应

postHandle方法
  后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理,modelAndView也可能为null。

afterCompletion方法
  整个请求处理完毕回调方法,即在视图渲染完毕时回调

3)拦截器适配器
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,不管你需不需要,此时spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。
在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true

4)测试一个拦截器
拦截器代码:

public class MyInterceptor1 extends HandlerInterceptorAdapter{
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    System.out.println("MyInterceptor1 preHandle");
    return true;
  }
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
    System.out.println("MyInterceptor1 postHandle");
  }
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
    System.out.println("MyInterceptor1 afterCompletion");
  }
}

配置文件:(注意此配置在文件中的配置顺序,要写在配置文件的上面)

<bean name="handlerInterceptor1" class="包名.MyInterceptor1"/>

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
  <property name="interceptors">
    <list>
      <ref bean="handlerInterceptor1"/>
    </list>
  </property>
</bean>

访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
TestController执行
MyInterceptor1 postHandle
MyInterceptor1 afterCompletion

5)测试俩个拦截器
俩个拦截器的代码和上面类似,只是每个输出的内容不同
配置文件:

<bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
<bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor2"/> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>

访问一个测试的Controller查看结果:
MyInterceptor1 preHandle
MyInterceptor2 preHandle
TestController执行
MyInterceptor2 postHandle
MyInterceptor1 postHandle
MyInterceptor2 afterCompletion
MyInterceptor1 afterCompletion

注意:<list>标签中引用拦截器的顺序会影响结果输出的顺序

6)拦截器mvc标签进行配置
注意:每个<mvc:interceptor>只能配置一个拦截器

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="handlerInterceptor1"/>
  </mvc:interceptor>
</mvc:interceptors>

例如1: 注意/*和/**的区别

<bean name="myInter1" class="com.briup.web.interceptor.MyInterceptor1" />

<bean class="com.briup.web.interceptor.MyInterceptor2" />

<bean class="com.briup.web.interceptor.MyInterceptor3" />

<!-- mvc提供的拦截器配置方式 -->
<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="myInter1"/>
  </mvc:interceptor>   <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/test"/>
    <ref bean="myInter2"/>
  </mvc:interceptor>   <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <ref bean="timeInter"/>
  </mvc:interceptor>
</mvc:interceptors>

7)拦截器是单例
因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,一个线程一个ThreadLocal,A 线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal。
举个例子:
记录执行Controller所用时间

public class TimeInterceptor extends HandlerInterceptorAdapter{
  //拦截器是单例,不是线程安全的,所以这里使用ThreadLocal
  private ThreadLocal<Long> local = new ThreadLocal<>();   @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
    long start = System.currentTimeMillis();
    local.set(start);
    return true;
  }
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
    long end = System.currentTimeMillis();
    System.out.println("共耗时:"+(end-local.get()));
  }
}

8)登录检查

public class LoginInterceptor extends HandlerInterceptorAdapter{
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
  //请求到登录页面放行
    if(request.getServletPath().startsWith("/login")) {
      return true;
    }   //如果用户已经登录放行
    if(request.getSession().getAttribute("username") != null) {
      return true;
    }   //其他没有登录的情况则重定向到登录页面
  response.sendRedirect(request.getContextPath() + "/login");   return false;
  }
}

注意:推荐能使用servlet规范中的过滤器Filter实现的功能就用Filter实现,因为HandlerInteceptor只有在SpringWebMVC环境下才能使用,因此Filter是最通用的、最先应该使用的。

3.基于注解的SpringMVC

1)用于支持注解的配置
使用基于注解的配置可以省略很多操作,更方便。我们之前所看到的所有的xml配置,如果替换成基于注解只需要在spring的xml文件中做如下配置:
<mvc:annotation-driven/>
在Spring中
处理器类可以使用 @Controller注解
业务逻辑层可以使用 @Service注解
数据持久层可以使用 @Repository注解

如果在处理器上使用 @Controller注解,那么还需要在配置文件中指定哪个包下面的类使用了该注解:
<context:component-scan base-package="com.briup.web.controller"></context:component-scan>

    <!-- 使容器可以识别mvc的注解 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 扫描指定包及其子包下所有的注解 -->
<context:component-scan base-package="com.briup.annotation"/>

2)基于注解的Controller
使用注解后,就不需要再实现特定的接口,任意一个javaBean对象都可以当做处理器对象,对象中任意一个方法都可以作为处理器方法。
只需
在类上加上 @Controller注解
方法上加上 @RequestMapping注解
即可

例如:
web.xml中:

<servlet>
  <servlet-name>SpringMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-web-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

src下面的spring-web-mvc.xml中:

<mvc:annotation-driven/>
<context:component-scan base-package="com.briup.web.controller"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
  <property name="prefix" value="/WEB-INF/jsp/"/>
  <property name="suffix" value=".jsp"/>
</bean>

自定义的Controller中:

@Controller
public class HomeController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

如上代码,使用 @Controller表明HomeController类是一个处理器类,通过 @RequestMapping("/home")表明当url请求名为/home时,调用home方法执行处理,当处理完成之后返回ModelAndView对象。因为在spring-web-mvc.xml中配置了视图解析器的前缀和后缀,所以最后视图home.jsp被返回

3)基于注解的Controller的返回值

1.返回ModelAndView,和之前一样

2.返回String,表示跳转的逻辑视图名字,模型可以通过参数传过来

@Controller
public class HomeController {
  @RequestMapping("/home")
  public String home(Model model){
    model.addAttribute("msg", "hello world");
    return "index";
  }
}

3.声明返回类型为void
可以通过参数获取request和response,分别使用服务器内部跳转和重定向,自己来决定要跳转的位置。

@Controller
public class HomeController {
@RequestMapping("/home")
  public void home(HttpServletRequest request,HttpServletResponse response){
    String username = request.getParameter("username");
    response.setContentType("text/html;charset=utf-8");
    response.getWriter().write("hello world! "+username);
    //或者使用servlet的方式进行跳转/重定向
  }
}

可以写一个类来测试所有方法:

@Controller
public class HelloController { //@RequestMapping("/test1")
//@RequestMapping(value = "/test1")
@RequestMapping(value= {"/test1","/annotest1"})
public ModelAndView test1(HttpServletRequest req,
HttpServletResponse reqs) throws Exception{ ModelAndView mv = new ModelAndView("test");
mv.addObject("name", "李四");
return mv;
} //如果方法返回值的类型为String,则表示返回的逻辑视图名
@RequestMapping("/test2")
public String test2() throws Exception{
return "hello";
} @RequestMapping("/test3")
public String test3(Model model) throws Exception{
model.addAttribute("name", "王五");
return "hello";
} @RequestMapping("/test4")
public void test4(HttpServletResponse response) throws Exception{
response.getWriter().write("hello world!");
response.getWriter().flush();
} /*
* 如果方法没有返回值,并且不通过响应用流的方式给客户端写回数据。
* 那么,SpringMVC会自动将@RequestMapping里面的参数,作为逻辑视图名
*/
@RequestMapping("/test5")
public void test5() throws Exception{
System.out.println("--------------");
} @RequestMapping("/test6")
public void test6(HttpServletRequest request,
HttpServletResponse response) throws Exception{
String path = "/WEB-INF/jsp/hello.jsp";
request.setAttribute("name", "赵四");
request.getRequestDispatcher(path).forward(request, response);
} @RequestMapping("/test7")
public void test7(HttpServletRequest request,
HttpServletResponse response) throws Exception{
String path = "/WEB-INF/jsp/hello.jsp";
response.sendRedirect(request.getContextPath()+"/test6");
}
}

4)Spring2.5中引入注解对控制器/处理器(controller/handler)支持
@Controller
用于标识是控制器/处理器类;
@RequestMapping
请求到处理器功能方法的映射规则;
@RequestParam
请求参数到处理器功能处理方法的方法参数上的绑定;
@ModelAttribute
请求参数到命令对象的绑定;
@SessionAttributes
用于声明session 级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session 中
@InitBinder
自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;

5).Spring3引入了更多的注解,其中包含了对RESTful架构风格的支持
@CookieValue
cookie数据到处理器功能处理方法的方法参数上的绑定;
@RequestHeader
请求头数据到处理器功能处理方法的方法参数上的绑定;
@RequestBody
请求的body体的绑定
@ResponseBody
处理器功能处理方法的返回值作为响应体
@ResponseStatus
定义处理器功能处理方法/异常处理器返回的状态码和原因;
@ExceptionHandler
注解式声明异常处理器;
@PathVariable
请求URI 中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;

6).Spring3中引入的mvc命名空间
mvc这个命名空间是在Spring3中引入的,其作用是用来支持mvc的配置
需要在<beans>中声明出这个命名空间及其对应的schemaLocation中的值
<mvc:annotation-driven>
自动注册基于注解风格的映射器和适配器:(也就是说这个mvc标签是基于注解的)
在spring2.5中是DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter

在spring3中是RequestMappingHandlerMapping和RequestMappingHandlerAdapter.
同时还支持各种数据的转换器.

配置自定义的处理器拦截器,例如:

<mvc:interceptors>
    <mvc:interceptor>
      <mvc:mapping path="/**"/>
        <ref bean="handlerInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>

收到相应请求后直接选择相应的视图,例如:
<mvc:view-controller path="/hello" view-name="test"></mvc:view-controller>

逻辑静态资源路径到物理静态资源路径的对应.例如:解决了静态资源拦截的问题

<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/> <mvc:default-servlet-handler>

当在web.xml中DispatcherServlet使用<url-pattern>/</url-pattern> 映射的时候,会静态资源也映射了,如果配置了这个mvc标签,那么再访问静态资源的时候就转交给默认的Servlet来响应静态文件,否则报404 找不到静态资源错误。

7).@Controller和@RequestMapping注解

1声明处理器

@Controller
public class HelloWorldController { }

2映射处理器中的【功能处理方法】

@Controller
public class HelloWorldController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

表明该方法映射的url路径为/home

3@RequestMapping也可以写在处理器类上

// /test/home
@RequestMapping("/test")
@Controller
public class HomeController {
  @RequestMapping("/home")
  public ModelAndView home(){
    ModelAndView mv = new ModelAndView("index");
    return mv;
  }
}

表明该方法映射的url路径为/test/home

8)请求映射@RequestMapping
假设浏览器发送了一个请求如下:
-------------------------------
POST /login HTTP1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,en;q=0.8,zh;q=0.5,en-US;q=0.3
Connection: keep-alive
Cookie: JSESSIONID=DBC6367DEB1C024A836F3EA35FCFD5A2
Host: 127.0.0.1:8989
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0

username=tom&password=123
--------------------------------

http协议的请求格式如下:
---------------------------------
请求方法 URL 协议版本号
请求头信息
请求头信息
请求头信息
..
回车换行
请求正文
---------------------------------

从格式中我们可以看到【请求方法、URL、请求头信息、请求正文】这四部分一般是可变的,因此我们可以把请求中的这些信息在处理器的【功能处理方法】中进行的映射,因此请求的映射分为如下几种:
URL路径映射
使用URL映射到处理器的功能处理方法;
请求方法映射限定
例如限定功能处理方法只处理GET请求;
请求参数映射限定
例如限定只处理包含username参数的请求;
请求头映射限定
例如限定只处理"Accept=application/json"的请求。

SpringMVC(AbstractController,拦截器,注解)的更多相关文章

  1. 基于注解风格的Spring-MVC的拦截器

    基于注解风格的Spring-MVC的拦截器 Spring-MVC如何使用拦截器,官方文档只给出了非注解风格的例子.那么基于注解风格如何使用拦截器呢? 基于注解基本上有2个可使用的定义类,分别是Defa ...

  2. SpringMVC—Struts2拦截器学习网址整理

    引自:http://blog.csdn.net/wp1603710463/article/details/49982683 SpringMVC—Struts2拦截器学习网址整理 最近项目中遇到权限相关 ...

  3. springmvc的拦截器

    什么是拦截器                                                         java里的拦截器是动态拦截action调用的对象.它提供了一种机制可以使 ...

  4. SpringMVC利用拦截器防止SQL注入

    引言 随着互联网的发展,人们在享受互联网带来的便捷的服务的时候,也面临着个人的隐私泄漏的问题.小到一个拥有用户系统的小型论坛,大到各个大型的银行机构,互联网安全问题都显得格外重要.而这些网站的背后,则 ...

  5. SpringMVC经典系列-14自己定义SpringMVC的拦截器---【LinusZhu】

    注意:此文章是个人原创.希望有转载须要的朋友们标明文章出处.假设各位朋友们认为写的还好,就给个赞哈.你的鼓舞是我创作的最大动力,LinusZhu在此表示十分感谢,当然文章中如有纰漏,请联系linusz ...

  6. SpringMVC 学习-拦截器 HandlerInterceptor 类

    一.拦截器 HandlerInterceptor 类的作用 SpringMVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理. 二.怎么使用呢? 1. ...

  7. springMVC的拦截器工作流程

    首先,springmvc的拦截器配置在这就不多说了.主要讲一下拦截器的三个方法的执行顺序. preHandle方法一定是最先执行的方法,如果它返回为false下面的方法均不执行. postHandle ...

  8. 转 :关于springmvc使用拦截器

    原博客: http://elim.iteye.com/blog/1750680 SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的 ...

  9. springmvc中拦截器与springmvc全局异常处理器的问题

    最近在做一个练手的小项目, 系统架构中用了springmvc的全局异常处理器, 做了系统的统一异常处理. 后来加入了springmvc的拦截器, 为了一些需求, 在拦截器中的 preHandle 方法 ...

  10. springboot + 拦截器 + 注解 实现自定义权限验证

    springboot + 拦截器 + 注解 实现自定义权限验证最近用到一种前端模板技术:jtwig,在权限控制上没有用springSecurity.因此用拦截器和注解结合实现了权限控制. 1.1 定义 ...

随机推荐

  1. 组合数学(math)

    组合数学(math) 题目描述 为了提高智商,zjy开始学习组合数学.某一天她解决了这样一个问题:“给一个网格图,其中某些格子有财宝.每次从左上角出发,只能往右或下走.问至少要走几次才能把财宝全部捡完 ...

  2. tomcat8.5在centos部署阿里云免费证书

    最近在做微信小程序,部署完服务器之后,发现报了个错误,说是我的域名不在以下合法域名列表中.对比了一下才发现我的域名还是http的没升级到https,之后我就到阿里云去申请了证书.中间有一次审核失败,查 ...

  3. python 实现异常退出

    https://blog.csdn.net/u013385362/article/details/81206822 有时当一个条件成立的情况下,需要终止程序,可以使用sys.exit()退出程序.sy ...

  4. 拾遗:ssh 公钥连接前的相关准备

    ssh 公钥连接条件: sshd_config 中启用公钥认证 authorized_keys 文件权限必须为 0600 目标用户的 家目录 权限必须为 0700 目标账户必须已设定登陆密码(即处于可 ...

  5. Java门面模式(或外观模式)

    门面模式(或外观模式)隐藏系统的复杂性,并为客户端提供一个客户端可以访问系统的接口. 这种类型的设计模式属于结构模式,因为此模式为现有系统添加了一个接口以隐藏其复杂性.门面模式涉及一个类,它提供客户端 ...

  6. centos7升级php5.4到php5.6

    history命令历史 8 yum provides php #自带的只有5.4版本 9 rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-relea ...

  7. Java中的数据结构有哪些?HashMap的工作原理是什么?

    Java中常用数据结构 常用的数据结构有哈希表,线性表,链表,java.util包中有三个重要的接口:List,Set,Map常用来实现基本的数据结构 HashMap的工作原理 HashMap基于ha ...

  8. Spring开发案例1半注解开发

    dao层: package cn.mepu.dao.imp; import cn.mepu.dao.AccountDao; import cn.mepu.domain.Account; import ...

  9. Mysql 查询视图出现The user specified as a definer ('root'@'%') does not exist的问题

    今天服务器Mysql版本在5.7升级到8.0+之后,部分网站(老的)访问视图出现The user specified as a definer ('root'@'%') does not exist问 ...

  10. oracle运维(持续更新)

    目录 简单命令使用 进入SQL*Plus 退出SQL*Plus 在sqlplus下得到帮助信息 显示表结构命令 DESCRIBE SQL*Plus 中的编辑命令 调用外部系统编辑器 运行命令文件 关于 ...