Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

Easter79
• 阅读 725

-------------------------注解扫面的bean的ID问题--------------------------

0.前提需要明白注解扫描出来的bean的id默认是类名首字母小写,当然可以指定id:

(1)只写注解不指定id

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

上面实际上是等价于xml中的下面配置:

<bean id="userServiceImpl" class="cn.qlq.service.UserService"></bean>

测试:(程序中获取spring容器中的bean)

@Action("saveUser")
    public String saveUser() {
        userService.saveUser();
        // 代码中获取容器中bean(依赖于web容器)
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        Object bean = wac.getBean("userServiceImpl");
        return "json";
    }

打断点查看bean:(获取到的是容器中的唯一bean的引用,且此对象的依赖关系也会取出来)

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

(2)指定id****属性:

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

上面等价于:

<bean id="xxx" class="cn.qlq.service.UserService"></bean>

测试代码:

@Action("saveUser")
    public String saveUser() {
        userService.saveUser();
        // 代码中获取容器中bean(依赖于web容器)
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        Object bean = wac.getBean("xxx");
        return "json";
    }

打断点查看bean:

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

(3)按照上面的思路,我们在不同的包下如果不指定id是不允许有类名字相同的类存在的,测试

 Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

 启动容器报错:(注解冲突)

org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [applicationContext.xml]; nested exception is java.lang.IllegalStateException: Annotation-specified bean name 'userServiceImpl' for bean class [cn.qlq.service.UserServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [cn.qlq.action.UserServiceImpl]
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:412)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
    at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:679)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1966)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

----------------研究Autowired和Resource两者不同----------------

  @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

1、共同点

  两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

2、不同点

(1)@Autowired

查看源码:

package org.springframework.beans.factory.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface Autowired {
    boolean required() default true;
}

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

public class TestServiceImpl {
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
        this.userDao = userDao;
    }
}

  @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao; 
}

  当然,您也可以通过 @Autowired 对方法或构造函数进行标注,如果构造函数有两个入参,分别是 bean1 和 bean2,@Autowired 将分别寻找和它们类型匹配的 Bean,将它们作为 CountryService (Bean1 bean1 ,Bean2 bean2) 的入参来创建 CountryService Bean。来看下面的代码:  对方法

public class Boss {     
    private Car car;     
    private Office office;     
    
     @Autowired    
    public void setCar(Car car) {     
        this.car = car;     
    }     
      
    @Autowired    
    public void setOffice(Office office) {     
        this.office = office;     
    }     
    …     
}   

  这时,@Autowired 将查找被标注的方法的入参类型的 Bean,并调用方法自动注入这些 Bean。而下面的使用方法则对构造函数进行标注:   

public class Boss {     
    private Car car;     
    private Office office;     
      
    @Autowired    
    public Boss(Car car ,Office office){     
        this.car = car;     
        this.office = office ;     
    }     
      
    …     
}  

(2)@Resource

查看源码:

package javax.annotation;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

    String name() default "";

    String lookup() default "";

    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 "";
}

  @Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

@Resource装配顺序:

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

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

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

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

例如以下写法都对:(当然下面只是写在成员变量中,也可以写在set方法上)

@Resource(type = cn.qlq.service.UserService.class)
    private UserService userService;

@Resource(name="userServiceImpl")
    private UserService userService;

    @Resource
    private UserService userService;

总结:

  • @Resource默认按照名称方式进行bean匹配(byName),@Autowired默认按照类型方式进行bean匹配(byType)
  • @Resource(import javax.annotation.Resource;)是J2EE的注解,@Autowired( import org.springframework.beans.factory.annotation.Autowired;)是Spring的注解

  Spring属于第三方的,J2EE是Java自己的东西。使用@Resource可以减少代码和Spring之间的耦合

----------------程序中获取spring容器中对象----------------

  我们知道在spring中容器启动有BeanFactory和ApplicationContext(其实现有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext 、XmlWebApplicationContext、AnnotationConfigApplicationContext四种基本是),所以我们首先获取到ApplicationContext,然后获取对象:(可以根据类型获取,也可以根据bean的Id获取)

// 获取webApplicationContext进而获取对象
        WebApplicationContext currentWebApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        TestUtils bean = currentWebApplicationContext.getBean(TestUtils.class);// 根据类型获取对象
        UserService bean2 = (UserService) currentWebApplicationContext.getBean("userService");// 根据id获取

我们打断点查看上面获取的bean:

(1)currentWebApplicationContext是取的XmlWebApplicationContext

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

(2)bean是获取容器的原生对象(通过IOC反射创建的)

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

(3)bean2是获取的AOP增强的代理对象。

Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象

   通过上面明白了spring ioc默认的都是原生对象  只有通过aop增强的对象才是代理对象。代理对象也是在原来的反射创建的对象上进行增强。

  实际上在web应用中,我们的dao层用的ORM框架,例如mybatis、hibernate与spring整合之后都获取的是增强过的代理对象;我们的service层一般也会用aop增强事务等操作。

点赞
收藏
评论区
推荐文章
Easter79 Easter79
3年前
spring注解
随着越来越多地使用Springboot敏捷开发,更多地使用注解配置Spring,而不是Spring的applicationContext.xml文件。Configuration注解:Spring解析为配置类,相当于spring配置文件Bean注解:容器注册Bean组件,默认id为方法名@Configurat
Easter79 Easter79
3年前
spring源码解析
前言上篇我们介绍了spring容器加载的方式,并重点介绍了基于xml配置解析和注解扫描两种容器加载的方式,封装和注册beandefinition的过程。今天我们分享BeanDefinition注册后的另一个重要过程bean的实例化过程的源码。容器加载流程!spring源码解析spring容器加载源码(bean实
Easter79 Easter79
3年前
Springboot中如何在Utils类中使用@Autowired注入bean
Springboot中如果希望在Utils工具类中,使用到我们已经定义过的Dao层或者Service层Bean,可以如下编写Utils类:1\.使用@Component注解标记工具类StatisticsUtils:2\.使用@Autowired(@Autowired和@Resource的区别不再介绍)注入我们需要的bean:3\.在工具类中
Stella981 Stella981
3年前
MyBatis接口(Bean)与配置信息(Mapper)绑定
目的MyBatis的XML配置文件解析成JAVA类并在内存中存储,但是在程序运行时需要对应的类去调用,而相应的调用类还没有实例化,现在流行的都是使用Spring去管理需要的对象,Spring提供2种方式,分别为XML与注解。下面来分析调用类的实例化及与配置绑定。1XML方式<bean id"menuMapper" cl
Stella981 Stella981
3年前
Spring Boot的@Service和@Autowired和@ComponentScan注解
SpringBoot的@Service和@Autowired和@ComponentScan注解SpringBoot项目的Bean装配默认规则是根据Application类(SpringBoot项目入口类)所在的包位置从上往下扫描,即只扫描该类所在的包及其子包。1.如果需要扫描其上层包中的类,则将其移动到其上层包中2.使用@ComponentSc
Stella981 Stella981
3年前
SpringBoot 2.0 系列006
SpringBoot2.0系列006启动实战之注解使用一些误区说明网上很多教程默认使用SpringBootApplcation注解,且只用这个即可扫描启动类包下所有的bean。而官方默认教程使用的是@EnableAutoConfiguration,这个注解
Wesley13 Wesley13
3年前
spring3.x注解自动注入
一、各种注解方式1.@Autowired注解(不推荐使用,建议使用@Resource)@Autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。@Autowired的标注位置不同,它们都会在Spring在初始化这个bean时,自动装配这个属性。要使@Autowired能够工作,还需要在配置文件中加入以下Xml代码
Easter79 Easter79
3年前
SpringMvc中@resource和@autowired区别
在java代码中使用@Autowired或@Resource注解方式进行装配这两个注解的区别是:@Autowired默认按类型装配,@Resource(https://my.oschina.net/u/929718)默认按名称装配,当找不到与名称匹配的bean才会按类型装配。如果我们想使用按名称装
Wesley13 Wesley13
3年前
1.1Spring Boot 环境配置和常用注解
SpringBoot常用注解:@Service:注解在类上,表示这是一个业务层bean@Controller:注解在类上,表示这是一个控制层bean@Repository:注解在类上,表示这是一个数据访问层bean@Component:注解在类上,表示通用bean,value不写默认就是类名首字母小写@Auto
Easter79 Easter79
3年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k