zoukankan      html  css  js  c++  java
  • Spring Boot框架

    一、SpringMVC发展史

      2004年 Spring Framework 1.0 final 正式问世,当时只包含一个完整的项目,他把所有的功能都集中在一个项目中,其中包含了核心的 IOC、AOP,同时也包含了其他的诸多功能,例如:JDBC、Mail、ORM、事务、定时任务等。Spring团队超前的眼光,在第一个版本的时候已经支持了很多第三方的框架,例如:Hibernate、ibatis、模板引擎等,为其后来快速发展奠定了基础。

      Spring 2.x增加对注解的支持,支持了基于注解的配置。

      Spring 3.x支持了基于类的配置。

      Spring 4.x全面支持jdk1.8,并引入RestController注解,直到今天依然是接口编程的首选。

      现在最近GA版本Spring 5.2.1,Spring正在稳步向前,越走越稳。

    二、总体架构

      

       引入Intercepter(拦截器),类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,本质上也会AOP,把符合横切关注点的所有功能都可以放入拦截器实现,Intercepter面向的是页面处理Handler(Controller),允许开发人员自定义某一请求路径上的处理程序执行链。

       引入HandlerMapping(路由器),定义了Spring MVC的路由机制,把请求地址映射到对应的Controller和Action。

       引入HandlerAdapter(适配器),这也是核心类,调用handler方法,处理请求数据,封装返回数据。

       引入ViewResolver(视图解析器),把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把View对象呈现给客户端的是View对象本身,而ViewResolver是把逻辑视图名称解析为对象的View对象。

    三、分类概述

    1、Intercepter

    a) 接口源码介绍

     1 public interface HandlerInterceptor {
     2 
     3     /**
     4      * 这个方法在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor 是链 
     5          * 式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor          
     6          * 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 
     7          * Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化 
     8          * 操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请 
     9          * 求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为 
    10          * false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返 
    11          * 回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是 
    12          * 最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
    13      */
    14     default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    15             throws Exception {
    16 
    17         return true;
    18     }
    19 
    20     /**
    21      * 这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是 
    22          * 它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个 
    23          * 方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方 
    24          * 法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的 
    25          * postHandle 方法反而会后执行。
    26      */
    27     default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    28             @Nullable ModelAndView modelAndView) throws Exception {
    29     }
    30 
    31     /**
    32      * 该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才 
    33          * 会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 
    34          * 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
    35      */
    36     default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
    37             @Nullable Exception ex) throws Exception {
    38     }
    39 
    40 }

    b) 示例程序

      1 package com.pine.property.manage.service.common;
      2 
      3 import java.util.List;
      4 
      5 import javax.servlet.http.HttpServletRequest;
      6 import javax.servlet.http.HttpServletResponse;
      7 
      8 import org.springframework.beans.factory.annotation.Autowired;
      9 import org.springframework.web.servlet.HandlerInterceptor;
     10 import org.springframework.web.servlet.ModelAndView;
     11 
     12 import com.baiyyy.core.common.AuthConstant;
     13 import com.baiyyy.core.common.BizException;
     14 import com.baiyyy.core.common.StatusCode;
     15 import com.baiyyy.core.service.IBaseService;
     16 import com.baiyyy.core.util.CookieUtil;
     17 import com.baiyyy.core.util.JedisUtil;
     18 import com.baiyyy.core.util.StringUtil;
     19 import com.pine.property.manage.entity.Account;
     20 import com.pine.property.manage.entity.Menu;
     21 import com.pine.property.manage.service.auth.message.FunctionData;
     22 /**
     23  * 1、每次请求延长登录缓存时间
     24  * 2、验证url访问权限
     25  * @author pinenut
     26  * @date 2018年2月22日
     27  */
     28 @Component
     29 public class AuthIntercepter implements HandlerInterceptor{
     30 
     31     @Autowired
     32     public HttpServletRequest request;
     33     
     34     private IBaseService<List<FunctionData>, Account> userAuthService;
     35     
     36     public boolean preHandle(HttpServletRequest httpRequest,
     37             HttpServletResponse response, Object handler) throws Exception {
     38 
     39         String currentUrl = httpRequest.getServletPath();
     40         
     41         //1、校验url权限
     42         this.authUrl(currentUrl);
     43         //2、更新用户cookie时间
     44         String cookieId = CookieUtil.getCookie(httpRequest.getCookies(), AuthConstant.SESSION_ID);
     45         JedisUtil.getInstance().STRINGS.setEx(cookieId, CommonConstant.USER_LOGIN_CONFIG_TIME_OUT, JedisUtil.getInstance().STRINGS.get(cookieId));
     46         
     47         
     48         return true;
     49         
     50     }
     51     
     52     /**
     53      * 校验url访问权限
     54      * @author liuqingsong
     55      * @date 2018年2月22日
     56      */
     57     private void authUrl(String currentUrl){
     58         List<FunctionData> functionDataList = this.userAuthService.process(null);
     59         boolean authed = false;
     60         for(FunctionData item : functionDataList){
     61             if(item.getMenuList() == null || item.getMenuList().size() == 0){
     62                 //只有一级菜单
     63                 if(currentUrl.toLowerCase().contains(item.getFunctionEntry().toLowerCase())){
     64                     authed = true;
     65                     break;
     66                 }
     67             }
     68             else{
     69                 //多级菜单
     70                 for(Menu menu : item.getMenuList()){
     71                     if(currentUrl.toLowerCase().contains(menu.getMenuUrl().toLowerCase())){
     72                         authed = true;
     73                         break;
     74                     }
     75                 }
     76                 if(authed){
     77                     break;
     78                 }
     79             }
     80         }
     81         
     82         if(!authed){
     83             throw new BizException(StatusCode.FAILURE_LOGIC, "用户无此功能权限!");
     84         }
     85     }
     86     
     87 
     88     @Override
     89     public void postHandle(HttpServletRequest request,
     90             HttpServletResponse response, Object handler,
     91             ModelAndView modelAndView) throws Exception {
     92         // TODO Auto-generated method stub
     93         
     94     }
     95 
     96     @Override
     97     public void afterCompletion(HttpServletRequest request,
     98             HttpServletResponse response, Object handler, Exception ex)
     99             throws Exception {
    100         // TODO Auto-generated method stub
    101         
    102     }
    103 
    104     public IBaseService<List<FunctionData>, Account> getUserAuthService() {
    105         return userAuthService;
    106     }
    107 
    108     public void setUserAuthService(
    109             IBaseService<List<FunctionData>, Account> userAuthService) {
    110         this.userAuthService = userAuthService;
    111     }
    112     
    113     
    114     
    115 }

     c) 多拦截器执行顺序

    2、HandlerMapping

    a) springmvc默认实现HandlerMapping 

    以下面UserController为例

    package com.baiyyy.basic.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.ModelAndView;
    /*
     * 通用页面
     * @author Administrator
     *
     */
    @RestController
    @RequestMapping(value = "/user")
    public class UserController {
    
         * 登陆页面
         * @return
         */
        @RequestMapping(value = "/loginFrame", method = RequestMethod.GET)
        public ModelAndView loginFrame(){
            ModelAndView result = new ModelAndView("login");
            return result;
        }
    }
    实现类 继承父类 说明 使用
    ControllerClassNameHandlerMapping AbstractUrlHandlerMapping 根据类名访问 Controller
    访问地址: http://ip:port/项目名/user;注:类的首字母要小写
    ControllerBeanNameHandlerMapping AbstractUrlHandlerMapping 根据 Bean 名访问 Controller
    访问地址: http://ip:port/项目名/userController;
    BeanNameUrlHandlerMapping AbstractUrlHandlerMapping 利用 BeanName 来作为 URL 使用
    <bean id="userController" name="/users" 
    class="com.qunar.web.controller.UserController"></bean>
    访问地址: http://ip:port/项目名/users;
    注:bean name属性必须要以“/”开头
    SimpleUrlHandlerMapping AbstractUrlHandlerMapping 可以将 URL 与处理器的定义分离,还可以对 URL 进行统一的映射管理
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/userlist.htm">userController</prop>
            </props>
        </property>
    </bean>
    访问地址: http://ip:port/项目名/userlist.htm;
     
     RequestMappingHandlerMapping  AbstractHandlerMethodMapping  @RequestMapping注解定义url  
    访问地址: http://ip:port/项目名/user/loginFrame;

     b)自定义handlerMapping

    package com.baiyyy.core.common;
    
    import java.lang.reflect.Method;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.AbstractHandlerMapping;
    
    /**
     * 定义restful接口handlermapping路由机制
     * 默认以Controller/Action作为url,访问地址不区分大小写
     * @author pinenut
     * @date   2016-06-23
     */
    public class RestfulHandlerMethodMapping extends AbstractHandlerMapping implements InitializingBean {
        
        private final Map<String, HandlerMethod> urlMap = new LinkedHashMap<String, HandlerMethod>();
        
        @Override
        public void afterPropertiesSet() throws Exception {
            this.initHandlerMethods();
        }
    
        @Override
        protected Object getHandlerInternal(HttpServletRequest request)
                throws Exception {
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request).toLowerCase();
            if (logger.isDebugEnabled()) {
                logger.debug("Looking up handler method for path " + lookupPath);
            }
            HandlerMethod handlerMethod = this.urlMap.get(lookupPath);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        
        private void initHandlerMethods(){
            if (logger.isDebugEnabled()) {
                logger.debug("Looking for request mappings in application context: " + getApplicationContext());
            }
            
            Map<String, Object> controllerBeans = this.getApplicationContext().getBeansWithAnnotation(RestController.class);
            for(Map.Entry<String, Object> controller : controllerBeans.entrySet()){
                Object handler = controller.getValue();
                Class<?> clazz = handler.getClass();
                Method[] methodList = clazz.getDeclaredMethods();
                for(Method method : methodList){
                    HandlerMethod handlerMethod = new HandlerMethod(handler, method);
                    
                    String url =  ("/" + clazz.getSimpleName().replaceAll("Controller", "") + "/" + method.getName()).toLowerCase();
                    
                    logger.debug(url);
                    this.urlMap.put(url, handlerMethod);
                }
            }
        }
    
        protected HandlerMethod createHandlerMethod(Object handler, Method method) {
            HandlerMethod handlerMethod;
            if (handler instanceof String) {
                String beanName = (String) handler;
                handlerMethod = new HandlerMethod(beanName,
                        getApplicationContext().getAutowireCapableBeanFactory(), method);
            }
            else {
                handlerMethod = new HandlerMethod(handler, method);
            }
            return handlerMethod;
        }
        
        
    }

    3、HandlerAdapter

    a) springmvc默认实现的HandlerAdapter 如下

    实现类 说明 使用场景 
    RequestMappingHandlerAdapter 可以执行 HadnlerMethod 类型的 Handler  主要是适配注解类处理器
    HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler  主要是适配静态资源处理器
    SimpleControllerHandlerAdapte 可以执行 Controller 类型的 Handler  适配实现了Controller接口或Controller接口子类的处理器

    4、ViewResolver

     a) InternalResourceViewResolver

    <!-- 视图渲染 -->
        <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- 视图前缀 -->
            <property name="prefix" value="/WEB-INF/view/"></property>
            <!-- 视图后缀 -->
            <property name="suffix" value=".jsp"></property>
            <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>
            <property name="order" value="1"/>
        </bean>

      InternalResourceViewResolver也是使用的最广泛的一个视图解析器。我们可以把InternalResourceViewResolver解释为内部资源视图解析器,这就是InternalResourceViewResolver的一个特性。InternalResourceViewResolver会把返回的视图名称都解析为InternalResourceView对象,InternalResourceView会把Controller处理器方法返回的模型属性都存放到对应的request属性中,然后通过RequestDispatcher在服务器端把请求forword重定向到目标URL。比如在InternalResourceViewResolver中定义了prefix=/WEB-INF/,suffix=.jsp,然后请求的Controller处理器方法返回的视图名称为test,那么这个时候InternalResourceViewResolver就会把test解析为一个InternalResourceView对象,先把返回的模型属性都存放到对应的HttpServletRequest属性中,然后利用RequestDispatcher在服务器端把请求forword到/WEB-INF/test.jsp。这就是InternalResourceViewResolver一个非常重要的特性,我们都知道存放在/WEB-INF/下面的内容是不能直接通过request请求的方式请求到的,为了安全性考虑,我们通常会把jsp文件放在WEB-INF目录下,而InternalResourceView在服务器端跳转的方式可以很好的解决这个问题。下面是一个InternalResourceViewResolver的定义,根据该定义当返回的逻辑视图名称是test的时候,InternalResourceViewResolver会给它加上定义好的前缀和后缀,组成“/WEB-INF/test.jsp”的形式,然后把它当做一个InternalResourceView的url新建一个InternalResourceView对象返回。

    b) BeanNameViewResolver

    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    
    <bean id="test" class="org.springframework.web.servlet.view.InternalResourceView">
        <property name="url" value="/index.jsp"/>
    </bean>
    @RequestMapping("/test")
    public String testXmlViewResolver() {
        return "test";
    }

      通过把返回的逻辑视图名称去匹配定义好的视图bean对象,BeanNameViewResolver要求视图bean对象都定义在Spring的application context中。

    四、总体流程

      

        

  • 相关阅读:
    结构struct 联合Union和枚举Enum的细节讨论
    ubuntu 查询 修改 时间
    在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)
    浅谈Android系统开发中LOG的使用
    如何单独编译Android源代码中的模块
    在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
    在Ubuntu上为Android系统编写Linux内核驱动程序
    在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务
    在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
    在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
  • 原文地址:https://www.cnblogs.com/pinenut/p/11851734.html
Copyright © 2011-2022 走看看