您的位置 首页 > 雕刻工具

mvc框架 自己写一个mvc框架吧

大家好,关于mvc框架很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于自己写一个mvc框架吧的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!

写一个框架吧,如果这个框架会用到一些配置上的东西,我自己习惯是先不用考虑这个配置文件应该是怎样的,什么形式的,先用一个java对象(比如叫Config.java)都给放进去。等到功能写的差不多了,需要考虑到使用配置文件了,就可以写一个工厂类,根据不同的配置(可能是xml,可能是json,甚至是注解)把刚才说的Config.java对象生成出来。

因为一个mvc的框架个人感觉主要做的事情就是通过http请求调用java中的方法。首先要做的就是怎样把一个请求地址和一个java中的方法绑定起来,使其形成一个对应关系。另外请求也是分请求类型的,比如get,post等等,所以还需要请求类型。

其次,要通过java的反射执行这个方法的话,还需要这个Method的所属Class的实例对象。

最后,因为这个方法是要通过http调用的,我们需要知道这个Method中的入参有哪些,每个参数是什么类型的,之后才能从每一次的请求中找到相应的参数,并转换成为对应的java类型。所以我们还需要每个参数的参数名称。

创建一个描述映射的类UrlMethodMapping

\nimportlombok.Getter;\nimportlombok.Setter;\nimportlombok.ToString;\nimportjava.lang.reflect.Method;\n/**\n*一个请求Url到Method的映射\n*\n*@authorhjx\n*/\n@ToString\n@Setter\n@Getter\npublicclassUrlMethodMapping{\n/**\n*请求地址\n*/\nprivateStringurl;\n/**\n*请求类型\n*/\nprivateRequestType[]requestTypes;\n/**\n*请求方法所属class实例\n*/\nprivateObjectobject;\n/**\n*method的所属class\n*/\nprivateClassobjectClass;\n/**\n*url对应的method\n*/\nprivateMethodmethod;\n/**\n*method的入参名称\n*顺序要保持一致\n*/\nprivateString[]paramNames;\n/**\n*method的入参类型\n*顺序要保持一致\n*/\nprivateClass[]paramClasses;\n}\n

这里我没有写getter和setter,是因为我用了一个叫做lombok的工具,很好用大家搜一下就知道怎么用了。

在上面的代码中有一个属性RequestType[]requestTypes这是一个枚举,主要是用来说明这个映射支持那些请求方式的。

现在将UrlMethodMapping数据填充起来

我在这里写了一个工厂类,提供了一个方法来组装UrlMethodMapping这个对象:

/**\n*@paramurl请求地址\n*@paramrequestTypeshttp请求方式\n*@paramobjectClass实例对象的Class\n*@parammethodurl对应的方法\n*@paramparamClasses请求参数类型\n*@return\n*/\npublicUrlMethodMappinggetUrlMethodMapping(\nStringurl,RequestType[]requestTypes,ClassobjectClass,Methodmethod,Class[]paramClasses\n){\nAssert.notNull(url,URL+NOT_FIND);\nAssert.notNull(requestTypes,REQUEST_TYPE+NOT_FIND);\nAssert.isTrue(requestTypes.length>0,REQUEST_TYPE+NOT_FIND);\nAssert.notNull(objectClass,CLASS+NOT_FIND);\nAssert.notNull(method,METHOD+NOT_FIND);\nAssert.notNull(paramClasses,PARAM_TYPES+NOT_FIND);\n//class实例化对象\nObjectobject=objectFactory.getObject(objectClass);\nAssert.notNull(object,"objectFactory.getObject()获取失败!objectClass:"+objectClass.getName());\n//获取参数名称\nString[]paramNames=paramNameGetter.getParamNames(method);\nAssert.notNull(paramNames,"paramNameGetter.getParamNames()执行失败!method:"+method.getName());\nAssert.isTrue(paramNames.length==paramClasses.length,"方法名称取出异常method:"+method.getName());\n//组装参数\nUrlMethodMappingmapping=newUrlMethodMapping();\nmapping.setMethod(method);\nmapping.setUrl(url);\nmapping.setRequestTypes(requestTypes);\nmapping.setObject(object);\nmapping.setParamClasses(paramClasses);\nmapping.setObjectClass(objectClass);\nmapping.setParamNames(paramNames);\nreturnmapping;\n}\n

在这个方法里,我用自己写的一个断言的工具类Assert来校验参数是否是正确的,如果参数不正确的话就会抛出异常信息。这段代码基本上是这个样子:

publicstaticvoidnotNull(Objectobj,Stringmsg){\nif(obj==null){\nthrownewRuntimeException(msg);\n}\n}\n

这段程序中还有两个对象:

1:objectFactory

是一个接口,主要用于通过Class来获取到实例化的对象,这里需要使用者自己实现。目的是为了和其他的IOC框架进行集成。比如在这个接口里可以通过从Spring容器中获取实例化的对象。

2:paramNameGetter

还是一个接口,主要用于从Method中获取入参的名称,我在这里提供了一个实现类,是通过asm来获取的。也可以再写一个通过注解获取参数名称的实现类。我在这里用的是asm

<dependency>\n<groupId>org.ow2.asm</groupId>\n<artifactId>asm</artifactId>\n<version>7.0</version>\n</dependency>\n

这里我们主要用到asm中

1:ClassReader

publicvoidaccept(\nfinalClassVisitorclassVisitor,//一个ClassVisitor对象\nfinalintparsingOptions//在访问类时必须解析的属性原型\n){\n...\n}\n

这个类要求我们在构造函数中传入class的全限定名称,就是class.getName();

2:ClassVisitor.java

publicMethodVisitorvisitMethod(\nfinalintaccess,//方法的访问标志\nfinalStringname,//方法的名称\nfinalStringdescriptor,//方法的描述符\nfinalStringsignature,//方法的签名\nfinalString[]exceptions//方法的异常类的内部名称\n){\n...\n}\n

这个方法会在执行classReader.accept()的时候被执行。返回值是一个MethodVisitor

3:MethodVisitor.java

publicvoidvisitLocalVariable(\nfinalStringname,//局部变量的名称\nfinalStringdescriptor,//局部变量的类型描述符\nfinalStringsignature,//此局部变量的类型签名\nfinalLabelstart,//对应于此局部变量范围的第一条指令\nfinalLabelend,//对应于此局部变量范围的最后一条指令\nfinalintindex//局部变量的索引\n){\n...\n}\n

这个方法会在methodVisitor.visitMethod()中被执行,没有返回值。我们需要的Method的入参名称就是在这里获取的。

因为这两个类是将整个Class的方法都扫描一遍,所以我们需要自己写两个类来继承它,在里面添加我们需要的逻辑。代码如下:

MethodParamNameClassVisitor.java

\nimportorg.objectweb.asm.*;\nimportjava.util.List;\n/**\n*asmclass访问器\n*用于提取方法的实际参数名称\n*\n*@authorhjx\n*/\npublicclassMethodParamNameClassVisitorextendsClassVisitor{\n/**\n*方法的参数名称\n*/\nprivateList<String>paramNames;\n/**\n*方法的名称\n*/\nprivateStringmethodName;\n/**\n*方法的参数类型\n*/\nprivateClass[]patamTypes;\n@Override\npublicMethodVisitorvisitMethod(\nintaccess,Stringname,Stringdescriptor,Stringsignature,String[]exceptions\n){\nMethodVisitorvisitMethod=super.visitMethod(access,name,descriptor,signature,exceptions);\nbooleansameMethod=sameMethod(name,methodName,descriptor,patamTypes);\n//如果是相同的方法,执行取参数名称的操作\nif(sameMethod){\nMethodParamNameMethodVisitorparamNameMethodVisitor=newMethodParamNameMethodVisitor(\nOpcodes.ASM4,visitMethod\n);\nparamNameMethodVisitor.paramNames=this.paramNames;\nparamNameMethodVisitor.paramLength=this.patamTypes.length;\nreturnparamNameMethodVisitor;\n}\nreturnvisitMethod;\n}\n/**\n*是否是相同的方法\n*\n*@parammethodName\n*@parammethodName2\n*@paramdescriptor\n*@paramparamTypes\n*@return\n*/\nprivatebooleansameMethod(StringmethodName,StringmethodName2,Stringdescriptor,Class[]paramTypes){\n//方法名相同\nAssert.notNull(methodName);\nAssert.notNull(methodName2);\nif(methodName.equals(methodName2)){\nType[]argumentTypes=Type.getArgumentTypes(descriptor);\n//参数长度相同\nif(argumentTypes.length==paramTypes.length){\n//参数类型相同\nfor(inti=0;i<argumentTypes.length;i++){\nif(!Type.getType(paramTypes[i]).equals(argumentTypes[i])){\nreturnfalse;\n}\n}\nreturntrue;\n}\n}\nreturnfalse;\n}\n/**\n*@paramparamNames取出的参数名称,传入一个空的集合\n*@parammethodName目标方法名称\n*@parampatamTypes目标方法的参数类型\n*/\npublicMethodParamNameClassVisitor(List<String>paramNames,StringmethodName,Class[]patamTypes){\nsuper(Opcodes.ASM4);\nthis.paramNames=paramNames;\nthis.methodName=methodName;\nthis.patamTypes=patamTypes;\n}\n/**\n*禁止的操作\n*无法正确使用,抛出异常\n*\n*@paramapi\n*/\npublicMethodParamNameClassVisitor(intapi){\nsuper(api);\nthrownewRuntimeException("不支持的操作,请使用构造函数:MethodParamNameClassVisitor(List<String>paramNames,intpatamLength)!");\n}\n}\n/**\n*用于取出方法的参数实际名称\n*/\nclassMethodParamNameMethodVisitorextendsMethodVisitor{\n/**\n*方法的参数名称\n*/\nList<String>paramNames;\n/**\n*方法的参数长度\n*/\nintparamLength;\n@Override\npublicvoidvisitLocalVariable(\nStringname,Stringdescriptor,Stringsignature,Labelstart,Labelend,intindex\n){\nsuper.visitLocalVariable(name,descriptor,signature,start,end,index);\n//index为0时,name是this\n//根据方法实际参数长度截取参数名称\nif(index!=0&&paramNames.size()<paramLength){\nparamNames.add(name);\n}\n}\npublicMethodParamNameMethodVisitor(intapi,MethodVisitormethodVisitor){\nsuper(api,methodVisitor);\n}\n}\n

这个类里,因为继承父类之后必须要实现一个带参数的构造方法:

publicMethodParamNameClassVisitor(intapi){\nsuper(api);\n}\n

但是这个方法我不想用它,就在方法结束后抛了一个异常出来。并新写了一个构造方法:

/**\n*@paramparamNames取出的参数名称,传入一个空的集合\n*@parammethodName目标方法名称\n*@parampatamTypes目标方法的参数类型\n*/\npublicMethodParamNameClassVisitor(\nList<String>paramNames,\nStringmethodName,\nClass[]patamTypes\n){\nsuper(Opcodes.ASM4);\nthis.paramNames=paramNames;\nthis.methodName=methodName;\nthis.patamTypes=patamTypes;\n}\n

其中paramNames传入一个空集合(不是null),在方法执行完毕后会在里面添加方法的入参名称。

这个类是这么用的(下面的代码就是上面说道的paramNameGetter的一个实现):

/**\n*通过asm获取method的入参名称\n*\n*@parammethod\n*@return\n*/\n@Override\npublicString[]getParamNames(Methodmethod){\nAssert.notNull(method);\nClassaClass=method.getDeclaringClass();\nParameter[]parameters=method.getParameters();\nStringmethodName=method.getName();\nStringclassName=aClass.getName();\nClassReaderclassReader=null;\ntry{\nclassReader=newClassReader(className);\n}catch(IOExceptione){\ne.printStackTrace();\n}\nClass[]paramClasses=newClass[parameters.length];\nfor(inti=0;i<paramClasses.length;i++){\nparamClasses[i]=parameters[i].getType();\n}\n//暂存参数名称\nList<String>paramNameList=newArrayList<>();\nMethodParamNameClassVisitormyClassVisitor=newMethodParamNameClassVisitor(\nparamNameList,methodName,paramClasses\n);\nclassReader.accept(myClassVisitor,0);\nreturnparamNameList.toArray(newString[]{});\n}\n

现在。映射关系UrlMethodMapping中的数据就全部填充好了。

mvc框架的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于自己写一个mvc框架吧、mvc框架的信息别忘了在本站进行查找哦。

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023