5. @Autowired与@Resource的区别

114 阅读3分钟

在上一节我们使用Autowired进行了bean的装配,@Autowired与@Resource都可以用来装配bean,但它们之前还是有一些区别,它们的区别具体体现为以下几点:

  • 来源不同
  • 对Constructor注入的支持不同
  • 查找顺序不同
  • 支持参数不同

1. 来源不同

@Resource位于包javax.annotation,是JDK原生的注解;

@Autowired位于包org.springframework.beans.factory.annotation ,是Spring2.5 之后引入的注解。

2. 对Constructor注入的支持不同

由于构造方法在对象创建的时候调用,执行的时期较早,由于jdk将@Resource的执行时期设计晚于构造方法调用的时期,因此在构造方法上使用@Resource注解编译器会报错:

image.png

3. 查找顺序不同

3.1 Bean在Spring中的存储方式

Bean在Spring中的存储方式是类似于Java中的HashMap的。

一个Bean对象有它的NameType,分别对应着xml文件中配置的Bean的idclass

也就是说通过同一个类可以创建两个id不同的Bean对象:

<bean id="user1" class="UserBean"></bean>
<bean id="user2" class="UserBean"></bean>

通过Java的数据结构HashMap<key, value>,我们可以通过key(键)找到对应的value(值),而Bean的Name+Type,就对应着key,而Bean对象就对应着value

3.2 @Autowired查找顺序

下面的代码中,DemoDao对应着TypedemoDao对应着Name

@Autowired
private DemoDao demoDao;

@Autowired是先通过Type查找,如果一个Type对应多个Bean,它才会去通过Name查找,如果找不到唯一的Name,那么就会抛出异常。

实验验证:

第一次,只有DemoDaoImpl实现DemoDao接口,UserService的代码如下,只有Bean的Type能匹配上,而Name无法匹配上:

@Service
public class UserService {
    @Autowired
    private DemoDao demoDao;

    public void doService() {
        demoDao.selectAll();
        System.out.println("Do User Service");
    }
}

成功找到Bean并运行:

select * from demo
Do User Service
Do User Controller.

第二次,我让DemoDaoImplUserDaoImpl都实现DemoDao接口:

@Repository
public class DemoDaoImpl implements DemoDao {

    @Override
    public void selectAll() {
        System.out.println("select * from demo");
    }
}
@Repository
public class UserDaoImpl implements DemoDao {
    @Override
    public void selectAll() {
        System.out.println("select * from user");
    }
}

运行程序发现报了一个没有唯一定义的Bean的异常:

NoUniqueBeanDefinitionException: No qualifying bean of type 'com.chenshu.dao.DemoDao' available: 

第三次,我同样让DemoDaoImplUserDaoImpl都实现DemoDao接口,并且让UserService中的NameDemoDaoImpl匹配上:

@Service
public class UserService {
    @Autowired
    private DemoDao demoDaoImpl;

    public void doService() {
        demoDaoImpl.selectAll();
        System.out.println("Do User Service");
    }
}

这次又能正常注入Bean,并成功调用Bean的方法:

select * from demo
Do User Service
Do User Controller.

上面的结论足以验证@Autowired的查找顺序是先通过Type查找再通过Name查找。

3.3 @Resource查找顺序

这里我直接给出结论:@Resource 是先根据名称查找,如果(根据名称)查找不到,再根据类型进行查找。

4. 支持参数不同

点开@Resource注解并查看它的Structure: image.png

点开@Autowired注解并查看它的Structure: image.png

对比二者,@Resource注解提供了更丰富的参数

4.1 引入需求

UserService代码如下:

@Service
public class UserService {
    @Autowired
    private DemoDao demoDao;

    public void doService() {
        demoDao.selectAll();
        System.out.println("Do User Service");
    }
}

此时有UserDaoImplDemoDaoImpl实现DemoDao接口,现在我想在不改变参数名的情况下,解决一个类对应两个Bean对象的问题

4.2 解决方案

方案一:通过@Resource注解

@Resource注解提供了一个name参数,通过该参数可以指定通过该参数的值来查找Bean:

@Service
public class UserService {
    @Resource(name = "userDaoImpl")
    private DemoDao demoDao;

    public void doService() {
        demoDao.selectAll();
        System.out.println("Do User Service");
    }
}

方案二:通过@Autowired注解

虽说@Resource能够通过参数便利地解决问题,但它还是有个硬伤:无法实现构造器注入。

@Autowired没有提供相应的参数实现该需求,但是我们可以通过额外的@Qualifier注解来实现:

@Service
public class UserService {
    @Autowired
    @Qualifier(value = "userDaoImpl")
    private DemoDao demoDao;

    public void doService() {
        demoDao.selectAll();
        System.out.println("Do User Service");
    }
}