了解springboot自动配置之前需要先了解mybatis的使用,以及mybatis如何集成到spring,还要了解一下工厂方法设计模式。
1.工厂方法 复杂对象的实例化交给工厂,消费者(使用方)只需要在工厂的中获取对象,不需要关心对象的生产过程。解决了多个类之前的强耦合(由多对多变为多对一),这样某个类的创建有修改时只需要更改工厂中的实现即可;开闭原则:
工厂抽象为接口,这样增加一个类时只需要增加一个具体工厂即可;
工厂中增加配置属性,而配置可以通过文件加载,这样更改对象参数只需要修改配置文件即可;
mybatis和spring使用了大量的工厂方法设计模式。
2.mybatis使用 可以回想一下使用jdbc操作数据库的代码,mybatis对它做了很好的封装,使用mybatis主要是创建SqlSessionFactory这个工厂,通过在工厂中获取sqlsession操作数据库,工厂的配置是通过读取配置文件获取。
创建SqlSessionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 String resource = "org/mybatis/example/mybatis-config.xml" ;InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream);<?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> <environments default ="development" > <environment id="development" > <transactionManager type="JDBC" /> <dataSource type="POOLED" > <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml" /> </mappers> </configuration> ----------java config DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();TransactionFactory transactionFactory = new JdbcTransactionFactory ();Environment environment = new Environment ("development" , transactionFactory, dataSource);Configuration configuration = new Configuration (environment);configuration.addMapper(BlogMapper.class); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(configuration);
创建mapper文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="org.mybatis.example.BlogMapper" > <select id ="selectBlog" resultType ="Blog" > select * from Blog where id = #{id} </select > </mapper > ```` 3. 使用sqlsession执行sql ```java try (SqlSession session = sqlSessionFactory.openSession()) { Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); }
mybatis使用代理MapperProxy操作数据库方式
1、2同上
创建mapper接口
1 2 3 4 package org.mybatis.example;public interface BlogMapper { Blog selectBlog (int id) ; }
创建代理类执行sql
1 2 3 4 try (SqlSession session = sqlSessionFactory.openSession()) { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101 ); }
官方文档
3.spring集成mybatis-spring 集成的方案主要是将SqlSessionFactory和Mapper的创建交给spring的工厂。
配置SqlSessionFactoryBean 此类实现了FactoryBean接口,意味着通过它创建的对象实际上是SqlSessionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" > <property name="dataSource" ref="dataSource" /> </bean> ----------------java config @Configuration public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory () throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean (); factoryBean.setDataSource(dataSource()); return factoryBean.getObject(); } }
创建mapper接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface UserMapper { @Select("SELECT * FROM users WHERE id = #{userId}") User getUser (@Param("userId") String userId) ; } <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean" > <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> -------java config @Configuration public class MyBatisConfig { @Bean public UserMapper userMapper () throws Exception { SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate (sqlSessionFactory()); return sqlSessionTemplate.getMapper(UserMapper.class); } }
注入maoper操作数据库
1 2 3 4 5 6 7 8 9 10 11 12 public class FooServiceImpl implements FooService { private final UserMapper userMapper; public FooServiceImpl (UserMapper userMapper) { this .userMapper = userMapper; } public User doSomeBusinessStuff (String userId) { return this .userMapper.getUser(userId); } }
官方文档
4.springboot自动配置mybatis 依赖于springboot的自动配置原理,启动时会扫描META-INF/spring.factories下的文件,反射获取类信息,并完成自动配置。 打开mybatis自动配置jar包下的文件内容如下:
1 2 3 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
找到自动配置的类MybatisAutoConfiguration,
1 2 3 4 5 6 7 8 @org.springframework.context.annotation.Configuration @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) @ConditionalOnBean(DataSource.class) @EnableConfigurationProperties(MybatisProperties.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class) public class MybatisAutoConfiguration { ... }
通过注解可以看到配置的条件,以及MybatisProperties
配置文件的信息,之前需要手动创建的sqlSessionFactory、sqlSessionTemplate都已经帮我们做好了配置,修改配置信息只需要修改配置文件即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { ... } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } }
完成了sqlSessionFactory的创建,那么Mapper的接口是如何创建的呢?
1 2 3 4 5 6 7 8 9 10 @org.springframework.context.annotation.Configuration @Import({ AutoConfiguredMapperScannerRegistrar.class }) @ConditionalOnMissingBean(MapperFactoryBean.class) public static class MapperScannerRegistrarNotFoundConfiguration { @PostConstruct public void afterPropertiesSet() { logger.debug("No {} found.", MapperFactoryBean.class.getName()); } }
可见这也是个配置类,spring工厂中没有这个MapperFactoryBean实例时,会自动导入AutoConfiguredMapperScannerRegistrar进行自动配置,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware { private BeanFactory beanFactory; private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); try { if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { for (String pkg : packages) { logger.debug("Using auto-configuration base package '{}'", pkg); } } scanner.setAnnotationClass(Mapper.class); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(packages)); } catch (IllegalStateException ex) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex); } }
可见它会扫描BasePackages下@Mapper注解的类实现