s(struts)s(spring)h(hibernate)—》ssh servlet

s(struts2) s(spring) h(hibernate(jpa))—>ssh2 fiter

MyBatis-plus 源码解析(hibernate,jpa hql)

前言
MyBatis-plus是完全基于MyBatis开发的一个增强工具,是在MyBatis的基础上做增强的框架,为简化开发、提高效率而生。它在MyBatis原本的框架上增加了很多实用性功能,比如乐观锁插件、字段自动填充功能、分页插件、条件构造器、sql 注入器等等。使用 MyBatis-plus 可以完全不写任何 XML 文件,直接使用继承了BaseMapper 接口的类对象完成对数据库的映射操作

基于映射的原理,MyBatis-plus 必然要实现 Mapper中的方法与 SQL 语句的对应转化,以下即为 MyBatis-plus 重要流程图例
image.png
1. Mapper 对象方法映射为 SQL 语句
在 MyBatis-plus 中, MybatisPlusAutoConfiguration 自动配置类的 sqlSessionFactory()方法为 Spring提供创建 sqlSession的工厂类对象,对 sqlSessionFactory 进行定义的定义类变为了 MybatisSqlSessionFactoryBean。在 sqlSessionFactory()方法中,除了注入 MyBatis本身的组件,还会注入MyBatis-plus 的 主键生成器、SQL 注入器等组件,最后通过 MybatisSqlSessionFactoryBean#getObject() 方法获取到 sqlSessionFactory 对象

  1. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  2. // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
  3. MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
  4. factory.setDataSource(dataSource);
  5. factory.setVfs(SpringBootVFS.class);
  6. if (StringUtils.hasText(this.properties.getConfigLocation())) {
  7. factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  8. }
  9. applyConfiguration(factory);
  10. if (this.properties.getConfigurationProperties() != null) {
  11. factory.setConfigurationProperties(this.properties.getConfigurationProperties());
  12. }
  13. ......
  14. // TODO 自定义枚举包
  15. if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
  16. factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
  17. }
  18. // TODO 此处必为非 NULL
  19. GlobalConfig globalConfig = this.properties.getGlobalConfig();
  20. // TODO 注入填充器
  21. if (this.applicationContext.getBeanNamesForType(MetaObjectHandler.class,
  22. false, false).length > 0) {
  23. MetaObjectHandler metaObjectHandler = this.applicationContext.getBean(MetaObjectHandler.class);
  24. globalConfig.setMetaObjectHandler(metaObjectHandler);
  25. }
  26. // TODO 注入主键生成器
  27. if (this.applicationContext.getBeanNamesForType(IKeyGenerator.class, false,
  28. false).length > 0) {
  29. IKeyGenerator keyGenerator = this.applicationContext.getBean(IKeyGenerator.class);
  30. globalConfig.getDbConfig().setKeyGenerator(keyGenerator);
  31. }
  32. // TODO 注入sql注入器
  33. if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false,
  34. false).length > 0) {
  35. ISqlInjector iSqlInjector = this.applicationContext.getBean(ISqlInjector.class);
  36. globalConfig.setSqlInjector(iSqlInjector);
  37. }
  38. // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
  39. factory.setGlobalConfig(globalConfig);
  40. return factory.getObject();
  41. }

2.MybatisSqlSessionFactoryBean#getObject() 执行懒加载策略,最后通过 buildSqlSessionFactory() 方法创建 SqlSessionFactory 工厂类对象。这个方法的流程很长,不过大致可以分为两个步骤:

