浅析 SpringMVC 原理和配置.

1、原理

      Spring MVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它可以帮你构建像Spring框架那样灵活和松耦合的Web应用程序,将请求处理的逻辑和视图中的渲染实现解耦。html

      

一、DispatcherServletSpring MVC的核心 。Spring MVC 中的请求页面都会委托给DispatcherServlet来执行处理。java

二、DispatcherServlet须要知道将请求发送给哪一个控制器,因此DispatcherServlet会查询一个或多个处理器映射(handler mapping) 来肯定请求的下一站在哪里。web

三、到了控制器(controller),请求会卸下其负载(用户提交的信息)并耐心等待控制器处理这些信息。spring

四、控制器在处理完成后,一般会产生一些信息,这些信息称为模型(model)。可是这个模型究竟是渲染哪一个页面的呢?因此控制器还会返回视图相关的东西。Spring 有个思想就是先后端分离,为了和视图解耦,因此控制器只返回了视图名。即,这里控制器返回了模型和视图名(modelAndViews)。json

tips:Model 实际上就是一个Map(也就是key-value对的集合),它会传递给视图,这样数据就能渲染到客户端了,当调用addAttribute()方法而且不指定key的时候,那么key会根据值的对象类型推断肯定,好比 List<Spittle>,那么推断他的 key 就是 spittleList。若是你但愿使用非Spring类型的话,那么能够用java.util.Map来代替Model。后端

五、MVC 要怎么依靠一个视图名找到对应的视图呢?答案就是 视图解析器(view resolver)。浏览器

六、视图解析器(ViewResolver )接口会根据试图名和Locale对象返回一个View实例。View 接口的任务就是接受Model 以及Servlet的request和response对象,并将输出结果渲染到response中。安全

七、视图 (好比 JSP)。最终会被相应的容器(好比Tomcat)解析成 HTML 页面,并响应用户的请求。服务器

tips实际上,设计良好的控制器自己只处理不多甚至不处理工做,而是将业务逻辑委托给一个或多个服务对象进行处理。session

2、使用 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一般是驱动应用后端的中间层和数据层组件。

一、实现:
    咱们经过继承 AbstractAnnotationConfigDispatcherServletInitializer 类来配置SpringMVC,以做为传统 XML 配置的替代方案。实际上,AbstractAnnotationConfigDispatcherServletInitializer  会 同时建立 DispatcherServlet 和 ContextLoaderListener 。固然,咱们须要手动配置咱们的映射路径、视图解析器 并启用组件扫描 以及一系列咱们能够自定义的配置。固然,若是咱们没有配置视图解析器,SpringMVC 会启用默认的视图解析器(经过查找 ID 与视图名称相匹配的Bean,而且这个Bena 要实现View 接口)。若是没有配置路径映射,DispatcherServlet会映射为应用的默认Servlet,因此它会处理全部的请求,包括对静态资源的请求,如图片和样式表等。

public class SplittrWebAppInitailzer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    /*返回会建立ContextLoaderListener 上下文*/
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    /*返回会建立 DispatcherServlet 上下文*/
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebConfig.class};
    }

    /*配置路径映射*/
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}
最小但可用的SpringMVC配置
@Configuration
@ComponentScan(basePackages = {"com"},
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)
        })
public class RootConfig {
}
RootConfig.java
@Configuration
@EnableWebMvc //启用SpringMVC,固然也可使用 <mvc:annotation-driven /> 注解驱动
@ComponentScan(basePackages = "com.controller")
public class WebConfig extends 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
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
WebConfig.java

    InternalResourceViewResolver所采起的方式并不那么直接。它遵循一种约定,会在视图名上添加前缀和后缀,进而肯定一个Web应用中视图资源的物理路径。当逻辑视图中包含斜线时,这个斜线也会带到资源的路径名中。
    经过  resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class)  配置了视图解析器的ViewClass后,能够保证 JSTL的格式化和信息标签可以得到Locale对象以及Spring中配置的信息资源。

二、测试:

    @RequestMapping(value = {"/","/home"},method = RequestMethod.GET)
    public String getHome(Model model){
        return "home";
    }
controller

       

三、请求参数说明

A、处理requet URL 部分(不含queryString)的注解: @PathVariable;
B、处理request header部分的注解: @RequestHeader, @CookieValue;
C、处理request body部分的注解:@RequestParam, @RequestBody;
D、处理attribute类型是注解: @SessionAttributes, @ModelAttribute;

@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等;
特殊状况下,也能够用来处理 application/x-www-form-urlencoded的内容,处理完的结果放在一个MultiValueMap<String, String>里。
返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。好比异步获取json数据,加上@responsebody后,会直接返回json数据

@ModelAttribute
该注解有两个用法,一个是用于方法上,一个是用于参数上;
用于方法上时: 一般用来在处理@RequestMapping以前,为请求绑定须要从后台查询的model;
用于参数上时: 用来经过名称对应,把相应名称的值绑定到注解的参数bean上;要绑定的值来源于:
    A) @SessionAttributes 启用的attribute 对象上;
    B) @ModelAttribute 用于方法上时指定的model对象;(会绑定须要的对象,好比model.addAttribute("pet", pet);
    C) 上述两种状况都没有时,new一个须要绑定的bean对象,而后把request中按名称对应的方式把值绑定到bean 对象的各个属性中。

(1) SpringMVC 在 处理表单的时候,能够接受一个POJO对象(不用添加任何注解)做为参数。对象中的属性会使用请求中同名的参数进行补充,默认调用@ModelAttribute。
                                                        能够接受一个基本数据类型(不用添加任何注解)做为参数。会使用请求中同名的参数进行补充,默认调用@RequestParam。

