架构探险——从零开始写Java Web框架
需求来源
web框架全盘掌控
序
实践代码地址:
业务模块
解决问题
控制层、业务层、持久层分层,利于模块解耦和复用。
使用方法
引入DHMap的jar包,控制层继承BasicController,业务层继承BasicServiceImpl,且都通过注解声明为spring组件,视图Bean继承VO,持久Bean继承PO,持久Bean上通过@Table对应数据库表名,域上@Column对应字段,@ID对应id,还可用@OneToOne,@OneToMany等实现表关联关系。
实现原理:
- 泛型控制层类,实现符合REST的CRUD方法,用于被继承使用
- 泛型业务层类,实现了CRUD方法,将VO转化为PO ,用于被继承使用
- 提供SQL的CRUD模板,只需拼入表的名、字段、值等信息
- 泛型持久层类,实现了CRUD方法,分析PO的注解,提取表信息
- 将表信息拼入SQL模板,形成SQL语句,调用mybatis实现结果返回
- 对返回结果解析,并反射实例化PO以及PO的域赋值
代码分析:
- 定义一系列注解
对带Blob,Clob注解等域需cast (XXX as CHAR) as XXX取出,Ignore表示该域不对应库字段 - BasicController.java为控制层基类
提供了CRUD以及分页查询等方法,返回结果格式统一为Result - BasicServiceImpl.java为业务层基类
- 接口方法实现于BasicService,提供了基本和批量的CRUD方法
- 初始化方法内通过反射获取类的泛型类型
- 单例化BasicMapperImpl时传过去
- 接受来自Controller的VO,反射创建PO并赋值,调用BasicMapperImpl方法
- BasicMapperImpl.java为持久层封装类
- 接口方法实现于BasicMapper,提供了基本和批量的CRUD方法,其中selectList用于字段匹配查询,接受Condition参数,可定义分页参数和基于WhereOperator比较符的where子句拼接
- 根据PO信息分析,转为对应sql语句
- 调用到SQL模板拼接中
- 调用mybatis实现结果返回
- 根据PO信息分析,反射到PO中,返回
- SqlMapper.java为SQL模板
之前使用BasicMapper.xml实现模板,但后续mybatis更新版本,可以通过@Insert等注解在接口使用 - Entity.java为PO信息分析类
- 获取PO所有域,分析@Table@@Column@ID等注解,对域重封装
- 记录域名和域对应库字段名
- 根据是否有@OneToOne,@OneToMany等注解的域,延伸到下一个PO,递归记录关联关系
- 记录每个PO的父PO,并取别名备用
- SqlUtil.java将PO信息转化为sql语句和将jdbc返回结果转为PO
其中getJoinPart方法将表关系转为ONE_A a left join ONE_B b on a.id=b.a_id等
handleResult方法将map转为list
IOC
概念
依赖注入,也叫控制反转,通过屏蔽接口的实现方法,实现对象的不同行为。
解决问题
项目越大,对象的实例化和相互引用关系关联等动作占据时间增多,spring框架的自动装配简化了该步骤。
使用方法
类上注解@Componet声明为组件,类域上注解@Autowired或@Resource声明为需注入的域。
实现原理
- 新建类加载器,扫描指定包下所有类
- 所有类中分析出组件类(带有或注解于@Componet的注解,如@Controller)
- 反射实例化组件类
- 找出组件类中需注入的域(带有注解@Autowired或@Resource)
- 对需注入的域,进行反射赋值
代码分析
- 定义组件注解
Component注解关系图为,@Controller,@Service,@Repository注解上注着@Componet,而@RestController上注着@Controller - ClassUtil.java实现获取包名下所有类
- 根据当前线程的类加载器,取到编译后文件根路径,如D:\DSS\DHMap\Trunk\target\classes
- 将制定包名转为文件路径,结合根路径,如com.dahua,结合为D:\DSS\DHMap\Trunk\target\classes\com\dahua
- 递归记录文件下后缀为.class的文件
- 若发现jar包则遍历jar内部文件,记录后缀为.class的文件
- 将.class文件路径转为类全限定名,如D:\DSS\DHMap\Trunk\target\classes\com\dahua\frame\ioc\annotatio\Component.class,转为com.dahua.frame.ioc.annotatio.Component
- 调用Class.forName载入字节,加载并记录该类
- ClassHelper.java实现获取组件类
- 递归获取@Componet的子注解
- 遍历所有类,判断是否带有@Componet或其子注解
- BeanHelper.java实现组件实例化、
遍历组件类
调用class的newInstance方法,调用类的无参构造区 //todo 可先判断有哪些构造器,再调用 - IOCHelper.java实现域相互注入
遍历组件类的域组,判断是否带有@Autowired或@Resource注解,通过类获取工厂中实例,调用field的set赋值 - ApplicationHelper.java实现以上诸类的静态代码块加载
MVC
概念
Model(模型)、View(视图)、Controller(控制器)分离。
解决问题
对Servlet解耦,解析URL分发到对应控制层。
使用方法
web.xml添加DispatcherServlet,URL匹配为/。
实现原理
- 获取带有@Controller或@RestController的类
- 解析并缓存类的每个方法对应的URL和Method
- servlet解析URL,并封装参数
- servlet分发到对应方法
- servlet对方法返回值进行分析,判断是页面跳转还是数据返回
AOP
概念
解决问题
使用方法
实现原理
Transaction
概念
事物的ACID,分别为原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。