• spring之mvc原理分析及简单模拟实现


      在之前的一篇博客中已经简单的实现了spring的IOC和DI功能,本文将在之前的基础上实现mvc功能。

    一 什么是MVC

      MVC简单的说就是一种软件实现的设计模式,将整个系统进行分层,M(model 数据模型,业务逻辑层) 、V(view 视图层)、C(controller 控制器调度),实现应用程序的分层开发。实现原理如下图:

    主要执行步骤:

      1 用户在发起request请求给前端控制器;

      2 控制器接收到请求后,经过一系统的过滤器,找到对应的请求处理映射;

      3 根据请求映射获得请求处理适配器;

      4 适配器将对请求进行处理并将处理结果(ModelAndView)返回给前端控制器;

      5 前端处理器将处理结果交给视图解析器解析;

      6 视图解析器将解析的结果返回给控制器;

      7 控制器将结果返回给用户。

    二 简单模拟实现

      创建一个核心控制器(DispatcherServlet)继承HttpServlet,配置在web.xml中,并指定要初始化的参数

     1 <!-- 核心servlet -->
     2     <servlet>
     3         <servlet-name>dispatcherServlet</servlet-name>
     4         <servlet-class>org.wl.test.spring.mvc.DispatcherServlet</servlet-class>
     5         <!-- 初始化参数 -->
     6         <init-param>
     7             <param-name>contextConfigLocation</param-name>
     8             <param-value>classpath:application.properties</param-value>
     9         </init-param>
    10         <!-- 启动时加载 -->
    11         <load-on-startup>0</load-on-startup>
    12     </servlet>
    13     <servlet-mapping>
    14         <servlet-name>dispatcherServlet</servlet-name>
    15         <url-pattern>/</url-pattern>
    16     </servlet-mapping>

      核心控制器,在web容器启动时执行init方法进行文件的初始化

      1 public class DispatcherServlet extends HttpServlet {
      2 
      3     private List<HandlerMapping> handlerMappingList = new ArrayList<HandlerMapping>();
      4 
      5     private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>();
      6 
      7     @Override
      8     public void init(ServletConfig config) throws ServletException {
      9         // web.xml 配置核心servlet 获取配置的信息
     10         String configFile = config.getInitParameter("contextConfigLocation");
     11         //定义一个当前上下文对象,实现基础包的扫描、IOC、DI
     12         ApplicationContext context = new ApplicationContext(configFile.replace("classpath:", ""));
     13         //获取扫描到的有Controller注解的类
     14         List<Object> controllerList = context.getControllerList();
     15         //初始化HandlerMapping
     16         initHandlerMapping(controllerList);
     17         //初始化HandlerAdapter
     18         initHandlerAdapter();
     19     }
     20 
     21 
     22     private void initHandlerAdapter() {
     23         if (handlerMappingList.size() == 0) {
     24             return;
     25         }
     26 
     27         handlerMappingList.forEach(handlerMapping -> {
     28             Method method = handlerMapping.getMethod();
     29             //方法的参数  <参数索引,参数名字>
     30             Map<Integer, String> paramMap = new HashMap<>();
     31 
     32             //使用了注解参数
     33             Annotation[][] annos = method.getParameterAnnotations();
     34             if(annos.length > 0){
     35                 for(int i=0; i<annos.length; i++){
     36                     for(Annotation anno : annos[i]){
     37                         if(anno instanceof RequestParam){
     38                             RequestParam requestParam = (RequestParam) anno;
     39                             String paramName = requestParam.value();
     40 
     41                             paramMap.put(i, paramName);
     42                         }
     43                     }
     44                 }
     45             }
     46             //直接用的servlet参数,如HttpServletRequest
     47             Class<?>[] paramTypes = method.getParameterTypes();
     48             if(paramTypes.length > 0){
     49                 for(int i=0; i<paramTypes.length; i++){
     50                     Class<?> typeClass = paramTypes[i];
     51                     if (typeClass == HttpServletRequest.class || typeClass == HttpServletResponse.class) {
     52                         String paramName = typeClass.getName();
     53 
     54                         paramMap.put(i, paramName);
     55                     }
     56                 }
     57             }
     58 
     59             HandlerAdapter handlerAdapter = new HandlerAdapter(paramMap);
     60             adapterMap.put(handlerMapping, handlerAdapter);
     61         });
     62     }
     63 
     64     /**
     65      * 完成请求方法与请求处理实例的映射关系
     66      * @param controllerList
     67      */
     68     private void initHandlerMapping(List<Object> controllerList) {
     69         if(controllerList.size() == 0){
     70             return;
     71         }
     72 
     73         controllerList.forEach(controllerObj -> {
     74             //类上的请求路径
     75             String classRequestUrl = "";
     76             if (controllerObj.getClass().isAnnotationPresent(RequestMapping.class)) {
     77                 RequestMapping classRequestMapping = controllerObj.getClass().getAnnotation(RequestMapping.class);
     78                 if(classRequestMapping != null){
     79                     classRequestUrl += urlHandler(classRequestMapping.value());
     80                 }
     81             }
     82             //方法上的请求路径
     83             Method[] methods = controllerObj.getClass().getDeclaredMethods();
     84             if(methods.length > 0){
     85                 for(int i=0; i<methods.length; i++){
     86                     String methodRequestUrl = "";
     87                     Method method = methods[i];
     88                     //必须是public修饰的方法
     89                     if(method.getModifiers() == Modifier.PUBLIC){
     90                         if (method.isAnnotationPresent(RequestMapping.class)) {
     91                             RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
     92                             if(methodRequestMapping != null){
     93                                 methodRequestUrl += urlHandler(methodRequestMapping.value());
     94                             }
     95 
     96                             String requestUrl = classRequestUrl + methodRequestUrl;
     97 
     98                             HandlerMapping handlerMapping = new HandlerMapping();
     99                             handlerMapping.setMethod(method);
    100                             handlerMapping.setUrl(requestUrl);
    101 
    102                             handlerMapping.setControllerInstance(controllerObj);
    103                             handlerMappingList.add(handlerMapping);
    104                         }
    105                     }
    106 
    107                 }
    108             }
    109 
    110         });
    111 
    112     }
    113 
    114     /**
    115      * url处理
    116      * @param url
    117      * @return
    118      */
    119     public String urlHandler( String url){
    120         if(!url.startsWith("/")){
    121             url = "/" + url;
    122         }
    123         if(url.endsWith("/")){
    124             url = url.substring(0, url.length() - 1);
    125         }
    126         return url;
    127     }
    128 
    129     @Override
    130     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    131         this.doPost(req, resp);
    132     }
    133 
    134     @Override
    135     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    136         doDispatcher(req, resp);
    137     }
    138 
    139     /**
    140      * 请求处理
    141      * @param req
    142      * @param resp
    143      */
    144     private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    145         req.setCharacterEncoding("utf-8");
    146         resp.setContentType("text/html;charset=UTF-8");
    147 
    148         String contextUrl = req.getContextPath();
    149         String requestUrl = req.getRequestURI();
    150 
    151         String url = requestUrl.replace(contextUrl, "");
    152         HandlerMapping handlerMapping = null;
    153         for(int i=0; i<handlerMappingList.size(); i++){
    154             if(url.equals(handlerMappingList.get(i).getUrl())){
    155                 handlerMapping = handlerMappingList.get(i);
    156                 break;
    157             }
    158         }
    159         if(handlerMapping == null){
    160             resp.getWriter().write("404, 未知的请求!");
    161         }else{
    162             HandlerAdapter adapter = adapterMap.get(handlerMapping);
    163             try {
    164                 Object result = adapter.handler(req, resp, handlerMapping);
    165 
    166                 viewResolve(req, resp, result);
    167             } catch (Exception e) {
    168                 e.printStackTrace();
    169                 resp.getWriter().write("500, 服务器发生异常!");
    170             }
    171         }
    172 
    173     }
    174 
    175     /**
    176      * 视图解析 返回
    177      * @param result
    178      */
    179     private void viewResolve(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception{
    180         if (result.getClass() == ModelAndView.class) {
    181             ModelAndView mv = (ModelAndView) result;
    182             String view = mv.getViewName();
    183             Map<String, Object> dataMap = mv.getData();
    184             if(dataMap.size() > 0){
    185                 for(String key : dataMap.keySet()){
    186                     request.setAttribute(key, dataMap.get(key));
    187                 }
    188             }
    189             request.getRequestDispatcher(view).forward(request, response);
    190         }
    191     }
    192 
    193 }

      ApplicationContext的具体实现如下,主要实现对执行资源文件的扫描,并完成IOC和DI

    public class ApplicationContext {
    
        /**
         * 配置文件
         */
        private static String PROPERTIES_FILE = "";
    
        /**
         * 初始化一个集合,存放扫描到的class对象
         */
        private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>());
    
        /**
         * 初始化map 存放别名与对象实例
         */
        private Map<String, Object> aliasInstanceMap = new HashMap<>();
    
        public ApplicationContext(String fileName) {
            PROPERTIES_FILE = fileName;
            try {
                String basePackage = getBasePackage(PROPERTIES_FILE);
    
                buildAliasInstanceMap(basePackage);
    
                doAutowired();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 完成别名与实例的映射
         */
        public void buildAliasInstanceMap(String basePackage) throws Exception {
    
            scanClasses(basePackage);
    
            if(classList.size() == 0){return;}
    
            for(Class<?> clazz : classList){
                if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)
                    || clazz.isAnnotationPresent(Autowired.class)) {
                    String alias = getAlias(clazz);
                    Object obj = aliasInstanceMap.get(alias);
    
                    //如果别名实例映射关系已经存在,则给出提示
                    if(obj != null){
                        throw new Exception("alias is exist!");
                    }else{
                        aliasInstanceMap.put(alias, clazz.newInstance());
                    }
                }
            }
    
            System.out.println(aliasInstanceMap);
        }
    
        /**
         * 属性对象的注入
         */
        public void doAutowired(){
            if (aliasInstanceMap.size() == 0) {
                return;
            }
    
            aliasInstanceMap.forEach((k, v)->{
    
                Field[] fields = v.getClass().getDeclaredFields();
    
                for(Field field : fields){
                    if (field.isAnnotationPresent(Autowired.class)) {
                        String alias = "";
    
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        if(autowired != null){
                            //注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类
                            if(!"".equals(autowired.value())){
                                alias = autowired.value();
                            }else{
                                Qualifier qualifier = field.getAnnotation(Qualifier.class);
                                if(qualifier != null){
                                    alias = qualifier.value();
                                }
                            }
                        }
    
                        if ("".equals(alias)) {
                            alias = getAlias(field.getType());
                        }
    
                        Object instance = null;
                        if(!"".equals(alias)){
                            instance = aliasInstanceMap.get(alias);
                        }
    
                        field.setAccessible(true);
    
                        try {
                            field.set(v, instance);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
            });
    
        }
    
        /**
         * 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写
         * @param clazz
         * @return
         */
        public String getAlias(Class<?> clazz){
            String alias = "";
            Controller controller = clazz.getAnnotation(Controller.class);
            if(controller != null){
                alias = controller.value();
            }
            Service service = clazz.getAnnotation(Service.class);
            if (service != null) {
                alias = service.value();
            }
            Autowired autowired = clazz.getAnnotation(Autowired.class);
            if(autowired != null){
                alias = autowired.value();
            }
    
            //注解中没有配置别名
            if("".equals(alias)){
                String simpleName = clazz.getSimpleName();
                alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
            }
            return alias;
        }
    
        /**
         * 跟据基础包名读取包及子包中的类对象
         * @param basePackage
         */
        public void scanClasses(String basePackage){
            if(basePackage == null || "".equals(basePackage)){return;}
    
            doScan(basePackage);
            System.out.println(classList);
        }
    
        private void doScan(String basePackage) {
            String path = basePackage.replaceAll("\.","/");
            URL url = this.getClass().getClassLoader().getResource(path);
            File file = new File(url.getFile());
            file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File childFile) {
                    String fileName = childFile.getName();
                    if(childFile.isDirectory()){
                        //当前文件是目录,递归 扫描下级子目录下的class文件
                        doScan(basePackage + "." + fileName);
                    }else{
                        if(fileName.endsWith(".class")){
                            String className = basePackage + "." + fileName.replace(".class", "");
                            try {
                                Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
                                classList.add(clazz);
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    return false;
                }
            });
        }
    
        /**
         * 从配置的属性文件中读取要扫描的包
         * @return
         */
        public String getBasePackage(String fileName) throws IOException {
            String basePackage;
            Properties prop = new Properties();
            InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);
            prop.load(in);
            basePackage = prop.getProperty("basePackage");
            return basePackage;
        }
    
        /**
         * 根据beanName 获取
         * @param beanName
         * @return
         */
        public Object getBean(String beanName){
            return aliasInstanceMap.get(beanName);
        }
    
        /**
         * 获取所有标注了controller的注解
         * @return
         */
        public List<Object> getControllerList(){
            List<Object> controllerList = new ArrayList<>();
            if(aliasInstanceMap.size() > 0) {
                aliasInstanceMap.values().forEach(obj -> {
                    if(obj.getClass().isAnnotationPresent(Controller.class)){
                        controllerList.add(obj);
                    }
                });
            }
            return controllerList;
        }
    
        public static void main(String[] args) throws Exception {
            String fileName = "application.properties";
            ApplicationContext context = new ApplicationContext(fileName);
            String basePackage = context.getBasePackage(PROPERTIES_FILE);
    
            context.buildAliasInstanceMap(basePackage);
    
            context.doAutowired();
            //测试
            UserController controller = (UserController) context.getBean("userController");
            controller.save();
        }
    
    
    }

      请求映射HandlerMapping,用来存放请求url和要执行的方法和方法所在对象实例,构建请求与请求处理的映射关系

    1 public class HandlerMapping {
    2 
    3     private String url;
    4     private Method method;
    5     private Object controllerInstance;
    6 
    7     //此处省去getter和setter
    8 }

      处理适配器HandlerAdapter,每一个请求映射都有一个请求处理适配器来完成请求的处理(handler)

     1 public class HandlerAdapter {
     2 
     3     private Map<Integer, String> paramMap;
     4 
     5     public HandlerAdapter(Map<Integer, String> paramMap){
     6         this.paramMap = paramMap;
     7     }
     8 
     9     public Object handler(HttpServletRequest request, HttpServletResponse response, HandlerMapping handlerMapping) throws Exception {
    10         Method method = handlerMapping.getMethod();
    11         Object classInstance = handlerMapping.getControllerInstance();
    12 
    13         int paramNum = method.getParameterCount();
    14         Object[] paramObj = new Object[paramNum];
    15         for(int i=0; i<paramNum; i++){
    16             String paramName = paramMap.get(i);
    17             if(paramName.equals(HttpServletRequest.class.getName())){
    18                 paramObj[i] = request;
    19             }else if(paramName.equals(HttpServletResponse.class.getName())){
    20                 paramObj[i] = response;
    21             } else {
    22                 paramObj[i] = request.getParameter(paramName);
    23             }
    24         }
    25         Object result = method.invoke(classInstance, paramObj);
    26         return result;
    27     }
    28 
    29 
    30     public Map<Integer, String> getParamMap() {
    31         return paramMap;
    32     }
    33 
    34     public void setParamMap(Map<Integer, String> paramMap) {
    35         this.paramMap = paramMap;
    36     }
    37 }

      处理结果ModelAndView,用来存放当前请求要返回的视图和数据

     1 public class ModelAndView {
     2 
     3     private String viewName;
     4     private Map<String, Object> data = new HashMap<>();
     5 
     6     public ModelAndView(String viewName) {
     7         this.viewName = viewName;
     8     }
     9 
    10     public void addAttribute(String name, Object value){
    11         data.put(name, value);
    12     }
    13 
    14     //此处省略getter和setter
    15 }

      视图展示返回的结果

    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <h3>hello mvc...</h3>
    <hr/>
    用 户 信 息:<%=request.getAttribute("user") %>
    </body>
    </html>

      浏览器端显示信息

    以上是模拟实现springmvc的主要代码,实现的源代码我已经上传在github,感兴趣的朋友可以访问

    https://github.com/wlzq/spring

  • 相关阅读:
    MySQL表行数查询最佳实践
    mysqldump备份表中有大字段失败的排错过程
    μC/OSⅡ在C8051F060上的移植及其应用
    OSSchedLock()函数透析
    32位与64位区别
    【Cesium】Animation显示系统时间
    【Cesium】根据经纬度计算距离
    【Cesium】 鼠标拾取椭球、地形、模型坐标点(经度+纬度+高程)
    【Python学习】Selenium模拟浏览器抓取数据
    【Cesium】polygon中的height和extrudedHeight的区别
  • 原文地址:https://www.cnblogs.com/love-wzy/p/10203655.html
Copyright © 2020-2023  润新知