Spring MVC 角色划分清晰,分工明细,并且和 Spring 框架无缝结合,Spring MVC 已经成为当前最主流的框架之一。并且随着 Spring3.0 的发布,全⾯超越 Struts2,成为最优秀的 MVC 框架。
但是很多开发者只知道写,但不知道为什么这么写、不知道如何进行优化,这是非常危险的。而且根据我多年的从业经验,Spring MVC 的问题是面试过程中出现的高频考察点,大厂对开发者的源码分析更加看重。
我在这里分享一个,有很多干货,包含jvm,netty,spring,线程,spring cloud等详细讲解,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需:点击这里领取!!! 暗号:CSDN
很多大厂会问:Spring MVC 源码你了解多少?是否有源码分析能力?
比如以下两个内容:
Spring MVC 请求处理流程是怎样的?
Spring MVC 框架中 ha.handle 方法
入口
断点从口进入
作为一名开发者,掌握源码框架的深度分析能力,在面试及工作中显得尤为重要。如果你只会框架的使用,那么你能做的只能是一些简单的后台系统(业务级的系统),你永远无法去做部门级、公司级、Apache 级的项目。
工作原理
用户发送请求到springMVC框架提供的DispatcherServlet 这个前端控制器(了解Struts2的朋友也都知道其实Struts2也有一个前端控制器web.xml中的Filter标签就是)
前端控制器会去找处理器去映射器(HandlerMapping),处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理拦截器(如果有则生成)一并返回给DispatcherServlet.
根据处理器映射器返回的处理器,DispatcherServlet会找"合适"的处理器适配器(HandlerAdapter)
处理器适配器HandlerAdapter会去执行处理器(Handler开发的时候会被叫成controller 也叫前端控制器在struts2中action也是一个后端控制器)执行之前会有转换器,数据绑定,校验器等等完成上面这些才回去执行
Handler
后端控制器Handler执行完成后返回一个ModelAndView对象
处理器适配器HandlerAdapter会将这个ModelAndView返回给前端控制器DispatcherServlet.前端控制器会将ModelAndView对象交给视图解析器ViewResolver
视图解析器ViewResolver解析ModelAndView对象后返回逻辑视图.
前端控制器DispatcherServlet对逻辑视图进行渲染(填充数据)之后返回真正的物理View并响应给浏览器.
组件说明
DispatcherServlet : 前端控制器
用户请求到达前端控制器,相当于MVC中的C,而DispatcherServlet是整个流程的核心,它来调用其他组件来处理用户请求,前端控制器的存在降低了其他组件之间的耦合度.
HandlerMapping : 处理器映射器
它的作用好比去看电影,要拿着电影票根据电影票上面的座位号找到座位上的座位,其中座位就是Handler , 电影票以及上面的座位号就是URL.
HandlerMapping 负责根据用户请求找到Handler即处理器 , springMVC提供了不同的映射器实现了不同的映射方式,例如 : 配置文件方式, 实现接口方式 ,注解方式等.
Handler : 处理器
Handler 是后端控制器, 在前端控制器的控制下后端控制器对具体的用户请求进行处理,Handler涉及到具体的用户请求,所以一般情况下需要程序员根据自己的业务开发.
HandlerAdapter : 处理器适配器
通过HandlerAdapter对处理器进行执行 , 这是适配器模式的应用 , 通过适配器可以对更多类型的处理器进行执行.
播放的电影是3D的你看不清楚,电影院跟你说你要想看清电影就必须佩戴3D眼镜,也就是说Handler满足一定的要求猜可以被执行.
SpringMVC 的配置
前端控制器需要再web.xml中进行配置
<?xml version="1.0" encoding="UTF-8"?><!-- 配置前端控制器 -->
<servlet>
<servlet-name>web-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载前端控制器配置文件 上下文配置位置-->
<init-param>
<!-- 备注:
contextConfigLocation:指定 SpringMVC 配置的加载位置,如果不指定则默认加载
WEB-INF/[DispatcherServlet 的 Servlet 名字]-servlet.xml
-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
<!-- 表示随WEB服务器启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>web-dispatcher</servlet-name>
<!-- 备注:可以拦截三种请求
第一种:拦截固定后缀的url,比如设置为 *.do、*.action, 例如:/user/add.action 此方法最简单,不会导致静态资源(jpg,js,css)被拦截
第二种:拦截所有,设置为/,例如:/user/add /user/add.action此方法可以实现REST风格的url,
很多互联网类型的应用使用这种方式.但是此方法会导致静态文件(jpg,js,css)被拦截后不能正常显示.需要特殊处理
第三种:拦截所有,设置为/*,此设置方法错误,因为请求到Action,当action转到jsp时再次被拦截,提示不能根据jsp路径mapping成功
-->
<!-- 默认匹配所有的请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在spring/spring-web.xml配置视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置视图解析器 -->
<!-- InternalResourceViewResolver:支持JSP视图解析 -->
<!-- viewClass:JstlView 表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl的相关jar包; -->
<!-- prefix 和 suffix:查找视图页面的前缀和后缀,最终视图的址为: -->
<!-- 前缀+逻辑视图名+后缀,逻辑视图名需要在controller中返回ModelAndView指定,比如逻辑视图名为hello,-->
<!-- 则最终返回的jsp视图地址 "WEB-INF/jsp/hello.jsp" -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 决定视图类型,如果添加了jstl支持(即有jstl.jar),那么默认就是解析为jstl视图 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
在spring/spring-web.xml的注解模式
<!-- 自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter, -->
<!-- 可用在xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。 -->
<mvc:annotation-driven/>
在spring/spring-web.xml 配置扫描web相关的bean
<context:component-scan base-package=“com.controller” />
springMVC中的注解
@Controller注解
用于标识这个类是一个后端控制器(类似于struts2种的action), 主要作用就是接收页面的参数,转发页面
@controller源码:
@Target({ElementType.TYPE}) // 表明只能定义在类上面
@Retention(RetentionPolicy.RUNTIME) //保留策略是RUNTIME,在JVM加载类时,会把注解加载到JVM内存中(它是唯一可以用反射来读取注解的策略)
@Documented //@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Component //spring框架规定当一个类不好归类(service、dao、controller)的时候可以使用这个注解,由此可见即便好归类内部还是使用的@Component注解
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any
*/
String value() default "";
}
@RequestMapping
这个注解的作用是跟@Controller不一样,这个注解可以定义在类上,也可以定义在方法上.
/**
* 1.@RequestMapping:除了修饰方法,还可以修饰类
* 2.类定义处:提供初步的请求信息映射.相对于WEB应用的根目录(窄化请求)
* 3.方法处:提供进一步的细分映射信息。相对于类定义处的URL。
* 若类定义处为标注@RequestMapping,则方法出的URL相对于WEB应用的根目录
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {}; //限制请求方式
String[] params() default {}; //要求请求的URL包含指定的参数
}
代码实例
@Controller
@RequestMapping("/demo")
public class IndexController {
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String index(Model model, HttpServletRequest request) {
// 在游览器访问 http://localhost:8080/demo/test 将进入这里
model.addAttribute("originURL", "");
model.addAttribute("controllerName", "index");
return "index";
}
}
@RequestMapping还支持Ant方格的请求
?:匹配文件中的一个字符
*:匹配文件中任意字符
**:**匹配多层路径
/user/*/createUser : 匹配 -/user/aa/createUser 或者 -/user/aa/createUser
/user/**/createUser : 匹配 -/user/aa/createUser 或者 -/user/createUser 或者 -/user/aa/cc/createUser
/user/createUser?? : 匹配 -/user/aa/createUseraa
@PathVariable
这个注解支持现在当下较为流行的Restful风格的URL.先说这个注解的作用,支持将URL中的占位符参数绑定到目标方法上的参数上,该功能也是springMVC实现Restful风格URL的重要措施.
代码实现
// http://localhost:8080/demo/sss
@RequestMapping(value = "/{slug:.+}", method = RequestMethod.GET)
public String index2(@PathVariable("slug") String slug, Model model) {
LOG.info("DemoController index2 slug " + slug);
// common
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index2");
model.addAttribute("slug", slug);
return "demo";
}
//slug = sss
我们熟悉的请求应该是POST和GET请求,这两个请求也是最常用的,而实际上http1.1请求还是put , delete 等 8种来表名请求的动作
在springMVC中要实现put和delete请求需要再web.xml额外配置一个过滤器,这个过滤器的作用就是吧post请求变为put和delete请求.
@RequestParam
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";//值即为请求参数的参数名
boolean required() default true;//该参数是否是必须。默认值为true
String defaultValue() default ValueConstants.DEFAULT_NONE;//请求参数的默认值
}
// http://localhost:8080/demo/para?slug=google
@RequestMapping(value = "/para", method = RequestMethod.GET)
public String index3(@RequestParam(value = "slug", defaultValue = "") String slug, Model model) {
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index3");
model.addAttribute("slug", slug);
return "demo";
}
slug = google
另外还有一点要提示一下,参数没有加这个注解也能映射成功,这是应为 SpringMVC 框架支持请求参数和目标方法参数一致的时候可以省略这个注解。
@ResponseBody
/**
* Annotation that indicates a method return value should be bound to the web
* response body. Supported for annotated handler methods in Servlet environments.
*
* 这个注解指明一个方法的返回值应该绑定在 web response body 中,在 Servlet 环境中支持注解处理方法
*
* <p>As of version 4.0 this annotation can also be added on the type level in
* which case it is inherited and does not need to be added on the method level.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
代码
// http://localhost:8080/demo/json
@RequestMapping(value = "/json", method = RequestMethod.POST)
public @ResponseBody Domain index7(HttpServletRequest request, Model model) {
LOG.info("DemoController demo index7");
model.addAttribute("originURL", "demo/");
model.addAttribute("controllerName", "demo");
model.addAttribute("controllerMethod", "index7");
Domain domain = new Domain();
domain.setDomain("gggoogle.com");
domain.setId(100);
return domain;
}
/* response body
{
"id": 100,
"domain": "gggoogle.com"
}
*/