在springmvc+shiro項(xiàng)目中使用druid監(jiān)聽controller層出現(xiàn)IllegalStateException異常

最近在做個(gè)spring+springmvc+shiro的整合,后面想再加個(gè)druid去監(jiān)聽spring,當(dāng)去監(jiān)聽的controller層的時(shí)候就報(bào)出了IllegalStateException異常

異常信息大概如下:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.
lang.IllegalStateException: The mapped handler method class 'com.zjw.shiro.controller.UserController' is 
not an instance of the actual controller bean class 'com.sun.proxy.$Proxy26'. If the controller requires
 proxying (e.g. due to @Transactional), please use class-based proxying.
HandlerMethod details: 
Controller [com.zjw.shiro.controller.UserController]
Method [public java.lang.String com.zjw.shiro.controller.UserController.login(com.zjw.shiro.entity.User,
javax.servlet.http.HttpServletRequest,org.springframework.web.servlet.mvc.support.RedirectAttributes)]
Resolved arguments: 
[0] [type=com.zjw.shiro.entity.User] [value=com.zjw.shiro.entity.User@59943548]
[1] [type=org.apache.shiro.web.servlet.ShiroHttpServletRequest] [value=org.apache.shiro.web.servlet.ShiroHttpServletRequest@2a4b3e88]
[2] [type=org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap] [value={}]

    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)

異常信息大概意思就是,對(duì)controller使用了jdk代理,要求你使用基于類實(shí)現(xiàn)的代理,其實(shí)就是讓你用CGLIB代理

我們?cè)賮砜聪鲁霈F(xiàn)報(bào)錯(cuò)信息的配置文件

druid相關(guān)配置
<!-- 開啟spring監(jiān)控  -->
<bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor"></bean>
 <bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
  <property name="patterns">
    <list>
      <value>com.zjw.shiro.controller.*</value>
      <value>com.zjw.shiro.service.*</value>
      <value>com.zjw.shiro.mapper.*</value>
    </list>
   </property>
</bean>

<aop:config>
  <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut"/>
</aop:config>
shiro相關(guān)配置

因?yàn)椋c下面配置做對(duì)比,我們這里稱為shiro配置1

<!-- 開啟Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>

 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
   <property name="securityManager" ref="securityManager"/>
 </bean>

出現(xiàn)異常肯定是配置文件出現(xiàn)了問題。
從我們上一篇文章《JDK和CGLIB生成動(dòng)態(tài)代理類的區(qū)別以及Spring動(dòng)態(tài)代理機(jī)制》可以知道,
Spirng的AOP的動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制也是這兩種:JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理
一般而言Spring默認(rèn)優(yōu)先使用JDK動(dòng)態(tài)代理技術(shù),只有在被代理類沒有實(shí)現(xiàn)接口時(shí),才會(huì)選擇使用CGLIB技術(shù)來實(shí)現(xiàn)AOP。
我們的controller層是沒有實(shí)現(xiàn)接口,應(yīng)該是用CGLIB代理,而spring的AOP會(huì)自動(dòng)根據(jù)運(yùn)行類選擇 JDK 或 CGLIB 代理,那應(yīng)該會(huì)自動(dòng)選擇CGLIB代理啊。
但我們的異常信息卻要求是我們使用基于類實(shí)現(xiàn)的代理——CGLIB代理,所以可以肯定是現(xiàn)在我們使用上面配置文件,因?yàn)槟承┰蚨鴮?dǎo)致我們使用了jdk代理。

《spring的bean二次代理問題》《springAOP應(yīng)盡量避免自己創(chuàng)建AutoProxyCreator》之前這兩篇文章可以知道,spring的配置文件配置不當(dāng),容易導(dǎo)致spring的bean二次代理問題,那么是不是我們配置文件也出現(xiàn)問題,所以導(dǎo)致了出現(xiàn)了多個(gè)ProxyCreator,出現(xiàn)了沖突,讓controller層使用的是jdk代理,所以最后出現(xiàn)上面IllegalStateException異常呢?

從上面druid相關(guān)配置,我們可以看到<aop:config><aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut"/>
</aop:config>這句配置,從之前的《springAOP應(yīng)盡量避免自己創(chuàng)建AutoProxyCreator》就可以知道,這句代碼是會(huì)注冊(cè)一個(gè)AutoProxyCreator、
而shiro相關(guān)配置有一句<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>也定義了一個(gè)DefaultAdvisorAutoProxyCreator,而且因?yàn)閜roxy-target-class的缺省,那肯定使用的是jdk代理。
所以有可能是兩個(gè)代理起了沖突,導(dǎo)致當(dāng)我們druid想去監(jiān)控controller的時(shí)候是使用jdk代理,jdk代理不能對(duì)類進(jìn)行代理所以才報(bào)了llegalStateException異常。

