@Autowired 和 @Resource
定义@Autowired对类成员变量、方法及构造函数进行标注,完成自动装配的工作@Resource在语义上被定义为通过其唯一的名称来标识特定的目标组件,其中声明的类型与匹配过程无关如果没有明确指定名称,则默认名称是从字段名称或设置方法(get、set方法)派生的。如果用在字段上,则采用字段名称如果用在在setter方法,它采用其属性名称(例如setProperty()方法,取property做为属
定义
@Autowired
对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@Resource
在语义上被定义为通过其唯一的名称来标识特定的目标组件,其中声明的类型与匹配过程无关
如果没有明确指定名称,则默认名称是从字段名称或设置方法(get、set方法)派生的。
如果用在字段上,则采用字段名称
如果用在在setter方法,它采用其属性名称(例如setProperty()方法,取property做为属性名称)。
区别
在Spring框架中,如果在Service层中需要注入其他依赖的对象,通常我们都会使用@Autowired或者@Resource注解,但是它们是有区别的
比如@Autowired跟Spring框架强耦合了, 如果换成其他框架,@Autowired就没作用了。而@Resource是JSR-250提供的,它是Java标准,绝大部分框架都支持
1 @Autowired 是通过 byType 的方式去注入的, 使用该注解,要求接口只能有一个实现类。
2 @Resource 可以通过 byName 和 byType的方式注入, 默认先按 byName的方式进行匹配,如果匹配不到,再按 byType的方式进行匹配。
3 @Qualifier 注解可以按名称注入, 但是注意是 类名。
包含的属性不同
@Autowired只包含一个参数:required,表示是否开启自动注入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。如下:
public @interface Autowired {
/**
* 是否开启自动注入,有些时候我们不想使用自动装配功能,可以将该参数设置成false。
*/
boolean required() default true;
}
public @interface Resource {
/**
* bean的名称
*/
String name() default "";
String lookup() default "";
/**
* Java类,被解析为bean的类型
*/
Class<?> type() default java.lang.Object.class;
enum AuthenticationType {
CONTAINER,
APPLICATION
}
/**
* 身份验证类型
*/
AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
/**
* 组件是否可以与其他组件之间共享
*/
boolean shareable() default true;
String mappedName() default "";
/**
* 描述
*/
String description() default "";
}
装配规则不同
@Autowired默认按byType自动装配,而@Resource默认byName自动装配
@Autowired如果要使用byName,需要使用@Qualifier一起配合。
@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。
注解应用的地方不同
@Autowired能够用在:构造器、方法、参数、成员变量和注解上
@Resource能用在:类、成员变量和方法上。
出处不同
@Autowired是Spring定义的注解,所以@Autowired只能在Spring框架下使用
@Resource是JSR-250定义的注解,@Resource则可以与其他框架一起使用
装配顺序不同
@Autowired默认先按byType进行匹配,如果发现找到多个bean,则又按照byName方式进行匹配,如果还有多个,则报出异常。
@Resource如果同时指定了name和type,流程如下:
只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean
只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行进行查找,如果找到就注入
什么时候用@Autowired,什么时候用@Resource
Autowired(来自于Spring)
按类型查找,书写方便,不用在后面跟名字,缺点:当一个Service有多个ServiceImpl去实现时,那么会报错,因为它不知道去实现哪一个@Qualifier 虽然用这个个可以解决这个错误,但是引起了效率低下,先按类型查找,再按名字查找
Resource(JDK自带)
按名字查找,后面要跟参数name,好处:当有多个Impl实现类时,可以通name快速找到
总结
当只有一个Impl实类的时候,随便用哪个都差不多
>=2 实现类的时候,最好用 @Resource,比 @Autowired + @Qualifier() 效率高
多态注入
参考一个接口有多个实现类,controller层调用指定实现类的三种方式(New、@Qualifier、@Resource)
同一接口有多个实现类,如何注入
接口:IAnimal
public Interface IAnimal{
......
}
实现类:DogImpl ,实现了IAnimal接口。
@Service("dogImpl") //只写@Service 默认 value 为首字母小写 daoImpl 和 @Component 效果一样
public class DaoImpl impliments IAnimal{
...
}
业务类:AnimalController
public class AnimalController {
@Autowired
private IAnimal animal;
......
}
假如有一个“动物”的接口 IAnimal, DogImpl类实现了接口 IAnimal, 且该接口只有 DogImpl这一个实现类,那么在引用实现类的时候,我们使用的是实现类的接口(像上面程序展示的那样)。Spring会按 byType的方式寻找接口的实现类,将其注入
假如有另一个实现类 CatImpl 也实现了接口 IAnimal, 这时候@Autowired去引用, 在同时存在两个实现类的情况下,会出现什么情况呢?
答:会报错。 这是由于 @Autowired 的特性决定的: @Autowired 的注入方式是 byType 注入, 当要注入的类型在容器中存在多个时,Spring是不知道要引入哪个实现类的,所以会报错。
使用 byName 方式
@Resource 默认是按照 byName 的方式注入的, 如果通过 byName 的方式匹配不到,再按 byType 的方式去匹配。所以上面的引用可以替换为:
public class AnimalController {
@Resource(name="dogImpl") //实现类1中 @Service注解中标定的名称,不写默认是下面的名字
private IAnimal dogImpl;
@Resource(name="catImpl") //实现类2中 @Service注解中标定的名称
private IAnimal catImpl;
}
@Qualifier 注解也是 byName的方式,但是与@Resource 有区别,@Qualifier 使用的是 类名。
public class AnimalController {
@Qualifier("DaoImpl") //实现类1的类名。注意区分与@Resource(name="dogImpl") 的区别。
private IAnimal dogImpl;
......
}
或者 @AutoWired + @Qualifier
@Controller
@RequestMapping("notice")
public class NoticeController{
@Autowired
@Qualifier("dogImpl")
private IAnimal animal;
}
注入一个接口的多个实现类在map/list 中
public interface TestService {
void test();
}
@Component("testOService")
public class TestOService implements TestService {
@Override
public void test() {
System.out.println("testOService");
}
}
@Component("testTwoService")
public class TestTwoService implements TestService {
@Override
public void test() {
System.out.println("testTwoService");
}
}
可以注入到 map 和 list 中,注入后的 map 和 list 不再是 null
@Service
public class UserInfoService {
@Autowired
private Map<String,TestService> testServiceMap;
@Resource
private List<TestService> services;
@PostConstruct
public void init(){
testServiceMap.get("testOService").test();
testServiceMap.get("testTwoService").test();
}
}

相关报错
@Autowired 注入爆红(无法注入)
Could not autowire. No beans of ‘xxxService‘ type found.
比较常见的四种原因:(先查看各个配置文件是否爆红)
1 如果使用注解配置 service 层 可能缺少了注解 (@Service)
2 如果用xml 配置 是否写了定义
3 注解正确的情况下:检查扫描包路径是否正确。
(检查各自组件的自动扫描组件<context:component-scan base-package = “XXXX”) 或者(Spring自动扫描context:annotation-config/)
4 service 类中 实现接口 存在相同方法名的接口
A component required a bean of type ‘xxxService’ that could not be found.
在 impl 中 用的 @Autowired 注入,但是 service 没写 @Service 或者 用的 @Resource 有重名 service
可能是 注入了 service,但是service没有 实现类
警告提示Field injection is not recommended
直接在变量上注解 @Autowired 有个警告提示Field injection is not recommended
Field injection is not recommended
Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".
变量依赖注入是不被建议的方式,它建议“总是采用构造器注入的方式建立依赖注入”
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findAllById(int id) {
return userMapper.findAllById(id);
}
}
依赖注入的三种方式
变量注入(Field Injection)
开发中最常见的注入方式
@Autowired
private UserMapper userMapper;
优点
1. 注入方式简单:加入要注入的字段,附上注解@AutoWired
2. 整体代码简洁明了
缺点
1. 对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NullPointException的存在
2. 使用field注入可能会导致循环依赖
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
构造器注入(Constructor Injection)
显式注明必须强制注入。通过强制指明依赖注入来保证这个类的运行
private UserMapper userMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
优点
1. 依赖不可变 components as immutable objects ,即注入对象为final
2. 依赖不可为空required dependencies are not null,省去对注入参数的检查。
当要实例化FooController的时候,由于只有带参数的构造函数,spring注入时需要传入所需的参数,所以有两种情况:
1) 有该类型的参数传入 => ok;
2) 无该类型参数传入,报错
3. 提升了代码的可复用性:非IOC容器环境可使用new实例化该类的对象。
4. 避免循环依赖:如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?
从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错。
缺点
当注入参数较多时,代码臃肿。
setter方法注入 (Setter Injection)
是一种选择注入,可有可无,即使没有注入这个依赖,那么也不会影响整个类的运行
private UserMapper userMapper;
@Autowired
public void setUserMapper(UserMapper userMapper){
this.userMapper = userMapper;
}
优点
1. 相比构造器注入,当注入参数太多或存在非必须注入的参数时,不会显得太笨重
2. 允许在类构造完成后重新注入
总结
依赖注入的核心思想之一就是被容器管理的类 不应该依赖被容器管理的依赖,换成白话来说就是如果这个类使用了依赖注入的类,那么这个类摆脱了这几个依赖必须也能正常运行。然而使用变量注入的方式是不能保证这点的。
既然使用了依赖注入方式,那么就表明这个类不再对这些依赖负责,这些都由容器管理,那么如何清楚的知道这个类需要哪些依赖呢?
它就要使用set方法方式注入或者构造器注入
Spring3.0官方文档建议使用setter注入 覆盖构造器注入。
Spring4.0官方文档建议使用构造器注入。
1-> 如果注入的属性是必选的属性,则通过构造器注入。
2-> 如果注入的属性是可选的属性,则通过setter方法注入。
3-> 至于field注入,不建议使用。
如果你使用的是构造器注入
恭喜你,当你有十几个甚至更多对象需要注入时,你的构造函数的参数个数可能会长到无法想像。
如果你使用的是field反射注入
如果不使用Spring框架,这个属性只能通过反射注入,太麻烦了!这根本不符合JavaBean规范。
还有,当你使用不是用Spring创建的对象时,还可能引起NullPointerException。
并且,你不能用final修饰这个属性。
如果你使用的是setter方法注入
那么你将不能将属性设置为final。
更多推荐


所有评论(0)