文章目录
一、Spring xml方式整合第三方框架
xml整合第三方框架有两种整合方案:
⚫ 不需要自定义命名空间,不需要使用Spring的配置文件配置第三方框架本身内容,例如:MyBatis
⚫ 需要引入第三方框架命名空间,需要使用Spring的配置文件配置第三方框架本身内容,例如:Dubbo
1. Spring整合MyBatis
(1)原始方式
需要mybatis-config.xml 配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.itheima.mapper"></package>
</mappers>
</configuration>
编写Mapper和Mapper.xml
public interface UserMapper {
List<User> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
<select id="findAll" resultType="com.itheima.pojo.User">
select * from tb_user
</select>
</mapper>
测试方法
public class MyBatisTest {
public static void main(String[] args) throws Exception {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(in);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
(2)引入mybatis-spring.jar方式
不再需要mybatis-config.xml文件
MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。
Spring整合MyBatis的步骤如下:
⚫ 导入MyBatis整合Spring的相关坐标;
⚫ 编写Mapper和Mapper.xml;
⚫ 配置SqlSessionFactoryBean和MapperScannerConfigurer;
⚫ 编写测试代码
①导入MyBatis整合Spring的相关坐标
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
②编写Mapper和Mapper.xml
public interface UserMapper {
List<User> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
<select id="findAll" resultType="com.itheima.pojo.User">
select * from tb_user
</select>
</mapper>
③在applicationContext.xml中配置SqlSessionFactoryBean和MapperScannerConfigurer
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--配置SqlSessionFactoryBean,作用将SqlSessionFactory存储到spring容器-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--MapperScannerConfigurer,作用扫描指定的包,产生Mapper对象存储到Spring容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"></property>
</bean>
④编写测试代码
public static void main(String[] args) {
ClassPathxmlApplicationContext applicationContext = new ClassPathxmlApplicationContext("applicationContext.xml");
UserMapper userMapper = applicationContext.getBean(UserMapper.class);
List<User> all = userMapper.findAll();
System.out.println(all);
}
2. Spring整合MyBatis的原理剖析
整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象(MapperScannerConfigurer ),SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:
⚫ SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;
⚫ MapperScannerConfigurer:需要进行配置,用于扫描指定mapper注册BeanDefinition;
⚫ MapperFactoryBean:Mapper的FactoryBean,调用getObject方法创建指定的Mapper;
⚫ ClassPathMapperScanner:definition.setAutowireMode(2) 修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。
SqlSessionFactoryBean源码:
配置SqlSessionFactoryBean作用是向容器中提供SqlSessionFactory,SqlSessionFactoryBean实现了FactoryBean和InitializingBean两个接口,所以会自动执行getObject() 和afterPropertiesSet()方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// spring生命周期中,属性填充后,会执行InitializingBean的afterPropertiesSet方法
// SqlSessionFactoryBean重写了afterPropertiesSet方法,用于创建SqlSessionFactory对象存入容器中
@Override
public void afterPropertiesSet() throws Exception {
this.sqlSessionFactory = buildSqlSessionFactory();
}
// 其他对象要引用SqlSessionFactory对象时,可以通过工厂方法getObject()从容器中获取SqlSessionFactory对象
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
MapperScannerConfigurer源码:
配置MapperScannerConfigurer作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean,MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,会在postProcessBeanDefinitionRegistry方法中向容器中注册MapperFactoryBean
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// 类加载路径下的Mapper扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
。。。
// ClassPathMapperScanner中没有scan方法,所以调的是父类ClassPathBeanDefinitionScanner的scan方法
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
}
ClassPathBeanDefinitionScanner 源码:
public class ClassPathBeanDefinitionScanner {
// 步骤一:父类scan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 步骤二:这里会先执行子类的doScan方法
doScan(basePackages);
。。。
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
// 步骤四:父类doScan方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
。。。
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 步骤五:注册beanDefinition,definitionHolder其实就是将beanDefinition包装了一层
// 将扫描到的类注册到beanDefinitionMap中,此时beanClass是当前类全限定名
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
}
步骤五:definitionHolder其实就是将beanDefinition包装了一层
此时注册的是UserMapper接口的beanDefinition信息,然后将beanDefinition存入beanDefinitionMap中后续就可以通过spring创建对象了
ClassPathMapperScanner 源码:
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
// 步骤三:子类doScan方法
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 步骤四:从子类中去调用父类doScan方法,获取BeanDefinitionHolder集合
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
。。。
} else {
// 步骤六:处理扫描后的所有beanDefinitions定义信息
this.processBeanDefinitions(beanDefinitions);
}
}
// 步骤六:处理扫描后的所有Mapper接口的beanDefinitions定义信息
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
。。。
// 设置Mapper的beanClass是org.mybatis.spring.mapper.MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
// 步骤七:
// autowireMode取值:1是根据名称自动装配,2是根据类型自动装配
definition.setAutowireMode(2);
。。。
}
}
步骤六:处理扫描后的所有Mapper接口的beanDefinitions定义信息
此时的beanClass=“com.itheima.mapper.UserMapper”,由于接口不能通过反射创建对象,所以需要通definition.setBeanClass(this.mapperFactoryBeanClass);将原有的"com.itheima.mapper.UserMapper"覆盖,设置为this.mapperFactoryBeanClass(mapperFactoryBeanClass实现了FactoryBean接口,所以会重写getObject方法)
@Override
public T getObject() throws Exception {
// 步骤八:底层调用的是mybatis源码通过sqlSession去创建Mapper对象
return getSqlSession().getMapper(this.mapperInterface);
}
// 步骤八:mybatis源码通过sqlSession去创建Mapper对象
public < T > T getMapper(Class< T> type, SqlSession sqlSession) {
MapperProxyFactory< T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
}
步骤七:覆盖完beanClass后,此时被扫描的Mapperbean类型就是MapperFactoryBean类型,所以接下来上面的步骤八会去调用getObject方法创建Mapper对象,由于创建Mapper对象会用到sqlSession,所以需要先注入sqlSession对象,因此这里definition.setAutowireMode(2);是为了通过类型进行注入sqlSession对象