简介
前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架。一直没有时间去补充相应的文档,现在正好抽时间去整理下。通过思路历程和代码注释,一方面重温下知识,另一方面准备后期去完善这个框架。
image传统JDBC连接
参照传统的JDBC连接数据库过程如下,框架所做的事情就是把这些步骤进行封装。
// 1\. 注册 JDBC 驱动
Class.forName(JDBC_DRIVER);
// 2\. 打开链接
conn = DriverManager.getConnection(DB_URL,USER,PASS);
// 3\. 实例化statement
stmt = conn.prepareStatement(sql);
// 4\. 填充数据
stmt.setString(1,id);
// 5\. 执行Sql连接
ResultSet rs = stmt.executeQuery();
// 6\. 结果查询
while(rs.next()){
// 7\. 通过字段检索
int id = rs.getInt("id");
String name = rs.getString("name");
String url = rs.getString("url");
}
思路历程
上述是执行Java连接数据库通用语句,ORM框架就是对象关系映射框架。我们需要做的重点是步骤4和7,将对象字段填充至数据库,将数据库的内容取出作为对象返回。了解了基本的方法后,开始动手写一个框架了。
数据库驱动
开始,需要构建一个驱动注册类,这个类主要用来对数据库驱动进行构造。详见代码块1
代码块1
数据库驱动类(com.simple.ibatis.dirver)
/**
* @Author xiabing
* @Desc 驱动注册中心,对数据库驱动进行注册的地方
**/
public class DriverRegister {
/*
* mysql的driver类
* */
private static final String MYSQLDRIVER = "com.mysql.jdbc.Driver";
/*
* 构建driver缓存,存储已经注册了的driver的类型
* */
private static final Map<String,Driver> registerDrivers = new ConcurrentHashMap<>();
/*
* 初始化,此处是将DriverManager中已经注册了驱动放入自己缓存中。当然,你也可以在这个方法内注册
常见的数据库驱动,这样后续就可以直接使用,不用自己手动注册了。
* */
static {
Enumeration<Driver> driverEnumeration = DriverManager.getDrivers();
while (driverEnumeration.hasMoreElements()){
Driver driver = driverEnumeration.nextElement();
registerDrivers.put(driver.getClass().getName(),driver);
}
}
/*
* 加载mysql驱动,此个方法可以写在静态代码块内,代表项目启动时即注册mysql驱动
* */
public void loadMySql(){
if(! registerDrivers.containsKey(MYSQLDRIVER)){
loadDriver(MYSQLDRIVER);
}
}
/*
* 加载数据库驱动通用方法,并注册到registerDrivers缓存中,此处是注册数据库驱动的方法
* */
public void loadDriver(String driverName){
Class<?> driverType;
try {
// 注册驱动,返回驱动类
driverType = Class.forName(driverName);
// 将驱动实例放入驱动缓存中
registerDrivers.put(driverType.getName(),(Driver)driverType.newInstance());
}catch (ClassNotFoundException e){
throw new RuntimeException(e);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
驱动注册后,需要建立数据库连接。但是框架如果一个请求建一个连接,那木势必耗费大量的资源。所以该框架引入池化的概念,对连接进行池化管理。详情见代码2
代码块2
数据源类(com.simple.ibatis.datasource)
PoolConnection
/**
* @Author xiabing
* @Desc 连接代理类,是对Connection的一个封装。除了提供基本的连接外,还想记录
这个连接的连接时间,因为有的连接如果一直连接不释放,那木我可以通过查看
这个连接已连接的时间,如果超时了,我可以主动释放。
**/
public class PoolConnection {
// 真实的数据库连接
public Connection connection;
// 数据开始连接时间
private Long CheckOutTime;
// 连接的hashCode
private int hashCode = 0;
public PoolConnection(Connection connection) {
this.connection = connection;
this.hashCode = connection.hashCode();
}
public Long getCheckOutTime() {
return CheckOutTime;
}
public void setCheckOutTime(Long checkOutTime) {
CheckOutTime = checkOutTime;
}
// 判断两个PoolConnection对象是否相等,其实是判断其中真实连接的hashCode是否相等
@Override
public boolean equals(Object obj) {
if(obj instanceof PoolConnection){
return connection.hashCode() ==
((PoolConnection) obj).connection.hashCode();
}else if(obj instanceof Connection){
return obj.hashCode() == hashCode;
}else {
return false;
}
}
@Override
public int hashCode() {
return hashCode;
}
}
NormalDataSource
/**
* @Author xiabing
* @Desc 普通数据源,这个数据源是用来产生数据库连接的。来一个请求就会建立一个数据库连接,没有池化的概念
**/
public class NormalDataSource implements DataSource{
// 驱动名称
private String driverClassName;
// 数据库连接URL
private String url;
// 数据库用户名
private String userName;
// 数据库密码
private String passWord;
// 驱动注册中心
private DriverRegister driverRegister = new DriverRegister();
public NormalDataSource(String driverClassName, String url, String userName, String passWord) {
// 初始化时将驱动进行注册
this.driverRegister.loadDriver(driverClassName);
this.driverClassName = driverClassName;
this.url = url;
this.userName = userName;
this.passWord = passWord;
}
// 获取数据库连接
@Override
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,userName,passWord);
}
// 移除数据库连接,此方法没有真正移除,只是将连接中未提交的事务进行回滚操作。
public void removeConnection(Connection connection) throws SQLException{
if(!connection.getAutoCommit()){
connection.rollback();
}
}
// 获取数据库连接
@Override
public Connection getConnection(String username, String password) throws SQLException {
return DriverManager.getConnection(url,username,password);
}
// 后续方法因为没有用到,所有没有进行重写了
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
PoolDataSource
/**
* @Author xiabing
* @Desc 池化线程池,对连接进行管理
**/
public class PoolDataSource implements DataSource{
private Integer maxActiveConnectCount = 10; // 最大活跃线程数,此处可根据实际情况自行设置
private Integer maxIdleConnectCount = 10; // 最大空闲线程数,此处可根据实际情况自行设置
private Long maxConnectTime = 30*1000L; // 连接最长使用时间,在自定义连接中配置了连接开始时间,在这里来判断该连接是否超时
private Integer waitTime = 2000; // 线程wait等待时间
private NormalDataSource normalDataSource; // 使用normalDataSource来产生连接
private Queue<PoolConnection> activeConList = new LinkedList<>(); // 存放活跃连接的队列
private Queue<PoolConnection> idleConList = new LinkedList<>(); // 存放空闲连接的队列
public PoolDataSource(String driverClassName, String url, String userName, String passWord) {
this(driverClassName,url,userName,passWord,10,10);
}
public PoolDataSource(String driverClassName, String url, String userName, String passWord,Integer maxActiveConnectCount,Integer maxIdleConnectCount) {
// 初始化normalDataSource,因为normalDataSource已经封装了新建连接的方法
this.normalDataSource = new NormalDataSource(driverClassName,url,userName,passWord);
this.maxActiveConnectCount = maxActiveConnectCount;
this.maxIdleConnectCount = maxIdleConnectCount;
}
/**
* @Desc 获取连接时先从空闲连接列表中获取。若没有,则判断现在活跃连接是否已超过设置的最大活跃连接数,没超过,new一个
* 若超过,则判断第一个连接是否已超时,若超时,则移除掉在新建。若未超时,则wait()等待。
**/
@Override
public Connection getConnection(){
Connection connection = null;
try {
connection = doGetPoolConnection().connection;
}catch (SQLException e){
throw new RuntimeException(e);
}
return connection;
}
public void removeConnection(Connection connection){
PoolConnection poolConnection = new PoolConnection(connection);
doRemovePoolConnection(poolConnection);
}
private PoolConnection doGetPoolConnection() throws SQLException{
PoolConnection connection = null;
while (connection == null){
// 加锁
synchronized (this){
// 判断是否有空闲连接
if(idleConList.size() < 1){
// 判断活跃连接数是否已经超过预设的最大活跃连接数
if(activeConList.size() < maxActiveConnectCount){
// 如果还可以新建连接,就新建一个连接
connection = new PoolConnection(normalDataSource.getConnection());
}else {
// 走到这一步,说明没有空闲连接,并且活跃连接已经满了,则只能看哪些连接超时,判断第一个连接是否超时
PoolConnection poolConnection = activeConList.peek();
// 这就是我为啥要给数据库连接加连接时间了
if(System.currentTimeMillis() - poolConnection.getCheckOutTime() > maxConnectTime){
// 若第一个连接已经超时了,移除第一个活跃连接
PoolConnection timeOutConnect = activeConList.poll();
if(!timeOutConnect.connection.getAutoCommit()){
// 如果该连接设的是非自动提交,则对事物进行回滚操作
timeOutConnect.connection.rollback();
}
// 置为空,让垃圾收集器去收集
timeOutConnect.connection.close();
timeOutConnect = null;
// 新建一个连接
connection = new PoolConnection(normalDataSource.getConnection());
}else {
// 走到这一步,代表所有连接都没有超时,只能等了
try{
// 于是等一会
this.wait(waitTime);
}catch (InterruptedException e){
// ignore错误,并退出
break;
}
}
}
}else {
// 这一步代表空闲连接队列里面有连接,则直接取出
connection = idleConList.poll();
}
if(connection != null){
// 设置这个连接的连接时间
connection.setCheckOutTime(System.currentTimeMillis());
// 然后放入活跃连接队列中
activeConList.add(connection);
}
}
}
return connection;
}
// 移除连接
private void doRemovePoolConnection(PoolConnection connection){
// 加锁
synchronized (this){
// 移除连接,这里需要重写PoolConnection的equal方法,因为只要真实连接相同,就可以代表这个封装的连接相等了
activeConList.remove(connection);
if(idleConList.size() < maxIdleConnectCount){
// 加入空闲连接队列中
idleConList.add(connection);
}
// 通知其他等待线程,让他们继续获取连接
this.notifyAll();
}
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return getConnection();
}
// 后面的方法没有用到就没有重写了
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
使用数据库连接池获取连接后,接下来就是关键了,生成SQL预处理语句,并进行数据填充。这让我想到了mybatis使用方法。
@Select("select * from users")
List<User> getUsers();
上面是mybatis基于注解的配置方式。除了注解,还支持xml。于是框架准备先使用基于注解的方式进行代码配置。首先,需要知道这些接口类的类名。于是提供一个配置,根据指定包名获取包下所有类的类名详情见代码3
代码块3
方法类(com.simple.ibatis.util)
PackageUtil
/**
* @Author xiabing
* @Desc 获取指定包下所有类的名称
**/
public class PackageUtil {
private static final String CLASS_SUFFIX = ".class";
private static final String CLASS_FILE_PREFIX = "classes" + File.separator;
private static final String PACKAGE_SEPARATOR = ".";
/**
* 查找包下的所有类的名字
* @param packageName
* @param showChildPackageFlag 是否需要显示子包内容
* @return List集合,内容为类的全名
*/
public static List<String> getClazzName(String packageName, boolean showChildPackageFlag){
List<String> classNames = new ArrayList<>();
String suffixPath = packageName.replaceAll("\\.", "/");
// 获取类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try{
Enumeration<URL> urls = loader.getResources(suffixPath);
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
if(url != null){
if ("file".equals(url.getProtocol())) {
String path = url.getPath();
classNames.addAll(getAllClassName(new File(path),showChildPackageFlag));
}
}
}
}catch (IOException e){
throw new RuntimeException("load resource is error , resource is "+packageName);
}
return classNames;
}
// 获取所有类的名称
private static List<String> getAllClassName(File file,boolean flag){
List<String> classNames = new ArrayList<>();
if(!file.exists()){
return classNames;
}
if(file.isFile()){
String path = file.getPath();
if(path.endsWith(CLASS_SUFFIX)){
path = path.replace(CLASS_SUFFIX,"");
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()).replace(File.separator, PACKAGE_SEPARATOR);
classNames.add(clazzName);
}
}else {
File[] listFiles = file.listFiles();
if(listFiles != null && listFiles.length > 0){
for (File f : listFiles){
if(flag) {
classNames.addAll(getAllClassName(f, flag));
}else {
if(f.isFile()){
String path = f.getPath();
if(path.endsWith(CLASS_SUFFIX)) {
path = path.replace(CLASS_SUFFIX, "");
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()).replace(File.separator,PACKAGE_SEPARATOR);
classNames.add(clazzName);
}
}
}
}
}
}
return classNames;
}
}
在知道接口类存放在哪些类后,我需要解析这些类中的各个方法,包括每个方法的注解,入参,和返回值。同时,我需要使用一个全局配置类,用于存放我解析后生成的对象,并且一些数据源配置信息也可以放在这个全局类中。详情见代码4
代码块4
核心类(com.simple.ibatis.core)
Config
/**
* @Author xiabing
* @Desc 框架的核心配置类
**/
public class Config {
// 数据源
private PoolDataSource dataSource;
// mapper包地址(即待解析的包名),后续改为List<String>,能同时加载多个mapper包 todo
private String daoSource;
// mapper核心文件(存放类解析后的对象)
private MapperCore mapperCore;
// 是否启用事务
private boolean openTransaction;
// 是否开启缓存
private boolean openCache;
public Config(String mapperSource,PoolDataSource dataSource){
this.dataSource = dataSource;
this.daoSource = mapperSource;
this.mapperCore = new MapperCore(this);
}
// 生成SQL语句执行器
public SimpleExecutor getExecutor(){
return new SimpleExecutor(this,this.getDataSource(),openTransaction,openCache);
}
// 后文都是基本的get,set方法
public PoolDataSource getDataSource() {
return dataSource;
}
public void setDataSource(PoolDataSource dataSource) {
this.dataSource = dataSource;
}
public String getDaoSource() {
return daoSource;
}
public void setDaoSource(String daoSource) {
this.daoSource = daoSource;
}
public MapperCore getMapperCore() {
return mapperCore;
}
public void setMapperCore(MapperCore mapperCore) {
this.mapperCore = mapperCore;
}
public boolean isOpenTransaction() {
return openTransaction;
}
public void setOpenTransaction(boolean openTransaction) {
this.openTransaction = openTransaction;
}
public boolean isOpenCache() {
return openCache;
}
public void setOpenCache(boolean openCache) {
this.openCache = openCache;
}
}
SqlSource
/**
* @Author xiabing5
* @Desc 将sql语句拆分为sql部分和参数部分,因为我们需要使用java对象对数据库预处理对象进行注入。具体看下面例子
* @example select * from users where id = {user.id} and name = {user.name}
* -> sql = select * from users where id = ? and name = ?
* -> paramResult = {user.id,user.name}
**/
public class SqlSource {
/**sql语句,待输入字段替换成?*/
private String sql;
/**待输入字段*/
private List<String> param;
/**select update insert delete*/
private Integer executeType;
public SqlSource(String sql){
this.param = new ArrayList<>();
this.sql = sqlInject(this.param,sql);
}
/**sql注入,对要注入的属性,必须使用{} 包裹,并替换为? 见例子 */
private String sqlInject(List<String> paramResult, String sql){
String labelPrefix = "{";
String labelSuffix = "}";
// 将语句的所有的{}全部都解析成?,并将{xxx}中的xxx提取出来。
while (sql.indexOf(labelPrefix) > 0 && sql.indexOf(labelSuffix) > 0){
String sqlParamName = sql.substring(sql.indexOf(labelPrefix),sql.indexOf(labelSuffix)+1);
sql = sql.replace(sqlParamName,"?");
paramResult.add(sqlParamName.replace("{","").replace("}",""));
}
return sql;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public List<String> getParam() {
return param;
}
public void setParam(List<String> param) {
this.param = param;
}
public Integer getExecuteType() {
return executeType;
}
public void setExecuteType(Integer executeType) {
this.executeType = executeType;
}
}
MapperCore
/**
* @Author xiabing5
* @Desc mapper方法解析核心类,此处是将接口中的方法进行解析的关键类
**/
public class MapperCore {
// select方法
private static final Integer SELECT_TYPE = 1;
// update方法
private static final Integer UPDATE_TYPE = 2;
// delete方法
private static final Integer DELETE_TYPE = 3;
// insert方法
private static final Integer INSERT_TYPE = 4;
/**mapper文件解析类缓存,解析后都放在里面,避免重复对一个mapper文件进行解析*/
private static Map<String,MethodDetails> cacheMethodDetails = new ConcurrentHashMap<>();
/**
* 全局配置
* */
private Config config;
public MapperCore(Config config){
this.config = config;
load(config.getDaoSource());
}
/*
* 加载,解析指定包名下的类
* */
private void load(String source){
/**加载mapper包下的文件*/
List<String> clazzNames = PackageUtil.getClazzName(source,true);
try{
for(String clazz: clazzNames){
Class<?> nowClazz = java.lang.Class.forName(clazz);
// 不是接口跳过,只能解析接口
if(!nowClazz.isInterface()){
continue;
}
/**接口上没有@Dao跳过。我们对接口类上加了@Dao后,就代表要解析*/
boolean skip = false;
Annotation[] annotations = nowClazz.getDeclaredAnnotations();
for(Annotation annotation:annotations){
// 该接口中的注释中是否有带@Dao
if(annotation instanceof Dao) {
skip = true;
break;
}
}
if(!skip){
continue;
}
// 调用反射接口,获取所有接口中的方法
Method[] methods = nowClazz.getDeclaredMethods();
for( Method method : methods){
// 解析方法详情
MethodDetails methodDetails = handleParameter(method);
// 解析@SELECT()等注解中的SQL内容
methodDetails.setSqlSource(handleAnnotation(method));
// 解析完成后放入缓存
cacheMethodDetails.put(generateStatementId(method),methodDetails);
}
}
}catch (ClassNotFoundException e){
throw new RuntimeException(" class load error,class is not exist");
}
}
/**
* 获得方法详情,从方法缓存池中获取,因为每个方法事先已经完成解析
* */
public MethodDetails getMethodDetails(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId);
}
return new MethodDetails();
}
/**
* 获得方法对应的sql语句
* */
public SqlSource getStatement(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).getSqlSource();
}
throw new RuntimeException(method + " is not sql");
}
/**
* 获得方法对应的参数名
* */
public List<String> getParameterName(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).getParameterNames();
}
return new ArrayList<>();
}
/**
* 获取方法返回类型
* */
public Class getReturnType(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).getReturnType();
}
return null;
}
/**
* 获得方法对应的参数类型
* */
public Class<?>[] getParameterType(Method method) {
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).getParameterTypes();
}
return new Class<?>[]{};
}
/**
* 获得方法是SELECT UPDATE DELETE INSERT
* */
public Integer getMethodType(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).getSqlSource().getExecuteType();
}
return null;
}
/**
* 获得方法是否返回集合类型list
* */
public boolean getHasSet(Method method){
String statementId = generateStatementId(method);
if(cacheMethodDetails.containsKey(statementId)){
return cacheMethodDetails.get(statementId).isHasSet();
}
return false;
}
/**
* 解析方法内的注解
* */
private MethodDetails handleParameter(Method method){
MethodDetails methodDetails = new MethodDetails();
// 获取方法输入参数数量
int parameterCount = method.getParameterCount();
// 获取方法输入各参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 获取方法输入各参数名称
List<String> parameterNames = new ArrayList<>();
Parameter[] params = method.getParameters();
for(Parameter parameter:params){
parameterNames.add(parameter.getName());
}
/*
* 获得方法参数的注解值替代默认值,如果使用了@Param注解,则默认使用注解中的值作为参数名
* */
for(int i = 0; i < parameterCount; i++){
parameterNames.set(i,getParamNameFromAnnotation(method,i,parameterNames.get(i)));
}
methodDetails.setParameterTypes(parameterTypes);
methodDetails.setParameterNames(parameterNames);
/** 获取方法返回类型*/
Type methodReturnType = method.getGenericReturnType();
Class<?> methodReturnClass = method.getReturnType();
if(methodReturnType instanceof ParameterizedType){
/** 返回是集合类 目前仅支持List todo*/
if(!List.class.equals(methodReturnClass)){
throw new RuntimeException("now ibatis only support list");
}
Type type = ((ParameterizedType) methodReturnType).getActualTypeArguments()[0];
/** 设置 返回集合中的对象类型 */
methodDetails.setReturnType((Class<?>) type);
/** 标注该方法返回类型是集合类型 */
methodDetails.setHasSet(true);
}else {
/** 不是集合类型,就直接设置返回类型 */
methodDetails.setReturnType(methodReturnClass);
/** 标注该方法返回类型不是集合类型 */
methodDetails.setHasSet(false);
}
return methodDetails;
}
/**
* 解析@select,@update等注解,获取SQL语句,并封装成对象
* */
private SqlSource handleAnnotation(Method method){
SqlSource sqlSource = null;
String sql = null;
/** 获取方法上所有注解 */
Annotation[] annotations = method.getDeclaredAnnotations();
for(Annotation annotation : annotations){
/** 如果有@select注解 */
if(Select.class.isInstance(annotation)){
Select selectAnnotation = (Select)annotation;
sql = selectAnnotation.value();
/** 语句封装成sqlSource对象 */
sqlSource = new SqlSource(sql);
/** 设置执行语句类型 */
sqlSource.setExecuteType(SELECT_TYPE);
break;
}else if(Update.class.isInstance(annotation)){
Update updateAnnotation = (Update)annotation;
sql = updateAnnotation.value();
sqlSource = new SqlSource(sql);
sqlSource.setExecuteType(UPDATE_TYPE);
break;
}else if(Delete.class.isInstance(annotation)){
Delete deleteAnnotation = (Delete) annotation;
sql = deleteAnnotation.value();
sqlSource = new SqlSource(sql);
sqlSource.setExecuteType(DELETE_TYPE);
break;
}else if(Insert.class.isInstance(annotation)){
Insert insertAnnotation = (Insert) annotation;
sql = insertAnnotation.value();
sqlSource = new SqlSource(sql);
sqlSource.setExecuteType(INSERT_TYPE);
break;
}
}
if(sqlSource == null){
throw new RuntimeException("method annotation not null");
}
return sqlSource;
}
/**
* 获取@Param注解内容
* */
private String getParamNameFromAnnotation(Method method, int i, String paramName) {
final Object[] paramAnnos = method.getParameterAnnotations()[i];
for (Object paramAnno : paramAnnos) {
if (paramAnno instanceof Param) {
paramName = ((Param) paramAnno).value();
}
}
return paramName;
}
/**
* 生成唯一的statementId
* */
private static String generateStatementId(Method method){
return method.getDeclaringClass().getName() + "." + method.getName();
}
/**
* 每个mapper方法的封装类
* */
public static class MethodDetails{
/**方法返回类型,若是集合,则代表集合的对象类,目前集合类仅支持返回List */
private Class<?> returnType;
/**方法返回类型是否是集合*/
private boolean HasSet;
/**执行类型,SELECT,UPDATE,DELETE,INSERT*/
private Integer executeType;
/**方法输入参数类型集合*/
private Class<?>[] parameterTypes;
/**方法输入参数名集合*/
private List<String> parameterNames;
/**sql语句集合*/
private SqlSource sqlSource;
public Class<?> getReturnType() {
return returnType;
}
public void setReturnType(Class<?> returnType) {
this.returnType = returnType;
}
public boolean isHasSet() {
return HasSet;
}
public void setHasSet(boolean hasSet) {
HasSet = hasSet;
}
public Integer getExecuteType() {
return executeType;
}
public void setExecuteType(Integer executeType) {
this.executeType = executeType;
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class<?>[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
public List<String> getParameterNames() {
return parameterNames;
}
public void setParameterNames(List<String> parameterNames) {
this.parameterNames = parameterNames;
}
public SqlSource getSqlSource() {
return sqlSource;
}
public void setSqlSource(SqlSource sqlSource) {
this.sqlSource = sqlSource;
}
}
}
SQL语句已经解析完毕,后面开始执行SQL语句。在之前,我想给执行SQL语句时加点功能,如提供select缓存,事务功能,详情见代码块5,代码块6
代码块5
缓存类(com.simple.ibatis.cache)
Cache
/**
* @author xiabing
* @description: 自定义缓存接口,主要提供增删改查功能
*/
public interface Cache {
/**放入缓存*/
void putCache(String key,Object val);
/**获取缓存*/
Object getCache(String key);
/**清空缓存*/
void cleanCache();
/**获取缓存健数量*/
int getSize();
/**移除key的缓存*/
void removeCache(String key);
}
SimpleCache
/**
* @author xiabing
* @description: 缓存的简单实现
*/
public class SimpleCache implements Cache{
/**用HashMap来实现缓存的增删改查*/
private static Map<String,Object> map = new HashMap<>();
@Override
public void putCache(String key, Object val) {
map.put(key,val);
}
@Override
public Object getCache(String key) {
return map.get(key);
}
@Override
public void cleanCache() {
map.clear();
}
@Override
public int getSize() {
return map.size();
}
@Override
public void removeCache(String key) {
map.remove(key);
}
}
LruCache
/**
* @author xiabing
* @description: 缓存包装类没,使缓存具有LRU功能
*/
public class LruCache implements Cache{
/**缓存大小*/
private static Integer cacheSize = 2000;
/**HashMap负载因子,可以自己指定*/
private static Float loadFactory = 0.75F;
/**真实缓存*/
private Cache trueCache;
/**使用LinkedHashMap来实现Lru功能*/
private Map<String,Object> linkedCache;
/**待移除的元素*/
private static Map.Entry removeEntry;
public LruCache(Cache trueCache){
this(cacheSize,loadFactory,trueCache);
}
public LruCache(Integer cacheSize, Float loadFactory, Cache trueCache) {
this.trueCache = trueCache;
/**初始化LinkedHashMap,并重写removeEldestEntry() 方法,不了解的可以看我之前的博客*/
this.linkedCache = new LinkedHashMap<String, Object>(cacheSize,loadFactory,true){
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
// 当缓存容量超过设置的容量时,返回true,并记录待删除的对象
if(getSize() > cacheSize){
removeEntry = eldest;
return true;
}
return false;
}
};
}
// 放入缓存,当待删除的元素不为空时,就在真实缓存中删除该元素
@Override
public void putCache(String key, Object val) {
// 真实缓存中放入
this.trueCache.putCache(key,val);
// linkedHashMap中放入,此处放入会调用重写的removeEldestEntry()方法
this.linkedCache.put(key,val);
// 当removeEldestEntry()执行后,如果有待删除元素,就开始执行删除操作
if(removeEntry != null){
removeCache((String)removeEntry.getKey());
removeEntry = null;
}
}
@Override
public Object getCache(String key) {
// 因为调用linkedHashMap中的get()方法会对链表进行再一次排序。见之前关于缓存的博客
linkedCache.get(key);
return trueCache.getCache(key);
}
@Override
public void cleanCache() {
trueCache.cleanCache();
linkedCache.clear();
}
@Override
public int getSize() {
return trueCache.getSize();
}
@Override
public void removeCache(String key) {
trueCache.removeCache(key);
}
}
代码块6
事务类(com.simple.ibatis.transaction)
Transaction
/**
* @Author xiabing
* @Desc 封装事务功能
**/
public interface Transaction {
/**获取数据库连接*/
Connection getConnection() throws SQLException;
/**事务提交*/
void commit() throws SQLException;
/**事务回滚*/
void rollback() throws SQLException;
/**关闭连接*/
void close() throws SQLException;
}
SimpleTransaction
/**
* @Author xiabing
* @Desc 事务的简单实现,封装了数据库的事务功能
**/
public class SimpleTransaction implements Transaction{
private Connection connection; // 数据库连接
private PoolDataSource dataSource; // 数据源
private Integer level = Connection.TRANSACTION_REPEATABLE_READ; // 事务隔离级别
private Boolean autoCommmit = true; // 是否自动提交
public SimpleTransaction(PoolDataSource dataSource){
this(dataSource,null,null);
}
public SimpleTransaction(PoolDataSource dataSource, Integer level, Boolean autoCommmit) {
this.dataSource = dataSource;
if(level != null){
this.level = level;
}
if(autoCommmit != null){
this.autoCommmit = autoCommmit;
}
}
/**获取数据库连接,并设置该连接的事务隔离级别,是否自动提交*/
@Override
public Connection getConnection() throws SQLException{
if(connection != null){
return connection;
}
this.connection = dataSource.getConnection();
this.connection.setAutoCommit(autoCommmit);
this.connection.setTransactionIsolation(level);
return this.connection;
}
@Override
public void commit() throws SQLException{
if(this.connection != null){
this.connection.commit();
}
}
@Override
public void rollback() throws SQLException{
if(this.connection != null){
this.connection.rollback();
}
}
/**关闭连接*/
@Override
public void close() throws SQLException{
/**如果该连接设置自动连接为false,则在关闭前进行事务回滚一下*/
if(autoCommmit != null && !autoCommmit){
connection.rollback();
}
if(connection != null){
dataSource.removeConnection(connection);
}
this.connection = null;
}
}
TransactionFactory
/**
* @Author xiabing
* @Desc 事务工厂,封装获取事务的功能
**/
public class TransactionFactory {
public static SimpleTransaction newTransaction(PoolDataSource poolDataSource, Integer level, Boolean autoCommmit){
return new SimpleTransaction(poolDataSource,level,autoCommmit);
}
}
代码真正执行SQL的地方,见代码块7
代码块7
语句执行类(com.simple.ibatis.execute)
Executor
/**
* @Author xiabing
* @Desc 执行接口类,后续执行器需要实现此接口
**/
public interface Executor {
/**获取Mapper接口代理类,使用了动态代理技术,见实现类分析*/
<T> T getMapper(Class<T> type);
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
}
SimpleExecutor
/**
* @Author xiabing
* @Desc 简单执行器类,对于Sql的执行和处理都依赖此类
**/
public class SimpleExecutor implements Executor{
/**全局配置类*/
public Config config;
/**mapper核心类,解析mapper接口类*/
public MapperCore mapperCore;
/**数据库源*/
public PoolDataSource poolDataSource;
/**事务*/
public Transaction transaction;
/**缓存*/
public Cache cache;
public SimpleExecutor(Config config,PoolDataSource poolDataSource){
this(config,poolDataSource,false,false);
}
public SimpleExecutor(Config config,PoolDataSource poolDataSource,boolean openTransaction,boolean openCache){
this.config = config;
this.mapperCore = config.getMapperCore();
this.poolDataSource = poolDataSource;
if(openCache){
// 设置缓存策略为LRU
this.cache = new LruCache(new SimpleCache());
}
if(openTransaction){
// 关闭自动提交
this.transaction = TransactionFactory.newTransaction(poolDataSource,Connection.TRANSACTION_REPEATABLE_READ,false);
}else {
// 开启自动提交
this.transaction = TransactionFactory.newTransaction(poolDataSource,null,null);
}
}
/**
* @Desc 获取mapper接口的代理类
为什么要使用代理类,因为我们写mapper接口类只写了接口,却没有提供它的实现。于是
使用动态代理机制对这些接口进行代理,在代理类中实现sql执行的方法。此处是参照
mybatis的设计。
**/
@Override
public <T> T getMapper(Class<T> type){
/**MapperProxy代理类分析见详情8.1*/
MapperProxy mapperProxy = new MapperProxy(type,this);
return (T)mapperProxy.initialization();
}
/*select 语句执行流程分析**/
public <E> List<E> select(Method method,Object[] args) throws Exception{
/**PreparedStatementHandle 生成器见9.1 */
PreparedStatementHandle preparedStatementHandle = new PreparedStatementHandle(mapperCore,transaction,method,args);
PreparedStatement preparedStatement = preparedStatementHandle.generateStatement();
/**执行查询接口,此处是真实调用sql语句的地方 */
preparedStatement.executeQuery();
ResultSet resultSet = preparedStatement.getResultSet();
/**查询方法的返回参数值,若是void类型,就不用返回任务东西 */
Class returnClass = mapperCore.getReturnType(method);
if(returnClass == null || void.class.equals(returnClass)){
return null;
}else {
/**ResultSetHandle 结果处理器见9.2 */
ResultSetHandle resultSetHandle = new ResultSetHandle(returnClass,resultSet);
return resultSetHandle.handle();
}
}
/*update 语句执行流程分析,update,delete,insert都是调用的这个方法**/
public int update(Method method,Object[] args)throws SQLException{
PreparedStatementHandle preparedStatementHandle = null;
PreparedStatement preparedStatement = null;
Integer count = null;
try{
/**PreparedStatementHandle 生成器见9.1 */
preparedStatementHandle = new PreparedStatementHandle(mapperCore,transaction,method,args);
preparedStatement = preparedStatementHandle.generateStatement();
/**返回受影响的行数 */
count = preparedStatement.executeUpdate();
}finally {
if(preparedStatement != null){
preparedStatement.close();
}
}
return count;
}
/**后续方法直接调用transaction相关方法*/
@Override
public void commit() throws SQLException{
transaction.commit();
}
@Override
public void rollback() throws SQLException{
transaction.rollback();
}
@Override
public void close() throws SQLException{
transaction.close();
}
}
因为我们mapper接口类只有接口,没有实现。但为什么可以调用这些接口方法?此处参照mybatis源码,自己通过动态代理技术实现接口的动态代理,在代理方法里写我们自己执行Sql的逻辑。代理类见代码块8
代码块8
mapper接口代理类(com.simple.ibatis.execute)
MapperProxy
/**
* @Author xiabing
* @Desc mapper接口代理类
**/
public class MapperProxy<T> implements InvocationHandler{
/**要代理的mapper接口类*/
private Class<T> interfaces;
/**具体的执行器,执行mapper中的一个方法相当于执行一条sql语句*/
private SimpleExecutor executor;
public MapperProxy(Class<T> interfaces,SimpleExecutor executor) {
this.interfaces = interfaces;
this.executor = executor;
}
/**反射方法,该方法自定义需要代理的逻辑。不了解可以去看下JAVA动态代理技术*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
/**object方法直接代理*/
if(Object.class.equals(method.getDeclaringClass())){
result = method.invoke(this,args);
}else {
// 获取方法是select还是非select方法
Integer methodType = executor.mapperCore.getMethodType(method);
if(methodType == null){
throw new RuntimeException("method is normal sql method");
}
if(methodType == 1){
/**调用执行器的select方法,返回集合类*/
List<Object> list = executor.select(method,args);
result = list;
/**查看接口的返回是List,还是单个对象*/
if(!executor.mapperCore.getHasSet(method)){
if(list.size() == 0){
result = null;
}else {
/**单个对象就直接取第一个*/
result = list.get(0);
}
}
}else{
/**返回受影响的行数*/
Integer count = executor.update(method,args);
result = count;
}
}
return result;
}
/**构建代理类*/
public T initialization(){
return (T)Proxy.newProxyInstance(interfaces.getClassLoader(),new Class[] { interfaces },this);
}
}
代码块7中执行SQL其实包含3个过程,生成SQL预处理语句,执行SQL,将SQL结果转为JAVA对象。详情见代码块9
代码块9
各生成器类(com.simple.ibatis.statement)
PreparedStatementHandle
/**
* @Author xiabing
* @Desc PreparedStatement生成器
**/
public class PreparedStatementHandle {
/**
* 全局核心mapper解析类
*/
private MapperCore mapperCore;
/**
* 待执行的方法
*/
private Method method;
private Transaction transaction;
private Connection connection;
/**
* 方法输入参数
*/
private Object[] args;
public PreparedStatementHandle(MapperCore mapperCore, Transaction transaction,Method method, Object[] args)throws SQLException {
this.mapperCore = mapperCore;
this.method = method;
this.transaction = transaction;
this.args = args;
connection = transaction.getConnection();
}
/**
* @Author xiabing5
* @Desc 参数处理核心方法 todo
**/
public PreparedStatement generateStatement() throws SQLException{
// 获取已经解析方法的sqlSource类,已经将待注入字段变为?,后续直接填充就好
SqlSource sqlSource = mapperCore.getStatement(method);
// 调用connection方法生成预处理语句
PreparedStatement preparedStatement = connection.prepareStatement(sqlSource.getSql());
// 获取方法输入参数的类型
Class<?>[] clazzes = mapperCore.getParameterType(method);
List<String> paramNames = mapperCore.getParameterName(method);
List<String> params = sqlSource.getParam();
// 详见typeInject方法,注入SQL参数
preparedStatement = typeInject(preparedStatement,clazzes,paramNames,params,args);
return preparedStatement;
}
/**
* @Author xiabing
* @Desc preparedStatement构建,将参数注入到SQL语句中
* @Param preparedStatement 待构建的preparedStatement
* @Param clazzes 该方法中参数类型数组
* @Param paramNames 该方法中参数名称列表,若有@Param注解,则为此注解的值,默认为类名首字母小写
* @Param params 待注入的参数名,如user.name或普通类型如name
* @Param args 真实参数值
**/
private PreparedStatement typeInject(PreparedStatement preparedStatement,Class<?>[] clazzes,List<String> paramNames,List<String> params,Object[] args)throws SQLException{
for(int i = 0; i < paramNames.size(); i++){
// 第i个参数名称
String paramName = paramNames.get(i);
// 第i个参数类型
Class type = clazzes[i];
if(String.class.equals(type)){
// 原始SQL中需要注入的参数名中是否有此参数名称
// example: select * from users where id = {id} and name = {name} 则{id}中的id和name就是如下的params里面内容
int injectIndex = params.indexOf(paramName);
/**此处是判断sql中是否有待注入的名称({name})和方法内输入对象名(name)相同,若相同,则直接注入*/
if(injectIndex >= 0){
preparedStatement.setString(injectIndex+1,(String)args[i]);
}
}else if(Integer.class.equals(type) || int.class.equals(type)){
int injectIndex = params.indexOf(paramName);
if(injectIndex >= 0){
preparedStatement.setInt(injectIndex+1,(Integer)args[i]);
}
}else if(Float.class.equals(type) || float.class.equals(type)){
int injectIndex = params.indexOf(paramName);
if(injectIndex >= 0){
preparedStatement.setFloat(injectIndex+1,(Float)args[i]);
}
}else {
/** 若待注入的是对象。example:
@SELECT(select * from users where id = {user.id} and name = {user.name})
List<User> getUser(User user)
*/
// 对象工厂,获取对象包装实例,见代码块10
ObjectWrapper objectWrapper = ObjectWrapperFactory.getInstance(args[i]);
for(int j = 0; j < params.size(); j++){
/**此处是判断对象的属性 如user.name,需要先获取user对象,在调用getName方法获取值*/
if((params.get(j).indexOf(paramName)) >= 0 ){
try{
String paramProperties = params.get(j).substring(params.get(j).indexOf(".")+1);
Object object = objectWrapper.getVal(paramProperties);
Class childClazz = object.getClass();
if(String.class.equals(childClazz)){
preparedStatement.setString(j+1,(String)object);
}else if(Integer.class.equals(childClazz) || int.class.equals(childClazz)){
preparedStatement.setInt(j+1,(Integer)object);
}else if(Float.class.equals(childClazz) || float.class.equals(childClazz)){
preparedStatement.setFloat(j+1,(Float)object);
}else {
/**目前不支持对象中包含对象,如dept.user.name todo*/
throw new RuntimeException("now not support object contain object");
}
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
}
}
}
}
return preparedStatement;
}
public void closeConnection() throws SQLException{
transaction.close();
}
}
ResultSetHandle
/**
* @Author xiabing
* @Desc ResultSet结果处理器,执行之后返回的json对象,转为java对象的过程
**/
public class ResultSetHandle {
/**转换的目标类型*/
Class<?> typeReturn;
/**待转换的ResultSet*/
ResultSet resultSet;
Boolean hasSet;
public ResultSetHandle(Class<?> typeReturn,ResultSet resultSet){
this.resultSet = resultSet;
this.typeReturn = typeReturn;
}
/**
* ResultSet处理方法,目前仅支持String,int,Float,不支持属性是集合类 todo
* */
public <T> List<T> handle() throws Exception{
List<T> res = new ArrayList<>(resultSet.getRow());
Object object = null;
ObjectWrapper objectWrapper = null;
Set<ClazzWrapper.FiledExpand> filedExpands = null;
// 返回类型若不是基本数据类型
if(!TypeUtil.isBaseType(typeReturn)){
// 生成对象
object = generateObj(typeReturn);
// 将对象封装成包装类
objectWrapper = ObjectWrapperFactory.getInstance(object);
/** 获取对象属性 */
filedExpands = objectWrapper.getMapperFiledExpands();
}
while (resultSet.next()){
/** 若返回是基础数据类型,则直接将结果放入List中并返回 */
if(String.class.equals(typeReturn)){
String val = resultSet.getString(1);
if(val != null){
res.add((T)val);
}
}else if(Integer.class.equals(typeReturn) || int.class.equals(typeReturn)){
Integer val = resultSet.getInt(1);
if(val != null){
res.add((T)val);
}
}else if(Float.class.equals(typeReturn) || float.class.equals(typeReturn)){
Float val = resultSet.getFloat(1);
if(val != null){
res.add((T)val);
}
}else { // 若返回的是对象(如User这种)
// 查找对象属性,一个个注入
for(ClazzWrapper.FiledExpand filedExpand:filedExpands){
// 如果对象属性是String类型,例如User.name是String类型
if(String.class.equals(filedExpand.getType())){
// resultSet中获取该属性
String val = resultSet.getString(filedExpand.getPropertiesName());
if(val != null){
// 填充到对象包装类中
objectWrapper.setVal(filedExpand.getPropertiesName(),val);
}
}else if(Integer.class.equals(filedExpand.getType()) || int.class.equals(filedExpand.getType())){
Integer val = resultSet.getInt(filedExpand.getPropertiesName());
if(val != null){
objectWrapper.setVal(filedExpand.getPropertiesName(),val);
}
}else if(Float.class.equals(filedExpand.getType()) || float.class.equals(filedExpand.getType())){
Float val = resultSet.getFloat(filedExpand.getPropertiesName());
if(val != null){
objectWrapper.setVal(filedExpand.getPropertiesName(),val);
}
}else {
continue;
}
}
// 后续将对象包装类转为真实对象,放入List中并返回。对象包装类见代码10
res.add((T)objectWrapper.getRealObject());
}
}
return res;
}
// 根据类型,根据反射,实例化对象
private Object generateObj(Class<?> clazz) throws Exception{
Constructor[] constructors = clazz.getConstructors();
Constructor usedConstructor = null;
// 获取无参构造器,若对象没有无参构造方法,则失败
for(Constructor constructor:constructors){
if(constructor.getParameterCount() == 0){
usedConstructor = constructor;
break;
}
}
if(constructors == null) {
throw new RuntimeException(typeReturn + " is not empty constructor");
}
// 利用反射生成实例
return usedConstructor.newInstance();
}
}
代码块9中大量用到了对象增强,类增强。使用这些原因是 在编写框架代码时,因为并不知道每个属性名,不可能把每个get或者set方法写死,只能通过将对象封装成包装类。然后利用反射,要填充某个对象,就调用setVal(属性名),获取对象类似。详情见代码块10
代码块10
类增强(com.simple.ibatis.reflect)
ClazzWrapper
/**
* @Author xiabing
* @Desc clazz解析类
**/
public class ClazzWrapper {
/**
* 待解析类
* */
private Class<?> clazz;
/**
* 该类存储的属性名
* */
private Set<String> propertiesSet = new HashSet<>();
/**
* 该类存储的属性名及属性类
* */
private Set<FiledExpand> filedExpandSet = new HashSet<>();
/**
* 该类存储的get方法。key为属性名,value为getxxx方法
* */
private Map<String,Method> getterMethodMap = new HashMap<>();
/**
* 该类存储的set方法。key为属性名,value为setxxx方法
* */
private Map<String,Method> setterMethodMap = new HashMap<>();
/**
* 缓存,避免对同一个类多次解析
* */
private static Map<String,ClazzWrapper> clazzWrapperMap = new ConcurrentHashMap<>();
public ClazzWrapper(Class clazz){
this.clazz = clazz;
// 对类进行解析,如果已经解析了,则不用二次解析
if(!clazzWrapperMap.containsKey(clazz.getName())){
// 获取该类的所有属性
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
// 获取属性的名称,属性类型
FiledExpand filedExpand = new FiledExpand(field.getName(),field.getType());
filedExpandSet.add(filedExpand);
propertiesSet.add(field.getName());
}
// 获取该类的方法
Method[] methods = clazz.getMethods();
for(Method method:methods){
String name = method.getName();
// 如果是get方法
if(name.startsWith("get")){
name = name.substring(3);
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
// 放入get方法缓存中
if(propertiesSet.contains(name)){
getterMethodMap.put(name,method);
}
}else if(name.startsWith("set")){
name = name.substring(3);
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
if(propertiesSet.contains(name)){
setterMethodMap.put(name,method);
}
}
else {
continue;
}
}
// 放入缓存,代表该类已经完成解析
clazzWrapperMap.put(clazz.getName(),this);
}
}
// 查找该类是否包含指定属性的get方法
public boolean hasGetter(String properties){
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.getterMethodMap.containsKey(properties);
}
// 查找该类是否包含指定属性的set方法
public boolean hasSetter(String properties){
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.setterMethodMap.containsKey(properties);
}
// 获取该类指定属性的set方法
public Method getSetterMethod(String properties){
if(!hasSetter(properties)){
throw new RuntimeException("properties " + properties + " is not set method") ;
}
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.setterMethodMap.get(properties);
}
// 获取该类指定属性的get方法
public Method getGetterMethod(String properties){
if(!hasGetter(properties)){
throw new RuntimeException("properties " + properties + " is not get method") ;
}
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.getterMethodMap.get(properties);
}
// 获取该类所有属性
public Set<String> getProperties(){
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.propertiesSet;
}
// 获取该类所有属性增强
public Set<FiledExpand> getFiledExpandSet(){
ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
return clazzWrapper.filedExpandSet;
}
/**
* 属性增强类
* */
public static class FiledExpand{
// 属性名称
String propertiesName;
// 属性类型
Class type;
public FiledExpand() {
}
public FiledExpand(String propertiesName, Class type) {
this.propertiesName = propertiesName;
this.type = type;
}
public String getPropertiesName() {
return propertiesName;
}
public void setPropertiesName(String propertiesName) {
this.propertiesName = propertiesName;
}
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
@Override
public int hashCode() {
return propertiesName.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj instanceof FiledExpand){
return ((FiledExpand) obj).propertiesName.equals(propertiesName);
}
return false;
}
}
}
ObjectWrapper
/**
* @Author xiabing
* @Desc 对象增强,封装了get,set方法
**/
public class ObjectWrapper {
// 真实对象
private Object realObject;
// 该对象的类的增强
private ClazzWrapper clazzWrapper;
public ObjectWrapper(Object realObject){
this.realObject = realObject;
this.clazzWrapper = new ClazzWrapper(realObject.getClass());
}
// 调用对象指定属性的get方法
public Object getVal(String property) throws Exception{
return clazzWrapper.getGetterMethod(property).invoke(realObject,null);
}
// 调用对象指定属性的set方法
public void setVal(String property,Object value) throws Exception{
clazzWrapper.getSetterMethod(property).invoke(realObject,value);
}
public Set<String> getProperties(){
return clazzWrapper.getProperties();
}
public Set<ClazzWrapper.FiledExpand> getMapperFiledExpands(){
return clazzWrapper.getFiledExpandSet();
}
public Object getRealObject(){
return realObject;
}
}
ObjectWrapperFactory
/**
* @Author xiabing5
* @Desc 对象增强类。使用对象增强是因为我们不知道每个对象的getXXX方法,只能利用反射获取对象的属性和方法,然后
统一提供getVal或者setVal来获取或者设置指定属性,这就是对象的增强的重要性
**/
public class ObjectWrapperFactory {
public static ObjectWrapper getInstance(Object o){
return new ObjectWrapper(o);
}
}
总结感悟
上述是我在写这个框架时的一些思路历程,代码演示请见我github。通过自己手写源码,对mybatis也有了个深刻的见解。当然有很多问题,后面也会进一步去完善,如果你对这个开源项目感兴趣,不妨star一下呀!
网友评论