一、分析
Controller 组件中往往需要用到 Service 组件的实例,Service 组件往往需要用到 Repository 组件的实例。
既然通过注解可以标识一个 Spring 组件,那么Spring 也应该可以通过注解的方式帮我们实现属性的装配。
二、实现依据
在指定要扫描的包时,<context:component-scan> 元素会自动注册一个 bean 的后置处理器:AutowiredAnnotationBeanPostProcessor 的实例。该后置处理器可以自动装配标记了 @Autowired、@Resource 或 @Inject 注解的属性。
三、@Autowired 注解使用
applicationContext.xml
<context:component-scan base-package="com.achang"></context:component-scan>
BookDao
@Repository
public class BookDao {
public void saveBook() {
System.out.println("保存了一本书");
}
}
BookService
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void save(){
System.out.println("正在调用Dao保存图书。。。。");
bookDao.saveBook();
}
}
BookServlet
@Controller
public class BookServlet {
//自动装配:自动为这个属性赋值
@Autowired
private BookService bookService;
public void doGet(){
bookService.save();
}
}
测试:
public class IOCTest {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test2(){
BookServlet bookServlet = (BookServlet) ioc.getBean("bookServlet");
bookServlet.doGet();
//正在调用Dao保存图书。。。。
//保存了一本书
}
使用 @Qualifier 可以指定要装配的组件的名字 id;
@Autowired 的自动装配就是一定装配上,可以设置 required 属性,默认为 true,设置为 false 后,如果在容器中找不到就不进行装配,且不会报错。
@Controller
public class BookServlet {
@Qualifier(value = "bookServiceExt")
@Autowired(required = false)
private BookService bookService;
public void doGet() {
bookService.save();
}
}
四、原理和区别
(1)先按照类型去找,如果找到了,装配成功;
(2)如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配;
(3)如果根据成员变量名作为id还是找不到bean,可以使用@Qualifier注解明确指定目标bean的id;
(4)@Autowired注解的required属性指定某个属性允许不被设置;
@Autowired 原理【***】
<!--@Autowired原理
@Autowired
private BookService bookService;
1、先按照类型去容器找到对应的组件; bookService = ioc.getBean("BookService.class");
①匹配成功;装配
②匹配失败;抛出异常报错(NoSushBeanDefinitionException)
③匹配多个;
按照变量名作为id继续匹配
1)匹配成功;装配
2)匹配失败;抛出异常报错
失败原因:Spring会使用id在容器中匹配
【可使用@Qualifier("xxxXxx")指定一个新id匹配】
1)匹配成功;装配
2)匹配失败;抛出异常报错
发现AutoWired标注的自动装配的属性默认是一定装配上,任何情况下没匹配就是抛出异常报错
找到就装配,找不到就拉倒
@Autowired(required=false):默认为true,给为匹配上的赋值为null
先按照类型找,找到装配,找不到就按变量名做id找,找到装配,找不到抛异常。
可以用@Qualifier指定id去匹配;此注解可修饰参数
五、在形参位置使用 @Qualifier 注解
观看源码,可以看到 Autowired 注解可以使用在构造器,字段,方法和注解上面。
Qualifier 注解可以使用在字段,方法,参数,类和注解上面。
案例:
/**
* 方法上有 @Autowired 注解
* 1. 这个方法也会在 bean 创建的时候自动运行
* 2. 这个方法的每一个参数都会自动注入值
* 3. 可以使用Qualifier注解给参数添加注解
* 4. 使用 required 属性表示是否必须要装配
* @param bookDao
*/
@Autowired(required = false)
public void saveBook(BookDao bookDao, @Qualifier("bookService") BookService bookService) {
System.out.println("Spring运行了这个方法" + bookDao + "===" + bookService);
}
五、@Autowired 注解
① 该注解根据类型实现自动装配;
② 构造器、普通字段(即使是非 public)、一些具有参数的方法都可以应用 @Autowired 注解;
③ 默认情况下,所有使用 @Autowired 注解的属性都需要被设置,当 Spring 找不到匹配的 bean 装配属性时,会抛出异常;
④ 若某一属性允许不被设置,可以设置@Autowired 注解的required 属性为 false。
⑤ 默认情况下,当 IOC 容器里存在多个类型兼容的 bean 时,Spring 会尝试匹配 bean 的 id是否与变量名相同,如果相同则进行装配,如果 bean 的 id 值不相同,通过类型的自动装配将无法工作。
此时可以在 @Qualifier 注解里提供 bean 的名称,Spring 甚至允许在方法的形参上标注 @Qualifier 注解以指定注入 bean 的名称;
⑥ @Autowired 注解也可以应用在数组类型的属性上,此时 Spring 将会把所有匹配 bean 进行自动装配;
⑦ @Autowired 注解也可以应用在集合属性上,此时 Spring 读取集合的类型信息,然后自动装配所有与之兼容的 bean;
⑧ @Autowired 注解用在 java.util.Map 上时,若该 Map 的键值为 String,那么 Spring 将自动装配与值类型兼容的 bean 作为值,并以 bean 的 id 值作为键。
六、@Autowired和@Resource、@Inject的区别
@Autowired、@Resource、@Inject都可以作为注入注解
@Autowired:最强大;Spring的注解
①扩展性差,依赖Spring容器框架
@Resource:j2ee;java的标准【jdk标准】
①扩展性强:切换成另一个容器框架,其还会可以使用
【@Inject:在EJB环境下使用()】
@Resource 注解要求提供一个 bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 bean 的名称
@Inject 和 @Autowired 注解一样也是按类型注入匹配的 bean,但没有 required 属性。
七、代码示例
表示控制器层:
//@Controller(value="userCtrl") //使用 value 属性来给自动生成的 bean 指定 id,默认是类名的首字母表小写
@Controller("userCtrl") // 如果只设置 value 属性,value可以省略;但如果有多个属性,各个属性间用逗号分隔
public class UserController {
@Autowired(required=true) //自动装配,required 该属性表示是否必须自动装配成功
private IUserService userService; // true 表示 IOC 容器中必须有一个 bean 与该属性相匹配,然后完成自动装配,不然 Spring 报错
// false 表示 IOC 容器中可以没有与该属性匹配的 bean,即不必自动装配成功,Spring 也不会报错
public void addUser() {
userService.addUser();
}
public UserController() {
super();
System.out.println("UserController构造器");
}
}
业务逻辑层:
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
/*
@Autowired // 这两个注解必须同时使用
@Qualifier(value="userDaoMybatisImpl") // 当有多个同类型的bean时,使用该注解指定 bean 的id来给形参赋值,与方法名无关
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
*/
@Override
public void addUser() {
userDao.addUser();
}
public UserServiceImpl() {
super();
System.out.println("UserServiceImpl构造器");
}
}
持久层:
类一:
@Repository
public class UserDaoImpl implements IUserDao {
@Override
public void addUser() {
System.out.println("UserDaoImpl:添加成功");
}
public UserDaoImpl() {
super();
System.out.println("UserDaoImpl构造器");
}
}
类二:
@Repository
public class UserDaoMybatisImpl implements IUserDao {
@Override
public void addUser() {
System.out.println("添加成功");
}
public UserDaoMybatisImpl() {
super();
System.out.println("UserDaoMybatisImpl构造器");
}
}
八、注解配置 bean 总结
基于注解的组件化管理:
@Component,@Controller(控制层),@Service(业务逻辑层),@Repository(持久层)
这四个注解的功能完全相同,都用来标识组件,只是在实际开发中,要在实现不同功能的类上加上相应的注解,以便区分;
完成组件化管理的过程:
1、在需要被 Spring 管理的类上加上对应的注解
2、在配置文件中通过 <context:component-scan> 标签对所设置的包结构进行扫描,就会将加上注解的类,作为spring的组件进行加载,
组件(即为 Spring 中管理的 bean)
作为 Spring 的组件进行加载:会自动在 Spring 的配置文件中生成相对应的bean,这些 bean的 id 默认会以类的首字母小写为值,也可以通过注解的 value 属性来给自动生成的 bean 指定 id。(如:@Controller(value="userCtrl") 或 @Controller("userCtrl"),只有 value 属性时,value 可以省略)
使用注解自动装配:
自动装配:在需要赋值的非字面量属性上,加上 @Autowired 注解,就可以在spring容器中,通过不同的方式匹配到相对应的bean;
@Autowired 装配时,默认使用 byType 的方式进行匹配,此时要求spring容器中只有一个能够为其赋值,否则会报错
当byType 实现不了装配时,会自动切换到 byName 的方式匹配,此时要求spring容器中,有一个bean的id和属性名一致
使用@Autowired 自动装配时,如果匹配到多个能够赋值的 bean,可以使用 @Qualifier(value="beanId") 来指定一个 bean 给属性赋值;
@Autowired 和 @Qualifier(value="beanId") 可以一起作用于带形参方法上,此时,@Qualifier(value="beanId") 将用 value 所指定 bean 作用于 方法的形参