為了驗(yàn)證這個(gè)說法,我們可以看下日志信息

AspectJAwareAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGLIB代理.png

我們定義的DefaultAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的JDK代理.png

從日志我們可以看到AspectJAwareAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGBLIB代理和我們定義的DefaultAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的JDK代理,一個(gè)UserController的bean有兩個(gè)不同的代理,所以起了沖突。
毫無疑問,肯定是配置文件問題導(dǎo)致了bean的兩次代理問題。

如何解決:

我們把配置文件修改一下再看日志會(huì)發(fā)生什么不同

druid配置保存不變,shiro還是自己創(chuàng)建DefaultAdvisorAutoProxyCreator,只是稍微做了修改
我們這里稱為shiro配置2

<!-- 開啟Shiro注解 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
  <!-- 新增了這句代碼,這句代碼意思是 這個(gè)屬性為true時(shí),表示被代理的是目標(biāo)類本身而不是目標(biāo)類的接口,實(shí)際就是強(qiáng)制為CGLIB代理->
  <property name="proxyTargetClass" value="true"/>
</bean>
 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
   <property name="securityManager" ref="securityManager"/>
 </bean>

可以看到配置2,是多一個(gè)代碼<property name="proxyTargetClass" value="true"/>。這句代碼意思是 這個(gè)屬性為true時(shí),表示被代理的是目標(biāo)類本身而不是目標(biāo)類的接口,實(shí)際就是強(qiáng)制為CGLIB代理。

現(xiàn)在我們?cè)賮砜纯慈罩居惺裁醋兓?/p>

AspectJAwareAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGLIB代理.png

我們定義的DefaultAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGLIB代理.png

把shiro的配置換成配置2后,我們調(diào)用controller層時(shí)候就沒有報(bào)出異常了。其實(shí)原因也很簡單因?yàn)槲覀儗?duì)controller的代理換成CGLIB代理,那肯定不會(huì)報(bào)錯(cuò)了。
但是這種配置2修改方式,雖然是解決了controller的代理問題,但是其實(shí)還是不好。因?yàn)槲覀兪峭ㄟ^把兩個(gè)AutoProxyCreator對(duì)controller的代理都編程CGLIB,所以才沒有報(bào)錯(cuò)。
但是這是一種治標(biāo)不治本的問題,因?yàn)檫€是存在bean二次代理。其實(shí)問題就在于AutoProxyCreator,我們定義兩個(gè),導(dǎo)致bean二次代理。

真正的解決方法的就是
《springAOP應(yīng)盡量避免自己創(chuàng)建AutoProxyCreator》這一篇文章所說的,避免自己創(chuàng)建AutoProxyCreator,直接采用<aop;config>就好了,這樣一來配置文件寫方便,也避免了bean的二次代理問題。

所以修改后的shiro配置3
<aop:config proxy-target-class="true"></aop:config>  
<bean class="  org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
    <property name="securityManager" ref="securityManager"/>  
</bean>   

修改成配置3之后當(dāng)然也是沒有報(bào)異常啦。再看看日志文件,


只有AspectJAwareAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGLIB代理.png

現(xiàn)在就只有AspectJAwareAdvisorAutoProxyCreator創(chuàng)建一個(gè)對(duì)UserController的CGLIB代理了,就不存在二次代理的問題了,
即便,在不同spring-dao.xml和spring-shiro.xml里面同時(shí)使用了<aop:config proxy-target-class="true"></aop:config> 也對(duì)一個(gè)bean只有一個(gè)代理。

總結(jié)

配置1是自己創(chuàng)建AutoProxyCreator,shiro官方文檔 和spring集成也是這樣寫。但是這種寫法,就像之前兩篇《spring的bean二次代理問題》《springAOP應(yīng)盡量避免自己創(chuàng)建AutoProxyCreator》反復(fù)在講,對(duì)于不了解spring源碼來說,是很容易中招,很容易就導(dǎo)致bean的兩次代理問題。所以還是建議采用<aop:config>去代替這種自己創(chuàng)建AutoProxyCreator的方法。shiro的話可以采用配置3就好了,開濤大神的博客的配置也是這樣寫的。

shiro的配置的話也可以參考《spring集成shiro的配置xml》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評(píng)論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,785評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,898評(píng)論 6 342
  • 什么是Spring Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,508評(píng)論 1 133
  • 我喜歡一本正經(jīng)的瞎搞 我更喜歡情不自禁的胡鬧
    愛嗨愛嗨呦閱讀 200評(píng)論 0 0
  • #作業(yè)#“每個(gè)人的行為背后都有其隱性動(dòng)機(jī),每一個(gè)行為都是其現(xiàn)有認(rèn)知能力下對(duì)自己利益最大化的考量。從隨處可見...
    張寶英閱讀 215評(píng)論 1 1