美文网首页一些收藏
@Autowired与@Resource有何区别

@Autowired与@Resource有何区别

作者: AC编程 | 来源:发表于2022-03-03 13:05 被阅读0次

    一、相同点

    这个两个注解都是用来完成组件的装配的,即利用依赖注入(DI),完成对IOC容器当中各个组件之间依赖的装配赋值。

    二、不同点

    2.1 来源不同
    2.1.1 @Resource

    @Resource是javaEE的注解,它遵循的是JSR-250规范,需要导入包javax.annotation.Resource

    2.1.2 @Autowired

    @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired

    2.2 装配顺序不同
    2.2.1 @Resource
    • 默认按照byName方式进行装配,属于J2EE自带注解,没有指定name时,name指的是变量名。

    • 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

    • 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

    • 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常。

    • 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配。如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

    2.2.2 @Autowired
    • 默认按byType自动注入,是Spring的注解。
    • 默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,@Autowired(required = false)
    • 按类型装配的过程中,如果发现找到多个bean,则又按照byName方式进行比对,如果还有多个,则报出异常。
    装配顺序图
    1、@Autowired的装配顺序图
    1
    2、@Resource的装配顺序图
    2.1 如果同时指定了name和type
    2
    2.2 如果指定了name
    3
    2.3 如果指定了type
    4
    2.4 如果既没有指定name,也没有指定type
    5

    三、选择

    @Autowired跟Spring强耦合了,如果换成了JFinal等其他框架,功能就会失效。而@Resource是JSR-250提供的,它是Java标准,绝大部分框架都支持,因此个人更倾向于选@Resource

    四、样例代码

    4.1 接口只有一个实现类
    4.1.1 接口
    public interface Animal {
        String talk();
    }
    
    4.1.2 实现类
    /**
     * 注意:@Service 默认value=dog,首字母小写
     */
    @Service
    public class Dog implements Animal {
        @Override
        public String talk() {
            return "Dog talk:";
        }
    }
    
    4.1.3 装配类
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:既没有指定name,又没有指定type,则默认按照byName方式进行装配。
         * 2、装配过程:通过byName没有找到name=animal的对象,则用byType去装配,找到了type为Animal的对象Dog,这里注入的是Dog对象。
         */
        @Resource
        private Animal animal;
    
        /**
         * 1、装配方式:既没有指定name,又没有指定type,则默认按照byName方式进行装配。
         * 2、装配过程:通过byName找到了name=dog的对象,直接注入。
         */
        @Resource
        private Animal dog;
    
        /**
         * 1、装配方式:默认按byType自动注入
         * 2、装配过程:通过byType去装配,找到type为Animal的对象Dog,这里注入的是Dog对象。
         */
        @Autowired
        private Animal animal2;
    
        @GetMapping("/talk")
        public void animalTalk() {
            animal.talk();
            dog.talk();
            animal2.talk();
        }
    }
    
    4.1.4 结果

    可以正常编译启动,运行正常。

    4.2 接口有多个实现类
    4.2.1 接口
    public interface Animal {
        String talk();
    }
    
    4.2.2 实现类
    /**
     * 注意:@Service 默认value=dog,首字母小写
     */
    @Service
    public class Dog implements Animal {
        @Override
        public String talk() {
            return "Dog talk:";
        }
    }
    
    
    /**
     * 注意:@Service 默认value=cat,首字母小写
     */
    @Service
    public class Cat implements Animal {
        @Override
        public String talk() {
            return "Cat talk";
        }
    }
    
    4.2.3 装配类
    4.2.3.1 @Resource默认按byName-失败
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:既没有指定name,又没有指定type,则默认按照byName方式进行装配。
         * 2、装配过程:通过byName没有找到name=animal的对象,则用byType去装配,找到了type为Animal的对象有两个:Dog、Cat,因此注入失败。
         */
        @Resource
        private Animal animal;
    
    }
    

    启动错误信息

    No qualifying bean of type 'com.alanchen.Animal' available: expected single matching bean but found 2: cat,dog
    
    4.2.3.2 @Resource默认按byName-成功
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:既没有指定name,又没有指定type,则默认按照byName方式进行装配。
         * 2、装配过程:通过byName找到了name=dog的对象,直接注入。
         */
        @Resource
        private Animal dog;
    
    }
    
    4.2.3.3 @Resource指定name按byName-成功
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
         * 2、装配过程:通过byName找到了name=dog的对象,直接注入。
         */
        @Resource(name = "dog")
        private Animal animal;
    
    }
    
    4.2.3.4 @Autowired默认按byType-失败
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:默认按byType自动注入
         * 2、装配过程:通过byType去装配,找到了type为Animal的对象有两个:Dog、Cat,因此注入失败。
         */
        @Autowired
        private Animal animal;
    }
    

    启动错误信息

    Field animal in com.alanchen.ZooController required a single bean, but 2 were found:
    
    4.2.3.5 @Autowired和@Qualifier一起配合按byName-成功
    @RestController
    @Controller(value = "/zoo")
    @RequestMapping("/zoo")
    public class ZooController {
    
        /**
         * 1、装配方式:和@Qualifier一起配合按byName
         * 2、装配过程:通过byName找到了name=dog的对象,直接注入。
         */
        @Autowired
        @Qualifier("dog")
        private Animal animal;
    }
    

    五、Spring注解

    5.1 @Qualifier

    @Qualifier意思是合格者,一般跟@Autowired配合使用,需要指定一个bean的名称,通过bean名称就能找到需要装配的bean。

    5.2 @Primary

    当我们使用自动配置的方式装配bean时,如果这个bean有多个候选者,假如其中一个候选者具有@Primary注解修饰,该候选者会被选中,作为自动配置的值。

    六、@Autowired的使用范围

    平时我们使用@Autowired都是使用在成员变量上,但@Autowired的强大之处,远非如此。先看看@Autowired注解的定义:

    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
        /**
         * Declares whether the annotated dependency is required.
         * <p>Defaults to {@code true}.
         */
        boolean required() default true;
    
    }
    
    6.1 成员变量

    在成员变量上使用Autowired注解

    @Service
    public class UserService {
    
        @Autowired
        private IUser user;
    }
    
    6.2 构造器

    在构造器上使用Autowired注解

    @Service
    public class UserService {
    
        private IUser user;
    
        @Autowired
        public UserService(IUser user) {
            this.user = user;
            System.out.println("user:" + user);
        }
    }
    

    在构造器上加Autowired注解,实际上还是使用了Autowired装配方式,并非构造器装配。

    6.3 方法

    在普通方法上加Autowired注解

    @Service
    public class UserService {
    
        @Autowired
        public void test(IUser user) {
           user.say();
        }
    }
    

    Spring会在项目启动的过程中,自动调用一次加了@Autowired注解的方法,我们可以在该方法做一些初始化的工作。

    也可以在setter方法上Autowired注解:

    @Service
    public class UserService {
    
        private IUser user;
    
        @Autowired
        public void setUser(IUser user) {
            this.user = user;
        }
    }
    
    6.4 参数

    可以在构造器的入参上加Autowired注解

    @Service
    public class UserService {
    
        private IUser user;
    
        public UserService(@Autowired IUser user) {
            this.user = user;
            System.out.println("user:" + user);
        }
    }
    

    也可以在非静态方法的入参上加Autowired注解

    @Service
    public class UserService {
    
        public void test(@Autowired IUser user) {
           user.say();
        }
    }
    

    七、@Autowired自动装配多个实例

    平时我们一般都是通过@Autowired自动装配单个实例,但这里我会告诉你,它也能自动装配多个实例。

    7.1 实现自动装配多个实例

    将UserService方法调整一下,用一个List集合接收IUser类型的参数

    @Service
    public class UserService {
    
        @Autowired
        private List<IUser> userList;
    
        @Autowired
        private Set<IUser> userSet;
    
        @Autowired
        private Map<String, IUser> userMap;
    
        public void test() {
            System.out.println("userList:" + userList);
            System.out.println("userSet:" + userSet);
            System.out.println("userMap:" + userMap);
        }
    }
    

    增加一个controller

    @RequestMapping("/u")
    @RestController
    public class UController {
    
        @Autowired
        private UserService userService;
    
        @RequestMapping("/test")
        public String test() {
            userService.test();
            return "success";
        }
    }
    

    userList、userSet和userMap都打印出了两个元素,说明@Autowired会自动把相同类型的IUser对象收集到集合中。

    7.2 用途

    可以与策略模式来搭配使用,变种后的策略模式:

    1、不需要content类来按条件选择具体的策略类。

    2、一个策略接口,多个具体策略实现类。

    3、每个具体策略实现类,通过入参参数判断自己是否需要执行,如果不需要执行,直接返回。

    4、调用策略的client类,通过@Autowired自动装配多个实例,一次性拿到所有策略实现类,通过循环调用这些策略实现类。

    5、有新的策略实现,新建新的具体实现类就可以了,不需要像以前一样去修改content类,真正实现了开闭原则。

    八、@Autowired注入失败场景

    8.1 没有加@Service注解

    在类上面忘了加@Controller@Service@Component@Repository等注解,Spring就无法完成自动装配的功能。

    8.2 注入Filter或Listener

    web应用启动的顺序是:listener->filter->servlet。众所周知,SpringMvc的启动是在DisptachServlet里面做的,而它是在listener和filter之后执行。如果我们想在listener和filter里面@Autowired某个bean,肯定是不行的,因为filter初始化的时候,此时bean还没有初始化,无法自动装配。如果工作当中真的需要这样做,我们该如何解决这个问题呢?答案是使用WebApplicationContextUtils.getWebApplicationContext获取当前的ApplicationContext,再通过它获取到bean实例。

    8.3 注解未被@ComponentScan扫描

    通常情况下,@Controller@Service@Component@Repository@Configuration等注解,是需要通过@ComponentScan注解扫描,收集元数据的。但是,如果没有加@ComponentScan注解,或者@ComponentScan注解扫描的路径不对,或者路径范围太小,会导致有些注解无法收集,到后面无法使用@Autowired完成自动装配的功能。有个好消息是,在springboot项目中,如果使用了@SpringBootApplication注解,它里面内置了ComponentScan注解的功能。

    8.4 循环依赖问题

    如果A依赖于B,B依赖于C,C又依赖于A,这样就形成了一个死循环。

    Spring的bean默认是单例的,如果单例bean使用@Autowired自动装配,大多数情况,能解决循环依赖问题。但是如果bean是多例的,会出现循环依赖问题,导致bean自动装配不了。还有有些情况下,如果创建了代理对象,即使bean是单例的,依然会出现循环依赖问题。

    相关文章

      网友评论

        本文标题:@Autowired与@Resource有何区别

        本文链接:https://www.haomeiwen.com/subject/wloyrrtx.html