手写mybatis-spring
什么是 MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
BeanFactory 和 FactoryBean
BeanFactory
1 负责生产和管理bean的一个工厂
2 是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象的依赖
3 多种实现:如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系
FactoryBean
1 FactoryBean 也是一个工厂,但只创建一个对象。
2 FactoryBean是一个接口,当在IOC容器中的Bean实现了FactoryBean后,通过getBean(String BeanName)获取到的Bean对象并不是FactoryBean的实现类对象,而是这个实现类中的getObject()方法返回的对象。要想获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。
定义我们自己的FactoryBean
/**
* Mapper的创建工厂
* @author yangyanping
* @date 2020-09-21
*/
public class MyMapperFactoryBean implements FactoryBean {
/**
* 接口
*/
private Class<?> mapperInterface;
public MyMapperFactoryBean(Class<?> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@Override
public Object getObject() throws Exception {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{mapperInterface}, (Object proxy, Method method, Object[] args) -> {
MySelect annotation = AnnotationUtils.findAnnotation(method, MySelect.class);
String sql = annotation.value();
System.out.println(sql);
return null;
});
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
}
注解类MySelect 定义
/**
* 注解
* @author yangyanping
* @date 2020-09-21
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MySelect {
/**
* SQL 语句
*/
String value();
}
ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载, 通常是启动类或配置类。 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类, 则会调用接口方法,将其中要注册的类注册成bean。 实现该接口的类拥有注册bean的能力。
MyBeanDefinitionRegistrar 类定义如下:
/**
* BeanDefinition 注册类
* @author yangyanping
* @date 2020-09-21
*/
public class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(MapperScan.class.getName()));
MapperScannerRegistrar scanner = new MapperScannerRegistrar(beanDefinitionRegistry);
String basePackages = annoAttrs.getString("basePackages");
Set<BeanDefinitionHolder> set = scanner.doScan(basePackages);
for (BeanDefinitionHolder holder : set) {
ScannedGenericBeanDefinition ben = (ScannedGenericBeanDefinition) holder.getBeanDefinition();
RootBeanDefinition beanDefinition = new RootBeanDefinition(MyMapperFactoryBean.class);
//类的全名 com.yyp.study.mybatis.mapper.AccountMapper
String name = ben.getMetadata().getClassName();
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(name);
//类的短名称 AccountMapper
String simpleName = name.substring(name.lastIndexOf(".") + 1);
//类名称 accountMapper
String lowerName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
beanDefinitionRegistry.registerBeanDefinition(lowerName, beanDefinition);
}
}
}
扫描注解定义:
/**
* 扫描注解
* @author yangyanping
* @date 2020-09-21
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MyBeanDefinitionRegistrar.class})
public @interface MapperScan {
@AliasFor("value")
String basePackages() default "";
@AliasFor("basePackages")
String value() default "";
}
@Configuration的使用
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器
/**
* 系统配置类
* @author yangyanping
* @date 2020-090-21
*/
@Configuration
@Import(MyBeanDefinitionRegistrar.class)
@MapperScan( basePackages = "com.yyp.study.mybatis.mapper")
public class AppConfig {
}
测试mybatis-spring中间件
首先定义2个 Mapper
/**
* 账号Mapper
* @author yangyanping
* @date 2020-09-21
*/
public interface AccountMapper {
@MySelect("select * from user")
User findById(Integer id);
}
/**
* 用户 Mapper
* @author yangyanping
* @date 2020-09-21
*/
public interface UserMapper {
@MySelect("select * from user")
User findById(Integer id);
}
编写我们的测试类代码,测试我们自己的mybatis-spring
/**
* mybatis 测试类
* @author yangyanping
* @date 2020-09-21
*/
public class MybatisTest {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean(AccountMapper.class).findById(1));
context.start();
System.in.read();
}
}
输出:
select * from user
null