google 网站打不开,单页网站做淘宝客,建设银行流水账网站查询,如何优化网页加载速度原标题#xff1a;从一次工程启动失败谈谈 spring 注解檀宝权Java 后端开发工程师#xff0c;负责度假 App 后端和广告后端开发维护工作#xff0c;熟悉 Tomcat#xff0c;Spring#xff0c;Mybatis#xff0c;会点 Python#xff0c;Lua。一、背景线上环境升级成 JDK8后…原标题从一次工程启动失败谈谈 spring 注解檀宝权Java 后端开发工程师负责度假 App 后端和广告后端开发维护工作熟悉 TomcatSpringMybatis会点 PythonLua。一、背景线上环境升级成 JDK8后 Tomcat 启动会经常失败调整 JVM 的栈大小为 2M 后失败频率大大降低但是偶尔还是会失败。捕获启动异常日志会看到下面异常信息(没找到附件上传地方暂不上传日志附件只截取一段感兴趣的可以直接找我)at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:519)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:320)at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:861)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:818)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:735)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:551)at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)at org.springframework.beans.factory.support.AbstractBeanFactory.getTypeForFactoryBean(AbstractBeanFactory.java:1356)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:710)at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:519)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:320)at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:861)Caused by: java.lang.StackOverflowError栈溢出异常很不正常的行为为什么启动时候会导致这么长的调用链二、相关的版本环境1. JDK82. Tomcat73. Spring3.1.2.RELEASE4. Mybatis-3.0.6.jar5. Mybatis-spring-1.0.2.jar三、分析3.1 栈调用分析仔细分析启动日志发现下面的异常信息不断重复熟悉 spring 源码的同学都知道这段代码是用来创建 bean但是创建的过程中发现依赖的 bean 不存在继续创建依赖的 bean。......at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)at org.springframework.beans.factory.support.AbstractBeanFactory.getTypeForFactoryBean(AbstractBeanFactory.java:1356)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:710)at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:519)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:343)at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:320)at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:861)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:818)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:735)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:551)at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)......关于 bean 之间的依赖我们一般使用 Autowired, Resource 注解来声明。看上面的异常信息明显使用的是 Autowired 注解。断点调试发现这个超长调用栈最终表现形式为service1-dao1-dao2-dao3-.....这就很奇怪了我们项目中 dao 使用的是 Mybatis Mapper XML 文件形式 dao 表现形式就是一个 Interface 文件, 根本不可能存在 dao 调用 dao 的情况。3.2 Autowired 注解Autowried 是类型匹配的注解 Spring 使用 AutowiredAnnotationBeanPostProcessor 来解析依赖。具体原理比较简单大致如下遍历项目中所有注入的 BeanDefinition 获取每个 BeanDefinition 类型校验此类型和 Autowired 声明的类型是否匹配匹配就认为是候选类型如果有多个候选类型选择最佳的一个找不到最佳的就抛出存在多个同一类型的 bean 异常,启动失败。那么每个 BeanDefinition 类型是什么了普通的 bean在注入到 spring 中就是其 beanClass 属性如果是 FactoryBean 真正的类型并不是其 beanClass 属性声明的值而是其方法 getObjectType() 返回的值 这就导致 spring 在查找类型的时候如果遇到 FactoryBean 就必须先实例化这个 bean 只有创建成功后才能调用对象的方法 getObjectType() 。Mybatis Mapper dao 被 MapperScannerConfigurer 扫描后注入到 spring 中的 beanClass 值为 org.mybatis.spring.mapper.MapperFactoryBean 正好就是个 FactoryBean 。3.3 MapperFactoryBean先看看 MapperFacotryBean 的声明:public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {......}public abstract class SqlSessionDaoSupport extends DaoSupport {private SqlSession sqlSession;private boolean externalSqlSession;Autowired(required false)public final void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (!this.externalSqlSession) {this.sqlSession new SqlSessionTemplate(sqlSessionFactory);}}Autowired(required false)public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {this.sqlSession sqlSessionTemplate;this.externalSqlSession true;}......}哈哈MapperFactoryBean 也是使用了 Autowired spring 在实例化 MapperFactoryBean 后仍需要继续按照3.2节的过程执行遍历导致出现这样的调用链service-dao1-mapper1-dao2-mapper2-dao3-mapper3-......所以随着系统 Mybatis Mapper dao 文件慢慢增多这个调用链会越来越长最终超过栈深度抛出栈溢出异常。3.4 Resourcespring 启动时候在解析 Autowired 依赖注入需要使用很大的篇幅去寻找候选 beanName 必须逐一遍历每个 BeanDefinition 费时费力即使整个系统只有一个这样类型的 BeanDefinition 。但是 spring 在注入 BeanDefinition 除了会 add beanDefinitionNames 还会建立 beanName- beanDefinitionMap 。所以使用 Resource 注解会大大降低依赖注入 BeanDefinition 寻找过程也就大大降低 bean 初始化调用栈。public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {synchronized (this.beanDefinitionMap) {Object oldBeanDefinition this.beanDefinitionMap.get(beanName);if (oldBeanDefinition ! null) {if (!this.allowBeanDefinitionOverriding) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDeion(), beanName,Cannot register bean definition [ beanDefinition ] for bean beanName : There is already [ oldBeanDefinition ] bound.);}else {if (this.logger.isInfoEnabled()) {this.logger.info(Overriding bean definition for bean beanName : replacing [ oldBeanDefinition ] with [ beanDefinition ]);}}}else {this.beanDefinitionNames.add(beanName);this.frozenBeanDefinitionNames null;}this.beanDefinitionMap.put(beanName, beanDefinition);resetBeanDefinition(beanName);}}四、结论上面异常的根本原因是项目大量使用 Autowired 注解而且 mybatis-spring 版本比较陈旧导致。最新版本的 mybatis-spring 已经去掉了 Autowired 注解MapperScannerConfigurer 在扫描后直接为生成的MapperFactoryBean.sqlSession 赋值不再使用注解去建立依赖关系。解决办法升级 mybatis-spring 版本 最好 mybatis, spring 版本一并升级目前这个确实太陈旧了尽量使用 Resource, 弃用 AutowiredAutowired 本意是根据注入的类型来选择一个最佳的候选 bean, 当注入到系统的中候选 bean 只有一个时是不关心 bean 的名称的当有多个候选 bean时候会进行 primary 判断或者名称判断。Resource 是根据名称查找 BeanDefinition, 当查找的的名称没有注入到 BeanFactory 中CommonAnnotationBeanPostProcessor 就转到 Autowired 查找。所以我们完全有理由弃用 Autowired。返回搜狐查看更多责任编辑