浅析 SpringMVC 原理和配置.
一、原理? ??? Spring MVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序,将请求处理的逻辑和视图中的渲染实现解耦。 ? ? ?? 1、DispatcherServlet是Spring MVC的核心 。Spring MVC 中的请求页面都会委托给DispatcherServlet来执行处理。 2、DispatcherServlet需要知道将请求发送给哪个控制器,所以DispatcherServlet会查询一个或多个处理器映射(handler mapping) 来确定请求的下一站在哪里。 3、到了控制器(controller),请求会卸下其负载(用户提交的信息)并耐心等待控制器处理这些信息。 4、控制器在处理完成后,通常会产生一些信息,这些信息称为模型(model)。但是这个模型到底是渲染哪个页面的呢?所以控制器还会返回视图相关的东西。Spring 有个思想就是前后端分离,为了和视图解耦,所以控制器只返回了视图名。即,这里控制器返回了模型和视图名(modelAndViews)。 tips:Model 实际上就是一个Map(也就是key-value对的集合),它会传递给视图,这样数据就能渲染到客户端了,当调用addAttribute()方法并且不指定key的时候,那么key会根据值的对象类型推断确定,比如 List<Spittle>,那么推断他的 key 就是 spittleList。如果你希望使用非Spring类型的话,那么可以用java.util.Map来代替Model。 5、MVC 要怎么依靠一个视图名找到对应的视图呢?答案就是 视图解析器(view resolver)。 6、视图解析器(ViewResolver )接口会根据试图名和Locale对象返回一个View实例。View 接口的任务就是接受Model 以及Servlet的request和response对象,并将输出结果渲染到response中。 7、视图?(比如 JSP)。最终会被相应的容器(比如Tomcat)解析成 HTML 页面,并响应用户的请求。 tips:实际上,设计良好的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一个或多个服务对象进行处理。 二、使用 Java 配置? ??按照传统的方式,像 DispatcherServlet 这样的Servlet会配置在web.xml文件中 ,但是,借助于Servlet 3规范和Spring 3.1的功能增强,这种方式已经不是唯一的方案了 。我们会使用Java将DispatcherServlet配置在Servlet容器中。开始前,我们先来理解下 DispatcherServlet 和 Servlet 监听器(也就是ContextLoaderListener) 这两个应用上下文 。 DispatcherServlet 上下文:当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类(即带有@configuration注解的配置类)中所声明的bean,主要是Web 组件中的 bean, 包括 控制器(controller)、映射器(handler mapping)、视图解析器(view resolver)等。 ContextLoaderListener 上下文:这个上下文 由?ContextLoaderListener? 创建,主要负责加载应用中的其他 bean 。这些bean通常是驱动应用后端的中间层和数据层组件。 1、实现: public class SplittrWebAppInitailzer extends AbstractAnnotationConfigDispatcherServletInitializer { /*返回会创建ContextLoaderListener 上下文*/ @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[]{RootConfig.class}; } 返回会创建 DispatcherServlet 上下文[] getServletConfigClasses() { new Class<?>[]{WebConfig.配置路径映射protected String[] getServletMappings() { new String[]{"/"}; } }最小但可用的SpringMVC配置 @Configuration @ComponentScan(basePackages = {"com"},excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.) }) RootConfig { }RootConfig.java @Configuration @EnableWebMvc //启用SpringMVC,当然也可以使用 <mvc:annotation-driven /> 注解驱动 @ComponentScan(basePackages = "com.controller") class WebConfig WebMvcConfigurerAdapter { /** * 在查找的时候,它会在视图名称上加一个特定的前缀和后缀 * (例如,名为home的视图将会解析为/WEB-INF/pages/home.jsp)。 * * @return @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/pages/"); resolver.setSuffix(".jsp"); 设置是否把所有在上下文中定义的bean作为request属性可公开访问。 这样在JSP 2.0中可使用${}来存取,JSTL中使用c:out。 默认为false。 resolver.setExposeContextBeansAsAttributes(true); resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); 设置解析JSTL return resolver; } * 通过调用DefaultServlet-HandlerConfigurer的enable()方法, * 我们要求DispatcherServlet将对静态资源的请求转发到Servlet容器 * 中默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求 * * @param configurer void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }WebConfig.java ? ? InternalResourceViewResolver所采取的方式并不那么直接。它遵循一种约定,会在视图名上添加前缀和后缀,进而确定一个Web应用中视图资源的物理路径。当逻辑视图中包含斜线时,这个斜线也会带到资源的路径名中。 2、测试:@RequestMapping(value = {"/","/home"},method = RequestMethod.GET) String getHome(Model model){ return "home"; }controller ? ? ? ? 3、请求参数说明A、处理requet URL 部分(不含queryString)的注解: @PathVariable; @RequestParam:可以处理get方式中的queryString的值,也可以处理post方式的body data 的值。用来处理Content-Type 为 application/x-www-form-urlencoded 编码的内容,提交方式GET、POST。 @RequestBody: 该注解常用来处理Content-Type: 不是application/x-www-form-urlencoded编码的内容,例如application/json,application/xml等; @modelattribute (1) SpringMVC 在 处理表单的时候,可以接受一个POJO对象(不用添加任何注解)作为参数。对象中的属性会使用请求中同名的参数进行补充,默认调用@modelattribute。 (2) 当InternalResourceViewResolver看到视图格式中的“redirect:”前缀时,它就知道要将其解析为重定向的规则,而不是视图的名称。InternalResourceViewResolver还能识别“forward:”前缀。当它发现视图格式中以“forward:”作为前缀时,请求将会前往(forward)指定的URL路径,而不再是重定向。 分享一篇这方面讲得特别好的博客:http://blog.csdn.net/truong/article/details/28097837 4、添加自定义Servlet、Filter、Listener class MyServlet HttpServlet { protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doGet(request,response); } void doGet(HttpServletRequest request,IOException { System.out.println("这是新建的Servlet"); } }自定义Servlet类 class MyServletInitializer implements WebApplicationInitializer { void onStartup(ServletContext servletContext) ServletException { ServletRegistration.Dynamic myServlet = servletContext.addServlet("MyServlet",MyServlet.); myServlet.addMapping("/myServlet"); } }注册Servlet ? ? 注册Filter、Listener 也可以用类似的方式。但是,如果你只是注册Filter,并且该Filter只会映射到DispatcherServlet上的话,那么在AbstractAnnotationConfigDispatcherServletInitializer中还有一种快捷方式。? class MyFilter Filter { destroy() { } void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain) ); chain.doFilter(req,resp); } void init(FilterConfig config) ServletException { } }自定义Filter类 Filter[] getServletFilters() { new Filter[]{ MyFilter()}; }在AbstractAnnotationConfigDispatcherServletInitializer的继承上添加... 三、使用 XML 配置<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!--Web应用图标:指出IDE和GUI工具用来表示Web应用的大图标和小图标--> icon> small-icon>/images/small.gif</large-icon>/images/large.gif> > tips:web.xml 的加载顺序是:ServletContext ->?context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。 四、结语?? ? 2017年最后一篇博文了,坚持在2017年的最后一个晚上写完。毕竟2017的事总不好意思拖一年呀!坚持写博客真是个好习惯,好记性毕竟不如白纸黑字来的牢靠啊,如果把记性比作网上搜索的话,博客就是自己的一份离线存储。 ? ? 本来想好好回顾下2017,打一大堆满满的文字,装一个文艺的青年。真到落笔的时候,什么都不想写。敬往事一杯酒,悠悠岁月不回头! ? ? 祝大家新年快乐!2018!我来了...... (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |