BeanFactory入门
BeanFactory继承关系
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//读取配置文件
reader.loadBeanDefinitions("applicationContext.xml");
BookDao bean = beanFactory.getBean("bookDao", BookDao.class);
ApplicationContext入门
ApplicationContext是继承自BeanFactory
ApplicationContext继承关系
// ApplicationContext 没有close方法
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bean = ctx.getBean("bookDao", BookDao.class);
ctx.close();
添加Spring-webmvc后
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.5</version>
</dependency>
BeanFactory和ApplicationContext区别
1)BeanFactory是Spring的早期接口,称为的Bean工厂,ApplicationContext是后期更高级接口,称之为Spring容器;
2) ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;
3) Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactory既有继承关系,又有融合关系。
4) Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建,而ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。
SpringBean配置详解
xml配置
<bean id="bookDao" class="com.dao.BookDao">
<!--setter注入-->
<property name="id" value="10"></property>
<property name="name" value="zhangsan"></property>
</bean>
<bean id="bookService" name="aaa ,bbb" class="com.service.BookService"
autowire="byName" lazy-init="true" init-method="init" destroy-method="destroys"
factory-method="abc" factory-bean="bookDao">
<!--property表示配置當前bean的屬性
name表示配置哪一个具体的屬性
ref表示參照哪一個bean-->
<!--<property name="bookdao" ref="bookDao"></property>-->
<!--构造器注入
name表示的是形参
ref表示参照哪一个bean
-->
<constructor-arg type="com.dao.BookDao" ref="bookDao"></constructor-arg>
<constructor-arg name="name" value="111"></constructor-arg>
</bean>
Bean的实例化配置
Spring的实例化方式主要如下两种:
构造方式实例化:底层通过构造方法对Bean进行实例化
无参构造
有参构造
工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化
静态工厂方式
实例工厂方式
实现FactoryBean规范延迟实例化
构造方式实例化(无参构造/有参构造)
public class BookDao {
public BookDao(String name){}
}
<!--无参构造-->
<!--
<bean id="bookDao" class="com.dao.BookDao"></bean>
-->
<--有参构造-->
<bean id="bookDao" class="com.dao.BookDao">
<property name="name" value="zhangsan"></property>
</bean>
工厂方式实例化(静态工厂方式/实例工厂方式/实现FactoryBean规范延迟实例化)
静态工厂方式
public class BookFactoryBean{
public static BookDao abc(){
return new BookDao();
}
}
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean = ctx.getBean("bookFactory");
System.out.println(bean);
<!--factory-method-->
<bean id="bookFactory" class="com.factory.BookFactoryBean" scope="prototype" factory-method="abc"></bean>
实例工厂方式
构造器实例化是spring容器通过bean对应的默认的构造函数来实例化bean。
实例工厂方式是获取下面的 方法来实例化Bean
public class BookFactoryBean{
public BookDao abc(){
return new BookDao();
}
}
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean = ctx.getBean("bookDao3");
System.out.println(bean);
<bean id="bookFactory" class="com.factory.BookFactoryBean"></bean>
<bean id="bookDao3" factory-bean="bookFactory" factory-method="abc"></bean>
先创建bookFactory工厂对象
实现FactoryBean规范延迟实例化
public class BookFactoryBean implements FactoryBean<BookDao> {
@Override
public BookDao getObject() throws Exception {
return new BookDao();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean = ctx.getBean("bookDao4");
System.out.println(bean);
<bean id="bookDao4" class="com.factory.BookFactoryBean"></bean>
真正的bookDao4在这里
Bean注入
集合注入
<!--集合注入-->
<bean id="gatherDao" class="com.dao.GatherDao">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="list">
<list>
<value>zhangsan</value>
<value>lisi</value>
<value>wangwu</value>
</list>
</property>
<property name="set">
<set>
<value>zhangsan</value>
<value>lisi</value>
<value>wangwu</value>
<value>wangwu</value>
</set>
</property>
<property name="map">
<map>
<entry key="county" value="china"></entry>
<entry key="province" value="henan"></entry>
<entry key="city" value="luoyang"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="county">china</prop>
<prop key="province">henan</prop>
<prop key="city">luoyang</prop>
</props>
</property>
</bean>
普通注入
<bean id="bookDao" class="com.dao.BookDao">
<!--setter注入-->
<property name="id" value="10"></property>
<property name="name" value="zhangsan"></property>
</bean>
<!--<bean id="bookDao" class="com.dao.BookDao">-->
<!--构造Bean方式注入-->
<!--<constructor-arg name="name" ref="bookDao"></constructor-arg> -->
<!--</bean>-->
自动装配方式
byName/byType
<!--按名称注入-->
<bean id="bookService" class="com.service.BookService" autowire="byName"></bean>
<!--按类型注入-->
bean id="bookService" class="com.service.BookService" autowire="buType"></bean>
命名空间种类
Spring 的 xml标签大体上分为两类,一种是默认标签,一种是自定义标签
默认标签:就是不用额外导入其他命名空间约束的标签,例如<bean>标签
自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如<context:property-placeholder/>标签
默认标签
标签 | 作用 |
<beans> | 一般作为xml文件的根标签,其他标签都是该标签的子标签 |
<bean> | Bean的配置标签 |
<import> | 外部资源导入标签 |
<alias> | 指定Bean的别名标签 |
<!-- 导入外部资源 -->
<import resource="applicationContext1.xml"></import>
<!-- 配置别名 -->
<alias name="bookDao" alias="aaa"></alias>
<!--
beans标签要放在最下面,不然后面的代码不能使用
profiles:切换环境
-->
<beans profile="dev">
<bean></bean>
</beans>
<beans profile="test">
<bean></bean>
</beans>
// 配置指定环境
//使用命令行动态参数,虚拟机参数位置加载-Dspring.profiles.active=test
//使用代码的方式设置环境变量System.setProperty("spring.profiles.active" ,"test")
System.setProperty("spring.profiles.active","dev");
自定义标签
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>形式的标签,称之为自定义标签,自定义标签的解析流程也是Spring xml扩展点方式之一,在《Spring整合其他框架》章节进行详细介绍
<!-―默认标签-->
<bean id="userDao" class="com.itheima.dao.imp1.UserDaoImpl" />
<!--自定义标签->
<context:property-placehoider/>
<mvc : annotation-driven/>
<dubbo : application name="application" / >
<!-- xmlns:context为其他jar包坐标 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
<context:property-placeholder></context:property-placeholder>
三种getBean的API
方法定义 | 返回值和参数 |
Object getBean (String beanName) | 根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转 |
T getBean (Class type) | 根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转 |
TgetBean (String beanName,Class type) | 根据beanName从容器中获得Bean实例,返回值为Class类型实例,无需强转 |
Spring配置非自定义的Bean
以上在xml中配置的Bean都是自己定义的,例如: UserDaolmpl,UserServicelmpl。但是,在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置
配置非自定义的Bean需要考虑如下两个问题:
被配置的Bean的实例化方式是什么?无参构造、有参构造、静态工厂方式还是实例工厂方式;被配置的Bean是否需要注入必要属性。
能够new的对象,都是有其有参构造或无参构造的,所以用构造方式实例化
不能够new的则表示要用到其他方式实例化
Durid配置
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql//localhost:3306/spring");
dataSource.setUsername("root");
dataSource.setPassword("123456");
<!--管理DuridDataSource对象
因为有set方法可以直接使用setter注入-->
<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/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
Connection配置
<!--
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("url", "root", "123456");
-->
<!--一看就知道要使用工厂方式实例化(这里使用静态工厂实例化)-->
<bean id="clazz" class="java.lang.Class" factory-method="forName">
<constructor-arg name="className" value="com.mysql.jdbc.Driver"></constructor-arg>
</bean>
<bean class="java.sql.DriverManager" factory-method="getConnection">
<constructor-arg name="url" value="jdbc:mysql//localhost:3306/spring"></constructor-arg>
<constructor-arg name="user" value="root"></constructor-arg>
<constructor-arg name="password" value="123456"></constructor-arg>
</bean>
property与constructor-arg区别
constructor-arg:通过构造方法注入
property:是通过setter方法注入
Bean实例化的基本流程
BeanDefinition对象
Spring容器在进行初始化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用getBean方法时则最终从该Map集合中取出Bean实例对象返回。
创建时内部会封装bean的所有信息,没有特殊制定的为默认值/空
单例池和流程
单例池(singletonObjects)是存放单例Bean的地方
- Bean 实例化的基本流程
1.加载xml配置文件,解析获取配置中的每个<bean>的信息,封装成一个个的BeanDefinition对象;
2.将BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;
3.ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
4.创建好的Bean实例对象,被存储到一个名为singletonObjects的Map<String,Object>中;
5.当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。
Spring的后处理器
-Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
1.BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;
2.BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
Spring的Bean工厂后处理器
Spring的Bean工厂后处理器入门
Bean工厂后处理器- BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
// 实现BeanFactoryPostProcessor,重写方法
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕,回调该方法");
// 修改某个BeanDefinition
// 不会允许获取整个BeanDefinitionMap的,但可以获取某个BeanDefinition
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("bookDao");
// 将bookDao对象改为gatherDao对象
beanDefinition.setBeanClassName("com.dao.GatherDao.");
}
}
<bean id="bookDao" class="com.dao.BookDao"></bean>
<bean id="gatherDao" class="com.dao.GatherDao"></bean>
<!-- 将MyBeanFactoryProcessor交由spring管理 -->
<bean class="com.processor.MyBeanFactoryProcessor"></bean>
Spring的Bean工厂后处理器注册
BeanFactoryPostProcessor注册BeanDefinition
没有在.xml文件中管理Bean
public class MyBeanFactoryProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("beanDefinitionMap填充完毕,回调该方法");
// 动态注册一个BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.dao.UserDao");
// 因为ConfigurableListableBeanFactory没有注册到BeanDefinition的方法只有注册到Singleton
// 强转为DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
defaultListableBeanFactory.registerBeanDefinition("userDao",beanDefinition);
}
}
<!-- 没有管理名为userDao的bean -->
<bean id="bookDao" class="com.dao.BookDao"></bean>
<bean id="gatherDao" class="com.dao.GatherDao"></bean>
<bean class="com.processor.MyBeanFactoryProcessor"></bean>
BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition的一个接口(一般可以用这个)
没有在.xml文件中管理Bean
方法执行顺序:
MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry
MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory
MyBeanFactoryProcessor的postProcessBeanFactory
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 动态注册一个BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.dao.UserDao");
beanDefinitionRegistry.registerBeanDefinition("userDao", beanDefinition);
}
// BeanFactoryPostProcessor的接口方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
<!-- 没有管理名为userDao的bean -->
<bean id="bookDao" class="com.dao.BookDao"></bean>
<bean id="gatherDao" class="com.dao.GatherDao"></bean>
<bean class="com.processor.MyBeanDefinitionRegistryPostProcessor"></bean>
实例化基本流程图完善
Spring的Bean后处理器
Spring的Bean后处理器入门
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 可以给bean设置属性
if (bean instanceof BookDao){
BookDao bookDao = (BookDao) bean;
bookDao.setName("111");
}
System.out.println(beanName+":postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessAfterInitialization");
return bean;
}
}
<bean id="bookDao" class="com.dao.BookDao"></bean>
<bean class="com.processor.MyBeanPostProcessor"></bean>
Bean后处理器--BeanPostProcessor
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
Before和After方法执行时机
完善实例化的基本流程图
在变成一个BeanDefinition之后要经过BeanFactoryPostProcessor,然后再从beanDefinitionMap中取出每一个BeanDefinition经过反射变为一个对象,再经过BeanPostProcessor的before方法和after方法 ,存储到单例池(SingletonObjects)当中。
springBean生命周期
Spring Bean的生命周期是从Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:
Bean的实例化阶段: Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等
spring高频面试题Bean的循环引用问题都是在这个阶段体现的;
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期。
SpringBean初始化过程
Bean实例的属性填充
Aware接口属性注入
BeanPostProcessor的before()方法回调
InitializingBean接口的初始化方法回调
自定义初始化方法init回调
BeanPostProcessor的after()方法回调
注入属性信息封装
BeanDefinition中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,
属性注入三种方式
Spring在进行属性注入时,会分为如下几种情况:
1.注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
2.注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
3.注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,引出三级缓存来存储“半成品Bean实例“和“完整Bean实例“解决循环引用的问题
三级缓存的设计原理
Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//最终存储单例bean成品的容器,即实例化和初始化后都完成后的bean,称为”一级缓存“
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//早期的bean单例池,缓存半成品对象,且当前对象已经被其他对象引用,称为”二级缓存“
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//单例bean工厂池,缓存半成品对象,对象未被引用,使用时再通过工厂创建bean,称为”三级缓存“
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
三级缓存描述
UserService实例化对象,但尚未初始化,将UserService存储到三级缓存;
UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
UserService注入UserDao;
UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
Aware接口
常用的Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。
四个常用
public class Method implements InitializingBean, ApplicationContextAware, BeanFactoryAware, BeanNameAware {
@Override
public void afterPropertiesSet() throws Exception {
//System.out.println("执行实现InitializingBean的afterPropertiesSet方法");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(beanFactory);
}
@Override
public void setBeanName(String beanName) {
System.out.println("bean名字"+beanName);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("执行容器"+applicationContext);
}
}
<bean id="method" class="com.dao.Method" init-method="init"></bean>
Spring IOC容器实例化Bean整体流程图
后期添加功能根据此图添加
Spring整合MyBatis
原始配置MyBatis
public static void main(String[] args) throws IOException {
//1.加载mybatis核心配置文件,获取sqlsessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取sqlsession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.执行sql
//List<User> users = sqlSession.selectList("com.mybatis.mapper.UserMapper.selectAll");
//3.1获取UserMapper接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
//4.释放资源
sqlSession.close();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置各个标签要遵循前后顺序-->
<!--配置别名-->
<typeAliases>
<!--配置包,此包下的类不区分大小写直接写-->
<package name="com.mybatis.pojo"/>
</typeAliases>
<!--
environments:配置数据库连接环境信息,可以配置多个environment,通过default决定执行哪一个库
dataSource:数据库连接池
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<!--<mapper resource="UserMapper.xml"/>-->
<!--包扫描的方式-->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>
使用Spring配置MyBatis
Spring整合MyBatis的步骤如下:
导入MyBatis整合Spring的相关坐标;
编写Mapper和Mapper.xml;
配置SqlSessionFactoryBean和MapperScannerConfigurer;
编写测试代码
导入坐标
<!-- spring整合mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!--管理DuridDataSource对象-->
<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/spring"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--配置sqlSessionFactoryBean 作用:将qlSessionFactory存储到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.mapper"></property>
</bean>
<!--注入,少了在代码中写的那些代码-->
<bean id="method" class="com.dao.Method">
<property name="methodMapper" ref="methodMapper"></property>
</bean>
public class Method{
//注入
private MethodMapper methodMapper;
public void setMethodMapper(MethodMapper methodMapper) {
this.methodMapper = methodMapper;
}
public void test(){
List<Method> all = methodMapper.findAll();
for (Method method : all){
System.out.println(method);
}
}
}
Spring整合MyBatis原理
整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:
SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;
MapperScannerConfigurer:需要进行配置,用于扫描指定mapper注册BeanDefinition;
MapperFactoryBean: Mapper的FactoryBean,获得指定Mapper时调用getObject方法;
ClassPathMapperScanner: definition.setAutowireMode(2)修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。
Spring集成第三方用自定义命名空间的方式
在工厂的META-INF下准备一个spring.handlers,注册一个当前标签的解析器实现BeanDefinitParser,在其中会有parse方法,会在spring解析到标签时会调用标签所对应的解析器的parse方法
- Spring xml方式整合第三方框架
通过上述分析,我们清楚的了解了外部命名空间标签的执行流程,如下:
将自定义标签的约束与物理约束文件与网络约束名称的约束以键值对形式存储到一个spring.schemas文件里,该文件存储在类加载路径的META-INF里,Spring会自动加载到;
将自定义命名空间的名称与自定义命名空间的处理器映射关系以键值对形式存在到一个叫spring.handlers文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到;
准备好NamespaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签,那么可以在init方法中为每个标签都注册一个BeanDefinitionParser,在执行NamespaceHandler的parse方法时在分流给不同的
BeanDefinitionParser进行解析(重写doParse方法即可)。