日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java spring注解维护,从一次工程启动失败谈谈 spring 注解

發布時間:2024/9/27 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java spring注解维护,从一次工程启动失败谈谈 spring 注解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原標題:從一次工程啟動失敗談談 spring 注解

檀寶權

Java 后端開發工程師,負責度假 App 后端和廣告后端開發維護工作,熟悉 Tomcat,Spring,Mybatis,會點 Python,Lua。

一、背景

線上環境升級成 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 @Resource

spring 啟動時候,在解析 @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, 棄用 @Autowired@Autowired 本意是根據注入的類型來選擇一個最佳的候選 bean, 當注入到系統的中候選 bean 只有一個時,是不關心 bean 的名稱的,當有多個候選 bean時候,會進行 primary 判斷或者名稱判斷。

@Resource 是根據名稱查找 BeanDefinition, 當查找的的名稱沒有注入到 BeanFactory 中,CommonAnnotationBeanPostProcessor 就轉到 @Autowired 查找。所以我們完全有理由棄用 @Autowired。返回搜狐,查看更多

責任編輯:

總結

以上是生活随笔為你收集整理的java spring注解维护,从一次工程启动失败谈谈 spring 注解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。