(2) 当InternalResourceViewResolver看到视图格式中的“redirect:”前缀时,它就知道要将其解析为重定向的规则,而不是视图的名称。InternalResourceViewResolver还能识别“forward:”前缀。当它发现视图格式中以“forward:”做为前缀时,请求将会前往(forward)指定的URL路径,而再也不是重定向。

分享一篇这方面讲得特别好的博客:http://blog.csdn.net/truong/article/details/28097837

四、添加自定义Servlet、Filter、Listener

public class MyServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("这是新建的Servlet");
    }
}
自定义Servlet类
public class MyServletInitializer implements WebApplicationInitializer {
    public void onStartup(ServletContext servletContext) throws ServletException {
        ServletRegistration.Dynamic myServlet = servletContext.addServlet("MyServlet", MyServlet.class);
        myServlet.addMapping("/myServlet");
    }
}
注册Servlet

    注册Filter、Listener 也能够用相似的方式。可是,若是你只是注册Filter,而且该Filter只会映射到DispatcherServlet上的话,那么在AbstractAnnotationConfigDispatcherServletInitializer中还有一种快捷方式。 

public class MyFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("过滤器的工做");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
自定义Filter类
    protected Filter[] getServletFilters() {
        return new Filter[]{new MyFilter()};
    }
在AbstractAnnotationConfigDispatcherServletInitializer的继承上添加...

3、使用 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</small-icon>
        <large-icon>/images/large.gif</large-icon>
    </icon>
    
<!--定义了WEB应用的名字--> <display-name>mvc</display-name>
<!--声明WEB应用的描述信息--> <description>mvc test</description>
<!--上下文参数:在servlet里面能够经过 getServletContext().getInitParameter("name")获得--> <!--设置根上下文配置文件位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
<!--配置过滤器--> <filter> <filter-name>encoding-filter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding-filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<!--配置监听器 注册ContextLoaderListener--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<!--配置Servlet 注册DispatcherServlet--> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!--会话超时配置(单位为分钟)--> <session-config> <session-timeout>120</session-timeout> </session-config>
<!--mime类型配置,用来指定对应的格式的浏览器处理方式--> <!--配置静态页面的打开编码--> <mime-mapping> <extension>htm</extension> <mime-type>text/html;charset=gb2312</mime-type> </mime-mapping> <mime-mapping> <extension>html</extension> <mime-type>text/html;charset=gb2312</mime-type> </mime-mapping> <!--欢迎文件页配置--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
<!--错误页面配置--> <!--配置了当系统发生404错误时,跳转到错误处理页面NotFound.jsp--> <error-page> <error-code>404</error-code> <location>/NotFound.jsp</location> </error-page> <!--配置了当系统发生java.lang.NullException(即空指针异常)时,跳转到错误处理页面error.jsp--> <error-page> <exception-type>java.lang.NullException</exception-type> <location>/error.jsp</location> </error-page> <!--以上是常见的配置,如下的东西也没搞懂怎么用,特别是 security-role 的含义指的是?--> <!--安全限制配置--> <!--与login-config元素联合使用,指定服务器应该怎样给试图访问受保护页面的用户受权--> <security-constraint> <web-resource-collection> <web-resource-name>ProtectedArea</web-resource-name> <url-pattern>/resources/*</url-pattern> <!--若是没有<http-method>方法,表示禁止全部的HTTP方法访问对应的资源--> <http-method>GET</http-method> </web-resource-collection> <!--哪些用户应该具备受保护资源的访问权 若是没有 <auth-constraint> ,配置其实是不起做用的。 若是内容为空,表示全部的身份都被禁止访问--> <auth-constraint> <role-name>ALL Role</role-name> </auth-constraint> </security-constraint> <!--登陆验证配置四种认证类型 --> <!-- BASIC:HTTP规范,Base64 这种方式被认为是最不安全的认证,由于它没有提供强烈的加密措施 --> <login-config> <auth-method>BASIC</auth-method> </login-config> <!-- DIGEST:HTTP规范,数据完整性强一些,但不是SSL 相比于BASIC认证,它是种比较安全的认证,它在认证时将请求数据 经过MD5的加密方式进行认证 --> <login-config> <auth-method>DIGEST</auth-method> </login-config> <!-- CLIENT-CERT:J2EE规范,数据完整性很强,公共钥匙(PKC) 这是一种基于客户端证书的认证方式,比较安全。但缺陷是在没有安全证书的客户端没法使用 --> <login-config> <auth-method>CLIENT-CERT</auth-method> </login-config> <!-- FORM:J2EE规范,数据完整性很是弱,没有加密,容许有定制的登陆界面 这是种基础自定义表单的认证,你能够指定登陆时的验证表单 --> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/error.jsp</form-error-page> </form-login-config> </login-config> <!--安全角色--> <!--这些角色将出如今servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可以使高级IDE处理安全信息更为容易(没看懂这句话)--> <security-role> <role-name>ALL Role</role-name> </security-role> </web-app>

tipsweb.xml 的加载顺序是:ServletContext -> context-param -> listener -> filter -> servlet ,而同个类型之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。

4、结语 

    2017年最后一篇博文了,坚持在2017年的最后一个晚上写完。毕竟2017的事总很差意思拖一年呀!坚持写博客真是个好习惯,好记性毕竟不如白纸黑字来的牢靠啊,若是把记性比做网上搜索的话,博客就是本身的一份离线存储。

    原本想好好回顾下2017,打一大堆满满的文字,装一个文艺的青年。真到落笔的时候,什么都不想写。敬往事一杯酒,悠悠岁月不回头!

    祝你们新年快乐!2018!我来了......

相关文章
相关标签/搜索