1.创建 MybatisXMLConfigBuilder 对象,调用其 parse() 方法去解析 XML 配置文件及 Mapper
2.解析获得的信息存储在 targetConfiguration 对象中,根据其信息创建 SqlSessionFactory 对象
[

](https://blog.csdn.net/weixin_45505313/article/details/104855453)

  1. protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
  2. final MybatisConfiguration targetConfiguration;
  3. // TODO 使用 MybatisXmlConfigBuilder 而不是 XMLConfigBuilder
  4. MybatisXMLConfigBuilder xmlConfigBuilder = null;
  5. ......
  6. } else if (this.configLocation != null) {
  7. // TODO 使用 MybatisXMLConfigBuilder
  8. // 1.1 创建 MybatisConfiguration 对象
  9. xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  10. // 2.1 将解析获得的信息的引用传递给 targetConfiguration 对象
  11. targetConfiguration = xmlConfigBuilder.getConfiguration();
  12. } else {
  13. LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
  14. // TODO 使用 MybatisConfiguration
  15. targetConfiguration = new MybatisConfiguration();
  16. Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
  17. }
  18. // TODO 无配置启动所必须的
  19. this.globalConfig = Optional.ofNullable(this.globalConfig).orElseGet(GlobalConfigUtils::defaults);
  20. this.globalConfig.setDbConfig(Optional.ofNullable(this.globalConfig.getDbConfig()).orElseGet(GlobalConfig.DbConfig::new));
  21. // TODO 初始化 id-work 以及 打印骚东西
  22. targetConfiguration.setGlobalConfig(this.globalConfig);
  23. ......
  24. // 1.2 开始解析 XML 配置文件 及 Mapper 接口
  25. if (xmlConfigBuilder != null) {
  26. try {
  27. xmlConfigBuilder.parse();
  28. LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
  29. } catch (Exception ex) {
  30. throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
  31. } finally {
  32. ErrorContext.instance().reset();
  33. }
  34. }
  35. targetConfiguration.setEnvironment(new Environment(MybatisSqlSessionFactoryBean.class.getSimpleName(),
  36. this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
  37. this.dataSource));
  38. if (this.mapperLocations != null) {
  39. if (this.mapperLocations.length == 0) {
  40. LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
  41. } else {
  42. for (Resource mapperLocation : this.mapperLocations) {
  43. if (mapperLocation == null) {
  44. continue;
  45. }
  46. try {
  47. XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
  48. targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
  49. xmlMapperBuilder.parse();
  50. } catch (Exception e) {
  51. throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
  52. } finally {
  53. ErrorContext.instance().reset();
  54. }
  55. LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
  56. }
  57. }
  58. } else {
  59. LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
  60. }
  61. // 2.2 根据 targetConfiguration 对象中保存的信息创建 SqlSessionFactory 对象
  62. final SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(targetConfiguration);
  63. ......
  64. return sqlSessionFactory;
  65. }

3.MybatisXMLConfigBuilder#parse() 会去解析配置文件,最后会调用到其内部方法 mapperElement()。这个方法完成解析 Mapper工作,并将其添加到配置类 MybatisConfiguration 中

  1. private void mapperElement(XNode parent) throws Exception {
  2. /*
  3. * 定义集合 用来分类放置mybatis的Mapper与XML 按顺序依次遍历
  4. */
  5. if (parent != null) {
  6. //指定在classpath中的mapper文件
  7. Set<String> resources = new HashSet<>();
  8. //指向一个mapper接口
  9. Set<Class<?>> mapperClasses = new HashSet<>();
  10. setResource(parent, resources, mapperClasses);
  11. // 依次遍历 首先 resource 然后 mapper
  12. for (String resource : resources) {
  13. ErrorContext.instance().resource(resource);
  14. InputStream inputStream = Resources.getResourceAsStream(resource);
  15. //TODO
  16. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
  17. configuration.getSqlFragments());
  18. mapperParser.parse();
  19. }
  20. for (Class<?> mapper : mapperClasses) {
  21. // 主要关注此处
  22. configuration.addMapper(mapper);
  23. }
  24. }
  25. }

