@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(来自于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是不知道要引入哪个实现类的
,所以会报错。
@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;
}
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();
}
}
Could not autowire. No beans of ‘xxxService‘ type found.
比较常见的四种原因:(先查看各个配置文件是否爆红
)
1 如果使用注解配置 service 层 可能缺少了注解 (@Service)
2 如果用xml 配置
是否写了定义
3 注解正确的情况下:检查扫描包路径是否正确
。
(检查各自组件的自动扫描组件
4 service 类中 实现接口 存在相同方法名的接口
在 impl 中 用的 @Autowired 注入,但是 service 没写 @Service 或者 用的 @Resource 有重名 service
可能是 注入了 service,但是service没有 实现类
直接在变量上注解 @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);
}
}
开发中最常见的注入方式
@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;
}
显式注明
必须强制注入
。通过强制指明依赖注入
来保证这个类的运行
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的时候才会报错。
缺点
当注入参数较多时,代码臃肿。
是一种选择注入
,可有可无
,即使没有注入这个依赖,那么也不会影响整个类的运行
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。