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.自定义适配器
一般情况下,springMVCSimpleControllerHandlerAdapter会是我们常用的适配器,也是SpringMVC中默认的适配器,该适配器中的主要代码如下:

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
		public boolean supports(Object handler) {
			return (handler instanceof Controller);
		}
		public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
				throws Exception {

			return ((Controller) handler).handleRequest(request, response);
		}
	}

从代码中可以看出,它首先会判断我们的handler是否实现了Controller接口,如果实现了,那么会调用Controller接口中的handleRequest方法

那么根据这种方式能看出,我们也可以有自己的适配器的实现,那么就可以让任意类成为SpringMVC中的handler了,无论我们的类是否实现了Controller接口

例如:
		自己的接口:
		public interface MyHandler {
			public ModelAndView handler_test(HttpServletRequest request, HttpServletResponse response)throws Exception;
		}
		自己的适配器:
		public class MyHandlerAdapter implements HandlerAdapter{
			@Override
			public boolean supports(Object handler) {
				return (handler instanceof MyHandler);
			}

			@Override
			public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
					throws Exception {
				return ((MyHandler)handler).handler_test(request, response);
			}

			@Override
			public long getLastModified(HttpServletRequest request, Object handler) {
				return -1L;
			}

		}

		自己的hander:(就是我们之前写的Controller)
		public class TestController implements MyHandler{
			@Override
			public ModelAndView handler_test(HttpServletRequest request, HttpServletResponse response) throws Exception {
				String name = request.getParameter("name");
				ModelAndView mv = new ModelAndView("hello");
				mv.addObject("name", name);
				return mv;
			}
		}
最后在spring的配置中把我们的适配器进行配置即可正常使用.适配器配置多个,从上到下适配

3.处理器拦截器/控制器拦截器
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="com.briup.web.interceptor.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)如果Controller等采用的注解配置,那么拦截器需要mvc标签进行配置
注意:每个mvc:interceptor只能配置一个拦截器

<mvc:interceptors>
			<mvc:interceptor>
				<mvc:mapping path="/**"/>
				<ref bean="handlerInterceptor1"/>
			</mvc:interceptor>
		</mvc:interceptors>
例如1: 注意/*和/**的区别
	<!--
    /**的意思是所有文件夹及里面的子文件夹
    /*是所有文件夹,不含子文件夹
    /是web项目的根目录
    -->
<mvc:interceptors>
			<!-- 下面所有的mvc映射路径都会被这个拦截器拦截 -->
			<bean class="com.briup.web.interceptor.MyInterceptor1" />

			<mvc:interceptor>
				<mapping path="/**"/>
				<exclude-mapping path="/admin/**"/>
				<bean class="com.briup.web.interceptor.MyInterceptor2" />
			</mvc:interceptor>
			<mvc:interceptor>
				<mapping path="/secure/*"/>
				<bean class="com.briup.web.interceptor.MyInterceptor3" />
			</mvc:interceptor>
		</mvc:interceptors>
7)拦截器是单例
	因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
	所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,一个线程一个ThreadLocal,A 线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal。

8)记录执行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()));
			}
		}
9)登录检查
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是最通用的、最先应该使用的。

4.基于注解的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>


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的方式进行跳转/重定向

				}
			}

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

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

findUser.action?groupId=1&userId=3
	get   group/1/user/3   1,3 查找
	public void find(){...}
	delete  group/1/user/3   1,3 删除
	public void delete(){...}

get post put delete

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

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

<mvc:interceptors>
		配置自定义的处理器拦截器,例如:
		<mvc:interceptors>
			<mvc:interceptor>
				<mvc:mapping path="/**"/>
				<ref bean="handlerInterceptor1"/>
			</mvc:interceptor>
		</mvc:interceptors>

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

	<mvc:resources>
		逻辑静态资源路径到物理静态资源路径的对应.例如:
		<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 找不到静态资源错误。

8.@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也可以写在处理器类上

@RequestMapping("/test")
	@Controller
	public class HomeController {
		@RequestMapping("/home")
		public ModelAndView home(){
			ModelAndView mv = new ModelAndView("index");
			return mv;
		}
	}
表明该方法映射的url路径为/test/home