1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
      <output id="hzk7v"></output>
    2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
    3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>

      写简单的mvc框架实例讲解

       更新时间£º2019年02月13日 16:27:23   投稿£ºlaozhang   我要评论

      在本篇内容中教给大家写一个简单的mvc框架步骤以及相关知识点£¬有需要的朋友们学习下¡£

      这一章先把支持注解的功能加上£¬这样就不需要经常地修改配置文件了¡£

      至于视图处理的地方£¬就还是先用json吧£¬找时间再写¡£

      项目地址在£ºhttps://github.com/hjx601496320/aMvc ¡£

      测?#28304;?#30721;在£ºhttps://github.com/hjx601496320/amvc-test ¡£

      怎么写呢£¿

      因为在之前写代码的时候£¬我把每个类要做的事情分的比较清楚£¬所以在添加这个功能的时候写起来还是比较简单的£¬需要修改的地方也比较小¡£

      这一章里我们需要干的事情有£º

      • 定义一个注解£¬标识某一个class中的被添加注解的方法是一个UrlMethodMapping¡£
      • 修改配置文件£¬添?#26377;?#35201;扫描的package¡£
      • 写一个方法£¬根据package中值找到其中所有的class¡£
      • 在UrlMethodMapping的工厂类UrlMethodMappingFactory中新加一个根据注解创建UrlMethodMapping的方法¡£
      • 在Application中的init()方法中£¬根据是否开启注解支持£¬执行新的工厂类方法¡£
      • 完了¡£

      多么简单呀¡«¡«¡«

      现在开始写

      定义一个注解Request

      关于怎样自定义注这件事£¬大家可以上网搜一下£¬比较简单¡£我这里只是简单的说一下¡£我先把代码贴出来£º

      import com.hebaibai.amvc.RequestType;
      import java.lang.annotation.*;
      
      /**
       * 表示这个类中的£¬添加了@Request注解的method被映射为一个http地址¡£
       *
       * @author hjx
       */
      @Documented
      @Target({ElementType.METHOD, ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Request {
      
        /**
         * 请求类型
         * 支持GET£¬POST£¬DELETE£¬PUT
         *
         * @return
         */
        RequestType[] type() default {RequestType.GET, RequestType.POST, RequestType.DELETE, RequestType.PUT};
      
        /**
         * 请求地址
         * 添加在class上时£¬会将value中的值添加在其他方法上的@Request.value()的值前£¬作为基础地址¡£
         *
         * @return
         */
        String value() default "/";
      }

      定义一个注解£¬需要用到一下几个东西£º

      1£º@interface£º说明这个类是一个注解¡£

      2£º@Retention£º注解的保留策略£¬有这么几个取值范围£º

      代码 说明
      @Retention(RetentionPolicy.SOURCE) 注解仅存在于源码中
      @Retention(RetentionPolicy.CLASS) 注解会在class字节码文件?#20889;?#22312;
      @Retention(RetentionPolicy.RUNTIME) 注解会在class字节码文件?#20889;?#22312;£¬运行时可以通过反射获取到

      因为我们在程序中需要取到自定义的注解£¬所以使用£ºRetentionPolicy.RUNTIME¡£

      3£º@Target£º作用目标£¬表示注解可以添加在什么地方£¬取值范围有£º

      代码 说明
      @Target(ElementType.TYPE) 接口¡¢类¡¢枚举¡¢注解
      @Target(ElementType.FIELD) 字段¡¢枚举的常量
      @Target(ElementType.METHOD) 方法
      @Target(ElementType.PARAMETER) 方法参数
      @Target(ElementType.CONSTRUCTOR) 构造函数
      @Target(ElementType.LOCAL_VARIABLE) 局部变量
      @Target(ElementType.ANNOTATION_TYPE) 注解
      @Target(ElementType.PACKAGE)

      3£º@Documented£º这个主要是让自定义注解保留在文档中£¬没啥实际意义£¬一般都给加上¡£

      4£ºdefault£º是给注解中的属性£¨看起来像是一个方法£¬也可能就是一个方法£¬但是我就是叫属性£¬?#26376;月Ô¡«¡«¡«£?#19968;个默认值¡£

      上面大致上讲了一下怎么定义一个注解£¬现在注解写完了£¬讲一下这个注解的用处吧¡£

      首?#26085;?#20010;注解可以加在class和method上¡£加在class上的时候表示这个类中会有method将要被处理成为一个UrlMethodMapping£¬然后其中的value属性将作为这个class中所有UrlMethodMapping的基础地址£¬type属性不起作用¡£加在method上的时候£¬就是说明这个method将被处理成一个UrlMethodMapping£¬注解的两个属性发挥其正常的作用¡£

      注解写完了£¬下面把配置文件改一改吧¡£

      修改框架的配置文件

      只需要添加一个属性就好了£¬修改完的配置文件这个样子£º

      {
       "annotationSupport": true,
       "annotationPackage": "com.hebaibai.demo.web",
      // "mapping": [
      //  {
      //   "url": "/index",
      //   "requestType": [
      //    "get"
      //   ],
      //   "method": "index",
      //   "objectClass": "com.hebaibai.demo.web.IndexController",
      //   "paramTypes": [
      //    "java.lang.String",
      //    "int"
      //   ]
      //  }
      // ]
      }

      1£ºannotationSupport 值是true的时候表示开启注解¡£

      2£ºannotationPackage 表示需要扫描的包的路径¡£

      3£º因为开了注解支持£¬为了防止重复注册 UrlMethodMapping£¬所以我把下面的配置注释掉了¡£

      写一个包扫描的方法

      这个方法需要将项目中jar文件和文件夹下所有符合条件的class找到£¬会用到递归£¬代码在ClassUtils.java中£¬由三个方法构成£¬分别是£º

      1£ºvoid getClassByPackage(String packageName, Set

      这个方法接收两个参数£¬一个是包名packageName£¬一个是一个空的Set(不是null)£¬在方法执行完毕会将包下的所有class填充进Set中¡£这里主要是判断了一下这个包中有那些类型的文件£¬并根据文件类型分别处理¡£

      注意£º如果是jar文件的类型£¬获取到的filePath是这样的£º

      file:/home/hjx/idea-IU/lib/idea_rt.jar!/com

      需要去掉头和尾£¬然后就可以吃了£¬鸡肉味£¡嘎?#28304;à¡«¡?处理之后的是这个样子£º

      /home/hjx/idea-IU/lib/idea_rt.jar

      下面是方法代码£º

      /**
       * 从给定的报名中?#39029;?#25152;有的class
       *
       * @param packageName
       * @param classes
       */
      @SneakyThrows({IOException.class})
      public static void getClassByPackage(String packageName, Set<Class> classes) {
        Assert.notNull(classes);
        String packagePath = packageName.replace(DOT, SLASH);
        Enumeration<URL> resources = ClassUtils.getClassLoader().getResources(packagePath);
        while (resources.hasMoreElements()) {
          URL url = resources.nextElement();
          //文件类型
          String protocol = url.getProtocol();
          String filePath = URLDecoder.decode(url.getFile(), CHARSET_UTF_8);
          if (TYPE_FILE.equals(protocol)) {
            getClassByFilePath(packageName, filePath, classes);
          }
          if (TYPE_JAR.equals(protocol)) {
            //截取文件的路径
            filePath = filePath.substring(filePath.indexOf(":") + 1, filePath.indexOf("!"));
            getClassByJarPath(packageName, filePath, classes);
          }
        }
      }

      2£ºvoid getClassByFilePath(String packageName, String filePath, Set

      将文件夹中的全部符合条件的class找到£¬用到递归¡£需要将class文件的绝?#26376;?#24452;截取成class的全限定名£¬代码这个样子£º

      /**
       * 在文件夹中递归?#39029;?#35813;文件夹中在package中的class
       *
       * @param packageName
       * @param filePath
       * @param classes
       */
      static void getClassByFilePath(
        String packageName,
        String filePath,
        Set<Class> classes
      ) {
        File targetFile = new File(filePath);
        if (!targetFile.exists()) {
          return;
        }
        if (targetFile.isDirectory()) {
          File[] files = targetFile.listFiles();
          for (File file : files) {
            String path = file.getPath();
            getClassByFilePath(packageName, path, classes);
          }
        } else {
          //如果是一个class文件
          boolean trueClass = filePath.endsWith(CLASS_MARK);
          if (trueClass) {
            //提取完整的类名
            filePath = filePath.replace(SLASH, DOT);
            int i = filePath.indexOf(packageName);
            String className = filePath.substring(i, filePath.length() - 6);
            //不是一个内部类
            boolean notInnerClass = className.indexOf("$") == -1;
            if (notInnerClass) {
              //根据类名加载class对象
              Class aClass = ClassUtils.forName(className);
              if (aClass != null) {
                classes.add(aClass);
              }
            }
          }
        }
      }

      3£ºvoid getClassByJarPath(String packageName, String filePath, Set

      将jar文件中的全部符合条件的class找到¡£没啥说的£¬下面是代码£º

      /**
       * 在jar文件中?#39029;?#35813;文件夹中在package中的class
       *
       * @param packageName
       * @param filePath
       * @param classes
       */
      @SneakyThrows({IOException.class})
      static void getClassByJarPath(
        String packageName,
        String filePath,
        Set<Class> classes
      ) {
        JarFile jarFile = new URLJarFile(new File(filePath));
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
          JarEntry jarEntry = entries.nextElement();
          String jarEntryName = jarEntry.getName().replace(SLASH, DOT);
          //在package下的class
          boolean trueClass = jarEntryName.endsWith(CLASS_MARK) && jarEntryName.startsWith(packageName);
          //不是一个内部类
          boolean notInnerClass = jarEntryName.indexOf("$") == -1;
          if (trueClass && notInnerClass) {
            String className = jarEntryName.substring(0, jarEntryName.length() - 6);
            System.out.println(className);
            //根据类名加载class对象
            Class aClass = ClassUtils.forName(className);
            if (aClass != null) {
              classes.add(aClass);
            }
          }
        }
      }

      这样£¬获取包名下的class就写完了¡«

      修改UrlMethodMappingFactory

      这里新添加一个方法£º

      List£¬将扫描包之后获取到的Class对象作为参数£¬返回一个UrlMethodMapping集合就好了¡£代码如下£º

      /**
       * 通过解析Class 获取映射
       *
       * @param aClass
       * @return
       */
      public List<UrlMethodMapping> getUrlMethodMappingListByClass(Class<Request> aClass) {
        List<UrlMethodMapping> mappings = new ArrayList<>();
        Request request = aClass.getDeclaredAnnotation(Request.class);
        if (request == null) {
          return mappings;
        }
        String basePath = request.value();
        for (Method classMethod : aClass.getDeclaredMethods()) {
          UrlMethodMapping urlMethodMapping = getUrlMethodMappingListByMethod(classMethod);
          if (urlMethodMapping == null) {
            continue;
          }
          //将添加在class上的Request中的path作为基础路径
          String url = UrlUtils.makeUrl(basePath + "/" + urlMethodMapping.getUrl());
          urlMethodMapping.setUrl(url);
          mappings.add(urlMethodMapping);
        }
        return mappings;
      }
      
      /**
       * 通过解析Method 获取映射
       * 注解Request不存在时跳出
       *
       * @param method
       * @return
       */
      private UrlMethodMapping getUrlMethodMappingListByMethod(Method method) {
        Request request = method.getDeclaredAnnotation(Request.class);
        if (request == null) {
          return null;
        }
        Class<?> declaringClass = method.getDeclaringClass();
        String path = request.value();
        for (char c : path.toCharArray()) {
          Assert.isTrue(c != ' ', declaringClass + "." + method.getName() + "请求路径异常£º" + path + " £¡");
        }
        return getUrlMethodMapping(
            path,
            request.type(),
            declaringClass,
            method,
            method.getParameterTypes()
        );
      }

      在这里校验了一下注解Request中的value的值£¬如果中间有空格的话会抛出异常¡£UrlUtils.makeUrl() 这个方法主要是将url中的多余¡±/?#27604;?#25481;£¬代码长这个样子£º

      private static final String SLASH = "/";
      
      /**
       * 处理url
       * 1£º去掉连接中相邻并重复的¡°/¡±,
       * 2£º链接开头没有¡°/¡±,则添加¡£
       * 3£º链接结尾有¡°/?#20445;?#21017;去掉¡£
       *
       * @param url
       * @return
       */
      public static String makeUrl(@NonNull String url) {
        char[] chars = url.toCharArray();
        StringBuilder newUrl = new StringBuilder();
        if (!url.startsWith(SLASH)) {
          newUrl.append(SLASH);
        }
        for (int i = 0; i < chars.length; i++) {
          if (i != 0 && chars[i] == chars[i - 1] && chars[i] == '/') {
            continue;
          }
          if (i == chars.length - 1 && chars[i] == '/') {
            continue;
          }
          newUrl.append(chars[i]);
        }
        return newUrl.toString();
      }

      这样通过注解获取UrlMethodMapping的工厂方法就写完了£¬下面开始修改加载框架的代码¡£

      修改Application中的init

      这里因为添加了一种使用注解方式获取UrlMethodMapping的方法£¬所以新建一个方法£º

      void addApplicationUrlMappingByAnnotationConfig(JSONObject configJson) ¡£在这里获取框架配置中的包名以及做一些配置上的校验£¬代码如下£º

      /**
       * 使用注解来加载UrlMethodMapping
       *
       * @param configJson
       */
      private void addApplicationUrlMappingByAnnotationConfig(JSONObject configJson) {
        String annotationPackage = configJson.getString(ANNOTATION_PACKAGE_NODE);
        Assert.notNull(annotationPackage, ANNOTATION_PACKAGE_NODE + NOT_FIND);
        //获取添加了@Request的类
        Set<Class> classes = new HashSet<>();
        ClassUtils.getClassByPackage(annotationPackage, classes);
        Iterator<Class> iterator = classes.iterator();
        while (iterator.hasNext()) {
          Class aClass = iterator.next();
          List<UrlMethodMapping> mappings = urlMethodMappingFactory.getUrlMethodMappingListByClass(aClass);
          if (mappings.size() == 0) {
            continue;
          }
          for (UrlMethodMapping mapping : mappings) {
            addApplicationUrlMapping(mapping);
          }
        }
      }

      之后把先前写的读取json配置生成urlMappin的代码摘出来£¬单独写一个方法£º

      void addApplicationUrlMappingByJsonConfig(JSONObject configJson)£¬这样使代码中的每个方法的功能?#32423;?#31435;出来£¬看起来比较整洁£¬清楚¡£代码如下£º

      /**
       * 使用文件配置来加载UrlMethodMapping
       * 配置中找不到的?#23433;?#25191;?#23567;?
       *
       * @param configJson
       */
      private void addApplicationUrlMappingByJsonConfig(JSONObject configJson) {
        JSONArray jsonArray = configJson.getJSONArray(MAPPING_NODE);
        if (jsonArray == null || jsonArray.size() == 0) {
          return;
        }
        for (int i = 0; i < jsonArray.size(); i++) {
          UrlMethodMapping mapping = urlMethodMappingFactory.getUrlMethodMappingByJson(jsonArray.getJSONObject(i));
          addApplicationUrlMapping(mapping);
        }
      }

      最后只要吧init()稍微修改一下就好了£¬修改完之后是这样的£º

      /**
       * 初始化配置
       */
      @SneakyThrows(IOException.class)
      protected void init() {
        String configFileName = applicationName + ".json";
        InputStream inputStream = ClassUtils.getClassLoader().getResourceAsStream(configFileName);
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        String config = new String(bytes, "utf-8");
        //应用配置
        JSONObject configJson = JSONObject.parseObject(config);
      
        //TODO:生?#21861;?#35937;的工厂类£¨先默认为每次都new一个新的对象£©
        this.objectFactory = new AlwaysNewObjectFactory();
        //TODO:不同的入参名称获取类£¨当前默认为asm£©
        urlMethodMappingFactory.setParamNameGetter(new AsmParamNameGetter());
        //通过文件配置加载
        addApplicationUrlMappingByJsonConfig(configJson);
        //是否开启注解支持
        Boolean annotationSupport = configJson.getBoolean(ANNOTATION_SUPPORT_NODE);
        Assert.notNull(annotationSupport, ANNOTATION_SUPPORT_NODE + NOT_FIND);
        if (annotationSupport) {
          addApplicationUrlMappingByAnnotationConfig(configJson);
        }
      }

      相关文章

      • 在 .NET Framework 2.0 中未处理的异常导致基于 ASP.NET 的应用程序意外退出

        在 .NET Framework 2.0 中未处理的异常导致基于 ASP.NET 的应用程

        如果在 Microsoft .NET Framework 2.0 上构建的基于 Microsoft ASP.NET 的应用程序中引发未处理的异常£¬该应用程序将会意外退出¡£如果出现这个问题£¬不会在应用程序日志中记录了解此问题所必需的异常信息¡£
        2009-11-11
      • ASP.NET中CKEditor与CKFinder的配置使用

        ASP.NET中CKEditor与CKFinder的配置使用

        这篇文章主要介绍了ASP.NET中CKEditor与CKFinder的配置使用的相关资料,需要的朋友可以参考下
        2015-06-06
      • asp.net get set用法

        asp.net get set用法

        属性的定义和使用 属性由两个部分组成£º属性头和存储器¡£存储器分为get访问器和set访问器¡£声明属性的一般形式为£º 修饰符 类型 属性名
        2008-05-05
      • Asp.net 网站性能优化二则分享

        Asp.net 网站性能优化二则分享

        Web服务器的性能优化有很多资料介绍了£¬多台主机负载均衡£¬查询结果的多级缓存£¬数据库索引优化等都是常见的优化手段¡£
        2011-08-08
      • .NET下模拟数组越界的方法详解

        .NET下模拟数组越界的方法详解

        这篇文章主要给大家介绍了关于.NET下模拟数组越界的相关资料£¬文中通过图文介绍的非常详细£¬对大家的学习或者工作具有一定的参考学习价值£¬需要的朋友们下面随着小编来一起学习学习吧
        2019-01-01
      • the sourcesafe database has been locked by the administrator之解决方法

        the sourcesafe database has been locked by the administrator

        今天早上打开soucesafe的时候出现提示£º¡°the sourcesafe database has been locked by the administrator"¡£仔细想想£¬¡¡可能是前天晚上用"f:\analyze.exe" -I- -DB -F -V3 -D "f:\vssData\data" 命今分析的时候锁定了database
        2009-04-04
      • .Net 下区别使用 ByRef/ByVal 的重要性 分享

        .Net 下区别使用 ByRef/ByVal 的重要性 分享

        这篇文章介绍了.Net 下区别使用 ByRef/ByVal 的重要性£¬有需要的朋友可以参?#23478;?#19979;
        2013-07-07
      • 厚积薄发,?#24403;?NET 2016

        厚积薄发,?#24403;?NET 2016

        一想到.NET 2016£¬脑海里?#26576;?#30340;第一个词就是厚积薄发¡£ .NET 2016 是 .NET 一次质的?#31245;ÇG?#19981;管难易£¬我们需要?#24403;?#21464;化
        2016-06-06
      • ASP.Net中英文复合检索文本框实现思路及代码

        ASP.Net中英文复合检索文本框实现思路及代码

        前段时间£¬写一个用户部门的管理页面£¬需要对后台获取的用户数据实现英汉检索功能于是就有了下文,编辑的很详细图文并茂呢£¿?#34892;?#36259;的你可不要错过了哈£¬或许本文对你有所帮助呢
        2013-02-02
      • Ajax.net 显示错误信息的设置

        Ajax.net 显示错误信息的设置

        有时候我们在使用asp.net想让他显示出错的原因等情况£¬大家可以仔细看下参数£¬属性等
        2009-01-01

      最新评论

      3dÊÔ»úºÅÖвÊÍø

        1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
          <output id="hzk7v"></output>
        2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
        3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>

            1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
              <output id="hzk7v"></output>
            2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
            3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>