zoukankan      html  css  js  c++  java
  • Spring5【五】Bean 的自动装配及注解开发

    7、bean 的自动装配

    • 自动装配是使用 Spring 满足 bean 依赖的一种方式

    • Spring 会在应用上下文中为某个 bean 寻找其依赖的 bean,即在上下文自动寻找并自动给 bean 装配属性

    由于在手动配置 xml 过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。采用自动装配将避免这些错误,并且使配置简单化。

    在 Spring 中有三种装配方式:

    1. 在 xml 中显式的配置

    2. 在 Java 中显式的配置

    3. 隐式的自动装配 bean【重要】

    Spring 的自动装配需要从两个角度来实现,或者说是两个操作:

    1. 组件扫描(component scanning):Spring 会自动发现应用上下文中所创建的 bean;
    2. 自动装配(autowiring):Spring 自动满足 bean 之间的依赖,也就是我们说的 IoC/DI;

    组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

    推荐不使用自动装配xml配置,而使用注解。

    7.1 测试

    环境搭建:一个人有两个宠物

    public class Dog {
        public void shout(){
            System.out.println("汪~");
        }
    }
    
    public class Cat {
        public void shout(){
            System.out.println("喵~");
        }
    }
    
    public class People {
        private Cat cat;
        private Dog dog;
        private String name;
        
        set/get/toString...
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="cat" class="com.song.pojo.Cat"/>    
        <bean id="dog" class="com.song.pojo.Dog"/>
        
        <bean id="people" class="com.song.pojo.People">
            <property name="name" value="张三"/>
            <property name="cat" ref="cat"/>
            <property name="dog" ref="dog"/>
        </bean>
    </beans>
    
    public class MyTest {
        @Test
        public void test(){
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            People people = context.getBean("people", People.class);
            people.getCat().shout(); // 喵~
            people.getDog().shout(); // 汪~
        }
    }
    结果正常输出
    

    7.2 byName 自动装配

    autowire byName (按名称自动装配)

    修改bean配置,增加一个属性 autowire="byName",使用 ref 的 property 可以省略:

    <bean id="cat" class="com.song.pojo.Cat"/>
    <bean id="dog" class="com.song.pojo.Dog"/>
    
    <!--
        byName: 会自动在容器上下文中查找,和自己对象 set 方法后面的值对应的 beanid
    -->
    <bean id="people" class="com.song.pojo.People" autowire="byName">
        <property name="name" value="张三"/>
        <!--<property name="cat" ref="cat"/>-->
        <!--<property name="dog" ref="dog"/>-->
    </bean>
    

    结论:

    当一个bean节点带有 autowire byName 的属性时:

    1. 将查找其类中所有的 set 方法名,例如 setCat,获得将 set 去掉并且首字母小写的字符串,即 cat。
    2. 去 spring 容器中寻找是否有此字符串名称 id 的对象。
    3. 如果有,就取出注入;如果没有,就报空指针异常。

    即,byName 必须保证 bean 的 id 唯一,并且这个 bean 要和自动注入的属性的 setXxx 方法的名字 xxx(将 set 去掉并且首字母小写) 一致。

    7.3 byType 自动装配

    autowire byType (按类型自动装配)

    修改bean配置,增加一个属性 autowire="byType":

    <bean id="cat" class="com.song.pojo.Cat"/>
    <bean class="com.song.pojo.Dog"/>
    
    <!--
        byType:会自动在容器上下文中查找,和自己对象 属性类型相同的 bean
    -->
    <bean id="people" class="com.song.pojo.People" autowire="byType">
        <property name="name" value="张三"/>
        <!--<property name="cat" ref="cat"/>-->
        <!--<property name="dog" ref="dog"/>-->
    </bean>
    

    结论:

    • byType 必须保证所有 bean 的 class 唯一,并且这个 bean 需要和自动注入的属性的类型一致。

    • byType 必须保证类型全局唯一。即:同一类型的对象,在spring容器中唯一,如果不唯一,会报不唯一的异常 NoUniqueBeanDefinitionException

    注意:对于 byName 自动装配,自动注入的属性的 bean 的 id 是可以省略的。

    7.4 使用注解实现自动装配

    The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.

    jdk1.5 开始支持注解,spring2.5 开始全面支持注解。

    使用注解的准备工作:

    1. 导入约束:context 约束

    2. 配置注解的支持:<context:annotation-config/>

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
              https://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              https://www.springframework.org/schema/context/spring-context.xsd">
      
          <context:annotation-config/>
      </beans>
      

    @Autowried

    直接在属性上使用即可,也可以在 set 方式上使用。

    使用 Autowired 可以不用编写 set 方法,前提是这个自动装配的属性在 IOC(Spring)容器中存在,且符合类型 byType。

    测试

    1. 在属性上使用 @Autowired 注解:

      public class People {
          @Autowired
          @Qualifier(value = "cat1")
          private Cat cat;
          @Autowired
          private Dog dog;
          private String name;
          
          getter方法……
      }
      
    2. 配置文件内容:

      <!--开启注解支持-->
      <context:annotation-config/>
      
      <bean id="cat" class="com.song.pojo.Cat"/>
      <bean id="dog" class="com.song.pojo.Dog"/>
      <bean id="people" class="com.song.pojo.People"/>
      
    3. 测试结果:成功输出

    总结

    @Autowired 首先按类型(byType)自动装配;如果同一类型有多个 bean 存在,再按照属性名(byName)进行装配,即 bean 的 id 与属性名一致;如果属性名装配不成功,可以使用 @Qualifier(value = “xxx”)装配与 id 一致的属性名。

    扩展

    Autowired 源码:

    public @interface Autowired {
     boolean required() default true;
    }
    

    关于对象为空的问题

    • Autowired 的 required 属性

      required 属性默认为 true,对象必须存对象,不能为 null;如果为 false,则这个对象可以为 null。

      // 如果显式定义了 Autowired 的 required 属性为false,说明这个对象可以为 null,否则不允许为空
      @Autowired(required = false)
      
    • @Nullable

      字段标记了这个注解,说明这个字段可以为 null

    @Qualifier

    如果 @Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,可以使用@Qualifier(value = "xxx") 去配置 @Autowired 的使用,指定一个唯一的 bean 对象注入(byName)。

    注意:@Qualifier(value = "xxx") 不能单独使用

    测试

    1. 配置文件修改内容,保证类型存在,但名字不为类的默认名字!

      <!--开启注解支持-->
      <context:annotation-config/>
      <bean id="cat1" class="com.song.pojo.Cat"/>
      <bean id="cat111" class="com.song.pojo.Cat"/>
      <bean id="dog2" class="com.song.pojo.Dog"/>
      <bean id="dog222" class="com.song.pojo.Dog"/>
      <bean id="people" class="com.song.pojo.People"/>
      
    2. 没有加 @Qualifier 测试,直接报错

    3. 在属性上添加 @Qualifier 注解

      public class People {
          @Autowired(required = false)
          @Qualifier(value = "cat111")
          private Cat cat;
          @Autowired
          @Qualifier(value = "dog222")
          private Dog dog;
          private String name;
      }
      
    4. 测试结果:成功输出

    @Resource

    @Resource 如果有指定的 name 属性,先按该属性进行 byName 方式查找装配;其次再进行默认的 byName 方式进行装配;如果以上都不成功,则按 byType 的方式自动装配;都不成功,则报异常。

    public class People {
        @Resource(name = "cat2")
        private Cat cat;
        @Resource
        private Dog dog;
        private String name;
    }
    

    小结:

    @Autowired 和 @Resource 异同:

    • 作用相同都是用注解方式注入对象,都是用来自动装配 bean 的,都可以放在属性字段,或写在 setter 方法上。

    • @Autowired (属于spring规范)默认通过 byType 的方式实现,而且必须要求这个对象存在(除非制定了 required 属性为 false),类型重复再通过 id 与属性名匹配(byName)找,都不合适则结合 @Qualifier 注解使用名字装配。

    • @Resource(属于 J2EE 复返) 默认通过 byName 的方式实现,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在 setter 方法上默认取属性名进行装配。如果找不到与名称匹配的bean时,则通过 byType 实现自动装配。如果两个都找不到的情况下就报错。注意,如果 name 属性一旦指定,就只会按照名称进行装配。

    • 执行顺序不同:@Autowired 先 byType 再 byName;@Resource 先 byName 再 byType。


    8、使用注解开发

    • 在 spring4 之后,想要使用注解形式,必须得要引入 aop 的包。

    • 使用注解需要导入 context 约束,增加注解的支持。

    • 必须指定要扫描的包,使这个包下的注解生效。扫描是为了类上的注解。

    Spring 的配置文件 applicationContext.xml :

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--指定要扫描的包,这个包下的注解就会生效-->
        <context:component-scan base-package="com.song"/>
        <context:annotation-config/>
    
    </beans>
    

    8.1 Bean 的实现(@Component)

    在指定包下编写类,增加注解

    // 等价于配置文件中 <bean id="user" class="com.song.pojo.User"/>
    @Component  // 组件
    public class User {
        public String name = "张三";
    }
    

    测试:

    public void test(){
       ApplicationContext Context =
           new ClassPathXmlApplicationContext("applicationContext.xml");
       User user = (User) Context.getBean("user");
       System.out.println(user.name); // 张三
    }
    

    注意:@Component("xxx") 可以指定 id

    • 如果是 @Component,即没有指定 id,那么这个 bean 的 id 就是当前类的首字母小写的字符串
    • 如果是 @Component("xxx"),即指定了 id,那么 getBean 时只能使用这个 id,即Context.getBean("xxx")

    8.2 属性注入(@Value)

    可以在属性名上添加注解,也可以在setter方法上添加。

    @Component
    public class User {
        // 相当于 <property name="name" value="张三"/>
        //@Value("张三")
        public String name;
    
        @Value("张三")
        public void setName(String name) {
            this.name = name;
        }
    }
    

    8.3 衍生的注解

    @Component 的三个衍生注解,在 web 开发中,会按照 mvc 三层架构分层。

    • dao 层【@Repository】
    • service 层【@Service】
    • web 层【@Controller】

    这四个注解功能都是一样的,都是代表将某个类注册到 Spring 中,装配 bean。

    8.4 自动装配

    前面已经学过。

    8.5 作用域(@Scope)

    • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
    • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
    @Component
    @Scope("prototype")
    public class User {
        //@Value("张三")
        public String name;
    
        @Value("张三")
        public void setName(String name) {
            this.name = name;
        }
    }
    

    8.6 小结

    xml 和注解比较

    • xml 更加万能,适用于任何场合,结构清晰,维护简单方便
    • 注解,不是自己提供的类使用不了,维护相对复杂,开发简单方便

    xml 与注解整合开发

    • xml 用来管理 bean
    • 注解只负责完成属性的注入
    • 使用时需要注意:必须让注解生效,需要开启注解的支持 <context:annotation-config/>

    9、使用 java 的方式配置 Spring

    完全不使用 Spring 的 xml 配置,全权交给 java来做。

    javaConfig 是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 之后它成为了一个核心功能。这种纯 java 的配置方式,在 SpringBoot 中随处可见。

    测试:

    1. 实体类

      // 注解意思:这个类被 Spring 接管了,注册到了容器中
      @Component // 其实不加这个注解也可以测试成功
      public class User {
          private String name;
      
          @Value("zhangsan") //属性注入值
          public void setName(String name) {
              this.name = name;
          }
          
          getter/toString……
      }
      
    2. 新建一个 config 配置包,编写一个 javaConfig 配置类

      //这个也会被 Spring 容器托管,注册到容器中,因为它本来就是一个 @Component
      // @Configuration 代表这是一个配置类,和之前的 beans.xml 一样
      @Configuration
      @ComponentScan("com.song.pojo")
      @Import(MyConfig2.class) //导入合并其他配置类,类似于配置文件中的 import 标签
      public class MyConfig {
      
          // 通过方法注册一个 bean,相当于之前写的一个 bean 标签
          // 方法的名字,相当于 bean 标签中的 id 属性
          // 方法的返回值,相当于 bean 标签中的 class 属性
          @Bean
          public User getUser(){
              return new User(); // 返回要注入 bean 的对象
          }
      }
      
      @Configuration  //代表这是一个配置类
      public class MyConfig2 {
      }
      
    3. 测试类

      public class MyTest {
          public static void main(String[] args) {
              // 如果完全使用了配置类方式去做,就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
              ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
              User getUser = (User) context.getBean("getUser"); // 参数为方法名       
              System.out.println(getUser.getName()); // zhangsan
              
              User user = (User) context.getBean("user"); // 参数为 类名的首字母小写
              System.out.println(user.getName()); // zhangsan
              
              System.out.println(getUser == user);//fasle
          }
      }
      

    思考:

    发现以下两种方式的注解操作都可以正确输出

    1. 只在 MyConfig 类中加 @Bean 注解,context.getBean("getUser") 参数需要是 @Bean 注解时的方法名;

    2. 不加 @Bean 注解,在 User 类中加 @Component 注解,在 MyConfig 类中加 @ComponentScan("xxx.xxx.xxx") 注解,context.getBean("user") 参数需要是类名的首字母小写字符串。

    3. 如上面的测试案例,将 @Bean 和 @Component 注解都加上,通过 getBean 得到的对象是两个不同的对象,也就是说,在 Spring 容器中存在着该类的两个对象。

  • 相关阅读:
    oralce索引的使用
    oracle中connect by prior的使用
    oracle函数listagg使用
    oracle函数的使用
    redis高可用集群搭建
    Node.js安装及环境配置之Windows篇
    Repeater 合并单元格
    c#16进制转浮点数单精度类型
    EF Core 实体映射表或视图
    docker 构建filebeat镜像
  • 原文地址:https://www.cnblogs.com/Songzw/p/13170118.html
Copyright © 2011-2022 走看看