4.MybatisConfiguration#addMapper()方法其实是去调用 MybatisMapperRegistry#addMapper() 方法,其核心是MybatisMapperAnnotationBuilder#parse()

  1. public <T> void addMapper(Class<T> type) {
  2. if (type.isInterface()) {
  3. if (hasMapper(type)) {
  4. // TODO 如果之前注入 直接返回
  5. return;
  6. // TODO 这里就不抛异常了
  7. // throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  8. }
  9. boolean loadCompleted = false;
  10. try {
  11. // TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
  12. knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
  13. // It's important that the type is added before the parser is run
  14. // otherwise the binding may automatically be attempted by the
  15. // mapper parser. If the type is already known, it won't try.
  16. // TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
  17. MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
  18. parser.parse();
  19. loadCompleted = true;
  20. } finally {
  21. if (!loadCompleted) {
  22. knownMappers.remove(type);
  23. }
  24. }
  25. }
  26. }

5 MybatisMapperAnnotationBuilder#parse() 方法真正开始完成 Mapper 接口中的方法与 SQL 语句的映射,其中 parseStatement()方法是解析 @Select/@Update 等注解写入的 SQL语句,而代码 GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type) 通过 MaBatis-plus的 SQL 注入器完成 Mapper 方法与 SQL 语句的转化

  1. @Override
  2. public void parse() {
  3. String resource = type.toString();
  4. if (!configuration.isResourceLoaded(resource)) {
  5. loadXmlResource();
  6. configuration.addLoadedResource(resource);
  7. final String typeName = type.getName();
  8. assistant.setCurrentNamespace(typeName);
  9. parseCache();
  10. parseCacheRef();
  11. SqlParserHelper.initSqlParserInfoCache(type);
  12. Method[] methods = type.getMethods();
  13. for (Method method : methods) {
  14. try {
  15. // issue #237
  16. if (!method.isBridge()) {
  17. // 解析 @Select 注解写入的 SQL
  18. parseStatement(method);
  19. SqlParserHelper.initSqlParserInfoCache(typeName, method);
  20. }
  21. } catch (IncompleteElementException e) {
  22. // TODO 使用 MybatisMethodResolver 而不是 MethodResolver
  23. configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));
  24. }
  25. }
  26. // TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sql
  27. if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
  28. GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
  29. }
  30. }
  31. parsePendingMethods();
  32. }

6.AbstractSqlInjector#inspectInject() 会完成 BaseMapper 接口中提供的通用方法对应的 SQL 语句准备,这部分主要通过 AbstractMethod#inject()方法完成

  1. @Override
  2. public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
  3. Class<?> modelClass = extractModelClass(mapperClass);
  4. if (modelClass != null) {
  5. String className = mapperClass.toString();
  6. Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
  7. if (!mapperRegistryCache.contains(className)) {
  8. List<AbstractMethod> methodList = this.getMethodList(mapperClass);
  9. if (CollectionUtils.isNotEmpty(methodList)) {
  10. TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
  11. // 循环注入自定义方法
  12. methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
  13. } else {
  14. logger.debug(mapperClass.toString() + ", No effective injection method was found.");
  15. }
  16. mapperRegistryCache.add(className);
  17. }
  18. }
  19. }

7、AbstractMethod#inject()方法并没有什么特别的操作,只是调用其子类实现 injectMappedStatement()方法。以 SelectOne#injectMappedStatement() 为例,其 SQL 语句的核心在于 SqlMethod 类,这个枚举类中缓存了可以动态拼接的 SQL 语句脚本,只需要填上参数 format 就可以得到 SQL 语句的执行脚本。以上过程结束,只需要将所有信息通过 addInsertMappedStatement()方法封装成 MappedStatement对象并将其加入到容器中,这样 Mapper接口方法调用时,就可以通过 动态代理 的方式找到其对应执行的 SQL 脚本,至此 SQL 语句准备及配置解析就完成了。最后拼接的 SQL 语句 脚本形式如下示例,实际执行数据库操作时会解析这个脚本完成变量替换,从而得到可执行的 SQL 语句

  1. <script>
  2. <choose>
  3. <when test="ew != null and ew.sqlFirst != null">
  4. ${ew.sqlFirst}
  5. </when>
  6. <otherwise></otherwise>
  7. </choose>
  8. SELECT
  9. <choose>
  10. <when test="ew != null and ew.sqlSelect != null">
  11. ${ew.sqlSelect}
  12. </when>
  13. <otherwise>id,name,type</otherwise>
  14. </choose>
  15. FROM node
  16. <if test="ew != null">
  17. <where>
  18. <if test="ew.entity != null">
  19. <if test="ew.entity.id != null">id=#{ew.entity.id}</if>
  20. <if test="ew.entity['name'] != null">AND name=#{ew.entity.name}</if>
  21. <if test="ew.entity['type'] != null">AND type=#{ew.entity.type}</if>
  22. </if>
  23. <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
  24. <if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal">AND</if>
  25. ${ew.sqlSegment}
  26. </if>
  27. </where>
  28. <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
  29. ${ew.sqlSegment}
  30. </if>
  31. </if>
  32. <choose>
  33. <when test="ew != null and ew.sqlComment != null">
  34. ${ew.sqlComment}
  35. </when>
  36. <otherwise></otherwise>
  37. </choose>
  38. </script>
  1. @Override
  2. public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
  3. SqlMethod sqlMethod = SqlMethod.SELECT_ONE;
  4. SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(),
  5. sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
  6. sqlWhereEntityWrapper(true, tableInfo), sqlComment()), modelClass);
  7. return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
  8. }

8、SqlSessionFactory对象的创建需要回到 MybatisSqlSessionFactoryBean#buildSqlSessionFactory()方法中,很容易追踪到 MybatisSqlSessionFactoryBuilder#build()方法,最后其实是通过 SqlSessionFactoryBuilder#build()方法创建了一个 DefaultSqlSessionFactory 对象返回

  1. public SqlSessionFactory build(Configuration config) {
  2. return new DefaultSqlSessionFactory(config);
  3. }

2. Mapper 操作数据库的流程

1、@MapperScan 注解通过 @Import(MapperScannerRegistrar.class) 引入扫描注册的类MapperScannerRegistrar,该类实现了ImportBeanDefinitionRegistrar接口并重写registerBeanDefinitions()方法,在该方法中注册了 MapperScannerConfigurer 类

  1. void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
  2. BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
  3. ......
  4. registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  5. }

2、MapperScannerConfigurer 是 Mapper接口的扫描配置类,实现了 BeanDefinitionRegistryPostProcessor 接口,其 postProcessBeanDefinitionRegistry()方法会在容器启动过程中被回调,通过 ClassPathMapperScanner#scan()方法完成 Mapper 的扫描注册

  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  2. if (this.processPropertyPlaceHolders) {
  3. processPropertyPlaceHolders();
  4. }
  5. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  6. scanner.setAddToConfig(this.addToConfig);
  7. scanner.setAnnotationClass(this.annotationClass);
  8. scanner.setMarkerInterface(this.markerInterface);
  9. scanner.setSqlSessionFactory(this.sqlSessionFactory);
  10. scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  11. scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  12. scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  13. scanner.setResourceLoader(this.applicationContext);
  14. scanner.setBeanNameGenerator(this.nameGenerator);
  15. scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  16. if (StringUtils.hasText(lazyInitialization)) {
  17. scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  18. }
  19. scanner.registerFilters();
  20. scanner.scan(
  21. StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  22. }

3、ClassPathMapperScanner#processBeanDefinitions() 将扫描到的 Mapper接口生成的对应 BeanDefinition 的 beanClass 属性替换为 MapperFactoryBean,这样每次获取 Mapper 实例实际是通过 MapperFactoryBean 的实例去获取
此处体现了 FactoryBean 的定位,即用于获取同一类 bean 的工厂 bean

  1. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  2. GenericBeanDefinition definition;
  3. for (BeanDefinitionHolder holder : beanDefinitions) {
  4. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  5. String beanClassName = definition.getBeanClassName();
  6. LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
  7. + "' mapperInterface");
  8. // the mapper interface is the original class of the bean
  9. // but, the actual class of the bean is MapperFactoryBean
  10. definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
  11. definition.setBeanClass(this.mapperFactoryBeanClass);
  12. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  13. boolean explicitFactoryUsed = false;
  14. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  15. definition.getPropertyValues().add("sqlSessionFactory",
  16. new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  17. explicitFactoryUsed = true;
  18. } else if (this.sqlSessionFactory != null) {
  19. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  20. explicitFactoryUsed = true;
  21. }
  22. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  23. if (explicitFactoryUsed) {
  24. LOGGER.warn(
  25. () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  26. }
  27. definition.getPropertyValues().add("sqlSessionTemplate",
  28. new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  29. explicitFactoryUsed = true;
  30. } else if (this.sqlSessionTemplate != null) {
  31. if (explicitFactoryUsed) {
  32. LOGGER.warn(
  33. () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  34. }
  35. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  36. explicitFactoryUsed = true;
  37. }
  38. if (!explicitFactoryUsed) {
  39. LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  40. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  41. }
  42. definition.setLazyInit(lazyInitialization);
  43. }
  44. }
  1. @Autowired 自动注入 Mapper 触发容器获取 bean 的方法,调用到 MapperFactoryBean#getObject()方法,最终调用到 sqlSessionTemplate#getMapper()方法
    1. @Override
    2. public <T> T getMapper(Class<T> type) {
    3. return getConfiguration().getMapper(type, this);
    4. }
    5.MyBatis-plus 使用的配置类是MybatisConfiguration,最终调用到 MybatisMapperRegistry#getMapper()方法,这里就进入了动态代理获取 MapperProxy 实例的流程
  1. @Override
  2. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  3. // TODO 这里换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
  4. final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
  5. if (mapperProxyFactory == null) {
  6. throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
  7. }
  8. try {
  9. return mapperProxyFactory.newInstance(sqlSession);
  10. } catch (Exception e) {
  11. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  12. }
  13. }

