Spring可以通过编写XML来配置Bean,也可以通过使用Spring的注解来装配Bean。
自动装配(autowiring) 与 自动检测(autodiscovery)
- 自动装配:让Spring自动识别如何装配bean的依赖关系,减少对<property>元素的使用。
- 自动检测:让Spring自动识别哪些类需要配置成Spring Bean,减少对<bean>元素的使用。
一、自动装配Bean
自动装配的4种类型:
- byName:把与bean名字(ID)相同的其他Bean自动装配到Bean的对应属性中。
- byType:把与bean类型相同的其他Bean自动装配到Bean的对应属性中。
- constructor:把与Bean的构造器入参具有相同类型的其他Bean装配到对应属性中。
- autodetect:首先尝试constructor进行自动装配,然后再尝试byType。
假设Spring配有一个Bean:
<bean id="userService" class="com.test.UserServiceImpl"> </bean>
对应于类 com.test.UserService:
public class UserServiceImpl { private UserDao userDao; // 属性 ... }
若配置的为byName,则Spring会将Bean的id为userDao的Bean自动装配到userService bean的userDao的属性中。
若配置的为byType,则Spring会将Bean的class类型为UserDao的Bean自动装配到userService bean的userDao的属性中。
若配置的为constructor,则类com.test.UserService中要有构造函数,如:
public class UserServiceImpl { private UserDao userDao; // 属性 // 构造函数 pulic UserServiceImpl(UserDao userDao) { this.userDao = userDao; } }
那么Spring会将类型为UserDao的bean装配到属性userDao中。
写个基于注解自动装配,maven工程如下:
UserServiceImpl类:
import org.springframework.beans.factory.annotation.Autowired; import com.test.dao.UserDao; import com.test.domain.User; public class UserServiceImpl implements UserService { @Autowired // 自动装配Bean,使用注解 private UserDao userDao; public void save(User user) { userDao.Save(user); System.out.println("saved!"); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
自动装配Bean是通过设置自动装配的类型来为Bean的属性装配依赖。所以在beans.xml中只指定Bean而不需要显式指定属性<property>
使用注解装配
从Spring2.5开始,最有趣的一种装配Spring Bean的方式是使用注解自动装配Bean的属性。使用注解自动装配与在XML中使用autowire属性自动装配并没有太大差别。但是使用注解方式允许更细粒度的自动装配,我们可以选择性地标注某个属性来对其应用自动装配。
Spring容器默认禁用注解装配。所以,在使用基于注解的自动装配前,我们需要在Spring配置中启用它。最简单的启用方式是使用Spring的Context命令空间配置中的<context:annotation-config>
元素,如下所示:
beans.xml中:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context = "http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-autowire="byName"> <context:annotation-config /> <!--开启Spring注解自动装配的功能 --> <bean id="userDao" class="com.test.dao.UserDaoImpl"></bean> <bean id="userService" class="com.test.service.UserServiceImpl"></bean> <!-- <context:component-scan base-package="com.test"></context:component-scan> --> </beans>
<context:annotation-config>
元素告诉Spring,我们打算使用基于注解的自动装配。一旦配置成功,我们可以对代码添加注解,标识Spring应该为属性、方法和构造器进行自动装配。
Spring 3 支持几种不同的用于自动装配的注解:
- Spring 自带的 @Atutowired注解;
- JSR-330 的 @Inject 注解;
- JSR-250 的 @Resource 注解;
二、自动检测Bean
当在Spring配置中增加<context:annotation-config>
时,我们希望Spring特殊对待我们所定义的Bean里的某一组注解,并使用这些注解指导Bean装配。即使<context:annotation-config>
有助于完成消除Spring配置中的<property>
和<constructor-arg>
元素,我们仍需要使用<bean>
元素显示定义Bean。
但是Spring还有另一个技巧。<context:component-scan>
元素除了完成与<context:annotation-config>
一样的工作,还允许Spring自动检测Bean和定义Bean。这意味着不使用<bean>元素,Spring应用中的大多数(或者所有)Bean都能实现定义和装配。
为了配置Spring自动检测,需要使用<context:component-scan>
元素来代替<context:annotation-config>
元素。
<context:component-scan>
元素会扫描指定的包及其所有子包,并查找能够自动注册为Spring Bean的类。base-package
的属性标识了<context:component-scan>
元素所扫描的包。
默认情况下,<context:component-scan>
查找使用构造型(stereotype)注解所标注的类,这些特殊的注解如下:
- @Component——通用的构造型注解,标识该类为Spring组件。
- @Controller——标识将该类定义为Spring MVC controller。
- @Repository——标识将该类定义为数据仓库。
- @Service——标识将该类定义为服务。
- 使用@Component标注的任意自定义注解。
修改beans.xml,使用<context:component-scan>
替代<bean>
的定义,如下所示:
<context:annotation-config /> <!-- <bean id="userDao" class="com.test.dao.UserDaoImpl"></bean> --> <!-- <bean id="userService" class="com.test.service.UserServiceImpl"></bean> --> <context:component-scan base-package="com.test"></context:component-scan>
修改UserDaoImpl,同样修改UserServiceImpl。
@Component //spring会自动检测 //@Qualifier("userDao") public class UserDaoImpl implements UserDao { public void Save(User user) { System.out.println("userName:" + user.getUserName()); System.out.println("password:" + user.getPassword()); } }
三、限定依赖
当有足够多的Bean满足装配条件,@Autowired注解没办法选择哪一个Bean才是它真正需要的,因此可以配合使用@Qualifier注解
如,除了UserDaoImpl实现接口UserDao,现在还有UserDaoImpl2实现,为UserDaoImpl2使用@Qualifier注解限定。
import com.test.domain.User; @Qualifier("userDaoOther") // 限定 public class UserDaoImpl1 implements UserDao { public void Save(User user) { System.out.println("userName1:" + user.getUserName()); System.out.println("password1:" + user.getPassword()); } }
在UserServiceImpl中,若需要装配UserDaoImpl2,除了使用@Autowired自动装配,还需要使用@Qualifier("userDaoOther")限定依赖
@Component("userService") public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDaoOther") // 限定依赖,最后依赖的Bean为UserDaoImpl2 private UserDao userDao; public void save(User user) { userDao.Save(user); System.out.println("saved!"); } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }