资讯专栏INFORMATION COLUMN

『SpringMVC』<context:include-filter>&<

darryrzhong / 937人阅读

摘要:现在给定一个项目的包结构在中有以下配置只扫描注解可以看出要把最后的包写上,不能包含子包,所以不能写成。

  大家好,我是猪弟,猪在我心中从来不是蠢的代名词,而是懒的代名词,本次准备记录一个在开发测试过程中遇到的问题,跟踪了三天spring和第三方RPC组件的源码,最终发现了问题是因为第三方组件没有处理好而父子容器导致的,还有一个因素是spring注解扫描重叠。

Spring版本:4.3.13.RELEASE

JDK版本:1.7_u25 64位

SpringMVC的配置中为了防止Spring重复创建同一个类的实例,一般会用到的两个子标签&&

但它使用的时候表现的效果并不是和语义上的完全一致,现在来看一下其中的坑:

在很多配置中一般都会把spring-config.xmlspring-mvc.xml进行分开配置,这种配置可以他们保证各司其职,在web.xml的一般配置中spring-mvc.xml实例创建初始化是以DispatchServlet为入口,而spring-config.xml实例创建初始化是以ContextLoadListener为入口的,容器的加载顺序: listener -> filter -> servlet ,所以spring容器先初始化,springmvc容器后初始化 。

    
    
        contextConfigLocation
        
            classpath:spring-config.xml
        
    
    
        org.springframework.web.context.ContextLoaderListener
    

    
    
        blog-spring-mvc
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            
                classpath:spring-mvc.xml
            
        
        1
    
    
        blog-spring-mvc
        /
    

如果在spring-mvc.xml中配置扫描的包和spring-config.xml中的发生重叠,那么会导致一个bean被创建两次,而且在spring中是存在父子容器的,spring容器是父容器,springmvc是子容器,springmvc创建的实例放在子容器中,spring创建的实例放在父容器中。

其实这同一个类的两个实例是不同的,springmvc创建实例默认对象不实现接口(大家都知道Controller是不用实现接口的),所以springmvc创建的实例是直接使用目标类的构造器来实例化的,而不是代理对象,即使一个类实现了接口,但如果该类是由springmvc实例化,那么springmvc也会直接使用该类的构造器直接创建一个对象(怎么去证明呢,你可以写一个定时任务,在定时任务中注入Controller的实例,然后debug查看实例对象的地址,如果是代理对象在地址上都会有一个$Proxy的标记,否则就不是代理对象),所以在controller层使用AOP时多数采用的是CGLIB子类代理。

Spring创建实例会判断目标类是否实现了接口,如果没实现接口那么就直接采用目标类构造器创建,像一般的service和dao都会采用接口方式编程,对于接口方式编程的类,spring创建的实例都是代理对象(这一点可以用debug的方式查看controller类中注入的service实例对象地址,他们都带有一个$Proxy的标记,很容易就能看出都是代理对象)。

那么为了防止重叠我们要把重叠的部分去掉,现在有下面的一个需求:

spring-mvc.xml中只对工程中所有用@Controller注解的类进行扫描创建实例。

spring-config.xml中要对工程中所有的非@Controller注解的类进行扫描创建实例。

现在给定一个项目的包结构:

xin.sun.blog.controlller

xin.sun.blog.service

(1)在spring-mvc.xml中有以下配置:



    

可以看出要把最后的包写上,不能包含子包,所以不能写成: base-package="xin.sun.blog" 。如果这样写,对于 include-filter 标签来讲它会扫描基包下面所有spring注解的类,而不是仅仅扫描 @Controller 。这点需要非常的注意,这一般会导致一个常见的错误,那就是事务不起作用,补救的方法是添加 use-default-filters="false"

(2)在spring-config.xml中有如下配置:



    

可以看到,他是要扫描xin.sun.blog包和子包下的所有spring注解的类,但是不包含@Controller注解的类。对于exculude-filter不存在包不精确导致都进行扫描的问题。

那么还有一个问题:当扫描的包不小心重叠了,导致类在父子容器各实例化了一遍,在 @Autowire 的时候会注入哪个容器中的对象呢?看一个Controller类,代码如下:

@Controller
public class MyController{

    @Autowired
    private IValidService validService;
    //其他代码省略 
}

答案是:Spring为了保证注入类的一致性,采用了双亲委托的机制,如果父容器中存在该类的实例那么优先使用父容器中的实例,如果父容器中没有该实例才会用子容器中的实例

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68326.html

相关文章

  • Python代码太臃肿怎么办?下给给大家总结几个小技巧

      小编写这篇文章的一个主要目的,主要是给大家介绍,关于如何处理代码臃肿的事项,但是如果要处理的话,还是比较的麻烦的,那么,遇到这种问题的话,需要怎么去处理呢?下面就给大家详细的解答下。  什么是单行代码  你可以将单行代码视为压缩在一起的代码块,使其适合一行。它是只包含在一行中的简洁、有用的程序。  为什么我需要它们  如果你并不喜欢写单行代码,或者你只是好奇为什么我们必须知道这些,那么下面是一...

    89542767 评论0 收藏0
  • 如何利用Pandas查询选取数据

      小编写这篇文章的主要目的,主要还是利用Pandas这门工具,去进行编程等一系列的一些操作,比如可以用来进行增删查改等一系列的操作步骤。那么,怎么利用Pandas去查询数据呢?下面就给大家详细解答下。  一,Pandas查询数据的几种方法  df[]按行列选取,这种情况一次只能选取行或者列  df.loc方法,根据行、列的标签值查询  df.iloc方法,根据行、列的数字位置查询,根据索引定位 ...

    89542767 评论0 收藏0
  • Python中双下使用方法解析

      在Python这门语言中,有一些比较特殊的使用方法,主要用到的是双下划线开始和结束,正是因为如此,他还有一个比较接地气的名字,叫做双下方法,感兴趣的话,可以详细的为大家进行解答一下。  前言  大家在写Python代码的时候有没有这样的疑问。  为什么数学中的+号,在字符串运算中却变成拼接功能,如'ab'+'cd'结果为abcd;而*号变成了重复功能,如'...

    89542767 评论0 收藏0
  • CSRF攻击

    一、什么是CSRF攻击我们常常听到这样一句话:默认的链接不要点,那些年也听过,邮箱中的垃圾链接不要点。 因为可能是黑客发起的CSRF攻击,所以在点击之前最好是确认链接的安全性。CSRF(Cross-site requests forgery)中文名:跨站脚本伪造简单的理解就是,黑客盗用了你的身份,以你的名义向你访问的站点发送请求。这些请求操作可能是转发邮件、获取发送内容,发起转账、获取权限等。CS...

    社区管理员 评论0 收藏0

发表评论

0条评论

darryrzhong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<