6.MybatisMapperProxyFactory#newInstance()方法给自动注入返回一个 MybatisMapperProxy 代理对象

  1. protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
  2. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
  3. }

7.调用 Mapper 接口的方法触发代理对象的 MybatisMapperProxy#invoke(),此时根据 Mapper 对象被调用的方法生成 MybatisMapperMethod 对象,通过MybatisMapperMethod#execute()去真正地执行 SQL 语句,从而完成数据库操作。此后的流程本文就不再分析,具体可参考文章 MyBatis Mapper 简要总结

  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. try {
  4. if (Object.class.equals(method.getDeclaringClass())) {
  5. return method.invoke(this, args);
  6. } else if (method.isDefault()) {
  7. return invokeDefaultMethod(proxy, method, args);
  8. }
  9. } catch (Throwable t) {
  10. throw ExceptionUtil.unwrapThrowable(t);
  11. }
  12. final MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
  13. return mapperMethod.execute(sqlSession, args);
  14. }

Spring5源码分析

https://gitee.com/whtznb/spring-framework-analysis
image.png

bean执行流程

image.png

首先,一个IoC容器应创建一个工厂(DefaultListableBeanFactory),可以使我们读取的资源文件可以存放。
然后,将配置文件通过一个规范(BeanDefinitionReader)加载出来。
接着,是bean对象实例化之前的一些准备(初始化啊、事件处理器、注册组件等);例如上图中的BeanFactoryPostProcessor、多播器等。
重要的地方来了,创建一个个的非懒加载的成品Bean对象(finishBeanFactoryInitialization方法)。
最后,是一些事件的发布、缓存、销毁等。

源码分析

从ClassPathXmlApplicationContext开始分析。在它的构造方法中,我们可以看见调用了父类(AbstractApplicationContext类)的构造方法、设置配置文件的加载路径以及核心方法refresh()方法。

image.png
父类AbstractApplicationContext的构造方法
image.png
setConfigLocations()方法
image.png
接下来,我们进入核心方法refresh()

image.png
我们重点看序号2和序号11,其他有兴趣可以自己点进去看看。
obtainFreshBeanFactory()方法
image.png
跟进refreshBeanFactory()方法,在AbstractRefreshableApplicationContext类中可以找到refreshBeanFactory()这个方法
image.png
createBeanFactory()方法中
image.png
loadBeanDefinitions()方法,也是委派给子类去实现。
image.png
我们进去子类AbstractXmlApplicationContext类的loadBeanDefinition()方法。在这里进行了配置文件读取规范的定义,我们继续跟进loadBeanDefinitions()方法。
image.png
loadBeanDefinitions()方法。传入的可能是个String[]或者Resource[]类型。但是大致流程都差不多:String[]->String->Resource[]->Resource->Document->BeanDefinition。这里就不过多深入了,感兴趣可以照这个流程看下去。
image.png
资源文件加载完成后,我们的BeanFactory差不多就创建好了。接着,我们到IoC最重要的过程,Bean对象(不是懒加载的)的实例化和初始化。这里为什么将实例化和初始化分开说呢,是想更好的帮助理解Bean对象的创建过程。其实Spring中更加的细分了一下,分成了实例化(createBeanInstance()方法)、填充属性(populateBean()方法)和初始化(initializeBean()方法)。
实例化:在堆中开辟了一块空间。属性都是系统默认值。
初始化:给属性完成具体的赋值操作,调用具体的初始化方法。

