除了使用XML文件方式来配置 bean 之外,我们还可以使用基于注解的方法来对 bean 进行配置
组件扫描(component scanning):Spring 能从classpath 下自动扫描、侦测和实例化具有特定注解的组件。
特定组件包括:
1. @Component:基本注解,标识一个受 Spring 管理的组件
2. @Repository:标识持久层组件
3. @Service:标识服务层(业务层)组件
4. @Controller:标识表现层组件
如果扫描到上述的特定注解,则spring会自动把这些类注册为bean。
对于扫描到的组件,Spring 有默认的命名策略:使用非限定类名,第一个字母小写(即User类生成的对象名字为user)。也可以在注解中通过 value 属性值标识组件的名称,例如 @Component(value="自定义对象名字") 或直接定义为 @Component("自定义对象名字")。
当在组件类上使用了特定的注解之后,还需要在Spring的配置文件中声明 <context:component-scan> 元素,它的使用规则如下:
1. base-package 属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类
2. 当需要扫描多个包时,可以使用逗号分隔
3. 如果希望扫描特定的类而非基包下的所有类,可以使用 resource-pattern 属性过滤特定的类,如
<!-- 指定扫描 annotation 的子包 autowire 下所有的类 -->
<context:component-scan base-package="com.bupt.spring.annotation" resource-pattern="autowire/*.class"/>
4. <context:include-filter> 子节点表示要包含的目标类
5. <context:exclude-filter> 子节点表示要排除在外的目标类
6. <context:component-scan> 下可以拥有若干个 <context:include-filter> 和 <context:exclude-filter> 子节点
7. <context:include-filter> 和 <context:exclude-filter> 子节点支持多种类型(type)的过滤表达式(常用的为第一、二个):
类别 | 示例 | 说明 |
annotation | com.spring.XxxAnnotation | 所有标注了XxxAnnotation的类,该类型采用目标类是否标注了某个注解进行过虑(根据注解进行过滤) |
assignable | com.spring.XxxService | 所有继承或扩展XxxService的类,该类型采用目标类是否继承或扩展某个特定类进行过滤(根据具体的类或接口进行过滤) |
aspectj | com.spring..*Service+ | 所有类名以Service结束的类及继承或者扩展他们的类,该类型采用AspectJ表达式进行过滤 |
regex | com.spring.anno..* | 所有com.spring.anno包下的类,该类型采用正则表达式根据类的名字进行过滤 |
custom | com.spring.XxxTypeFilter | 采用XxxTyperFilter通过代码的方式定义过滤规则,该类型必须实现org.springframework.core.type.TypeFilter接口 |
<!-- 指定过滤 annotation 包及其子包下所有标注有 @Repository 的类,将它们排除在扫描之外 --> <context:component-scan base-package="com.bupt.springtest.annotation"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
<!-- 指定过滤 annotation 包及其子包下所有标注有 @Repository 的类,指定只扫描这些类,需要和 use-default-filters 配合使用--> <!-- use-default-filters 默认为true,它会扫描 annotation 包及其子包下标有那四种注解的类。 将其设为false,则只会去扫描满足 context:include-filter 条件的注解 ,其它的不再扫描 --> <context:component-scan base-package="com.bupt.springtest.annotation" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
<!-- 指定不扫描 UserRepository 类及其子类 --> <context:component-scan base-package="com.bupt.springtest.annotation"> <context:exclude-filter type="assignable" expression="com.bupt.springtest.annotation.repository.UserRepository"/> </context:component-scan>
<!-- 指定只扫描 UserRepository 类及其子类 --> <context:component-scan base-package="com.bupt.springtest.annotation" use-default-filters="false"> <context:include-filter type="assignable" expression="com.bupt.springtest.annotation.repository.UserRepository"/> </context:component-scan>
除了将标注了注解的 bean 交付给 IoC 容器管理,注解还可用于标注 bean 与 bean 之间的关联关系
<context:component-scan> 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例,该实例可以自动装配具有 @Autowired(推荐使用) 和 @Resource 以及 @Inject 注解的属性。
<context:annotation-config> 元素也可用于注册 BeanPostProcessor 实例,但它们存在区别,而且 <context:component-scan> 能做的事情更多。它们之间具体的差异可以参照 http://blog.csdn.net/baple/article/details/16864835。
下面分别介绍这几种属性用法:
@Autowired(默认按照类型进行注入) 注解自动装配具有兼容类型的单个 bean 属性,它具有以下特点:
1. 构造器,普通字段(即使是非public),一切具有参数的方法(如:setXxx方法上)都可以应用 @Autowired 注解
2. 默认情况下,所有使用 @Autowired 注解的属性都需要被设置。否则当Spring找不到匹配的 bean 装配属性时,会抛出异常。若某一属性允许不被设置,可以设置 @Autowired 注解的 required 属性为 false ( @Autowired(required=false) ),此时若找不到匹配的 bean 装配,则不会抛出异常。
3. 默认情况下,当IoC容器里面存在多个类型兼容的 bean 时,通过类型的自动装配将无法工作,此时可以在 @Qualifier 注解里提供 bean 的名称。Spring允许对方法的入参标注 @Qualifier 以指定注入 bean 的名称
//方法一:对类型 UserRepository 进行实例化,且对象名字叫 userRepository,后面注入时可以使用 @Resource(name="userRepository") @Repository("userRepository") public class UserRepositoryImp1 implements UserRepository { @Override public void save() { //... } } @Repository class UserRepositoryImp2 implements UserRepository { @Override public void save() { //... } }
@Service public class UserService { //方法二:通过@Qualifier指定装配的是哪个bean @Autowired @Qualifier("userRepositoryImp2") private UserRepository userRepository; /* * 也可以这样写,写在set方法上面 @Autowired @Qualifier("userRepositoryImp2") public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } 或者这样写 @Autowired public void setUserRepository(@Qualifier("userRepositoryImp2")UserRepository userRepository) { this.userRepository = userRepository; } */ public void add() { userRepository.save(); } }
4. @Autowired 注解也可以应用在数组类型的属性上,此时,Spring将会把所有匹配的 bean 进行自动装配
5. @Autowired 注解也可以应用在集合类型的属性上,此时Spring读取该集合的类型信息,然后自动装配与之兼容的 bean
6. @Autowired 注解用在 java.util.Map上时,若该Map的键值为String,那么Spring将自动装配与之 Map 值兼容的 bean,此时 bean的名称作为键值
@Resource 和 @Inject 注解的功能与 @Resource 类似
@Resource(默认按照名称注入) 要求提供一个 name = bean名称 的属性,若该属性为空,则自动采用标注处的变量或方法名作为 bean 的名称。若无名称匹配,则按类型注入
@Inject 和 @Autowired 注解一样也是按类型匹配注入的 bean,但没有 required 属性
既然可以使用注解为 Spring 的 bean 自动装配其他 bean 的引用,我们同样希望能够使用注解来装配简单的值。Spring 3.0 引入了 @Value,它是一个新的装配注解,可以让我们使用注解装配 String 类型的值和基本类型的值,如 int、boolean。
我们可以通过 @Value 直接标注某个属性、方法或者方法参数,并传入一个 String 类型的表达式来装配属性,如:
@Value("Eruption") private String song;
实际上装配简单的值并不是 @Value 擅长的,它不仅仅可以装配一个静态值,它是一种有效的基于注解驱动的装配方式,它可以根据SpEL表达式来进行动态的求值计算,如:
@Value("#{systemProperties.myFavouriteSong}") private String song;
Spring 4.x 新特性:泛型依赖注入
Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量
package com.bupt.springtest.annotation; public class User{ }
package com.bupt.springtest.annotation; public class BaseRepository<T>{ }
package com.bupt.springtest.annotation; import org.springframework.beans.factory.annotation.Autowired; public class BaseService<T> { //属性会被子类继承 @Autowired private BaseRepository<T> repository; public void add() { System.out.println(repository); } }
@Repository public class UserRepository extends BaseRepository<User>{ }
@Service public class UserService extends BaseService<User>{ }
<context:component-scan base-package="com.bupt.springtest.annotation"/>
public class AnnotationTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BaseService userService = (BaseService) ctx.getBean("userService"); userService.add();
//打印结果:com.bupt.springtest.annotation.UserRepository@1b0d3ed,即在 BaseService<T> 中,注入的 repository 属性的真正的类型是其子类的类型
//因为在获取 userService 时确定了泛型 T 为 User,真正注入 repository 属性的是 <T> 对应的子类类型
}
}