• spring Mvc 执行原理 及 xml注解配置说明 (六)


    Spring MVC 执行原理

     在 Spring Mvc 访问过程里,每个请求都首先经过 许多的过滤器,经 DispatcherServlet 处理;

    一个Spring MVC工程里,可以配置多个的 dispatcherServlet ,每个 DispatcherServlet 可以对应多个的 HandlerMapping ,每个 HandlerMapping 可以有自己的 Interceptor (拦截器)。

    1. 请求首先 由 前端 DispatcherServlet 捕获;

    2. DispatcherServlet 经过 HandlerMapping 获取适当的 Hanlder ,也即 Controller ,并返回给 DispatcherServlet;

    3. 如果有设置 拦截器,首选执行拦截器的 preHandler 方法,并把执行结果返回 DispatcherServlet;

    4. DispatcherServlet 根据捕获的请求,以及 Handler (Controller) ,获取到适当的 HandlerAdapter 处理,并把结果返回给 DispatcherServlet,结果为 (View 和 Model);

    5. 如果有设置拦截器,就是执行 拦截器的 postHandler 方法,并返回 DispatcherServlet;

    6. DispatcherServlet 根据获取的 view 跟 model 结合 ViewResolver,返回所指的 视图模板,并返回给 DispatcherServlet;

    7. DispatcherServlet 结合 视图模型跟model ,执行 render() 渲染界面,并返回给客户端;

    一个DispatcherServlet 都有自己 上下文配置文件, 这个配置继承自根的上下文配置文件;

    每个 DispatcherServlet 都可配置多个不同的 HandlerMapping 映射方式;每个 HandlerMapping 都实现了 Ordered 接口,可以上下文文件配置中设置优先使用的 HandlerMapping;

    用户的请求被 DispatcherServlet 捕获后,DispatcherServlet 使用优先级高的 HandlerMapping 查找可用的 Handler,如果没有找到合适的 Handler,就使优先级次之的 HandlerMapping 继续查找,直到找到为止;

    比如:Spring MVC 提供主要的 HanderMapping 有 DefaultAnnotationHandlerMapping, SimpleUrlHandlerMapping, BeanNameUrlHandlerMapping

    <bean id="defaultAnnoHandlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="order" value="1"/>
    </bean>
    
    <bean id="simpleUrlMapping" class="org.
    springframework.Web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="order" value="2"/>
        <!--  ... -->
    </bean>
    
    <bean id="beanNameUrlMapping" class="org.springframework.Web.servlet.
    handler.BeanNameUrlHandlerMapping">
        <!-- ... -->
    </bean>

    如果不为HandlerMapping明确指定order,那么默认值为Integer.MAX_VALUE,对应最低优先级。

    如果 DefaultAnnotationHandlerMapping 找到相应的 Handler ,就把数据传给相应的 Controller 类下的相应的 HandlerAdapter (方法处)处理;如果没有找到,就到 SimpleUrlHandlerMapping 处理;

    Spring MVC xml配置说明

     1. Web.xml 配置

    1>. 首先 Spring 监听器:

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

    它是整个 spring 框架的入口,会自动装载 应用程序根上下文的 配置文件;因为他实现了 ServletContextListener 这个接口,项目启动时,就会执行它实现的方法。

    2>. 根应用程序上下文配置:

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/root-context.xml</param-value>
        </context-param>

    也可叫 spring 全局配置,在这里可以配置 数据源 等一些 全局的spring 基本的配置;dispatcherServlet 可以继承自这里的配置;

    3>. diapatcherServlet 配置:

    <!-- Processes application requests -->
        <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
            
        <servlet-mapping>
            <servlet-name>appServlet</servlet-name>
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>

    load-on-startup 值为 "1" 表示该 servlet (dispatcherServlet 也是一个 servlet) 随 servlet 容器同时启动。

    dispatcherservlet 的 init-param 节点不是必须的,如果没有配置,该 dispatcherServlet 对应的 上下文配置文件 就会到 /WEB-INF/[servlet-name]-servlet.xml 加载;

    servlet-mapping 的 url-pattern 的值 "*.do" 表示,servlet 只会拦截 .do 后缀的请求,这种配置不会影响静态资源的访问。

    4>. 配置工程项目编码:

    <filter>
        <filter-name>CharacterEncodingFilter</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>
        <init-param>
          <param-name>forceEncoding</param-name>
          <param-value>true</param-value>
        </init-param>
      </filter>
      
      <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>

    该过滤器如果没有配置,请求访问可能会遇乱码;

    -------------------------------------------------------------

    现在配置 diapatcherServlet 对应的 上下文配置文件: /WEB-INF/spring/appServlet/servlet-context.xml (上面配置的)

    1>.  启动扫描所有的 Controller

    <context:component-scan base-package="com.study.web"/>

    主要作用于 @Controller,假如他的包结构是这样的:

    com.study.web

    |---controller

    |-----|-----abcController.java

       |-----cdeController.java

    那么上面的 base-package 写为 com.study.web.controller 也是正确的;

    2>. <mvc:annotation-driven /> 说明:

    是一种简写形式,可以让初学者快速成应用默认的配置方案,会默认注册 DefaultAnnotationHandleMapping以及AnnotionMethodHandleAdapter 这两个 Bean, 这两个 Bean ,前者对应类级别, 后者对应到方法级别;

    上在面的 DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter 是 Spring 为 @Controller 分发请求所必需的。

    annotation-driven 扫描指定包中类上的注解,常用的注解有:

    @Controller 声明Action组件
    @Service 声明Service组件 @Service("myMovieLister")
    @Repository 声明Dao组件
    @Component 泛指组件, 当不好归类时.
    @RequestMapping("/menu") 请求映射
    @Resource 用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName")
    @Autowired 用于注入,(srping提供的) 默认按类型装配
    @Transactional( rollbackFor={Exception.class}) 事务管理
    @ResponseBody
    @Scope("prototype") 设定bean的作用域

    3>. 视图解析类,对模型视图名称的解析,在请求时模型视图名称添加前后缀

    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <beans:property name="prefix" value="/WEB-INF/views/" />
            <beans:property name="suffix" value=".jsp" />
        </beans:bean>

    InternalResourceViewResolver 提供了对 JstlView 的支持: <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> 

    Spring Mvc 2.5 版本以前,提供了 注解 功能,实现了对 REST 的全面支持; REST 通过不带扩展名的 URL 来访问系统资源;REST 会把每个请求资源当成是静态,每个 URL 资源都是一个静态资源;

    它通 @RequestMapping主@PathVariable 注释提供访问映射,由@RequestMapping 中的 value 及 method 就可以支持Rest式的访问。


    通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。

    比如 使用的是 Velocity 技术,那么只要把 suffix 的 value 值改为 ".vm" 即可;

    4>.  Spring 拦截器:

    全局配置:

    <mvc:interceptors>
    
            <bean class="com.study.web.interceptor.MyInterceptor"></bean>
    
    </mvc:interceptors>

    基于路径的拦截器:

    <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**/*.do"/>
                <beans:bean class="com.study.interceptor.utils.StuInterceptor">
                    <beans:property name="startWrk">
                        <beans:value>1</beans:value>
                    </beans:property>
                    
                    <beans:property name="endWrk">
                        <beans:value>24</beans:value>
                    </beans:property>
                    
                    <beans:property name="redirectUrl">
                        <beans:value>http://127.0.0.1:8080/interceptor/static/timeout.htm</beans:value>
                    </beans:property>
                </beans:bean>
            </mvc:interceptor>
        </mvc:interceptors>

    相关的 Java 类 : StuInterceptor ( 该类的作用是拦截在指定的时间范围外,则页面请求转到/interceptor/static/timeout.htm )

    package com.study.interceptor.utils;
    
    import java.util.Calendar;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    //import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import com.study.interceptor.controller.HomeController;
    
    public class StuInterceptor  implements HandlerInterceptor /*extends HandlerInterceptorAdapter*/ {
        
        private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
        
        private int startWrk;
        private int endWrk;
        private String redirectUrl;
        
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            logger.info("Interceptor", "I am in Custom Interceptor!");
            
            Calendar cal = Calendar.getInstance();
            
            int hour = cal.get(Calendar.HOUR_OF_DAY);
            
            if (hour > startWrk && hour < endWrk) {
                return true;
            } else {
                response.sendRedirect(redirectUrl);
                return false;
            }
        }
        
        public int getStartWrk() {
            return this.startWrk;
        }
        
        public void setStartWrk(int startWrk) {
            this.startWrk = startWrk;
        }
        
        public int getEndWrk() {
            return this.endWrk;
        }
        
        public void setEndWrk(int endWrk) {
            this.endWrk = endWrk;
        }
        
        public String getRedirectUrl() {
            return this.redirectUrl;
        }
        
        public void setRedirectUrl(String redirectUrl) {
            this.redirectUrl = redirectUrl;
        }
    
        @Override
        public void postHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            // TODO Auto-generated method stub
            System.out.println("after posthandler");  
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            // TODO Auto-generated method stub
            System.out.println("after completion");  
        }
    }

    5>. 在 HandlerMapping 内配置拦截器:

    <!-- Mapping -->  
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  
        <property name="mappings">  
            <props>  
                <prop key="/abc.do">abcController</prop>  
                <prop key="def.kara">defController</prop>   
            </props>  
        </property>  
        <!-- 注册拦截器 -->  
        <property name="interceptors">  
            <list>  
                <ref bean="myInterceptor"></ref>  
            </list>  
        </property>  
    </bean>  
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name="interceptors"> 
    <list> 
    <bean class="com.study.web.MyInteceptor"></bean> 
    </list> 
    </property> 
    </bean>

    6>. 使用 placeHolder 加载应用程序属性配置文件

    <context:property-placeholder location="/WEB-INF/config.properties"/>

    比如 加载 mysql jdbc 配置信息:

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/springdb
    username=root
    password=123

    那么 mysql 的数据源配置的 驱动信息,用户信息,就可以配置成这样:

    <context:property-placeholder location="classpath:META-INF/mybatis/mysql.properties" />
        
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${driver}"></property>
            <property name="url" value="${url}"></property>
            <property name="username" value="${username}"></property>
            <property name="password" value="${password}"></property>
        </bean>
  • 相关阅读:
    Spring 注解详解01
    java 排序算法
    Java 文件拼接器
    oracle join
    Oracle 去重
    Java 笔试面试
    Oracle 笔试题02
    jvm性能查看常用命令
    关于对JMM(java内存模型)的个人理解
    RSA前端加密后端解密避免出现明文密码
  • 原文地址:https://www.cnblogs.com/editor/p/3895439.html
Copyright © 2020-2023  润新知