整合spring自定义封装jdbc,模仿mybatis(一,jdk动态代理)

技术要点

  • spring 自定义包扫描
  • 自定义BeanFactoryPostProcessor注册bean定义
  • jdk动态代理
  • jdbc基本操作
  • java反射的大量使用

1.定义注解

@MapperScan

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Import(MapperScan.class)
//对应mybatis的@MapperScan,这里使用了@Import,后面必须配置MapperScan的导入规则
public @interface MyMapperScan {
    /**
     * 自定义组件扫描,默认类全名
     * @return
     */
    String[] value() default {};
}

@MySelect

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
//对应mybatis的@Select
public @interface MySelect {
    String value() default "";
}

2.扫描

/**
 * 用于注册,返回MyMapperScan的类全名
 */
public class MapperScan implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName());
        return (String[])annotationAttributes.get("value");
    }
}

3.mapper代理工厂对象

/**
 * 为Mapper生成代理对象
 * @author authorZhao
 * @param <T>
 */
public class MyFactoryBean<T> implements FactoryBean<T> {

    private Logger logger = LoggerFactory.getLogger(MyFactoryBean.class);

    private Class<T> interfaceClass;

    public MyFactoryBean(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    @Override
    public T getObject() throws Exception {
        logger.info("正在为{}生成代理对象",interfaceClass.getName());
        T t = (T)MapperProxy.getObject(interfaceClass);
        if(t==null){
            logger.error("{}代理对象,生成失败",interfaceClass.getName());
        }
        logger.info("{}代理对象,生成成功",interfaceClass.getName());
        return t;
    }
    
    @Override
    public Class<T> getObjectType() {
        return interfaceClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

4.mapper接口

public interface MyMapper {
    @MySelect("select * from crm_gb_goods where id=#{id}")
    Map getGoodsById(Integer id);
}

5.mapper组件的注册

/**
 * 这个类参考别人的,说一下 {@link BeanDefinitionRegistryPostProcessor}的作用,这个类继承了 {@link BeanFactoryPostProcessor}
 * BeanFactoryPostProcessor主要有一个方法,在bean定义加载的时候做一些事情,但这时候是没有bean实例的,spring不推荐这个时候
 * 进行bean的实例操作,这里采用工厂模式,必须提前注册工厂的bean定义
 */
public class MapperDefinitionRegistry implements  BeanDefinitionRegistryPostProcessor {
    private static final Logger logger = LoggerFactory.getLogger(MapperDefinitionRegistry.class);

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    //写死支持MyMapper,其实扫描应该用在这里,动态获取那些类需要代理,但是后面发现用错地方了
        Class beanClazz = MyMapper.class;

/*        //使用RootBeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition(Car.class);
        //beanDefinition.setAttribute("car",car1);
		//这里是我之前直接用spring上下文进行注册的方法
        registry.registerBeanDefinition("car",beanDefinition);*/

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();          definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);

        definition.setBeanClass(MyFactoryBean.class);

        //这里采用的是byType方式注入,类似的还有byName等
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);
        logger.info("{}定义信息注册完毕",beanClazz.getSimpleName());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String str = "开始注册bean";
        System.out.println(str);
    }
}

6.代理工具类

**
 * 代理工具类,直接生成代理对象
 * @author authorZhao
 */
public class MapperProxy {

    private static final Logger logger = LoggerFactory.getLogger(MapperProxy.class);

    private static class MyInvocationHandler implements InvocationHandler {
        /**
         * 接口名
         */
        String interfaceName;

        /**
         * 构造函数
         * @param interfaceName
         */
        public MyInvocationHandler(String interfaceName) {
            super();
            this.interfaceName = interfaceName;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this,args);
            }
            //获取方法上注解信息的工具类,这里不贴了,想了解的可以话,上github
            MySelect annotation = AnnotationUtil.getAnnotation(method, MySelect.class);
            if(annotation==null)throw new RuntimeException("找不着select");
            String sql = annotation.value();
            //未支持spel表达式
            sql = sql.replace("#{id}",String.valueOf(args[0]));
            ResultSet resultSet =  SqlQuery.querySql(sql);
//结果映射,还未实现
            Object object = OrmUtil.convertResultSet(resultSet,method.getReturnType());
            return null;
            //return proxy;
        }

    }

    public static Object getObject(final Class interfaceInfo) {
        // 获取接口名
        String interfaceName = interfaceInfo.getName();

        ClassLoader classLoader = interfaceInfo.getClassLoader();
        Class[] interfaces = { interfaceInfo };
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(interfaceName);
        //创建代理对象
        Object object = null;
        try {
            object = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
        }catch (Exception e){
            System.out.println("获取不到代理对象");
            e.printStackTrace();
        }
        return object;
    }
}

7.jdbc封装

/**
 * jdbc封装类,目前只设置了查询方法
 */
public class SqlQuery {

    private static final Logger logger = LoggerFactory.getLogger(SqlQuery.class);

    public static ResultSet querySql(String sql)throws ClassNotFoundException, SQLException {      
        //1.加载驱动
        //Class.forName("com.mysql.cj.jdbc.Driver");
        DataSource dataSource = MySpringContext.getBean(DataSource.class);
        Connection conn = dataSource.getConnection();//2.获取连接
        logger.info("正在执行的sql:{}",sql);
        PreparedStatement prs = (PreparedStatement) conn.prepareStatement(sql);//3.创建ProparedStatement 对象
        ResultSet res =prs.executeQuery();//4.执行sql语句并将查询结果返回
        logger.info("返回的结果:{}",res);
        while(res.next()) {//5.遍历查询结果
            
        }
        //6.关闭资源
        return res;

    }
}

8.测试

注解模式测试

public class Test1 {
    @Test
    public void test1(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SqlConfig.class);
        //context.start();
        MyMapper mapper = context.getBean(MyMapper.class);
        Map goodsById = mapper.getGoodsById(8);
    }
}

boot测试

@SpringBootTest(classes = SqlApplication.class)
public class BootSqlTest {
    @Autowired
    private MyMapper myMapper;
    @Test
    public void test1(){
        myMapper.getGoodsById(8);
    }
}

boot启动类和配置类

@SpringBootApplication
@Import({SqlConfig.class})
public class SqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SqlApplication.class, args);
    }

}
//这两个类放一起

@Configuration
@MyMapperScan({"com.git.sql.config.MapperDefinitionRegistry",
        "com.git.sql.config.MyBatisBeanPostProcessor"
,"com.git.sql.util.MySpringContext"})
public class SqlConfig {

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF8&allowMultiQueries=true");
        dataSource.setUsername("user");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

}

9结束

基本功能暂时就这样,后面会看看mybatis源码继续模仿,

想到之前瞎写ioc的实现真是笑死了,spring源码搭建都比我自己的实现时间长,本次及基本模拟了jdk动态代理,下一步将完成前面的bean定义注册的扫描特性

里面有dubbo和mq的demo,还有本次代码的

本次代码在provider的sql目录下面,其他的工具类都在里面

参考文章: https://blog.csdn.net/lichuangcsdn/article/details/89694363 这个是注册bean工厂的

本文为作者原厂,转载请注明出处

发布了42 篇原创文章 · 获赞 13 · 访问量 8302

猜你喜欢

转载自blog.csdn.net/weixin_43328357/article/details/103407532