zoukankan      html  css  js  c++  java
  • 关于springMVC

    一 mvc设计模式

      MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。 MVC 中每个部分各司其职:
    Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。
    View(视图): 通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。
    Controller(控制器): 是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。MVC提倡:每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码;分层是为了解耦,解耦是为了维护⽅便和分⼯协作。

    二 springMVC请求处理流程

    第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
    第⼆步: DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
    第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截
    器(如果 有则⽣成)⼀并返回DispatcherServlet
    第四步: DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
    第五步:处理器适配器执⾏Handler
    第六步: Handler执⾏完成给处理器适配器返回ModelAndView
    第七步:处理器适配器向前端控制器返回 ModelAndView, ModelAndView 是SpringMVC 框架的⼀个
    底层对 象,包括 Model 和 View
    第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
    第九步:视图解析器向前端控制器返回View
    第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
    第⼗⼀步:前端控制器向⽤户响应结果

    三 关于mvc

      mvc是一个常用的表现层框架 他能很方便的帮我们有效处理很多事情 那么怎么简易的实现mvc功能

    首先 在了解大致原理后总结归纳

     那么 按照第一步 首先定义配置文件 配置文件中定义了扫描包路径

    然后 需要自定义注解

    1 @Documented
    2 @Target(ElementType.FIELD)
    3 @Retention(RetentionPolicy.RUNTIME)
    4 public @interface DemoAutowired {
    5     String value() default "";
    6 }

    因为Autowired是作用在字段上 所以target定义为FIELD

    又因为要在JVM运行是保留 所以Retention定义为RUNTIME

    其他选项可以查看源码

    定义完注解 就需要进行bean对象的初始化

     1 // ioc容器
     2     // 基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理
     3     private void doInstance()  {
     4 
     5         if(classNames.size() == 0) return;
     6 
     7         try{
     8 
     9             for (int i = 0; i < classNames.size(); i++) {
    10                 String className =  classNames.get(i);  // com.lagou.demo.controller.DemoController
    11 
    12                 // 反射
    13                 Class<?> aClass = Class.forName(className);
    14                 // 区分controller,区分service'
    15                 if(aClass.isAnnotationPresent(LagouController.class)) {
    16                     // controller的id此处不做过多处理,不取value了,就拿类的首字母小写作为id,保存到ioc中
    17                     String simpleName = aClass.getSimpleName();// DemoController
    18                     String lowerFirstSimpleName = lowerFirst(simpleName); // demoController
    19                     Object o = aClass.newInstance();
    20                     ioc.put(lowerFirstSimpleName,o);
    21                 }else if(aClass.isAnnotationPresent(LagouService.class)) {
    22                     LagouService annotation = aClass.getAnnotation(LagouService.class);
    23                     //获取注解value值
    24                     String beanName = annotation.value();
    25 
    26                     // 如果指定了id,就以指定的为准
    27                     if(!"".equals(beanName.trim())) {
    28                         ioc.put(beanName,aClass.newInstance());
    29                     }else{
    30                         // 如果没有指定,就以类名首字母小写
    31                         beanName = lowerFirst(aClass.getSimpleName());
    32                         ioc.put(beanName,aClass.newInstance());
    33                     }
    34 
    35 
    36                     // service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到ioc中,便于后期根据接口类型注入
    37                     Class<?>[] interfaces = aClass.getInterfaces();
    38                     for (int j = 0; j < interfaces.length; j++) {
    39                         Class<?> anInterface = interfaces[j];
    40                         // 以接口的全限定类名作为id放入
    41                         ioc.put(anInterface.getName(),aClass.newInstance());
    42                     }
    43                 }else{
    44                     continue;
    45                 }
    46 
    47             }
    48         }catch (Exception e) {
    49             e.printStackTrace();
    50         }
    51 
    52 
    53 
    54 
    55 
    56 
    57     }

    在初始化过程中 我们将拿到的方法 参数 路径存储起来 所以需要再定义一个实体类 创建这些属性用于封装

    然后 以全限定类名为id存到map中 方便后面使用

    然后 就是实现依赖注入

     1 // ioc容器
     2     // 基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理
     3     private void doInstance()  {
     4 
     5         if(classNames.size() == 0) return;
     6 
     7         try{
     8 
     9             for (int i = 0; i < classNames.size(); i++) {
    10                 String className =  classNames.get(i);  // com.lagou.demo.controller.DemoController
    11 
    12                 // 反射
    13                 Class<?> aClass = Class.forName(className);
    14                 // 区分controller,区分service'
    15                 if(aClass.isAnnotationPresent(LagouController.class)) {
    16                     // controller的id此处不做过多处理,不取value了,就拿类的首字母小写作为id,保存到ioc中
    17                     String simpleName = aClass.getSimpleName();// DemoController
    18                     String lowerFirstSimpleName = lowerFirst(simpleName); // demoController
    19                     Object o = aClass.newInstance();
    20                     ioc.put(lowerFirstSimpleName,o);
    21                 }else if(aClass.isAnnotationPresent(LagouService.class)) {
    22                     LagouService annotation = aClass.getAnnotation(LagouService.class);
    23                     //获取注解value值
    24                     String beanName = annotation.value();
    25 
    26                     // 如果指定了id,就以指定的为准
    27                     if(!"".equals(beanName.trim())) {
    28                         ioc.put(beanName,aClass.newInstance());
    29                     }else{
    30                         // 如果没有指定,就以类名首字母小写
    31                         beanName = lowerFirst(aClass.getSimpleName());
    32                         ioc.put(beanName,aClass.newInstance());
    33                     }
    34 
    35 
    36                     // service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到ioc中,便于后期根据接口类型注入
    37                     Class<?>[] interfaces = aClass.getInterfaces();
    38                     for (int j = 0; j < interfaces.length; j++) {
    39                         Class<?> anInterface = interfaces[j];
    40                         // 以接口的全限定类名作为id放入
    41                         ioc.put(anInterface.getName(),aClass.newInstance());
    42                     }
    43                 }else{
    44                     continue;
    45                 }
    46 
    47             }
    48         }catch (Exception e) {
    49             e.printStackTrace();
    50         }
    51 
    52     }

    这时将url method 都拿到了 但是并没有关联 所以需要构造一个HandlerMapping处理器映射器将两者关联起来

     1 private void initHandlerMapping() {
     2         if(ioc.isEmpty()) {return;}
     3 
     4         for(Map.Entry<String,Object> entry: ioc.entrySet()) {
     5             // 获取ioc中当前遍历的对象的class类型
     6             Class<?> aClass = entry.getValue().getClass();
     7 
     8 
     9             if(!aClass.isAnnotationPresent(LagouController.class)) {continue;}
    10 
    11 
    12             String baseUrl = "";
    13             if(aClass.isAnnotationPresent(LagouRequestMapping.class)) {
    14                 LagouRequestMapping annotation = aClass.getAnnotation(LagouRequestMapping.class);
    15                 baseUrl = annotation.value(); // 等同于/demo
    16             }
    17 
    18 
    19             // 获取方法
    20             Method[] methods = aClass.getMethods();
    21             for (int i = 0; i < methods.length; i++) {
    22                 Method method = methods[i];
    23 
    24                 //  方法没有标识LagouRequestMapping,就不处理
    25                 if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;}
    26 
    27                 // 如果标识,就处理
    28                 LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class);
    29                 String methodUrl = annotation.value();  // /query
    30                 String url = baseUrl + methodUrl;    // 计算出来的url /demo/query
    31 
    32                 // 把method所有信息及url封装为一个Handler
    33                 Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url));
    34 
    35 
    36                 // 计算方法的参数位置信息  // query(HttpServletRequest request, HttpServletResponse response,String name)
    37                 Parameter[] parameters = method.getParameters();
    38                 for (int j = 0; j < parameters.length; j++) {
    39                     Parameter parameter = parameters[j];
    40 
    41                     if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
    42                         // 如果是request和response对象,那么参数名称写HttpServletRequest和HttpServletResponse
    43                         handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
    44                     }else{
    45                         handler.getParamIndexMapping().put(parameter.getName(),j);  // <name,2>
    46                     }
    47 
    48                 }
    49 
    50 
    51                 // 建立url和method之间的映射关系(map缓存起来)
    52                 handlerMapping.add(handler);
    53 
    54             }
    55 
    56 
    57         }
    58 
    59     }

    全存储在handlerMapping中 到此为止 init方法算是完成了

    现在需要的东西都有了 但是并没有使用到 所以在重写的doPost方法中 我们来重写这些东西 使他们被使用起来

     1 @Override
     2     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     3         // 处理请求:根据url,找到对应的Method方法,进行调用
     4         // 获取uri
     5 //        String requestURI = req.getRequestURI();
     6 //        Method method = handlerMapping.get(requestURI);// 获取到一个反射的方法
     7         // 反射调用,需要传入对象,需要传入参数,此处无法完成调用,没有把对象缓存起来,也没有参数!!!!改造initHandlerMapping();
     8 //        method.invoke() //
     9 
    10 
    11         // 根据uri获取到能够处理当前请求的hanlder(从handlermapping中(list))
    12         Handler handler = getHandler(req);
    13 
    14         if(handler == null) {
    15             resp.getWriter().write("404 not found");
    16             return;
    17         }
    18 
    19         // 参数绑定
    20         // 获取所有参数类型数组,这个数组的长度就是我们最后要传入的args数组的长度
    21         Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
    22 
    23 
    24         // 根据上述数组长度创建一个新的数组(参数数组,是要传入反射调用的)
    25         Object[] paraValues = new Object[parameterTypes.length];
    26 
    27         // 以下就是为了向参数数组中塞值,而且还得保证参数的顺序和方法中形参顺序一致
    28 
    29         Map<String, String[]> parameterMap = req.getParameterMap();
    30 
    31         // 遍历request中所有参数  (填充除了request,response之外的参数)
    32         for(Map.Entry<String,String[]> param: parameterMap.entrySet()) {
    33             // name=1&name=2   name [1,2]
    34             String value = StringUtils.join(param.getValue(), ",");  // 如同 1,2
    35 
    36 //            if ("zhengshuang".equals(value)){
    37 //                Exception exception = new Exception();
    38 
    39 //            }
    40 
    41             // 如果参数和方法中的参数匹配上了,填充数据
    42             if(!handler.getParamIndexMapping().containsKey(param.getKey())) {continue;}
    43 
    44             // 方法形参确实有该参数,找到它的索引位置,对应的把参数值放入paraValues
    45             Integer index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 个位置
    46 
    47             paraValues[index] = value;  // 把前台传递过来的参数值填充到对应的位置去
    48 
    49         }
    50 
    51 
    52         int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0
    53         paraValues[requestIndex] = req;
    54 
    55 
    56         int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1
    57         paraValues[responseIndex] = resp;
    58 
    59 
    60 
    61 
    62         // 最终调用handler的method属性
    63         try {
    64             handler.getMethod().invoke(handler.getController(),paraValues);
    65         } catch (IllegalAccessException e) {
    66             e.printStackTrace();
    67         } catch (InvocationTargetException e) {
    68             e.printStackTrace();
    69         }
    70 
    71 
    72     }
    73 
    74     private Handler getHandler(HttpServletRequest req) {
    75         if(handlerMapping.isEmpty()){return null;}
    76 
    77         String url = req.getRequestURI();
    78 
    79         for(Handler handler: handlerMapping) {
    80             Matcher matcher = handler.getPattern().matcher(url);
    81             if(!matcher.matches()){continue;}
    82             return handler;
    83         }
    84 
    85         return null;
    86 
    87     }

    到此为止 算是完成了 代码部分

    测试:

    输入一个被拦截的用户

    输入正常用户

    不被拦截 (因为懒,所以没做输出页 后台打印)

  • 相关阅读:
    图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案
    图片上传-下载-删除等图片管理的若干经验总结2
    HDU 1195 Open the Lock
    HDU 1690 Bus System
    HDU 2647 Reward
    HDU 2680 Choose the best route
    HDU 1596 find the safest road
    POJ 1904 King's Quest
    CDOJ 889 Battle for Silver
    CDOJ 888 Absurdistan Roads
  • 原文地址:https://www.cnblogs.com/yuztmt/p/14608973.html
Copyright © 2011-2022 走看看