好了,我们进入finishBeanFactoryInitialization()方法,里面你会看到一些对beanFactory的属性设置,其中重点的是preInstantiateSingletons()方,点进去,它会调用DefaultListableBeanFactory的preInstantiateSingletons()方法。
image.png
我们可以看到getBean()方法,这里就是准备开始进行bean对象的创建了。点进去,我们可以看真正执行的是doGetBean()方法
image.png
doGetBean()方法,就是根据不同的Bean采用不同的创建策略。
1. 如果Bean是单例的,则在容器创建之前先从缓存中查找,确保整个容器只存在一个实例对象
2. 如果Bean是原型模式的,则容器每次都会创建一个新的实例对象
3. 指定了Bean的生命周期

image.png
image.png
我们进入createBean(),发现还有一个doCreateBean方法(),终于,我们到了真正创建Bean对象的方法。点进去。

image.png
我们发现我们终于找到了之前所说的那三个方法了,创建、填充和初始化。
createBeanInstance()方法返回的是一个BeanWrapper,bean的封装类。
populateBean()则是将bean的一些属性字段进行解析、填充。
在initializeBean()中
image.png
到此,我们一开始的流程图所有的地方差不多都完成了。其中有些细节方面没点进去看看,主要是大致了解IoC的过程。可以自行debug进去看看。

SpringMVC工作流程 struts(servlet) struts2(filter)

image.png
1.DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性,系统扩展性提高。由框架实现
2.HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求的url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml配置方式,实现接口方式,注解方式等。由框架实现
3.Handler:处理器。Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
4.HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。由框架实现。
5.ModelAndView是springmvc的封装对象,将model和view封装在一起。
6.ViewResolver:视图解析器。ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
7View:是springmvc的封装对象,是一个接口, springmvc框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

执行流程对应的代码
1.请求到达前端控制器的第一站,先做些准备工作

  1. @Override
  2. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3. if (logger.isDebugEnabled()) {
  4. String requestUri = urlPathHelper.getRequestUri(request);
  5. logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +
  6. " request for [" + requestUri + "]");
  7. }
  8. //保护现场
  9. // Keep a snapshot of the request attributes in case of an include,
  10. // to be able to restore the original attributes after the include.
  11. Map<String, Object> attributesSnapshot = null;
  12. if (WebUtils.isIncludeRequest(request)) {
  13. logger.debug("Taking snapshot of request attributes before include");
  14. attributesSnapshot = new HashMap<String, Object>();
  15. Enumeration<?> attrNames = request.getAttributeNames();
  16. while (attrNames.hasMoreElements()) {
  17. String attrName = (String) attrNames.nextElement();
  18. if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
  19. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  20. }
  21. }
  22. }
  23. //将框架相关信息存储至request,方便后面的处理器和视图用到
  24. // Make framework objects available to handlers and view objects.
  25. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  26. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  27. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  28. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  29. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  30. if (inputFlashMap != null) {
  31. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  32. }
  33. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  34. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  35. //请求分发
  36. try {
  37. doDispatch(request, response);
  38. }
  39. finally {
  40. // Restore the original attribute snapshot, in case of an include.
  41. if (attributesSnapshot != null) {
  42. restoreAttributesAfterInclude(request, attributesSnapshot);
  43. }
  44. }
  45. }

