在SpringBoot中针对MyBatis中的配置如下
# mybatis配置
mybatis.mapper-locations=classpath*:sqlMapper/**/*.xml
mybatis.type-aliases-package=com.example.durid.demo.entity
那么在对应类org.mybatis.spring.boot.autoconfigure.MybatisProperties中获取配置参数的地方打上断点,不如如下位置:
public String[] getMapperLocations() {
return this.mapperLocations;
}
public void setMapperLocations(String[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
以debug模式启动Spring Boot程序:
拷贝出来的调用栈信息如下:
setMapperLocations:118, MybatisProperties (org.mybatis.spring.boot.autoconfigure)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:346, JavaBeanBinder$BeanProperty (org.springframework.boot.context.properties.bind)
bind:96, JavaBeanBinder (org.springframework.boot.context.properties.bind)
bind:79, JavaBeanBinder (org.springframework.boot.context.properties.bind)
bind:56, JavaBeanBinder (org.springframework.boot.context.properties.bind)
lambda$bindDataObject$5:452, Binder (org.springframework.boot.context.properties.bind)
get:-1, 1371495133 (org.springframework.boot.context.properties.bind.Binder$$Lambda$100)
withIncreasedDepth:572, Binder$Context (org.springframework.boot.context.properties.bind)
withDataObject:558, Binder$Context (org.springframework.boot.context.properties.bind)
access$400:513, Binder$Context (org.springframework.boot.context.properties.bind)
bindDataObject:450, Binder (org.springframework.boot.context.properties.bind)
bindObject:391, Binder (org.springframework.boot.context.properties.bind)
bind:320, Binder (org.springframework.boot.context.properties.bind)
bind:308, Binder (org.springframework.boot.context.properties.bind)
bind:238, Binder (org.springframework.boot.context.properties.bind)
bind:225, Binder (org.springframework.boot.context.properties.bind)
bind:89, ConfigurationPropertiesBinder (org.springframework.boot.context.properties)
bind:107, ConfigurationPropertiesBindingPostProcessor (org.springframework.boot.context.properties)
postProcessBeforeInitialization:96, ConfigurationPropertiesBindingPostProcessor (org.springframework.boot.context.properties)
applyBeanPostProcessorsBeforeInitialization:416, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
initializeBean:1788, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:595, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveCandidate:276, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1287, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1207, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveAutowiredArgument:885, ConstructorResolver (org.springframework.beans.factory.support)
createArgumentArray:789, ConstructorResolver (org.springframework.beans.factory.support)
autowireConstructor:228, ConstructorResolver (org.springframework.beans.factory.support)
autowireConstructor:1358, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1204, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:557, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:409, ConstructorResolver (org.springframework.beans.factory.support)
instantiateUsingFactoryMethod:1338, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1177, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:557, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveCandidate:276, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1287, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1207, DefaultListableBeanFactory (org.springframework.beans.factory.support)
autowireByType:1511, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean:1406, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:594, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveCandidate:276, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1287, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1207, DefaultListableBeanFactory (org.springframework.beans.factory.support)
inject:640, AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement (org.springframework.beans.factory.annotation)
inject:130, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:399, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1422, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:594, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveCandidate:276, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1287, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1207, DefaultListableBeanFactory (org.springframework.beans.factory.support)
inject:640, AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement (org.springframework.beans.factory.annotation)
inject:130, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:399, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1422, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:594, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:517, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:323, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 697508322 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$154)
getSingleton:222, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:321, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:879, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:878, AbstractApplicationContext (org.springframework.context.support)
refresh:550, AbstractApplicationContext (org.springframework.context.support)
refresh:141, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:747, SpringApplication (org.springframework.boot)
refreshContext:397, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
run:1215, SpringApplication (org.springframework.boot)
main:10, DemoApplication (com.example.durid.demo)
从调用栈底部开始分析 会发现这个配置真正开始读取是在完成Bean工厂实例化的时候
获取一个名称为userController的bean
查看一下UserController的定义
package com.example.durid.demo.controller;
import com.example.durid.demo.entity.User;
import com.example.durid.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// http://localhost:8083/user/list
@RequestMapping("/list")
public List<User> list() {
return userService.list();
}
// http://localhost:8083/user/get/2
@RequestMapping("/get/{id}")
public User get(@PathVariable("id") Integer id) {
return userService.findById(id);
}
// http://localhost:8083/user/delete/2
@PostMapping("/delete/{id}")
public boolean delete(@PathVariable("id") Integer id) {
return userService.deleteById(id);
}
// http://localhost:8083/user/add
@PostMapping("/add")
public boolean add(@RequestBody User user) {
return userService.add(user);
}
}
UserController依赖UserService类型的Bean,猜测后面应该会引起UserService类型Bean的初始化
再分析一下UserService类
package com.example.durid.demo.service.impl;
import com.example.durid.demo.entity.User;
import com.example.durid.demo.mapper.UserMapper;
import com.example.durid.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public boolean add(User user) {
return userMapper.insert(user) == 1;
}
@Override
public List<User> list() {
return userMapper.selectAll();
}
@Override
public boolean deleteById(Integer id) {
return userMapper.deleteByPrimaryKey(id) == 1;
}
@Override
public User findById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
}
UserService依赖于UserMapper类型的Bean,因此后面会导致UserMapper类型Bean的初始化
而UserMapper是一个接口,通过动态代理生成一个MapperFactoryBean类型的实现,这个逻辑属于MyBatis的范畴,此处略去不表。
分析一下MapperFactoryBean类的定义
/**
* Copyright 2010-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.spring.mapper;
import static org.springframework.util.Assert.notNull;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.FactoryBean;
/**
* BeanFactory that enables injection of MyBatis mapper interfaces. It can be set up with a SqlSessionFactory or a
* pre-configured SqlSessionTemplate.
* <p>
* Sample configuration:
*
* <pre class="code">
* {@code
* <bean id="baseMapper" class="org.mybatis.spring.mapper.MapperFactoryBean" abstract="true" lazy-init="true">
* <property name="sqlSessionFactory" ref="sqlSessionFactory" />
* </bean>
*
* <bean id="oneMapper" parent="baseMapper">
* <property name="mapperInterface" value="my.package.MyMapperInterface" />
* </bean>
*
* <bean id="anotherMapper" parent="baseMapper">
* <property name="mapperInterface" value="my.package.MyAnotherMapperInterface" />
* </bean>
* }
* </pre>
* <p>
* Note that this factory can only inject <em>interfaces</em>, not concrete classes.
*
* @author Eduardo Macarron
*
* @see SqlSessionTemplate
*/
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
// intentionally empty
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* {@inheritDoc}
*/
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
/**
* {@inheritDoc}
*/
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
}
// ------------- mutators --------------
/**
* Sets the mapper interface of the MyBatis mapper
*
* @param mapperInterface
* class of the interface
*/
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* Return the mapper interface of the MyBatis mapper
*
* @return class of the interface
*/
public Class<T> getMapperInterface() {
return mapperInterface;
}
/**
* If addToConfig is false the mapper will not be added to MyBatis. This means it must have been included in
* mybatis-config.xml.
* <p>
* If it is true, the mapper will be added to MyBatis in the case it is not already registered.
* <p>
* By default addToConfig is true.
*
* @param addToConfig
* a flag that whether add mapper to MyBatis or not
*/
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
}
/**
* Return the flag for addition into MyBatis config.
*
* @return true if the mapper will be added to MyBatis in the case it is not already registered.
*/
public boolean isAddToConfig() {
return addToConfig;
}
}
该类又继承了SqlSessionDaoSupport
/**
* Copyright 2010-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.spring.support;
import static org.springframework.util.Assert.notNull;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.support.DaoSupport;
/**
* Convenient super class for MyBatis SqlSession data access objects. It gives you access to the template which can then
* be used to execute SQL methods.
* <p>
* This class needs a SqlSessionTemplate or a SqlSessionFactory. If both are set the SqlSessionFactory will be ignored.
* <p>
*
* @author Putthiphong Boonphong
* @author Eduardo Macarron
*
* @see #setSqlSessionFactory
* @see #setSqlSessionTemplate
* @see SqlSessionTemplate
*/
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
/**
* Set MyBatis SqlSessionFactory to be used by this DAO. Will automatically create SqlSessionTemplate for the given
* SqlSessionFactory.
*
* @param sqlSessionFactory
* a factory of SqlSession
*/
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
/**
* Create a SqlSessionTemplate for the given SqlSessionFactory. Only invoked if populating the DAO with a
* SqlSessionFactory reference!
* <p>
* Can be overridden in subclasses to provide a SqlSessionTemplate instance with different configuration, or a custom
* SqlSessionTemplate subclass.
*
* @param sqlSessionFactory
* the MyBatis SqlSessionFactory to create a SqlSessionTemplate for
* @return the new SqlSessionTemplate instance
* @see #setSqlSessionFactory
*/
@SuppressWarnings("WeakerAccess")
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* Return the MyBatis SqlSessionFactory used by this DAO.
*
* @return a factory of SqlSession
*/
public final SqlSessionFactory getSqlSessionFactory() {
return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
}
/**
* Set the SqlSessionTemplate for this DAO explicitly, as an alternative to specifying a SqlSessionFactory.
*
* @param sqlSessionTemplate
* a template of SqlSession
* @see #setSqlSessionFactory
*/
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
/**
* Users should use this method to get a SqlSession to call its statement methods This is SqlSession is managed by
* spring. Users should not commit/rollback/close it because it will be automatically done.
*
* @return Spring managed thread safe SqlSession
*/
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
/**
* Return the SqlSessionTemplate for this DAO, pre-initialized with the SessionFactory or set explicitly.
* <p>
* <b>Note: The returned SqlSessionTemplate is a shared instance.</b> You may introspect its configuration, but not
* modify the configuration (other than from within an {@link #initDao} implementation). Consider creating a custom
* SqlSessionTemplate instance via {@code new SqlSessionTemplate(getSqlSessionFactory())}, in which case you're
* allowed to customize the settings on the resulting instance.
*
* @return a template of SqlSession
*/
public SqlSessionTemplate getSqlSessionTemplate() {
return this.sqlSessionTemplate;
}
/**
* {@inheritDoc}
*/
@Override
protected void checkDaoConfig() {
notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
}
该类有setSqlSessionFactory和setSqlSessionTemplate,会触发SqlSessionFactory类型Bean的注入
然后开始SqlSessionFactory类型Bean的解析
-- 查看sqlSessionFactory的mbd定义信息:
Root bean: class [null]; scope=singleton; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration; factoryMethodName=sqlSessionFactory; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]
从mbd信息可知:该类的定义在MybatisAutoConfiguration类中的sqlSessionFactory方法中:
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
return factory.getObject();
}
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean
查看该类的构造方法
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}
此时会触发MybatisProperties类型Bean的初始化化操作
MybatisProperties类有默认构造函数,而且不依赖其他类型的Bean,但此处在初始化这种类型Bean的时候是需要注入配置参数到Bean属性中的,而这个操作是由ConfigurationPropertiesBindingPostProcessor来进行的。
ConfigurationPropertiesBindingPostProcessor进行参数的注入操作
查看该类的方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
添加一个条件断点,再次debug
在进行配置类后置处理之前会通过ConfigurationPropertiesBean包装一下
查看该类的注释:
Provides access to @ConfigurationProperties
bean details, regardless of if the annotation was used directly or on a @Bean
factory method. This class can be used to access all
configuration properties beans in an ApplicationContext, or individual beans
on a case-by-case basis (for example, in a BeanPostProcessor
).
提供对@ConfigurationProperties Bean详细信息的访问,无论注释是直接使用还是在@Bean工厂方法上使用。 可以使用此类访问ApplicationContext中的所有配置属性Bean,也可以视情况访问单个Bean(例如,在BeanPostProcessor中)。
其中提供了获取所有配置属性Bean的方法如下:
public static Map<String, ConfigurationPropertiesBean> getAll(ApplicationContext applicationContext) {
Assert.notNull(applicationContext, "ApplicationContext must not be null");
if (applicationContext instanceof ConfigurableApplicationContext) {
return getAll((ConfigurableApplicationContext) applicationContext);
}
Map<String, ConfigurationPropertiesBean> propertiesBeans = new LinkedHashMap<>();
applicationContext.getBeansWithAnnotation(ConfigurationProperties.class)
.forEach((beanName, bean) -> propertiesBeans.put(beanName, get(applicationContext, bean, beanName)));
return propertiesBeans;
}
获取单个Bean
public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) {
Method factoryMethod = findFactoryMethod(applicationContext, beanName);
return create(beanName, bean, bean.getClass(), factoryMethod);
}
此处是通过第二个方法获取,该方法注解为:
Return a @ConfigurationPropertiesBean
instance for the given bean details or null
if the bean is not a @ConfigurationProperties
object. Annotations are considered both on the bean itself, as well as any factory method (for example a @Bean
method).
返回给定bean详细信息的@ConfigurationPropertiesBean实例;如果该bean不是@ConfigurationProperties对象,则返回null。 注释在bean本身以及任何工厂方法(例如@Bean方法)上均被考虑。
此处也会考虑加在工厂方法上的@ConfigurationPropertie注解
但是当前类中不存在工厂方法
通过传入的参数创建对象
private static ConfigurationPropertiesBean create(String name, Object instance, Class<?> type, Method factory) {
// 查找实例或工厂方法上是否存在ConfigurationProperties注解
ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class);
if (annotation == null) {
return null;
}
// 查找Validated注解 此处会返回null
Validated validated = findAnnotation(instance, type, factory, Validated.class);
Annotation[] annotations = (validated != null) ? new Annotation[] { annotation, validated }: new Annotation[] { annotation };
// 解析ResolvableType信息
ResolvableType bindType = (factory != null) ? ResolvableType.forMethodReturnType(factory): ResolvableType.forClass(type);
// 解析bindTarget信息
Bindable<Object> bindTarget = Bindable.of(bindType).withAnnotations(annotations);
if (instance != null) {
bindTarget = bindTarget.withExistingValue(instance);
}
// 构造一个ConfigurationPropertiesBean对象
return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
}
返回ConfigurationPropertiesBindingPostProcessor类中进行bind操作
private void bind(ConfigurationPropertiesBean bean) {
if (bean == null || hasBoundValueObject(bean.getName())) {
return;
}
Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"
+ bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
try {
this.binder.bind(bean);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(bean, ex);
}
}
然后通过ConfigurationPropertiesBinder进行值的绑定
private void bind(ConfigurationPropertiesBean bean) {
if (bean == null || hasBoundValueObject(bean.getName())) {
return;
}
Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"+ bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
try {
this.binder.bind(bean);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(bean, ex);
}
}
ConfigurationPropertiesBinder类注释如下:
Internal class used by the ConfigurationPropertiesBindingPostProcessor to handle the actual @ConfigurationProperties binding.
ConfigurationPropertiesBindingPostProcessor使用的内部类来处理实际的@ConfigurationProperties绑定。
BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
// 获取bindTarget对象
Bindable<?> target = propertiesBean.asBindTarget();
// 获取注解
ConfigurationProperties annotation = propertiesBean.getAnnotation();
// 根据目标类型和注解获取一个BindHandler处理器 Callback interface that can be used to // handle additional logic during element binding.
BindHandler bindHandler = getBindHandler(target, annotation);
return getBinder().bind(annotation.prefix(), target, bindHandler);
}
target=[Bindable@1e4c6583 type = org.mybatis.spring.boot.autoconfigure.MybatisProperties, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(ignoreInvalidFields=false, ignoreUnknownFields=true, prefix=mybatis, value=mybatis)]]
annotation=@org.springframework.boot.context.properties.ConfigurationProperties(ignoreInvalidFields=false, ignoreUnknownFields=true, prefix=mybatis, value=mybatis)
bindHandler=
private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperties annotation) {
List<Validator> validators = getValidators(target);
BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler();
if (annotation.ignoreInvalidFields()) {
handler = new IgnoreErrorsBindHandler(handler);
}
if (!annotation.ignoreUnknownFields()) {
UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter();
handler = new NoUnboundElementsBindHandler(handler, filter);
}
if (!validators.isEmpty()) {
handler = new ValidationBindHandler(handler, validators.toArray(new Validator[0]));
}
// 此处没有配置ConfigurationPropertiesBindHandlerAdvisor 此处不会进行处理
for (ConfigurationPropertiesBindHandlerAdvisor advisor : getBindHandlerAdvisors()) {
handler = advisor.apply(handler);
}
return handler;
}
private List<ConfigurationPropertiesBindHandlerAdvisor> getBindHandlerAdvisors() {
return this.applicationContext.getBeanProvider(ConfigurationPropertiesBindHandlerAdvisor.class).orderedStream().collect(Collectors.toList());
}