spring和mybatis整合
问题:
1.mybatis为什么未实例化接口但是可以调用接口的方法?
2.mybatis如何实现把bean交给spring管理的?
代码:
常规整合框架编码
/**
* dao
*/
public interface IndexDao {
@Select({"select * from dao"})
public List<Map<String,Object>> list();
}
public interface IndexDao1 {
@Select("select * from dao1")
public List<Map<String,Object>> list();
}
public interface IndexDao2 {
@Select("select * from dao2")
public List<Map<String,Object>> list();
}
/**
* service
*/
@Component
public class IndexService {
//从spring容器当中拿出来自动注入
//对象 不是接口
@Autowired
IndexDao indexDao;
@Autowired
IndexDao1 indexDao1;
@Autowired
IndexDao2 indexDaon2;
public List<Map<String,Object>> list(){
//查询数据库
indexDao.list();
indexDao1.list();
indexDaon2.list();
return null;
};
}
自定义编码
模拟mybatis每个接口生成代理对象
public class CustomSqlSession {
/**
* 模拟mybatis产生代理对象
* 符合需求的对象
* 1、代理
* 2、这个对象能够具备的功能 查询数据库 --执行对应sql
* @param clazz
* @return
*/
public Object getMapper(Class clazz){
//他的底层源码会去重写toString 没有实现
Object proxy = Proxy.newProxyInstance(CustomSqlSession.class.getClassLoader(),
new Class[]{clazz}, new CustomInvocationHandler());
return proxy;
}
class CustomInvocationHandler implements InvocationHandler {
//所有代理对象的功能 查询数据 执行对应方法的sql语句
//1、得到sql语句
//2、conn ---执行sql语句
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断有没有加这个注解
if (method.isAnnotationPresent(Select.class)) {
//得到这个注解对象
Select select = method.getAnnotation(Select.class);
//调用value方法得到注解里面的value的值--sql
String sql = select.value()[0];
System.out.println("jdbc conn ");
System.out.println("execute query :" + sql);
}
if (method.getName().equals("toString")){
return proxy.getClass().getName();
}
return null;
}
}
}
定义一个FactoryBean来生产CustomSqlSession代理对象
public class CustomFactoryBean implements FactoryBean{
Class mapperInterface;
public CustomFactoryBean(){
System.out.println("create CustomFactoryBean.");
}
public CustomFactoryBean(Class mapperInterface){
this.mapperInterface=mapperInterface;
}
@Override
public Object getObject() throws Exception {
CustomSqlSession customSqlSession = new CustomSqlSession();
//mybatis返回出来的代理对象
Object mapper = customSqlSession.getMapper(mapperInterface);
//FactoryBean return mapper对象会存到spring容器当中
return mapper;
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
public void setMapperInterface(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
需要把所有的接口生成的bean注入到spring容器中,所以需要生成对于的beanDefinition对象,然后把beanDefinition对象放入到map(缓存)中(spring中 bean的实例化就是根据beanDefinition对象),所以需要定义一个Registrar,把beanDefinition放入map中。
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 源码为 spring scan 扫描出所有符合目录下条件的类,简化为自定义集合
List<Class> list = new ArrayList<>();
list.add(IndexDao.class);
list.add(IndexDao1.class);
list.add(IndexDao2.class);
// 生成dao对于的beanDefinition 放入到map中,后需要bean的实例化就是通过这个map(缓存)
for (Class aClass : list) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CustomFactoryBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
// 属性赋值
beanDefinition.getPropertyValues().add("mapperInterface",aClass);
// beanName
String beanName=(aClass.getSimpleName().substring(0,1).toLowerCase())+aClass.getSimpleName().substring(1);
// 注入到spring容器中
registry.registerBeanDefinition(beanName,beanDefinition);
}
}
}
CustomImportBeanDefinitionRegistrar本身也是个bean需要被管理,需要注入到容器。
@Configuration
@ComponentScan("com.shadow")
@Import(CustomImportBeanDefinitionRegistrar.class)
public class AppConfig {
}
测试
public class TestIndex {
public static void main(String[] args) {
//显示的去得到applicationContext对象
//书写格式
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(AppConfig.class);
IndexService bean = applicationContext.getBean(IndexService.class);
bean.list();
}
}
/**
*create CustomFactoryBean.
*create CustomFactoryBean.
*create CustomFactoryBean.
*jdbc conn
*execute query :select * from dao
*jdbc conn
*execute query :select * from dao1
*jdbc conn
*execute query :select * from dao2
*/
@Import注解
新建一个ImportConfig,在类上面加上@Configuration,加上@Configuration是为了能让Spring 扫描到这个类,并且直接通过@Import引入TestA类
import bean注入spring的三种方式:
// 直接导入类
@Import({TestA.class})
@Configuration
public class ImportConfig {
}
// 通过ImportSelector 方式导入的类
public class SelfImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.test.importdemo.TestC"};
}
}
@Import({SelfImportSelector.class})
@Configuration
public class ImportConfig {
}
// 通过ImportBeanDefinitionRegistrar 方式导入的类
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition root = new RootBeanDefinition(TestD.class);
registry.registerBeanDefinition("testD", root);
}
}
@Import({SelfImportBeanDefinitionRegistrar.class})
@Configuration
public class ImportConfig {
}
总结:
1.一个dao接口会通过动态代理来生成动态代理对象(sqlsession)
2.需要定义一个factoryBean来管理动态代理对象 sqlsessionFactory
3.需要把sqlsessionFactory,放入到bean的初始化的定义对象,即生成对应的BeanDefinition
4.ImportBeanDefinitionRegistrar来把BeanDefinition放入缓存中,用来后续的初始化