Mapper 扫描须要依赖 Maybtis/Spring 这个我的项目,官网文档:
https://mybatis.org/spring/zh/index.html
Mapper 扫描依赖两种形式:
- 通过 @Mapper 注解 (想通过该注解实现扫描 Mapper ,须要依赖 mybatis/spring-boot-starter 这个我的项目)
- 通过 @MapperScan 注解
无论是 @Mapper 还是 @MapeprScan 注解,底层都是须要去注册一个 MapperScannerConfigurer 的 Bean , 而后通过该 Bean 来实现 Mapper 的被动注入。
@Mapper 注解
@Mapper 注解次要应用在 Mapper 接口上,如果要实现只对 @Mapper 注解的接口实现扫描,则须要引入 mybatis/spring-boot-starter 这个我的项目。
mybatis/spring-boot-starter 应用办法请参考:https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/site/zh/markdown/index.mdAutoConfiguredMapperScannerRegistrar
则是主动扫描 @Mapper 注解接口的实现类。
然而该类启用的条件,必须是 Spring 没有找到 MapperScannerConfigurer
和 MapperFactoryBean
的 bean 。
源码剖析
AutoConfiguredMapperScannerRegistrar
是 MybatisAutoConfiguration
的动态外部类,故这里展现的是 MybatisAutoConfiguration
的源码。
/*** MybatisAutoConfiguration 是一个配置类 * 启用该配置类须要我的项目存在 SqlSessionFactory 和 SqlSessionFactoryBean 这两个类(这两个类都在 mybatis/spring 我的项目中)* mybatis/spring-boot-starter 会主动引入 mybatis/spring*/@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })@ConditionalOnSingleCandidate(DataSource.class)@EnableConfigurationProperties(MybatisProperties.class)@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })public class MybatisAutoConfiguration implements InitializingBean { // 疏忽局部源码,这里只展现要探讨的局部 /** * 对 @Mapper 注解主动扫描的实现类 * 它继承了 ImportBeanDefinitionRegistrar 接口,实现了 registerBeanDefinitions() 办法 * registerBeanDefinitions() 办法次要是创立并注册 MapperScannerConfigurer 的 BeanDefinition */ public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 如果没有配置主动扫描的包门路,那么则终止扫描 Mapper if (!AutoConfigurationPackages.has(this.beanFactory)) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); return; } logger.debug("Searching for mappers annotated with @Mapper"); // 获取主动扫描的门路 List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); } // 创立一个 MapperScannerConfigurer 的 BeanDefinitionBuilder // 并且设置 MapperScannerConfigurer 的字段,如 processPropertyPlaceHolders、annotationClass、basePackage 等字段 // 后续 MapperScannerConfigurer 就会通过这些字段信息去扫描包 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); // annotationClass 示意扫描的包中必须要有指定的注解,这里指定要有 Mapper 这个注解 builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName) .collect(Collectors.toSet()); if (propertyNames.contains("lazyInitialization")) { // Need to mybatis-spring 2.0.2+ builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); } if (propertyNames.contains("defaultScope")) { // Need to mybatis-spring 2.0.6+ builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); } // 默认设置 sqlSessionTemplateBeanName 的值为 sqlSessionTemplate boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, Boolean.TRUE); if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; Optional<String> sqlSessionTemplateBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory)); Optional<String> sqlSessionFactoryBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory)); if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) { builder.addPropertyValue("sqlSessionTemplateBeanName", sqlSessionTemplateBeanName.orElse("sqlSessionTemplate")); } else { builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get()); } } builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册 MapperScannerConfigurer 的 BeanDefinition registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } // 疏忽前面的代码 } /** * 该类应用了 @Configuration 注解,故在调用 AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions() 前,先通过这个类的逻辑: * 如果是曾经存在了 MapperFactoryBean 或者是 MapperScannerConfigurer 的 Bean * 那么就不会加载 AutoConfiguredMapperScannerRegistrar 的 Bean 了,因而也不会去创立 MapperScannerConfigurer 的 BeanDefinition,做后续的 Mapper 扫描了。 * 其中 MapperScannerConfigurer 的 Bean 能够通过应用 @MapperScan 注解来创立(故如果应用了 @MapperScan 注解,就不会主动扫描 @Mapper 注解的 Mapper) * 或者咱们自定义了一个 Bean ,返回的是 MapperFactoryBean ,那么也不会主动扫描 @Mapper 注解的 Mapper */ @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) @Import(AutoConfiguredMapperScannerRegistrar.class) @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { @Override public void afterPropertiesSet() { logger.debug( "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } }}
@MapperScan 注解
@MapperScan 注解是 mybatis 主动扫描的罕用形式。
它通过应用了 @Import
注解,导入了 MapperScannerRegistrar
类,并且后续通过 MapperScannerRegistrar
来做 Mapper 扫描的逻辑。MapperScan
的源码如下:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)@Repeatable(MapperScans.class)public @interface MapperScan { // 疏忽 MapperScan 注解的成员信息}
MapperScannerRegistrar
MapperScannerRegistrar
与扫描 @Mapper
注解的 AutoConfiguredMapperScannerRegistrar
一样,也是继承了 ImportBeanDefinitionRegistrar
接口,重写了 registerBeanDefinitions
办法。
另外 MapperScannerRegistrar
外部保护了 RepeatingRegistrar
动态类,该类继承了 MapperScannerRegistrar
,性能与 MapperScannerRegistrar
统一,然而给 @MapperScans
注解应用。
故这里只探讨 MapperScannerRegistrar
的源码。
源码剖析
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { /** * 实现了 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions() 办法 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 通过 AnnotationAttributes.fromMap() 办法,获取 @MapperScan 注解的成员变量的值 // 如果没有应用到 @MapperScan 注解,那么 AnnotationAttributes.fromMap() 办法就会返回 null // 说白了就是判断有没有用到 @MapperScan 注解,如果没有用到,就不会主动扫描 Mapper AnnotationAttributes mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { // 调用上面的办法,去创立 MapperScannerConfigurer 的 BeanDefinition registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0)); } } /** * 创立并注册 MapperScannerConfigurer 的 BeanDefinition * 在创立过程中把 @MapperScan 注解的成员变量 * 都增加到 MapperScannerConfigurer 的 BeanDefinition 的 PropertyValues 上 */ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) { // 先创立 MapperScannerConfigurer 的 BeanDefinition BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); // 把 MapperScan 注解的成员变量,都增加到 BeanDefinition 的 PropertyValues 上 builder.addPropertyValue("processPropertyPlaceHolders", true); Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { builder.addPropertyValue("annotationClass", annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { builder.addPropertyValue("markerInterface", markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef")); } String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef")); } List<String> basePackages = new ArrayList<>(); basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText) .collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName) .collect(Collectors.toList())); if (basePackages.isEmpty()) { basePackages.add(getDefaultBasePackage(annoMeta)); } String lazyInitialization = annoAttrs.getString("lazyInitialization"); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization", lazyInitialization); } String defaultScope = annoAttrs.getString("defaultScope"); if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) { builder.addPropertyValue("defaultScope", defaultScope); } builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages)); // for spring-native builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 最初注册 BeanDefinition 到 BeanDefinitionRegistry,给后续 Bean 的创立应用 registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); } private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) { return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index; } private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) { return ClassUtils.getPackageName(importingClassMetadata.getClassName()); }}
MapperScannerConfigurer
MapperScannerConfigurer 是 Mapper 主动注入的外围,它实现了以下这些接口:
BeanDefinitionRegistryPostProcessor
InitializingBean
ApplicationContextAware
BeanNameAware
其中,主动注入 Mapper 的逻辑,次要是实现了 BeanDefinitionRegistryPostProcessor#registerBeanDefinitions()
办法。
源码如下:
// MapperScannerConfigurer 的一些成员变量,用于给扫描 Mapper 在生成 BeanDefinition 时应用private String basePackage;private boolean addToConfig = true;private String lazyInitialization;private SqlSessionFactory sqlSessionFactory;private SqlSessionTemplate sqlSessionTemplate;private String sqlSessionFactoryBeanName;private String sqlSessionTemplateBeanName;private Class<? extends Annotation> annotationClass;private Class<?> markerInterface;private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;private ApplicationContext applicationContext;private String beanName;private boolean processPropertyPlaceHolders;private BeanNameGenerator nameGenerator;private String defaultScope;/*** 先去创立 ClassPathMapperScanner , 而后调用 ClassPathMapperScanner#scan() 办法去扫描 Mapper */@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { 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(lazyInitialization)) { scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); } scanner.registerFilters(); scanner.scan( StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}
从下面的源码中看,实际上是去创立 ClassPathMapperScanner
的类,而后调用 scan()
办法来创立 Mapper。
ClassPathMapperScanner#scan()
ClassPathMapperScanner
是 ClassPathBeanDefinitionScanner
的子类,而下面提到的 scan()
办法理论是 ClassPathBeanDefinitionScanner
的。
该办法次要是扫描指定包门路下的 Mapper ,而后转换成 BeanDefinition,这些 BeanDefintion 携带的 beanClass 信息是 MapperFactoryBean 。
并在后续的 Spring Bean 创立流程中,把这些 BeanDefinition 转换成对应的 Bean(本文不探讨 Spring Bean 初始化流程)。
源码如下:
/*** ClassPathBeanDefinitionScanner#scan() 办法*/public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // 因为 ClassPathMapperScanner 重写了 doScan() 办法 // 故这里实际上是调用 ClassPathMapperScanner#doScan() 办法 doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);}/*** ClassPathMapperScanner#doScan() 办法*/public Set<BeanDefinitionHolder> doScan(String... basePackages) { // 调用 ClassPathBeanDefinitionScanner#doScan() 办法 ,扫描 Mapper Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { if (printWarnLogIfNotFoundMappers) { LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } } else { // 把扫描失去的 BeanDefinitionHolder 列表元素中的 BeanDefinition 的 beanClass 属性设置成 MapperFactoryBean 类型 processBeanDefinitions(beanDefinitions); } return beanDefinitions;}/*** ClassPathBeanDefinitionScanner#doScan() 办法* 这里实际上是扫描指定包门路下的 Mapper , 并将它们封装成 BeanDefinitionHolder 列表返回 */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) { // 从指定包目录下扫描 Mapper Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 对 BeanDeifintion 设置默认值 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 判断这个 BeanDefinition 是否存在,失常来说是不存在的,如果存在的话,那就证实这个 Mapper 的名字重名或者其余起因。 // 如果 BeanDefinition 不存在,则增加这个 BeanDefinition 到 BeanDefinitionRegistry 中去 // 并增加到要返回的 BeanDefinition 列表上,给后续转换 MapperFactoryBean 应用 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions;}/*** ClassPathMapperScanner#processBeanDefinitions()* 该办法就是将扫描到的 Mapper 的 BeanDefinitionHolder 的信息进行批改* 其中蕴含了将 BeanDefinitionHolder 的 BeanDefinition 的 beanClass 信息批改成 MapperFactoryBean 的逻辑* 因而前面通过 BeanDefinition 所创立进去的 Bean,实际上是 MapperFactoryBean 类型*/private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { AbstractBeanDefinition definition; BeanDefinitionRegistry registry = getRegistry(); for (BeanDefinitionHolder holder : beanDefinitions) { definition = (AbstractBeanDefinition) holder.getBeanDefinition(); boolean scopedProxy = false; // 如果这个 BeanDefinition 是一个代理类,那么就去拿它代理的 BeanDefinition if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition) Optional .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()) .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException( "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]")); scopedProxy = true; } String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); // 将 Mapper 全限定类名增加到构造方法的参数进去 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 try { // 尝试将 Mapper 全限定类名增加到 BeanDefinition 的 PropertyValues 的 map 中去。 definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName)); } catch (ClassNotFoundException ignore) { // ignore } // 批改 BeanClass 的信息,把 Mapper 的 class 替换成 MapperFactoryBean definition.setBeanClass(this.mapperFactoryBeanClass); // 上面的代码,都是将 ClassPathMapperScanner 的成员变量的信息,都增加到 BeanDifinition 的 PropertyValues 中去。 definition.getPropertyValues().add("addToConfig", this.addToConfig); // Attribute for MockitoPostProcessor // https://github.com/mybatis/spring-boot-starter/issues/475 definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization); if (scopedProxy) { continue; } if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) { definition.setScope(defaultScope); } // 对非单例类型的 Mapper 做的逻辑 if (!definition.isSingleton()) { BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition()); } }}
MapperFactoryBean
MapperFactoryBean 是 Mybatis/Spring 用来示意 Mapper 的 Bean ,它继承了 SqlSessionDaoSupport
,实现了 FactoryBean
接口。
所以在获取 MapperFactoryBean 的 Bean 时,实际上是调用了 MapperFactoryBean#getObject()
办法,返回 MapperProxy
的代理类。
对于 MapperFactoryBean
,后续再独自探讨。