@Autowired 和 @Resource

定义

@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能用在:类、成员变量和方法上

出处不同

@AutowiredSpring定义的注解,所以@Autowired只能在Spring框架下使用

@Resource是JSR-250定义的注解,@Resource则可以与其他框架一起使用

装配顺序不同

@Autowired默认先按byType进行匹配,如果发现找到多个bean,则又按照byName方式进行匹配,如果还有多个,则报出异常。
@Autowired 和 @Resource_第1张图片

@Resource如果同时指定了name和type,流程如下:
@Autowired 和 @Resource_第2张图片
只是指定了@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;
    ......
}

假如有一个“动物”的接口 IAnimalDogImpl类实现了接口 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 中,注入后的 maplist 不再是 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 和 @Resource_第3张图片

相关报错

@Autowired 注入爆红(无法注入)

Could not autowire. No beans of ‘xxxService‘ type found.
在这里插入图片描述
比较常见的四种原因:(先查看各个配置文件是否爆红

1 如果使用注解配置 service 层 可能缺少了注解 (@Service)

2 如果用xml 配置 是否写了定义

3 注解正确的情况下:检查扫描包路径是否正确
检查各自组件的自动扫描组件

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项目启动的时候,就会抛出:BeanCurrentlyInCreationExceptionRequested 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

你可能感兴趣的:(报错debug,springboot,java,idea,zookeeper)