2.开始处理请求
//通过url查找HandlerMap中最相近的key(url),然后由key获取HandlerMapping对象
//通过处理器映射器获取处理器;
//通过查询处理器适配器获得

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. int interceptorIndex = -1;
  5. try {
  6. ModelAndView mv;
  7. boolean errorView = false;
  8. try {
  9. processedRequest = checkMultipart(request);
  10. // Determine handler for the current request
  11. //步骤3.1~3.4用于获取包含处理器Handler和拦截器AdapterIntercepters的处理器执行链HandlerExecutionChain
  12. mappedHandler = getHandler(processedRequest, false);
  13. if (mappedHandler == null || mappedHandler.getHandler() == null) {
  14. noHandlerFound(processedRequest, response);
  15. return;
  16. }
  17. // Determine handler adapter for the current request.
  18. //步骤4.1~4.2,根据HandlerExecutionChain中的处理器Handler获取处理器适配器
  19. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  20. // Process last-modified header, if supported by the handler.
  21. String method = request.getMethod();
  22. boolean isGet = "GET".equals(method);
  23. if (isGet || "HEAD".equals(method)) {
  24. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  25. if (logger.isDebugEnabled()) {
  26. String requestUri = urlPathHelper.getRequestUri(request);
  27. logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
  28. }
  29. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  30. return;
  31. }
  32. }
  33. // Apply preHandle methods of registered interceptors.
  34. HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
  35. if (interceptors != null) {
  36. for (int i = 0; i < interceptors.length; i++) {
  37. HandlerInterceptor interceptor = interceptors[i];
  38. if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
  39. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  40. return;
  41. }
  42. interceptorIndex = i;
  43. }
  44. }
  45. // Actually invoke the handler.
  46. //5.1~5.3通过处理器适配器HandlerApapter来调用处理器完成对请求的处理
  47. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  48. // Do we need view name translation?
  49. if (mv != null && !mv.hasView()) {
  50. mv.setViewName(getDefaultViewName(request));
  51. }
  52. // Apply postHandle methods of registered interceptors.
  53. if (interceptors != null) {
  54. for (int i = interceptors.length - 1; i >= 0; i--) {
  55. HandlerInterceptor interceptor = interceptors[i];
  56. interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
  57. }
  58. }
  59. }
  60. catch (ModelAndViewDefiningException ex) {
  61. logger.debug("ModelAndViewDefiningException encountered", ex);
  62. mv = ex.getModelAndView();
  63. }
  64. catch (Exception ex) {
  65. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  66. mv = processHandlerException(processedRequest, response, handler, ex);
  67. errorView = (mv != null);
  68. }
  69. // Did the handler return a view to render?
  70. if (mv != null && !mv.wasCleared()) {
  71. render(mv, processedRequest, response);
  72. if (errorView) {
  73. WebUtils.clearErrorRequestAttributes(request);
  74. }
  75. }
  76. else {
  77. if (logger.isDebugEnabled()) {
  78. logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
  79. "': assuming HandlerAdapter completed request handling");
  80. }
  81. }
  82. // Trigger after-completion for successful outcome.
  83. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
  84. }
  85. catch (Exception ex) {
  86. // Trigger after-completion for thrown exception.
  87. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  88. throw ex;
  89. }
  90. catch (Error err) {
  91. ServletException ex = new NestedServletException("Handler processing failed", err);
  92. // Trigger after-completion for thrown exception.
  93. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
  94. throw ex;
  95. }
  96. finally {
  97. // Clean up any resources used by a multipart request.
  98. if (processedRequest != request) {
  99. cleanupMultipart(processedRequest);
  100. }
  101. }
  102. }

3 getHandler(HttpServletRequest request),经由HandlerMapping对象获取HandlerExecutionChain(处理器和拦截器)

  1. /**
  2. * Return the HandlerExecutionChain for this request.
  3. * <p>Tries all handler mappings in order.
  4. * @param request current HTTP request
  5. * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
  6. */
  7. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  8. for (HandlerMapping hm : this.handlerMappings) {
  9. if (logger.isTraceEnabled()) {
  10. logger.trace(
  11. "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
  12. }
  13. HandlerExecutionChain handler = hm.getHandler(request);
  14. if (handler != null) {
  15. return handler;
  16. }
  17. }
  18. return null;
  19. }
  1. HandlerAdapter getHandlerAdapter(Object handler),根据Handler获取HandlerAdapter ```java protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) {
    1. logger.trace("Testing handler adapter [" + ha + "]");
    } if (ha.supports(handler)) {
    1. return ha;
    } } throw new ServletException(“No adapter for handler [“ + handler +
    1. "]: Does your handler implement a supported interface like Controller?");
    }
  1. 5.使用处理器完成对请求的处理
  2. ```java
  3. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  4. throws Exception {
  5. ((Servlet) handler).service(request, response);
  6. return null;
  7. }
  8. public void service(ServletRequest req, ServletResponse res)
  9. throws ServletException, IOException
  10. {
  11. HttpServletRequest request;
  12. HttpServletResponse response;
  13. if (!(req instanceof HttpServletRequest &&
  14. res instanceof HttpServletResponse)) {
  15. throw new ServletException("non-HTTP request or response");
  16. }
  17. request = (HttpServletRequest) req;
  18. response = (HttpServletResponse) res;
  19. service(request, response);
  20. }