前言:因为好久没有碰ssm组合式开发的东西了,加上学的时候稍微咸鱼了一点,好多东西其实都忘了,打算抽时间一点一点的把这些内容捡起来。
这次我们来聊一聊context:component-scan注解,今天看到一段代码,在配置文件aplicationContext.xml中看到<context:component-scan>
这个注解,代码如下:
<context:component-scan base-package="com.hrms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
我们先来聊一聊这个配置的作用吧。
通常情况下,我们创建一个spring的项目,如果注册bean对象是通过注解注册而非配置文件的话,在配置文件当中都会看到<context:component-scan>
这个注解,这个注解的作用大家想必都能反应过来,扫描包呗,开启注解扫描。或者说更加准确的说,是注册bean对象,当配置完这个标签之后,spring就会自动扫描base-package
属性下面的所有包,如果扫描的包中有被@Service, @Controller, @Repository, @Component注解的类的话,根据spring控制反转的功能,spring会把这些类注册成bean。
打个比方说:我在一个类上写上@Component注解,spring就会将这个类注册成一个bean对象,并放到容器中,bean对象默认的名字是类名的首字母小写,如果我写上@Component(value="abc")
,那么bean对象的名字就是abc,很好理解吧。
啥?还不理解?那我举个栗子:(已明白的小伙伴请往下翻)
比方说我这里有一个实体类,我想把它注入到spring的ioc容器当中:
public class User{
private String name;
private Inteager age;
}
用配置文件的方式我们应该怎么做呢?当然是在配置文件当中写入下面的代码:
<bean name="user" class="xx.xx.User"></bean>
然后要用这个对象的时候,我需要先通过配置文件创建一个配置文件对象(例如通过ClassPathXmlApplicationContext
类去new一个配置对象),然后调用配置对象的getBean方法来获取这个对象
是不是感觉挺麻烦的,我又不想向这个对象当中注入什么复杂的属性,甚至不用注入属性,那我为什么还要在配置文件当中去写这样一行代码呢?于是spring官方提供了比较人性化的操作:
@Component
public class User{
private String name;
private Inteager age;
}
这样通过一个注解即可完成注册操作,后面需要用的时候在加上@Autowired
注解即可,便捷不少吧。当然这里的@Component
可以根据情况换成@Service
、@Controller
、@Repository
,默认情况下,只要有这四个属性注解在类上,就相当于告诉Spring,这个类是需要它来管理的。
- @Controller: 用于控制器层
- @Service: 用于服务层
- @Repository: 用于DAO
- @Component:用于其他组件
所以如果仅仅是在配置文件中写<context:component-scan base-package="com.sparta.trans"/>
Use-default-filter
属性此时为true时,那么会对base-package包或者子包下所有的java类进行扫描,并把匹配的java类注册成bean。这种情况下可以发现扫描的力度还是非常大的
但是,在SSM组合式开发当中,Controller控制层应当是由SpringMVC单独管理的,不应该交给spring管理,原因是因为:SSM组合式开发当中,spring的配置文件与springmvc的配置文件分开加载,在spring容器初始化的时候,会先加载(web.xml)<context-param>中的配置,之后再加载<servlet>中的<init-param>去加载前端控制器。加载springmvc的时候,如果扫描到@service
会重新加载这个service的bean(都是没有aop配置事务控制的),可能会覆盖之前的service,导致service的事务失效。因此service层是不应该让SpringMVC的配置文件加载的时候再加载一遍的,所以不能只写上文中提到的单单开启注解扫描的配置代码
看到这里,小伙伴应该都明白了,这种做法可能导致事务失效!oh my god!多么令人害怕的事情,不过不要紧,因为<context:component-scan>
这个标签还有两个子标签,可以完美的解决这个问题,我们把目光再次回到本文开头提到的代码:
<context:component-scan base-package="com.hrms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
这里有一个<context:exclude-filter>
注解,另外一个注解是<context:include-filter>
注解,顾名思义,exclude即排除,就是说,我扫描的时候排除掉你这个注解,排除的注解我在后面通过expression
来指定(这里排除掉的是@Controller注解),而include即包含,我扫描的时候只加载被指定的注解注解过的类。相当于手机的黑名单和白名单,(这里注意如果使用白名单记得把use-default-filters
这个过滤器属性设为false,否则他会采用默认的过滤器,白名单就失效了)。这样就可以完美的避免重复加载的问题,防止事务失效。
说完了作用,我们来看看这两个注解的属性,这两个属性都使用type和expression属性一起协作来定义组件扫描策略:
过滤器类型 | 描述 |
---|---|
annotation | 过滤器扫描使用注解所标注的那些类,通过expression属性指定要扫描的注解 |
assignable | 过滤器扫描派生于expression属性所指定类型的那些类 |
aspectj | 过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类 |
custom | 使用自定义的org.springframework.core.type.TypeFliter实现类,该类由expression属性指定 |
regex | 过滤器扫描类的名称与expression属性所指定正则表示式所匹配的那些类 |
- 再次强调一下:若使用
include-filter
去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖
这样我们就可以愉快的开始使用注解开发啦,也不用担心事务失效的问题。如果你想扫描什么包,或者不想扫描什么包,完全可以根据表格中的属性进行定制,在不需要对bean对象进行复杂的属性注入的时候,这种开发方式是简洁且方便的。(当然需要复杂的属性注入的时候还是写配置文件吧,不一定方便但绝对易读简洁)
参考博文: