美文网首页
在springmvc+shiro项目中使用druid监听cont

在springmvc+shiro项目中使用druid监听cont

作者: carway | 来源:发表于2017-12-25 15:39 被阅读0次

    最近在做个spring+springmvc+shiro的整合,后面想再加个druid去监听spring,当去监听的controller层的时候就报出了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)
    

    异常信息大概意思就是,对controller使用了jdk代理,要求你使用基于类实现的代理,其实就是让你用CGLIB代理

    我们再来看下出现报错信息的配置文件

    druid相关配置
    <!-- 开启spring监控  -->
    <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相关配置

    因为,与下面配置做对比,我们这里称为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>
    

    出现异常肯定是配置文件出现了问题。
    从我们上一篇文章《JDK和CGLIB生成动态代理类的区别以及Spring动态代理机制》可以知道,
    Spirng的AOP的动态代理实现机制也是这两种:JDK动态代理和CGLib动态代理
    一般而言Spring默认优先使用JDK动态代理技术,只有在被代理类没有实现接口时,才会选择使用CGLIB技术来实现AOP。
    我们的controller层是没有实现接口,应该是用CGLIB代理,而spring的AOP会自动根据运行类选择 JDK 或 CGLIB 代理,那应该会自动选择CGLIB代理啊。
    但我们的异常信息却要求是我们使用基于类实现的代理——CGLIB代理,所以可以肯定是现在我们使用上面配置文件,因为某些原因而导致我们使用了jdk代理。

    《spring的bean二次代理问题》《springAOP应尽量避免自己创建AutoProxyCreator》之前这两篇文章可以知道,spring的配置文件配置不当,容易导致spring的bean二次代理问题,那么是不是我们配置文件也出现问题,所以导致了出现了多个ProxyCreator,出现了冲突,让controller层使用的是jdk代理,所以最后出现上面IllegalStateException异常呢?

    从上面druid相关配置,我们可以看到<aop:config><aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut"/>
    </aop:config>这句配置,从之前的《springAOP应尽量避免自己创建AutoProxyCreator》就可以知道,这句代码是会注册一个AutoProxyCreator、
    而shiro相关配置有一句<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>也定义了一个DefaultAdvisorAutoProxyCreator,而且因为proxy-target-class的缺省,那肯定使用的是jdk代理。
    所以有可能是两个代理起了冲突,导致当我们druid想去监控controller的时候是使用jdk代理,jdk代理不能对类进行代理所以才报了llegalStateException异常。

    为了验证这个说法,我们可以看下日志信息

    AspectJAwareAdvisorAutoProxyCreator创建一个对UserController的CGLIB代理.png 我们定义的DefaultAdvisorAutoProxyCreator创建一个对UserController的JDK代理.png

    从日志我们可以看到AspectJAwareAdvisorAutoProxyCreator创建一个对UserController的CGBLIB代理和我们定义的DefaultAdvisorAutoProxyCreator创建一个对UserController的JDK代理,一个UserController的bean有两个不同的代理,所以起了冲突。
    毫无疑问,肯定是配置文件问题导致了bean的两次代理问题。

    如何解决:

    我们把配置文件修改一下再看日志会发生什么不同

    druid配置保存不变,shiro还是自己创建DefaultAdvisorAutoProxyCreator,只是稍微做了修改
    我们这里称为shiro配置2

    <!-- 开启Shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
      <!-- 新增了这句代码,这句代码意思是 这个属性为true时,表示被代理的是目标类本身而不是目标类的接口,实际就是强制为CGLIB代理->
      <property name="proxyTargetClass" value="true"/>
    </bean>
     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
       <property name="securityManager" ref="securityManager"/>
     </bean>
    

    可以看到配置2,是多一个代码<property name="proxyTargetClass" value="true"/>。这句代码意思是 这个属性为true时,表示被代理的是目标类本身而不是目标类的接口,实际就是强制为CGLIB代理。

    现在我们再来看看日志有什么变化。

    AspectJAwareAdvisorAutoProxyCreator创建一个对UserController的CGLIB代理.png
    我们定义的DefaultAdvisorAutoProxyCreator创建一个对UserController的CGLIB代理.png

    把shiro的配置换成配置2后,我们调用controller层时候就没有报出异常了。其实原因也很简单因为我们对controller的代理换成CGLIB代理,那肯定不会报错了。
    但是这种配置2修改方式,虽然是解决了controller的代理问题,但是其实还是不好。因为我们是通过把两个AutoProxyCreator对controller的代理都编程CGLIB,所以才没有报错。
    但是这是一种治标不治本的问题,因为还是存在bean二次代理。其实问题就在于AutoProxyCreator,我们定义两个,导致bean二次代理。

    真正的解决方法的就是
    《springAOP应尽量避免自己创建AutoProxyCreator》这一篇文章所说的,避免自己创建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之后当然也是没有报异常啦。再看看日志文件,


    只有AspectJAwareAdvisorAutoProxyCreator创建一个对UserController的CGLIB代理.png

    现在就只有AspectJAwareAdvisorAutoProxyCreator创建一个对UserController的CGLIB代理了,就不存在二次代理的问题了,
    即便,在不同spring-dao.xml和spring-shiro.xml里面同时使用了<aop:config proxy-target-class="true"></aop:config> 也对一个bean只有一个代理。

    总结

    配置1是自己创建AutoProxyCreator,shiro官方文档 和spring集成也是这样写。但是这种写法,就像之前两篇《spring的bean二次代理问题》《springAOP应尽量避免自己创建AutoProxyCreator》反复在讲,对于不了解spring源码来说,是很容易中招,很容易就导致bean的两次代理问题。所以还是建议采用<aop:config>去代替这种自己创建AutoProxyCreator的方法。shiro的话可以采用配置3就好了,开涛大神的博客的配置也是这样写的。

    shiro的配置的话也可以参考《spring集成shiro的配置xml》

    相关文章

      网友评论

          本文标题:在springmvc+shiro项目中使用druid监听cont

          本文链接:https://www.haomeiwen.com/subject/crjlgxtx.html