1.先了解什么是@Autowired
@Autowired
是 Spring 框架中用于实现依赖自动注入的核心注解。它通过类型匹配自动装配 Spring 容器中的 Bean,极大简化了依赖管理。2.使用@Autowired的场景
2.1 @Autowired的使用
1.首先有BookDao、BookDaoImpl、BookService、BookServiceImpl、App 类
public interface BookDao { public void save(); } @Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } public interface BookService { public void save(); } @Service public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } } //测试 public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookService bookService = ctx.getBean(BookService.class); bookService.save(); } }
2.运行main方法肯定会有错误
出现问题的原因是,在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,但是目前是没有提供配置注入BookDao的,所以bookDao对象为Null,调用其save方法就会报
控指针异常
。3.对于这个问题使用注解该如何解决?
有很多种解决方式,本次我们使用@Autowired
(1) 在BookServiceImpl类的bookDao属性上添加
@Autowired
注解@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; // public void setBookDao(BookDao bookDao) { // this.bookDao = bookDao; // } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
注意:
@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是
写在属性上并将setter方法删除掉(
使用
XML 配置注入必须要有set方法)
为什么setter方法可以删除呢?
自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
普通反射只能获取public修饰的内容
暴力反射除了获取public修饰的内容还可以获取private修改的内容
所以此处无需提供setter方法
(2)@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如有BookDaoImpl2、BookDaoImpl2
@Repository public class BookDaoImpl1 implements BookDao { public void save() { System.out.println("book dao save ...2"); } } @Repository public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2"); } }
这个时候再次运行App,就会报错
此时,按照类型注入就无法区分到底注入哪个对象,解决方案:
按照名称注入
先给两个Dao类分别起个名称
@Repository("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } @Repository("bookDao2") public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2" ); } }
此时就可以注入成功,但是得思考个问题:
@Autowired是按照类型注入的,给BookDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?
@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫
bookDao
而容器中也有一个booDao
,所以可以成功注入。分析下面这种情况是否能完成注入呢?
不行,因为按照类型会找到多个bean对象,此时会按照@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; } @Repository("bookDao1") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } @Repository("bookDao2") public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2" ); } }
bookDao
名称去找,因为IOC容器只有名称叫bookDao1
和bookDao2
,所以找不到,会报NoUniqueBeanDefinitionException
2.2 注解实现按照名称注入
当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候该如何解决,就需要使用到
@Qualifier
来指定注入哪个名称的bean对象。@Service public class BookServiceImpl implements BookService { @Autowired @Qualifier("bookDao1") private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
@Qualifier注解后的值就是需要注入的bean的名称。
注意:@Qualifier不能独立使用,必须和@Autowired一起使用