Spring注解方式整合Mybatis

Spring注解方式整合Mybatis

一、概述

二、实验

2.1注入数据源的各属性

先引入配置文件:

java 复制代码
@PropertySource("classpath:jdbc.properties")

再是注入属性:

java 复制代码
@Value("${jdbc.driver}")
    String driverClassName;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;

2.2创建数据源且注册成为Bean

java 复制代码
@Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername( username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }

2.3创建SqlSessionFactoryBean且注册成为Bean

java 复制代码
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
//        SqlSessionFactoryBean sqlSessionFactoryBean = sqlSessionFactoryBean(dataSource);
        SqlSessionFactoryBean sqlSessionFactoryBean =new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

2.4设置接口扫描

java 复制代码
@ComponentScan("com.itheima")

总的配置类(里面含有xml配置的对照):

java 复制代码
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.beans.OtherBean3;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;

//这个注解代表:这是一个核心配置类;这个类交给了Spring容器管理(就是相当于替换Spring配置文件)
@Configuration
//<context:component-scan base-package="com.itheima"/>:配置注解扫描,只有一个时直接写,有多个写成数组
@ComponentScan("com.itheima")
//<context:property-placeholder location="classpath:jdbc.properties"/>:引入配置文件
@PropertySource("classpath:jdbc.properties")
// <import resource="":导入其他配置类
@Import(OtherBean3.class)
//下面是接口扫描
// <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
//<!--        指定要扫描的包-->
//        <property name="basePackage" value="com.itheima.mapper"></property>
//    </bean>
@MapperScan("com.itheima.mapper")
public class SpringConfig2 {
    @Value("${jdbc.driver}")
    String driverClassName;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;
    //     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
//        <property name="driverClassName" value="${jdbc.driver}"/>
//        <property name="url" value="${jdbc.url}"/>
//        <property name="username" value="${jdbc.username}"/>
//        <property name="password" value="${jdbc.password}"/>
//    </bean>
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUsername( username);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
//    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
//<!--        最终是要得到由Sqlsession得到Conection,需要数据源属性-->
//        <property name="dataSource" ref="dataSource"></property>
//    </bean>
    @Bean
//    在这里是通过类型进行注入的
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
//        SqlSessionFactoryBean sqlSessionFactoryBean = sqlSessionFactoryBean(dataSource);
        SqlSessionFactoryBean sqlSessionFactoryBean =new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
}

结果:

三、原理剖析

先是进入@MapperScan看看里面:

java 复制代码
@MapperScan("com.itheima.mapper")
java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.mybatis.spring.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Import;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.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;

    String lazyInitialization() default "";
}

我们发现它导入了一个MapperScannerRegistrar,继续跟进:

java 复制代码
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
            builder.addPropertyValue("annotationClass", annotationClass);
        }

然后注册了一个MapperScannerConfigurer.class ,是不是好熟悉啊?其实到这就是与xml配置一样了,都是通过MapperScannerConfigurer.class来注册,只不过注解方式还导入了一个MapperScannerRegistrar,再由MapperScannerRegistrar注册MapperScannerConfigurer.class,而xml方式是直接配置MapperScannerConfigurer.class。当然我们也还可以发现BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);,BeanDefinitionBuilder也是可以注册BeanDefinition的,以前我们都是通过new RootBeanDefinition(),然后由BeanDefinitionRegistry注册,现在又发现一种方式。

java 复制代码
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(this.lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
        }

        scanner.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }

看吧,依然是调用postProcessBeanDefinitionRegistry的scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));,然后进入scan方法:

java 复制代码
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();

        for(String basePackage : basePackages) {
            for(BeanDefinition candidate : this.findCandidateComponents(basePackage)) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }

                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }

                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }

        return beanDefinitions;
    }

同样是先注册BeanDefinitionHolder,然后注册BeanDefinition放到BeanDefinitionMap中,核心代码都是完全一样的。

相关推荐
wuqingshun3141592 小时前
谈谈你对泛型的理解
java·开发语言·jvm
重生之后端学习2 小时前
105. 从前序与中序遍历序列构造二叉树
java·数据结构·后端·算法·深度优先
前路不黑暗@2 小时前
Java项目:Java脚手架项目的地图的POJO
android·java·开发语言·spring boot·学习·spring cloud·maven
2501_926978332 小时前
分形时空理论框架:从破缺悖论到意识宇宙的物理学新范式引言(理论概念版)--AGI理论系统基础1.1
java·服务器·前端·人工智能·经验分享·agi
西门吹雪分身2 小时前
K8S之Pod调度
java·容器·kubernetes·k8s
弹简特2 小时前
【JavaEE08-后端部分】SpringMVC03-SpringMVC第二大核心处理请求之Cookie/Session和获取header
java·spring boot·spring·java-ee
追随者永远是胜利者2 小时前
(LeetCode-Hot100)10. 正则表达式匹配
java·算法·leetcode·go
追随者永远是胜利者2 小时前
(LeetCode-Hot100)17. 电话号码的字母组合
java·算法·leetcode·职场和发展·go
程序员的那些事_2 小时前
微软用 Rust 开发了一个库操作系统 LiteBox
开发语言·后端·rust