赞
踩
目录
Mybatis框架可以单独使用,需要用到Mybatis所提供的一些类构造出对应的Mapper对象,然后就能使用Mybatis框架提供的功能,我们先看一个Demo:
- @Test
- public void testMybatis() throws IOException {
- // 读取配置文件
- InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
- // 解析配置文件,得到SqlSession工厂类
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- // 从工厂中获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
- // 然后从SqlSession中获取Mapper的代理对象
- UserMapper mapper = sqlSession.getMapper(UserMapper.class);
- String result = mapper.selectById();
- sqlSession.commit();
- sqlSession.flushStatements();
- sqlSession.close();
- }
这是一段很普通的Mybatis的代码,我们点进去看一下getMapper()方法,看一下Mybatis如何生成UserMapper代理对象,这个方法是SqlSession接口的方法,这个接口里面有很多我们平常使用的CRUD方法:
- public interface SqlSession extends Closeable {
- <T> T selectOne(String var1);
-
- <T> T selectOne(String var1, Object var2);
-
- <E> List<E> selectList(String var1);
-
- <E> List<E> selectList(String var1, Object var2);
-
- <E> List<E> selectList(String var1, Object var2, RowBounds var3);
-
- <K, V> Map<K, V> selectMap(String var1, String var2);
-
- <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);
-
- <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);
-
- <T> Cursor<T> selectCursor(String var1);
-
- <T> Cursor<T> selectCursor(String var1, Object var2);
-
- <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);
-
- void select(String var1, Object var2, ResultHandler var3);
-
- void select(String var1, ResultHandler var2);
-
- void select(String var1, Object var2, RowBounds var3, ResultHandler var4);
-
- int insert(String var1);
-
- int insert(String var1, Object var2);
-
- int update(String var1);
-
- int update(String var1, Object var2);
-
- int delete(String var1);
-
- int delete(String var1, Object var2);
-
- void commit();
-
- void commit(boolean var1);
-
- void rollback();
-
- void rollback(boolean var1);
-
- List<BatchResult> flushStatements();
-
- void close();
-
- void clearCache();
-
- Configuration getConfiguration();
-
- <T> T getMapper(Class<T> var1);
-
- Connection getConnection();
- }
主要看它默认的实现类DefaultSqlSession的方法:
所属类:org.apache.ibatis.session.defaults.DefaultSqlSession
- public <T> T getMapper(Class<T> type) {
- return this.configuration.getMapper(type, this);
- }
所属类:org.apache.ibatis.session.Configuration
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- return this.mapperRegistry.getMapper(type, sqlSession);
- }
所属类:org.apache.ibatis.binding.MapperRegistry
- public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
- MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
- if (mapperProxyFactory == null) {
- throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
- } else {
- try {
- return mapperProxyFactory.newInstance(sqlSession);
- } catch (Exception var5) {
- throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
- }
- }
- }
mapperProxyFactory.newInstance(sqlSession)就会返回代理对象:
所属类:org.apache.ibatis.binding.MapperProxyFactory
- public class MapperProxyFactory<T> {
- private final Class<T> mapperInterface;
- private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
-
- public MapperProxyFactory(Class<T> mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- public Class<T> getMapperInterface() {
- return this.mapperInterface;
- }
-
- public Map<Method, MapperMethod> getMethodCache() {
- return this.methodCache;
- }
-
- protected T newInstance(MapperProxy<T> mapperProxy) {
- return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
- }
-
- public T newInstance(SqlSession sqlSession) {
- MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
- return this.newInstance(mapperProxy);
- }
- }
把mapper传进去,通过JDK的动态代理生成代理对象。我们可以看一下MapperProxy类,它实现了InvocationHandler接口。
- public class MapperProxy<T> implements InvocationHandler, Serializable {
- private static final long serialVersionUID = -6424540398559729838L;
- private final SqlSession sqlSession;
- private final Class<T> mapperInterface;
- private final Map<Method, MapperMethod> methodCache;
-
- public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
- this.sqlSession = sqlSession;
- this.mapperInterface = mapperInterface;
- this.methodCache = methodCache;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (Object.class.equals(method.getDeclaringClass())) {
- return method.invoke(this, args);
- }
-
- if (this.isDefaultMethod(method)) {
- return this.invokeDefaultMethod(proxy, method, args);
- }
- } catch (Throwable var5) {
- throw ExceptionUtil.unwrapThrowable(var5);
- }
-
- MapperMethod mapperMethod = this.cachedMapperMethod(method);
- return mapperMethod.execute(this.sqlSession, args);
- }
-
- private MapperMethod cachedMapperMethod(Method method) {
- MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
- if (mapperMethod == null) {
- mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
- this.methodCache.put(method, mapperMethod);
- }
-
- return mapperMethod;
- }
当我们得到代理对象,去执行Mapper接口里面的方法时,Mybatis就会进入MapperProxy类的invoke()方法,得到方法上面的注解(如:@Select("select 'user'")),然后去执行目标方法。
所有MapperProxy类很重要,上面这些生成代理逻辑,都是Mybatis提供的。
回到Spring,如果Mybatis要和Spring整合,除了Mybatis自带的jar包之外,还需整合的jar包:
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis-spring</artifactId>
- <version>1.3.1</version>
- </dependency>
为什么呢?举个例子:
- @Component
- public class UserService {
-
- @Autowired
- private UserMapper userMapper;
-
- public void test() {
- System.out.println(userMapper.selectById());
- }
-
- }
上面的代码貌似没问题,但是如果没有整合jar包,就会报找不到userMapper这个Bean的错误。
我们知道,Spring在Bean的实例化中进行属性注入时,找不到userMapper,因为在过程中需要把Mybatis生成的UserMapper代理对象注入到这里,赋值该属性,才能进行后续的操作,Spring和其他框架整合亦如此。会把Mybatis创建的类(或代理对象)注册到Spring容器,Spring在用的时候才能找到。
这个章节要说的就是如何把Mybatis生成userMapper变成Spring所需的Bean,但是UserMapper是接口,所以不能用固有的思路通过Spring上下文去getBean(),接口是无法实例化的。
答案就是利用FactoryBean接口,例如:
- /**
- * @author Kieasar
- */
- @Component
- public class BaecFactoryBean implements FactoryBean {
-
-
- @Override
- public Object getObject() {
- // 参数中把类加载器传进去,然后new一个UserMapper,生成代理对象
- Object newInstance = Proxy.newProxyInstance(BaecFactoryBean.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() {
- @Override
- public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
- System.out.println("执行代理对象的方法");
- return null;
- }
- });
- return newInstance;
- }
-
- // 把生成的代理对象的类型返回
- @Override
- public Class<?> getObjectType() {
- return UserMapper.class;
- }
- }
此时,这个Bean对象所对应的Bean就是getObject()方法返回的代理对象(newInstance),接下来,单元测试一下:
- @Test
- public void main() {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(AppConfig.class);
- context.refresh();
-
- UserService userService = (UserService) context.getBean("userService");
- userService.test();
- }
执行结果没有报错,说明属性被赋值了,很明显,因为通过属性去找,能找到上面BaecFactoryBean生成的代理对象。
但是test()方法打印结果却为null,因为此时执行的是代理对象的invoke()方法。
上面的例子,貌似我们实现了Mybatis的功能,但是有个问题,此时只有一个UserMapper,如果再有OrderMapper或其他很多的类需要注入,难道我们要写很多个FactoryBean吗?所以,当当前的思路是不行滴~
于是,又想到,代码不要写死,这样不就可以了嘛:
- /**
- * @author Kieasar
- */
- @Component
- public class BaecFactoryBean implements FactoryBean {
-
- private Class mapperInterface;
-
- public BaecFactoryBean(Class mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- @Autowired
- public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
- sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
- this.sqlSession = sqlSessionFactory.openSession();
- }
-
- @Override
- public Object getObject() {
- Object newInstance = Proxy.newProxyInstance(BaecFactoryBean.class.getClassLoader(), new Class[]{mapperInterface}, new InvocationHandler() {
- @Override
- public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
- System.out.println("执行代理对象的方法");
- return null;
- }
- });
- return newInstance;
- }
-
- // 把生成的代理对象的类型返回
- @Override
- public Class<?> getObjectType() {
- return mapperInterface;
- }
- }
但是,@Component注解修饰的类,注入的时候是单例的,支持生成一个代理对象,不能生成我们需要的若干个Bean,但是,这样可以呀~
- @Test
- public void main() {
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
- context.register(AppConfig.class);
- context.refresh();
-
- AbstractBeanDefinition userMapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
- userMapperBeanDefinition.setBeanClass(BaecFactoryBean.class);
- userMapperBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
- // 得到一个userMapper代理对象
- context.registerBeanDefinition("userMapper",userMapperBeanDefinition);
-
- AbstractBeanDefinition orederMapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
- orederMapperBeanDefinition.setBeanClass(BaecFactoryBean.class);
- orederMapperBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
- // 得到一个orderMapper代理对象
- context.registerBeanDefinition("orderMapper",orederMapperBeanDefinition);
-
- UserService userServcie = (UserService) context.getBean("userServcie");
- userServcie.test();
- }
确实可以,但是如果有很多的类需要注入呢……
用扫描的方式,则需要一个扫描器,继承ClassPathBeanDefinitionScanner类,就有了扫描器的功能。
- public class BaecBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
-
- public BaecBeanDefinitionScanner(BeanDefinitionRegistry registry) {
- super(registry);
- }
-
- @Override
- protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
- Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
-
- for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
- BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
-
- // 往构造方法的入参中传值的话,传的就是这个mapper解析之后的BeanDefinition的名称
- beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
- beanDefinition.setBeanClassName(BaecFactoryBean.class.getName());
- }
- return beanDefinitionHolders;
- }
-
- @Override
- protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
- return beanDefinition.getMetadata().isInterface();
- }
- }
但是Spring不关心接口,Mybatis只关心接口,所以,还需要加工一下,重写isCandidateComponent()方法,如果是接口,返回true,只拦截接口,不拦截类,如上。
扫描哪儿呢?还是注解方便,自定义一个注解:
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- @Import(BaecBeanDefinitionRegistrar.class)
- public @interface BaecMapperScan {
-
- String value();
- }
并且需要把BaecBeanDefinitionRegistrar类导入进来,这个类用来注册BeanDefinition:
- public class BaecBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
-
- // 参数AnnotationMetadata就是注解的元数据信息
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
- // 获取注解信息
- Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(BaecMapperScan.class.getName());
- // 获取扫描路径
- String path = (String) annotationAttributes.get("value");
- // 引入扫描器
- BaecBeanDefinitionScanner scanner = new BaecBeanDefinitionScanner(registry);
- // ClassPathBeanDefinitionScanner扫描器默认只扫描@Component注解的类
- // 所以,重写IncludeFilter()方法,让它扫描所有的类
- scanner.addIncludeFilter(new TypeFilter() {
- @Override
- public boolean match(@NotNull MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
- return true;
- }
- });
- // 扫描路径
- scanner.scan(path);
- }
- }
我们定义的BaecBeanDefinitionScanner继承自ClassPathBeanDefinitionScanner扫描器,默认只扫描@Component注解的类,可以看一下ClassPathBeanDefinitionScanner的扫描过程,doScan()方法主要会调到这里:
所属类:springframework.context.annotation.ClassPathScanningCandidateComponentProvider
- private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
- Set<BeanDefinition> candidates = new LinkedHashSet<>();
- try {
- // 获取basePackage下所以的文件资源
- String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
- resolveBasePackage(basePackage) + '/' + this.resourcePattern;
- // 根据路径获取资源,class文件的file对象
- Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
- boolean traceEnabled = logger.isTraceEnabled();
- boolean debugEnabled = logger.isDebugEnabled();
- for (Resource resource : resources) {
- if (traceEnabled) {
- logger.trace("Scanning " + resource);
- }
- if (resource.isReadable()) {
- try {
- // 元数据读取器
- MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
- // excludeFilters includeFilters过滤器判断
- if (isCandidateComponent(metadataReader)) {
- // 构造BeanDefiniiton
- ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
- sbd.setSource(resource);
- // 又判断一次,符合条件才加入
- if (isCandidateComponent(sbd)) {
- if (debugEnabled) {
- logger.debug("Identified candidate component class: " + resource);
- }
- candidates.add(sbd);
- }
- else {
- if (debugEnabled) {
- logger.debug("Ignored because not a concrete top-level class: " + resource);
- }
- }
- }
- else {
- if (traceEnabled) {
- logger.trace("Ignored because not matching any filter: " + resource);
- }
- }
- }
-
- ----省略无关代码----
这个方法isCandidateComponent()就是用来判断有没有@Component注解:
- protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
- // 排除过滤器,返回false
- for (TypeFilter tf : this.excludeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return false;
- }
- }
- // 符合includeFilters的才会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditianal
- for (TypeFilter tf : this.includeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return isConditionMatch(metadataReader);
- }
- }
- return false;
- }
所以,UserMapper和OrderMapper要想被扫描到,就重写IncludeFilter()方法,让它扫描所有的类。
- scanner.addIncludeFilter(new TypeFilter() {
- @Override
- public boolean match(@NotNull MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
- return true;
- }
- });
但是自定义的BaecFactoryBean是自己实现代理对象,要整合Mybatis,就需要Mybatis生成得代理对象,顺着这个思路,换成MyBatis的。
- @Component
- public class BaecFactoryBean implements FactoryBean {
-
- private Class mapperInterface;
-
- private SqlSession sqlSession;
-
- public BaecFactoryBean(Class mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- // 通过注入SqlSessionFactory拿到SqlSession
- @Autowired
- public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
- sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
- this.sqlSession = sqlSessionFactory.openSession();
- }
-
- @Override
- public Object getObject() {
- return sqlSession.getMapper(mapperInterface);
- }
-
- // 把生成的代理对象的类型返回
- @Override
- public Class<?> getObjectType() {
- return mapperInterface;
- }
- }
那么,只要Spring能拿到SqlSessionFactory这个Bean,就能取到SqlSession对象,如何拿呢?这就需要程序员自己实现了:
- @ComponentScan("com.baec")
- @BaecMapperScan("com.baec.mapper")
- public class AppConfig {
-
- @Bean
- public SqlSessionFactory sqlSessionFactory() throws IOException {
- InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- return sqlSessionFactory;
- }
-
- }
当然,也需要提前把Mybatis的配置文件mybatis.xml准备好。
另外,如果不使用Spring的注解,有什么办法注入SqlSession呢?
- public class BaecBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
-
- public BaecBeanDefinitionScanner(BeanDefinitionRegistry registry) {
- super(registry);
- }
-
- @Override
- protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
- Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
-
- for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
- GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
-
- // 往构造方法的入参中传值的话,传的就是这个mapper解析之后的BeanDefinition的名称
- beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
- beanDefinition.setBeanClassName(BaecFactoryBean.class.getName());
- // 自动找到BaecFactoryBean类里面的set方法,然后根据类型去找Bean
- beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
- }
- return beanDefinitionHolders;
- }
-
- @Override
- protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
- return beanDefinition.getMetadata().isInterface();
- }
- }
把BeanDefinition强转成GenericBeanDefinition,位置AutowiredMode属性为按类型注入,Spring会自动找到BaecFactoryBean类里面的set方法,然后根据类型去找Bean。
然后,上面的BaecFactoryBean就可以修改为:
- public class BaecFactoryBean implements FactoryBean {
-
- private Class mapperInterface;
-
- private SqlSession sqlSession;
-
- public BaecFactoryBean(Class mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
- sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
- this.sqlSession = sqlSessionFactory.openSession();
- }
-
- @Override
- public Object getObject() {
- return sqlSession.getMapper(mapperInterface);
- }
-
- @Override
- public Class<?> getObjectType() {
- return mapperInterface;
- }
- }
没有使用Spring的注解,但是实现了注入。
接下来,我们看一下Mybatis的原理,从@MapperScan注解入手:
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE})
- @Documented
- @Import({MapperScannerRegistrar.class})
- public @interface MapperScan {
- String[] value() default {};
-
- String[] basePackages() default {};
-
- Class<?>[] basePackageClasses() default {};
-
- Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
-
- Class<? extends Annotation> annotationClass() default Annotation.class;
-
- Class<?> markerInterface() default Class.class;
-
- String sqlSessionTemplateRef() default "";
-
- String sqlSessionFactoryRef() default "";
-
- Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
- }
它也是导入了MapperScannerRegistrar类,该类也是实现了ImportBeanDefinitionRegistrar接口:
- public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
- private ResourceLoader resourceLoader;
-
- public MapperScannerRegistrar() {
- }
-
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
- if (this.resourceLoader != null) {
- scanner.setResourceLoader(this.resourceLoader);
- }
-
- Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
- if (!Annotation.class.equals(annotationClass)) {
- scanner.setAnnotationClass(annotationClass);
- }
-
- Class<?> markerInterface = annoAttrs.getClass("markerInterface");
- if (!Class.class.equals(markerInterface)) {
- scanner.setMarkerInterface(markerInterface);
- }
-
- Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
- if (!BeanNameGenerator.class.equals(generatorClass)) {
- scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
- }
-
- Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
- if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
- scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
- }
-
- scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
- scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
- List<String> basePackages = new ArrayList();
- String[] var10 = annoAttrs.getStringArray("value");
- int var11 = var10.length;
-
- int var12;
- String pkg;
- for(var12 = 0; var12 < var11; ++var12) {
- pkg = var10[var12];
- if (StringUtils.hasText(pkg)) {
- basePackages.add(pkg);
- }
- }
-
- var10 = annoAttrs.getStringArray("basePackages");
- var11 = var10.length;
-
- for(var12 = 0; var12 < var11; ++var12) {
- pkg = var10[var12];
- if (StringUtils.hasText(pkg)) {
- basePackages.add(pkg);
- }
- }
-
- Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
- var11 = var14.length;
-
- for(var12 = 0; var12 < var11; ++var12) {
- Class<?> clazz = var14[var12];
- basePackages.add(ClassUtils.getPackageName(clazz));
- }
-
- scanner.registerFilters();
- scanner.doScan(StringUtils.toStringArray(basePackages));
- }
-
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
- }
在这里面,Mybatis同样创建了一个扫描器ClassPathMapperScanner,扫描@MapperScan注解,毋庸置疑,该类也是继承自Spring的扫描器——ClassPathBeanDefinitionScanner类,在它的doScan方法中的processBeanDefinitions()中,同样也设置了AutowireMode属性为:
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
接下来,再看一下ClassPathMapperScanner中创建的MapperFactoryBean,是把Mybatis生成代理对象转化为Bean最关键的一环,这个类也实现了FactoryBean接口:
- public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
- private Class<T> mapperInterface;
- private boolean addToConfig = true;
-
- public MapperFactoryBean() {
- }
-
- public MapperFactoryBean(Class<T> mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- protected void checkDaoConfig() {
- super.checkDaoConfig();
- Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
- Configuration configuration = this.getSqlSession().getConfiguration();
- if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
- try {
- configuration.addMapper(this.mapperInterface);
- } catch (Exception var6) {
- this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
- throw new IllegalArgumentException(var6);
- } finally {
- ErrorContext.instance().reset();
- }
- }
-
- }
-
- public T getObject() throws Exception {
- return this.getSqlSession().getMapper(this.mapperInterface);
- }
-
- public Class<T> getObjectType() {
- return this.mapperInterface;
- }
-
- public boolean isSingleton() {
- return true;
- }
-
- public void setMapperInterface(Class<T> mapperInterface) {
- this.mapperInterface = mapperInterface;
- }
-
- public Class<T> getMapperInterface() {
- return this.mapperInterface;
- }
-
- public void setAddToConfig(boolean addToConfig) {
- this.addToConfig = addToConfig;
- }
-
- public boolean isAddToConfig() {
- return this.addToConfig;
- }
- }
getObject()方法中也是通过getSqlSession().getMapper(this.mapperInterface)拿到的,属性mapperInterface也是通过MapperFactoryBean类的构造方法注入的。
而getSqlSession()方法在它的父类SqlSessionDaoSupport中定义了,和上面的例子一样。
我们回到文章一开头的代码,这句代码就相当于开了一个事务。
- // 从工厂中获取SqlSession对象
- SqlSession sqlSession = sqlSessionFactory.openSession();
它与Mybatis的一级缓存和二级缓存有关,但是,如果一级缓存打开了,第二次去执行同样的sql时就不会去请求数据库了,因为SqlSession会缓存第一次执行sql的结果,会执行MapperProxy的invoke()方法:
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (Object.class.equals(method.getDeclaringClass())) {
- return method.invoke(this, args);
- }
-
- if (this.isDefaultMethod(method)) {
- return this.invokeDefaultMethod(proxy, method, args);
- }
- } catch (Throwable var5) {
- throw ExceptionUtil.unwrapThrowable(var5);
- }
-
- MapperMethod mapperMethod = this.cachedMapperMethod(method);
- // 主要执行这里
- return mapperMethod.execute(this.sqlSession, args);
- }
当执行某个方法时,会判断方法是@Select、@Insert等注解,以@Select为例,最终会调用
- public Object execute(SqlSession sqlSession, Object[] args) {
- Object param;
- Object result;
- switch(this.command.getType()) {
- case INSERT:
- param = this.method.convertArgsToSqlCommandParam(args);
- result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
- break;
- case UPDATE:
- param = this.method.convertArgsToSqlCommandParam(args);
- result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
- break;
- case DELETE:
- param = this.method.convertArgsToSqlCommandParam(args);
- result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
- break;
- case SELECT:
- if (this.method.returnsVoid() && this.method.hasResultHandler()) {
- this.executeWithResultHandler(sqlSession, args);
- result = null;
- } else if (this.method.returnsMany()) {
- result = this.executeForMany(sqlSession, args);
- } else if (this.method.returnsMap()) {
- result = this.executeForMap(sqlSession, args);
- } else if (this.method.returnsCursor()) {
- result = this.executeForCursor(sqlSession, args);
- } else {
- param = this.method.convertArgsToSqlCommandParam(args);
- // 最终会调用到这里
- result = sqlSession.selectOne(this.command.getName(), param);
- }
- break;
- case FLUSH:
- result = sqlSession.flushStatements();
- break;
- default:
- throw new BindingException("Unknown execution method for: " + this.command.getName());
- }
执行到result = sqlSession.selectOne(this.command.getName(), param);拿到了sql和方法的参数。
在MapperFactoryBean的父类SqlSessionDaoSupport中的SqlSession其实是SqlSessionTemplate类:
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
我们用到的UserMapper是通过SqlSessionTemplate.getMapper()返回的代理对象,那这个SqlSessionTemplate是什么东东呢?
先看它的构造函数:
- public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
- this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
- }
-
- public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
- this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
- }
-
- public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
- Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
- Assert.notNull(executorType, "Property 'executorType' is required");
- this.sqlSessionFactory = sqlSessionFactory;
- this.executorType = executorType;
- this.exceptionTranslator = exceptionTranslator;
- this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
- }
传入一个SqlSessionFactory ,最终通过JDK的动态代理生成一个SqlSession的代理对象,并赋值给sqlSessionProxy属性。
再看它的getMapper()方法:
- public <T> T getMapper(Class<T> type) {
- return this.getConfiguration().getMapper(type, this);
- }
参数传的是类型和this,去生成代理对象。mapper.selectById()调用的就是SqlSessionTemplate.selectOne()方法:
- public <T> T selectOne(String statement, Object parameter) {
- return this.sqlSessionProxy.selectOne(statement, parameter);
- }
就是调用的sqlSessionProxy就是构造函数生成的代理对象的selectOne()方法。
此时还没有真正去执行sql,执行sql是DefaultSqlSession对象,执行sql最终会进入SqlSessionInterceptor类,它是构造方法中生成动态代理导进去的,调用到它的invoke():
- private class SqlSessionInterceptor implements InvocationHandler {
- private SqlSessionInterceptor() {
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
-
- Object unwrapped;
- try {
- // 执行sql就是在这
- Object result = method.invoke(sqlSession, args);
- if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
- sqlSession.commit(true);
- }
-
- unwrapped = result;
- } catch (Throwable var11) {
- unwrapped = ExceptionUtil.unwrapThrowable(var11);
- if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
- SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
- sqlSession = null;
- Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
- if (translated != null) {
- unwrapped = translated;
- }
- }
-
- throw (Throwable)unwrapped;
- } finally {
- if (sqlSession != null) {
- SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
- }
-
- }
-
- return unwrapped;
- }
SqlSessionUtils.getSqlSession()方法就是从Session工厂中得到DefaultSqlSession:
所属类:org.mybatis.spring.SqlSessionUtils
- public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
- Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
- Assert.notNull(executorType, "No ExecutorType specified");
- // 通过Spring的事务管理器,从ThreadLocal中是否有SqlSession
- SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
- SqlSession session = sessionHolder(executorType, holder);
- // 有则返回
- if (session != null) {
- return session;
- } else {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Creating a new SqlSession");
- }
-
- // 没有,则从DefaultSqlSessionFactory工厂类中创建一个新的DefaultSqlSession
- session = sessionFactory.openSession(executorType);
- registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
- return session;
- }
- }
总之,调用流程就是SqlSessionTemplate.selectOne()---->SqlSessionProxy.selectOne()--->DefaultSqlSession.selectOne()。这儿抛出两个问题:
SqlSessionTemplate的作用是什么?
因为DefaultSqlSession是线程不安全的,没有锁和同步方法,当不同线程同时调用DefaultSqlSession时,DefaultSqlSession确是同一个,存在并发安全问题。
SqlSessionTemplate就是用来解决线程安全这个问题,但该类里面的方法却没有加锁,所以只要保证每个线程有单独的DefaultSqlSession,一个线程执行sql时是同一个DefaultSqlSession就可以了,如何做的?
通过ThreadLocal,每个线程执行之前,先看ThreadLoca中是否有DefaultSqlSession,有直接拿来用,没有则创建。
上面SqlSessionUtils.getSqlSession()方法中的TransactionSynchronizationManager.getResource(),就是Spring的事务,底层就是ThreadLocal:
- private static Object doGetResource(Object actualKey) {
- // resources是ThreadLocal包装的Map,用来缓存资源的,比如缓存当前线程中由某个DataSource所创建的数据库连接
- Map<Object, Object> map = resources.get();
- if (map == null) {
- return null;
- }
-
- // 获取DataSource对象所对应的数据库连接对象
- Object value = map.get(actualKey);
- // Transparently remove ResourceHolder that was marked as void...
- if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
- map.remove(actualKey);
- // Remove entire ThreadLocal if empty...
- if (map.isEmpty()) {
- resources.remove();
- }
- value = null;
- }
- return value;
- }
检查ThreadLocal中是否有SqlSession,返回的是SqlSession的包装类SqlSessionHolder,有则从SqlSessionHolder中取出,没有,则从DefultSqlSession工厂类中创建一个新的:
- SqlSessionFactory.openSession(executorType);
- registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
然后执行registerSessionHolder()方法,把DefultSqlSession包装成SqlSessionHolder,存到ThreadLocal。
- private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
- if (TransactionSynchronizationManager.isSynchronizationActive()) {
- Environment environment = sessionFactory.getConfiguration().getEnvironment();
- if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
- }
-
- SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
- // 通过事务管理器,存到ThreadLocal
- TransactionSynchronizationManager.bindResource(sessionFactory, holder);
- TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
- holder.setSynchronizedWithTransaction(true);
- holder.requested();
为什么Spring和Mybatis整合之后一级缓存会失效?
在上面的源码中,注意有个条件TransactionSynchronizationManager.isSynchronizationActive(),是否开启了Spring事务,如果为true才去执行后面的逻辑。
不符合带来的结果就是,一个线程去执行不同的sql时,发现ThreadLocal中没有SqlSession,于是每次都去创建,导致一级缓存失效,因为一级缓存的运行机制就是同一个SqlSession执行同一个sql时,返回之前缓存的结果。
如何解决?就是方法上加@Transactional注解,就会使用Spring的事务管理器建立的数据库连接,Mybatis如何拿到事务管理器的连接呢?就是prepareStatement()方法:
所属类:org.apache.ibatis.executor.SimpleExecutor
- private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
- // 获取数据库连接
- Connection connection = this.getConnection(statementLog);
- Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
- handler.parameterize(stmt);
- return stmt;
- }
进而调用到org.apache.ibatis.executor.BaseExecutor.getConnection()方法:
- protected Connection getConnection(Log statementLog) throws SQLException {
- Connection connection = this.transaction.getConnection();
- return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
- }
这个Transaction是Mybatis的事务管理接口,包括事务的提交、回滚,及数据库连接的获取。
会先在DefaultSqlSessionFactory.openSessionFromDataSource()方法中通过TransactionFactory创建的,进而创建了Executor执行器。
- private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
- Transaction tx = null;
-
- try {
- Environment environment = this.configuration.getEnvironment();
- TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
- tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
- Executor executor = this.configuration.newExecutor(tx, execType);
- return new DefaultSqlSession(this.configuration, executor, autoCommit);
-
- ----省略无关代码----
- }
Spring整合Mybatis后得到的是SpringManagedTransaction类,它继承自Transaction接口:
- public class SpringManagedTransaction implements Transaction {
- private static final Log LOGGER = LogFactory.getLog(SpringManagedTransaction.class);
- private final DataSource dataSource;
- private Connection connection;
- private boolean isConnectionTransactional;
- private boolean autoCommit;
-
- public SpringManagedTransaction(DataSource dataSource) {
- Assert.notNull(dataSource, "No DataSource specified");
- this.dataSource = dataSource;
- }
-
- public Connection getConnection() throws SQLException {
- if (this.connection == null) {
- this.openConnection();
- }
- return this.connection;
- }
-
- private void openConnection() throws SQLException {
- this.connection = DataSourceUtils.getConnection(this.dataSource);
- this.autoCommit = this.connection.getAutoCommit();
- this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
- }
-
- public void commit() throws SQLException {
- if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
- this.connection.commit();
- }
- }
-
- public void rollback() throws SQLException {
- if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
- this.connection.rollback();
- }
- }
-
- public void close() throws SQLException {
- DataSourceUtils.releaseConnection(this.connection, this.dataSource);
- }
-
- public Integer getTimeout() throws SQLException {
- ConnectionHolder holder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.dataSource);
- return holder != null && holder.hasTimeout() ? holder.getTimeToLiveInSeconds() : null;
- }
- }
openConnection()中的DataSourceUtils.getConnection(this.dataSource)就可以拿到由Spring事务管理器创建的数据库连接。
如果方法没有开启事务,那么在执行sql时候,每个sql有自己的SqlSession对象来执行。
如果开启了Spring事务,就是多个sql属于同一个事务,那应该用一个SqlSession来执行多个sql。所以,在没有开启Spring事务的时候,SqlSession的一级缓存并不是失效了,而是存在的生命周期太短了(执行完一个sql后就被销毁了,下一个sql执行时又是一个新的SqlSession)。
Spring为什么要这样设计?解决线程安全问题,一般情况下不建议使用Mybatis的一级缓存,如果使用,就会涉及到事务的隔离级别,假如设置的隔离级别是读未提交,那么同一条sql查到的结果可能是一样的(从缓存中拿的),把隔离界别忽略了。那数据库的隔离级别重要还是Mybatis的一级缓存重要?肯定是数据库的隔离级别重要。
如果非要使用Mybatis的一级缓存,提供另外一种方法,用@Bean创建一个SqlSession的Bean:
- @ComponentScan("com.baec")
- @BaecMapperScan("com.baec.mapper")
- public class AppConfig {
-
- @Autowired
- public SqlSessionFactory sqlSessionFactory() throws IOException {
- InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- return sqlSessionFactory;
- }
-
- @Bean
- public SqlSession sqlSession() throws IOException {
- return sqlSessionFactory().openSession();
- }
这样,在Service中就可以直接使用了:
- @Component
- public class UserService {
-
- @Autowired
- private UserMapper userMapper;
-
- @Autowired
- private SqlSession sqlSession;
-
- public void test() {
- sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
- sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
- sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
- }
- }
这样一来,这三个Sql执行使用的SqlSession 就是同一个了,而且是同一个线程。
如果加了@MapperScan注解,会扫描、注入BeanDefinition,Mapper的代理对象Bean,即MapperFactoryBean<T>,MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport又继承了DaoSupport,而DaoSupport类实现了InitializingBean接口,所以各种Mapper创建时会执行afterPropertiesSet()方法:
- public abstract class DaoSupport implements InitializingBean {
-
- /** Logger available to subclasses. */
- protected final Log logger = LogFactory.getLog(getClass());
-
-
- @Override
- public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
- // Let abstract subclasses check their configuration.
- checkDaoConfig();
-
- // Let concrete implementations initialize themselves.
- try {
- initDao();
- }
- catch (Exception ex) {
- throw new BeanInitializationException("Initialization of DAO failed", ex);
- }
- }
初始化的时候首先会调用checkDaoConfig(),检查SqlSession是否为空,会调到这里:
- public abstract class SqlSessionDaoSupport extends DaoSupport {
- private SqlSession sqlSession;
- private boolean externalSqlSession;
-
- public SqlSessionDaoSupport() {
- }
-
- public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
- if (!this.externalSqlSession) {
- this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
- }
- }
-
- public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
- this.sqlSession = sqlSessionTemplate;
- this.externalSqlSession = true;
- }
-
- public SqlSession getSqlSession() {
- return this.sqlSession;
- }
-
- protected void checkDaoConfig() {
- Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
- }
- }
接下来,会进入到子类MapperFactoryBean.checkDaoConfig():
- protected void checkDaoConfig() {
- super.checkDaoConfig();
- Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
- Configuration configuration = this.getSqlSession().getConfiguration();
- if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
- try {
- // 把泛型Class<T>的mapperInterface添加到Mapper中
- configuration.addMapper(this.mapperInterface);
- } catch (Exception var6) {
- this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
- throw new IllegalArgumentException(var6);
- } finally {
- ErrorContext.instance().reset();
- }
- }
-
- }
把泛型Class<T>的mapperInterface添加到Mapper中。
另外,如果不想写@MapperScan注解,还有一种方式可以扫描注入Mybatis生成的代理对象Bean,整合Mybatis:
- @ComponentScan("com.baec")
- public class AppConfig {
-
- @Bean
- public MapperScannerConfigurer mapperScannerConfigurer(){
- MapperScannerConfigurer configurer = new MapperScannerConfigurer();
- // 指定扫描路径
- configurer.setBasePackage("com.baec.mapper");
- return configurer;
- }
-
- @Bean
- public JdbcTemplate jdbcTemplate() {
- return new JdbcTemplate(dataSource());
- }
-
- @Bean
- public DataSource dataSource() {
- DriverManagerDataSource dataSource = new DriverManagerDataSource();
- dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/tuling?characterEncoding=utf-8&useSSL=false");
- dataSource.setUsername("root");
- dataSource.setPassword("Zhouyu123456***");
- return dataSource;
- }
-
- @Bean
- public SqlSessionFactory sqlSessionFactory() throws Exception {
- SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
- sessionFactoryBean.setDataSource(dataSource());
- return sessionFactoryBean.getObject();
- }
-
- }
SqlSessionFactory类实现了InitializingBean和FactoryBean接口,它的getObject()方法调用了afterPropertiesSet()方法:
- public SqlSessionFactory getObject() throws Exception {
- if (this.sqlSessionFactory == null) {
- this.afterPropertiesSet();
- }
- return this.sqlSessionFactory;
- }
- public void afterPropertiesSet() throws Exception {
- this.sqlSessionFactory = this.buildSqlSessionFactory();
- }
buildSqlSessionFactory()方法中会构建出Mybatis的核心配置类Configuration,在这里面创建了Spring的事务管理器SpringManagedTransactionFactory工厂,通过该类可以拿到SpringManagedTransaction。
MapperScannerConfigurer实现了BenDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry()方法中创建了扫描器,实现了扫描BeanDefiniiton的功能:
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
- if (this.processPropertyPlaceHolders) {
- this.processPropertyPlaceHolders();
- }
-
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
- scanner.setAddToConfig(this.addToConfig);
- scanner.setAnnotationClass(this.annotationClass);
- scanner.setMarkerInterface(this.markerInterface);
- scanner.setSqlSessionFactory(this.sqlSessionFactory);
- scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
- scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
- scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
- scanner.setResourceLoader(this.applicationContext);
- scanner.setBeanNameGenerator(this.nameGenerator);
- scanner.registerFilters();
- scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
- }
最后,我们总结一下Spring整合Mybatis底层源码执行流程:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。