coding

  1. // 先来两个注解
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Select {
        String value();
    }
    
    
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
    public @interface Mapper {
    }
    
  2. // jdbc
    import java.sql.*;
    
    /**
     * @author 郑查磊
     */
    public class JdbcConnection {
    
        private static Connection connection = null;
    
        static {
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT",
                        "root", "root");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static Statement getStatement() {
            if (connection == null) {
                throw new ConnectionException(500, "连接获取异常,数据库未初始化!");
            }
            try {
                return connection.createStatement();
            } catch (SQLException e) {
                throw new ConnectionException(500, "连接获取异常!");
            }
        }
    }
    
  3. // 初始化Mapper
    import cn.hutool.core.util.ClassUtil;
    import cn.runjava.demo.annotion.Mapper;
    import cn.runjava.demo.cache.CacheMethod;
    import cn.runjava.demo.exception.MapperNotFondException;
    import cn.runjava.demo.proxy.Invocation;
    
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.*;
    
    /**
     * @author 郑查磊
     */
    public class InitMapper {
    
        private static List<CacheMethod> cacheMethods = new ArrayList<CacheMethod>();
    
        private static Map<Class, Object> maps = new HashMap<Class, Object>();
    
        public static void getMappers(String packageName) {
            // 获取当前包下以及子包下所以的类
    		//  ClassUtil.scanPackageByAnnotation 此处使用了hutool util 类似于 commons
            Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(packageName, Mapper.class);
    
            if (classes.size() < 0) {
                System.err.println("### 初始化失败");
                System.err.println("### Mapper未找到");
                throw new MapperNotFondException(500, "Mapper未找到");
            }
    
            for (Class<?> aClass : classes) {
    
                // 获取所有的方法
                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
    
                    CacheMethod cacheMethod = new CacheMethod();
                    cacheMethod.setMethodName(method.getName());
                    cacheMethod.setResultType(method.getReturnType());
                    cacheMethod.setParameter(method.getParameterTypes());
    
                    cacheMethods.add(cacheMethod);
                }
    			// 创建动态代理
                Object o = Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new Invocation());
                // 加入缓存
                maps.put(aClass, o);
            }
    
        }
    
        public static List<CacheMethod> getCacheMethods() {
            return cacheMethods;
        }
    
        public static <T> T getMapper(Class<T> t) {
            return (T)maps.get(t);
        }
    
    }
    
  4. // 异常补上
    /**
     * @author 郑查磊
     */
    @Data
    public class ConnectionException extends RuntimeException {
    
        private Integer code;
        private String msg;
    
        public ConnectionException() {
        }
    
        public ConnectionException(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    }
    
    
    public class MapperNotFondException extends RuntimeException  {
    
        private Integer code;
        private String msg;
    
        public MapperNotFondException() {
        }
    
        public MapperNotFondException(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    }
    
    
  5. 
    /**
     * @author 郑查磊
     */
    @Data
    public class CacheMethod {
    
        private String methodName;
        private Class<?>[] parameter;
        private Class<?> resultType;
    }
    
    
  6. // 核心来了
    import cn.runjava.demo.annotion.Select;
    import cn.runjava.demo.cache.CacheMethod;
    import cn.runjava.demo.init.InitMapper;
    import cn.runjava.demo.jdbc.JdbcConnection;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.sql.*;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author 郑查磊
     */
    public class Invocation implements InvocationHandler {
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            List<CacheMethod> cacheMethods = InitMapper.getCacheMethods();
            for (CacheMethod cacheMethod : cacheMethods) {
                if (method.getName().equals(cacheMethod.getMethodName())) {
                    Annotation annotation = method.getAnnotations()[0];
                    if (annotation instanceof Select) {
                        Select annotation1 = (Select) annotation;
                        String sql = annotation1.value();
                        Statement statement = JdbcConnection.getStatement();
                        Connection connection = statement.getConnection();
                        PreparedStatement preparedStatement = connection.prepareStatement(sql);
                        for (int i = 0; i < args.length; i++) {
                            Object arg = args[i];
                            sql = sql.replace("?", arg.toString());
                        }
                        System.out.println(sql);
                        ResultSet resultSet = preparedStatement.executeQuery(sql);
    					// ResultSet 封装一下就行了 哈哈 此处可以参考mybatis的方法,等会贴代码
                        while (resultSet.next()) {
                            return resultSet.getString(1);
                        }
                    }
                }
            }
            return null;
        }
    }
    
    
  7. public class Main {
        public static void main(String[] args) {
            InitMapper.getMappers("cn.runjava.demo.mapper");
            UserMapper mapper = InitMapper.getMapper(UserMapper.class);
            String name = mapper.queryUserById(1);
            System.out.println(name);
        }
    }
    // 结果
    select name from user where id = 1
    张三
    
    
  8. 最后补充一下mybatis是如何做的实体返回的数据封装 org.apache.ibatis.reflection.Reflector 前面的文章中有写 在反射查找set方法的时候 性能可能会 一些XX mybatis做了method的缓存 在项目初始化的时候 存入

Mybatis是这样做的 取属性 和所有的method做匹配 有get set 并且 方法名长度大于3的 认为是字段

在做设置值的时候只要方法名称对的上 就被认为这个字段的set方法做匹配 可以精确用.equals 做比较