控制器Controller
- 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
- 控制器负责解析用户的请求并将其转换为一个模型。
- 在Spring MVC中一个控制器类可以包含多个方法
- 在Spring MVC中,对于Controller的配置方式有很多种
在开发中我们一般使用注解方式进行开发
Controller接口
@FunctionalInterface
public interface Controller {
/**
* Process the request and return a ModelAndView object which the DispatcherServlet
* will render. A {@code null} return value is not an error: it indicates that
* this object completed request processing itself and that there is therefore no
* ModelAndView to render.
* @param request current HTTP request
* @param response current HTTP response
* @return a ModelAndView to render, or {@code null} if handled directly
* @throws Exception in case of errors
*/
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
主要是看一下这里的注释,是处理请求并返回一个ModelAndView对象,DispatcherServlet将渲染该对象。 一个null的返回值并不是一个错误:它表示这个对象自己完成了请求处理,没有ModelAndView可以渲染。
在AbstractController类中实现了Controller类,handleRequest实现如下
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}
// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
过程可以大致看一下,首先是设置header的allow,允许资源可以用哪些HTTP方法进行请求,比如Allow: GET, HEAD,Allow:*,接着委托给WebContentGenerator进行检查和准备,然后如果需要,在同步块中执行handleRequestInternal,否则直接执行handleRequestInternal方法。
handleRequestInternal方法的实现
/**
* Retrieves the URL path to use for lookup and delegates to
* {@link #getViewNameForRequest}. Also adds the content of
* {@link RequestContextUtils#getInputFlashMap} to the model.
*/
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
String viewName = getViewNameForRequest(request);
if (logger.isTraceEnabled()) {
logger.trace("Returning view name '" + viewName + "'");
}
return new ModelAndView(viewName, RequestContextUtils.getInputFlashMap(request));
}
getViewNameForRequest获取视图的名字,再进行logger.trace,最后创建ModelAndView对象返回。
但是在真正用的时候我们是只重写handleRequest,封装Model,返回ModelAndView就行了。
public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
//封装对象,放在ModelAndView中。Model
mv.addObject("msg","HelloSpringMVC!");
//封装要跳转的视图,放在ModelAndView中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
说明:
- 实现接口Controller定义控制器是较老的办法
- 缺点:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦;(网站开发随便就是几十个接口,创建几十个类是不能接受的)
Controller注解
@Controller注解类型用于声明Spring类的实例是一个控制器(被spring接管,注解@Contronller的类下面的所有的String方法,返回值都会被视图解析器解析,如果可以跳转就跳转),Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描。<context:component-scan base-package="com.kuang.controller"/>
Controller注解的定义,很简单里面用的就就一个value
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
注解本质就只是一个接口,程序处理的难点是在反射,而框架把这些处理内容隐藏起来了,除非阅读源码很难接触到。