前言
“风吹起如花般破碎的流年,而你的笑容摇晃摇晃,成为我命途中最美的点缀,看天,看雪,看季节深深的暗影。”- - 张爱玲
一、SSA
- 我们都知道spring-framework的框架默认对bean的管理都是单例的,如果一个单例的类A里需要调用一个原型对象B,如果在A中@Autowired注入了B这个原型对象,那么从A中每次get到的B都是同一个(下面会证明这个现象),这里就发生了错误。
- 此时我们可以使用在A中注入@Autowired ApplicationContext或者实现ApplicationContextAware接口拿到ApplicationContext,再从ApplicationContext中获取想要的bean。spring启动完成了,经过遍历spring的单例池并没有发现ApplicationContext这个对象。那ApplicationContext从哪里来又是怎样被@Autowired注解注入bean的呢,接下面从spring源代码看看。
二、GTTP
1.看下基本类,模拟的mybatis扩展spring接口实现查询
- 这里创建的CustomSqlsession customSqlsession = new CustomSqlsession();其实就是上一代理的获取传入接口的代理对象的类
- mapperInterface就是传入的接口,这里模拟的传入的就是IndexDao;
- 这里提供了setMapperInterface是个set方法,意味着后面mybatis根据spring提供的接口规则将自己的代理对象注入spring的时候,spring通过这个set方法给mapperInterface设置值。
- CustomFactoryBean这个类里也可以提供有参构造方法,spring根据有参构造填充mapperInterface这个属性。
package com.cusmybatis.example3.factorybean;
import com.cusmybatis.example3.sqlSession.CustomSqlsession;
import org.springframework.beans.factory.FactoryBean;
public class CustomFactoryBean implements FactoryBean {
Class mapperInterface;
@Override
public Object getObject() {
CustomSqlsession customSqlsession = new CustomSqlsession();
Object mapper = customSqlsession.getMapper(mapperInterface);
return mapper;
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
public void setMapperInterface(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
- 在spring实例化周期的过程中,这里实现了ImportBeanDefinitionRegistrar接口,来注入mybatis代理的对象
- 很明显你要注入mybatis代理对象必须按照spring提供的规矩来。也就是上面的(implements FactoryBean)实现FactoryBean接口,用BeanDefinitionBuilder构建mybatis的beanDefinition模型,将构建好的模型注册(registry.registerBeanDefinition)进去。
- 有一个大家很熟悉的后置处理器(implements BeanFactoryPostProcessor ),顾名思义,实现这个后置处理器可以根据实现类提供的beanFactory拿到你想要的beanDefinition,但这个是后置处理器,是已经走完注册BeanDefinition流程的接口,所以扩展这个接口只能获取不能在这里注册BeanDefinition;
- beanDefinition也提供了相应的属性设置的方法可以是.getPropertyValues().add(“mapperInterface”, IndexDao.class)来设置
package com.cusmybatis.example3.bd;
import com.cusmybatis.example3.dao.IndexDao;
import com.cusmybatis.example3.factorybean.CustomFactoryBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.AnnotationMetadata;
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
MergedAnnotations annotations = importingClassMetadata.getAnnotations();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CustomFactoryBean.class);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinition.getPropertyValues().add("mapperInterface", IndexDao.class);
registry.registerBeanDefinition("indexDao", beanDefinition);
}
}
下面是模拟@MapperScan注解的自定义注解@CustomScan用到的所有的类。
可以看到成功从容器中获取到IndexDao,并用它的代理对象执行了数据库查询。
2.查看mybatis扩展spring源码
可以看到,用Mybatis扩展spring的注解MapperScan来查询数据库依然可以,且代理对象变成了org.apache.ibatis.binding.MapperProxy@7e5c856f。接下来就看下源代码中的处理过程是否和上面模拟的一样。
-
可以看到@MapperScan(“com.cusmybatis.example3.dao”)中 @MapperScan注解上同样是@Import注解 导入了一个MapperScannerRegistrar.class类,这个类和笔者的@CustomScan注解导入的CustomImportBeanDefinitionRegistrar类一样实现了ImportBeanDefinitionRegistrar接口重写了registerBeanDefinitions方法。 -
可以看到这个MapperScannerRegistrar类的BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);这行逻辑代码第一行就构建了BeanDefinitionBuilder ,当然这里的MapperScannerConfigurer顾名思义扫描了所有的指定包下的dao;除了第一行下面的逻辑都是 做 builder.addPropertyValue(),这个和笔者的
这里的作用类似,是给BeanDefinition配置属性,这个属性是属于BeanDefinition的。最后一行 registry.registerBeanDefinition(beanName, builder.getBeanDefinition())将构建好的就是BeanDefinition注册到spring。接下来就重点看下MapperScannerConfigurer扫描干的事。
-
可以看到在MapperScannerConfigurer类的扫描过程中,在指定的com.cusmybatis.example3.dao包下扫描出了IndexDao。理论上扫描出来后应该是给它的BeanDefinition填充属性,这个属性是代理类需要的属性。 -
上图可以看到,扫描出来的beanDefinitions只有一个也就是MyBatis mapper 有一个IndexDao。但是在for (BeanDefinitionHolder holder : beanDefinitions) { 会循环遍历它,遍历的操作里有 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this.mapperFactoryBeanClass); 两行代码,查看属性知道beanClassName的值就是com.cusmybatis.example3.dao.IndexDao,也就是definition将来要实例化出来的对象的Constructor的参数是com.cusmybatis.example3.dao.IndexDao,实例化出来的对象是mapperFactoryBeanClass,这个是mybaits的提供的类实现了implements FactoryBean接口
作用就和上图的意思一样。当然理解上面的过程前提是对spring的beanDefinition有一定的理解。
总结
总结就是:
- mybaits自个儿单独使用是。
- 但是要接入spring,
|