这篇笔记是学习web开发时基于反射和泛型的产物,实际开发时不须要去造这种轮子,仅供参考html
以前写了一篇随笔,说明了javaweb中如何自动封装请求头中的数据到指定实体类中
javaweb 自动封装请求头中的数据到指定实体类中
在随笔中,前端请求的接口是肯定的,就是添加数据,如今咱们但愿这个封装能跟贴近实际使用一点,或者说跟自动化一点前端
整个项目要以数据库的表为基准,有多少张表,就要有多少个实体类,在以前的开发中,咱们都是单表开发,根据功能去拆分出多个servlet、service和dao。java
如图就是典型的单表开发,根据功能书写多个servlet、service和dao。
web
可是如今有不少表,若是每一个表都要根据功能去写servlet,整个项目就会很冗余,也很不直观。
因此咱们不该该根据功能去写dao\service\servlet,而是应该根据表写这些东西。
一张表对应一个实体类,对应一个dao和daoImpl,对应一个service和serviceImpl,对应一个servlet。
在dao中写五个基本的方法(增删改查、查全部)
而后在这个servlet中的doPost/doGet下用switch列出全部功能(增删改查、查全部)ajax
若是这样作,咱们就要在servlet中对浏览器的请求进行判断,是执行增删改查,仍是查全部
浏览器端表单提交请求类型:
服务器端servlet接收请求:
spring
封装目标:数据库
前端各个模块发送的请求会指向后端不一样的servlet,而且经过请求参数type的值说明须要执行的操做。
后端每一个servlet上都经过使用switch-case调用对应的方法。
以上的操做存在许多重复代码,并且每次新增功能后,还得去维护switch-case,所以要下降代码重复性,使用反射实现switch-case要实现的功能。json
每一个servlet中都直接将请求头和响应头传给方法,在方法中完成请求头的解析,一方面形成代码重复,另外一方面也与“servlet只容许给方法传递对象”的代码规范相违背
由于方法通常都放在service里,而servlet调用service的方法时,应该只传递对象,而不能传递请求头、响应头。
所以咱们要下降重复性,消灭传递给方法的请求头和响应头,作到“只给方法传递对象”后端
若是”只给方法传递对象“,那么就要求全部的方法在执行完毕后都要将结果返回给servlet,由servlet进行响应——这也会形成代码重复,这也要解决。数组
以前写servle时,前端每一个模块对应一个servlet,前端发送的请求被各模块指定的servlet的doGet和doPost接收,从中取出type,使用switch-case进行判断。
好比前端的模块一须要执行查询操做,那么就须要后端的servlet_1取出请求参数type=show,而后进入case=show的分支,去调用指定的方法。
而后前端的模块二须要执行添加操做,那么就须要后端的servlet_2取出请求参数type=add,而后进入case=add的分支,去调用指定的方法。
这样后期维护起来太麻烦,每加入一个新模块就要重写一次doGet、doPost,而且各个模块里还须要维护switch-case
例图:ajax中实现级联菜单,取出请求参数type,而后进入不一样的分支
所以咱们如今要转变整个项目的编写思路。
首先前端保持不变,依然是根据不一样的模块指向后端不一样的servlet,请求参数依然是根据不一样的需求给出不一样的type值
然后端则设立一个BaseServlet去实现doGet和doPost以及编码设置,而后基于反射和泛型取出请求参数type的值,令servlet下的对应方法自动执行。
全部模块的servlet经过继承BaseServlet,实现代码的降重,而servlet中根据模块需求写各自的方法。
核心方法——经过反射获取类中的同名方法
语法 | 说明 |
---|---|
Method 变量名 = Class对象 . getMethod ("方法名", 指向参数类型的Class对象) | 获取类中某个带参的方法,因为存在方法重载的可能,所以须要给定方法的参数表 |
①代码写在父类上,这是为了代码降重,但其实是在子类中被执行,此时this代指的是各类BaseServlet类的子类。
②下图代码中没有设置编码格式,是由于这部分代码被放在了过滤器Filter中
③做为基础类的BaseServlet继承HttpServlet,别的Servlet则继承该BaseServlet
④虽然目前没见过,可是有时候doGet和doPost须要执行不一样的代码,到时候就在BaseServlet中修改一下doGet中的代码便可
①子类直接继承BaseServlet,简化了doGet和doPost的操做
②子类中根据模块需求编写响应的方法,从父类继承的反射方法会自动解析前端发来的请求调用对应的方法
③子类中要对请求头进行数据解析,转换为对象,而后使用
在上面的代码中,已经实现了自动根据type类型去执行同名的操做,而且写在了父类servlet上,令全部子类servlet都具有该功能。
能够说已经实现了整个项目的代码降重以及使用反射替代switch-case的目的
可是实际上存在三个问题:
①若是找不到同名的type,会报错,由于上面的代码中并无进行同名判断以及结果为空时的处理
②使用invoke调用方法时传入的参数顺序是固定的,如今这种写法要求子类中的方法参数表只能是 "请求头,响应头",但实际上可能会不一样
③每一个子servlet都须要对请求头进行解析,将其中存储的json对象转为实体对象,再传给各自的方法。对请求头进行解析的代码也应该进行降重,让每一个子servlet只须要编写方法。
在执行完下图代码后,咱们能够得到一个指向肯定的方法的Method对象,若是没有取到,则返回null,不会因为空指针而报错,解决了问题①
注意:代码在子类中执行,获取到的是子类Servlet的方法,建议子类servlet中的方法都设置为公开方法,避免这里还得暴力破解
此时若是想使用invoke调用该Method对象,须要给出该对象须要的参数,即解决Method对象.invoke(this,?)中的?
经过自动识别方法的参数表须要的参数类型以及参数类型的顺序,就能解决问题②,即?
经过自动封装请求头中的数据,就能解决问题③,即代码降重,关于图中的第五步操做——自动解析与封装请求头数据的进一步说明 点击这里了解
跳转连接中能说明清楚图中第五步对应实际开发中的哪一个场景需求,又是如何实现的
现状分析
上图解决了问题②和③,可是里面存在一个问题:
对方法参数表中参数类型的判断只局限于请求头、响应头、指定路径下的实体类,若是方法的参数表里存在字符串或者需求的是其余路径下的实体类就会出问题
具体说明
其余路径:第四步判断是否为实体类时借助了常量 "com.javasm.entity",若是咱们传递的实体类对象是vo类或者包名叫bean而不是entity,那就会出问题
存在字符串或者其余数据类型:方法需求直接传入一个或多个字符串数据或者其余数据类型,因为这种状况没有写在循环判断中,就会致使整个object数组元素与参数表对不上
解决办法
针对存在字符串或者其余数据类型:目前没办法把这部分完美解决,须要spring的四个包进行辅助,这些包的底层机制目前也还不清楚
针对路径的状况,须要JAVA动态识别方法中参数表须要的实体对象的全类名,获取的具体实现原理 点击这里了解
下面给出正式版相比基础版的改动,其余没给出的与基础版一致,这些改动解决了实体类的路径问题。
父类servlet
①设置父类为泛型类
②声明一个Class对象 用于储存当前servlet类声明的泛型类型
具体到应该声明一个对象仍是一个数组得看父类声明泛型时设置了几个泛型,一般设置为1个,最多设置2个。
若是只设置了一个泛型,那么此时子类的泛型个数就是一个,Class对象设置为一个对象便可
若是设置了多个泛型,那么此时子类的泛型个数就是多个,此时Class对象就要设置为数组,而且下方对Class数组的使用就要逐个取出分别判断
③建立一个构造函数,在里面编写获取servlet声明的泛型的代码
也能够建立一个非静态代码块,总之要求获取servlet声明的泛型的代码要在整个servlet类执行时就执行
④上图代码执行完毕后就能得到servlet在声明泛型时的具体类型,此时就能对Class对的全类名进行一个断定,若是相同,说明此时Class对指向的就是一个实体类而不是别的东西,此时就能实现条件都不知足时提示“你这方法须要的参数既不是请求头又不是响应头还不是实体类,我这个servlet封装搞不定这种复杂状况,方法中止”
子类Servlet
子类继承父类时要声明泛型的实际类型,能够声明多个,但通常最多声明2个,就好比继承Map集合时声明K和V的数据类型,总之要包含子类中各类方法使用时的全部数据类型
上面的代码实现了自动读取请求参数type,而后执行对应的方法,而且在方法的参数表须要传入实体对象时,自动建立一个实体对象给到方法。
如今须要作的就是当servlet调用的方法执行完毕时,对前端的响应。
以前对前端的响应都是直接在各个子servlet下进行,各个子Servlet的响应方法都同样,这无疑是重复代码,所以能够放在父类servlet下统一编写。
子类Servlet
首先在子类Servlet的方法中,执行完毕后根据需求能够分别请求转发、重定向、异步请求,根据数据类型跳转能够分为页面、servlet、json数据,根据方法需求返回
返回时根据"响应类型:响应的数据"手动拼接
父类Servlet
在父类Servlet编写的是全部servlet都能拿到的代码,代码的目的简单来讲就是判断。
使用method对象.invoke以后得到的是一个Object对象
此时执行响应结果时,须要先把传入的object强制转换为字符串,再进行后面的判断
上面给出了响应代码的封装,如今更进一步,基于注解去封装响应代码
以前的版本在使用时要求方法返回时要在原数据前加上响应类型,约定请求转发加 f 、重定向加 r 、AJAX加 a
若是编写方法时不按照这个要求来作,就会产生问题,而实际开发中,这种对于返回类型的要求很不友好,很容易被忽略
①建立注解类。
②注解所能选择的值则放在枚举类中,这样数据的安全性就更高了。
经过使用注解,能将响应方法的添加转移到注解上,这样方法的响应方式与方法的返回值实现了解耦
可是如今的问题是编写方法时忘记加上注解了,就会致使没法响应。
这种状况目前没办法解决,由于当前的封装手段只能尽可能保证数据的完整性,不能检测是否添加了注解,或者添加的是不是须要添加的注解
原来:
如今: