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中,核心代码都是完全一样的。