<sup id="sjju3"><noscript id="sjju3"></noscript></sup>
    <big id="sjju3"></big>

  • <blockquote id="sjju3"></blockquote>
    <blockquote id="sjju3"></blockquote>

      <td id="sjju3"></td>

      <big id="sjju3"></big>
        <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>
      1. 写简单的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;荊?#19981;管难易,我们需要?#24403;?#21464;化
          2016-06-06
        • ASP.Net中英文复合检索文本框实现思路及代码

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

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

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

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

        最新评论

        2018白小姐一肖中特马
        <sup id="sjju3"><noscript id="sjju3"></noscript></sup>
        <big id="sjju3"></big>

      2. <blockquote id="sjju3"></blockquote>
        <blockquote id="sjju3"></blockquote>

          <td id="sjju3"></td>

          <big id="sjju3"></big>
            <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>
          1. <sup id="sjju3"><noscript id="sjju3"></noscript></sup>
            <big id="sjju3"></big>

          2. <blockquote id="sjju3"></blockquote>
            <blockquote id="sjju3"></blockquote>

              <td id="sjju3"></td>

              <big id="sjju3"></big>
                <code id="sjju3"><strong id="sjju3"><dl id="sjju3"></dl></strong></code>