• spring bean的自动装配


    6.bean的自动装配

    • 装配:Spring 在 Bean 与 Bean 之间建立依赖关系的行为

    • IOC 容器虽然功能强大,但只是一个容器而已,自己并不能独自完成装配工作。需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。

    • 3种装配方式:

    6.1 **方式1:**在xml中显式配置

    在 XML 配置中通过 和 中的 ref 属性,手动维护 Bean 与 Bean 之间的依赖关系的。

    • 问题:对于只有少量 Bean 的应用来说,在xml这种方式已经足够满足我们的需求了。但随着应用的功能不断发展,容器中包含的 Bean 会越来越多,Bean 和 Bean 之间的依赖关系也越来越复杂,这就使得我们所编写的 XML 配置也越来越复杂,越来越繁琐。

    • 而过于复杂的 XML 配置可读性差,而且编写起来极易出错,严重的降低了开发人员的开发效率。为了解决这一问题,Spring 框架还为我们提供了“自动装配”功能。

    6.2 方式2:Spring 自动装配(隐式配置)

    自动装配功能:让 Spring 容器依据自动装配的规则,(五种),为**指定的 Bean 从应用的上下文(AppplicationContext 容器)**中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。这一过程不需要使用 和 元素 ref 属性来实现。

    • 优点:能够简化 Spring 应用的 XML 配置,降低工作量。

    注意:Spring 框架默认不支持自动装配的,要使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。

    • Spring 框架 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应
    属性值说明
    byName按名称自动装配。 Spring 会根据的 Java 类中对象属性的名称,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 id 或 name 属性值与这个对象属性的名称相同,则获取这个 Bean,并与当前的 Java 类 Bean 建立关联关系。
    byType按类型自动装配。 Spring 会根据 Java 类中的对象属性的类型,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 class 属性值与这个对象属性的类型相匹配,则获取这个 Bean,并与当前的 Java 类的 Bean 建立关联关系。
    constructor与 byType 模式相似,不同之处在与它应用于构造器参数(依赖项),如果在容器中没有找到与构造器参数类型一致的 Bean,那么将抛出异常。其实就是根据构造器参数的数据类型,进行 byType 模式的自动装配。
    default表示默认采用上一级元素 设置的自动装配规则(default-autowire)进行装配。
    no默认值,表示不使用自动装配,Bean 的依赖关系必须通过 和 元素的 ref 属性来定义。
    • 注意:
    • byName自动装配规则:要求XML 文件中 Bean 的 id 或 name 必须与类中的属性名称相同。否则返回null对象,并且 Bean 的 id或 name 区分大小写和Bean 的 id要全局唯一

    • byType自动装配规则:XML 文件中 Bean 的 id 或 name 与类中的属性名可以不同或省略,只要 Bean 的 class 属性值与类中的对象属性的类型相同,就可以完成自动装配。

      • 如果同时存在多个相同类型的 Bean,则注入失败,并且引发异常
    • default自动装配规则:要求上一级元素 需要设置default-autowire自动装配规则

    • 举例理解:一家公司有职员和电脑

    • 实体类创建

    public class Person {
        private String name;
        ...get/set/toString/constructor
    }
    
    • 1
    • 2
    • 3
    • 4
    public class Computer {
        private String name;
        ...get/set/toString/constructor
        }
    
    • 1
    • 2
    • 3
    • 4
    public class Company {
        private Person person;
        private Computer computer;
        ...get/set/toString/constructor
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • xml中设置自动装配方式
    <bean id="computer" class="com.zk.pojo.Computer">
        <property name="name" value="电脑"/>
    </bean>
    <bean id="person" class="com.zk.pojo.Person">
        <property name="name" value="职员"/>
    </bean>
    
    <bean id="company" class="com.zk.pojo.Company" autowire="byName"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 测试
    @Test
    public void test01(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Company company = context.getBean("company", Company.class);
        System.out.println(company.toString());
    }
    
    //Company{person=person{name='职员'}, computer=computer{name='电脑'}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6.3 方式3:在java中显示配置

    • 不用配置Spring的xml配置了,用java来做

    • JavaConfig时spring的一个子项目,在spring4之后成为了一个核心功能。在springBoot中使用非常频繁

    • 有两种方式,配置类+@bean 或者 配置类+实体(@Component)+扫描包

    6.3.1 方式1:配置类+@bean
    public class User {
        @Value("张三")
        private String name;
        @Value("18")
        private int age;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class Shop {
        @Value("电脑")
        private String name;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 不开起组件扫描的MyConfig组件配置类
    • @Bean跳转是跳转到使用的地方,不是类定义
    /**
     * @Configuration是一个@Component同样会被注册到容器中,交给容器托管
     * @Configuration代表是一个配置类,和之前看到的beans.xml*/
    @Configuration
    public class MyConfig {
         /**
          * @Bean相当于beans中的单个的bean标签,注册到容器中
          * 方法名字相当于bean标签的id
          * 方法返回值相当于bean标签的Class属性
          * 返回要注入的bean对象
         * */
       @Bean
        public static User user(){
            return new User();
        }
        @Bean
        public static Shop shop(){
            return new Shop();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 测试结果
    public class TestConfig {
        @Test
        public void test01(){
        	//若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
            User user = (User)context.getBean("user");
            System.out.println(user.toString());
            Shop shop = (Shop)context.getBean("shop");
            System.out.println(shop.toString());
        }
    }
    //User{name='张三', age=18}
    //Shop{name='电脑'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    6.3.2 方式2:配置类+实体(@Component)+扫描包
    • 实体类定义+@Component
    @Component
    public class User {
        @Value("张三")
        private String name;
        @Value("18")
        private int age;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    @Component
    public class Shop {
        @Value("电脑")
        private String name;
        
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • MyConfig组件配置类
    /**
     * @Configuration是一个@Component同样会被注册到容器中,交给容器托管
     * @Configuration代表是一个配置类,和之前看到的beans.xml*/
    @Configuration
    @ComponentScan("com.zk")
    public class MyConfig {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 测试结果
    public class TestConfig {
        @Test
        public void test01(){
       		//若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
            User user = (User)context.getBean("user");
            System.out.println(user.toString());
            Shop shop = (Shop)context.getBean("shop");
            System.out.println(shop.toString());
        }
    }
    //User{name='张三', age=18}
    //Shop{name='电脑'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    6.3.3 配置类整合
    • 可以通过@Import(OtherConfig.class)注解将其他的配置类整合进来
    • @Import({ CustomerConfig.class, SchedulerConfig.class })
      
      • 1
    • @Import注解在4.2之前只支持导入配置类

    • 在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean

    • @Import(DemoService.class) // 在spring 4.2之前是不不支持的
      
      • 1
    • import注解主要用在基于java代码显式创建bean的过程中,用于将多个分散的java config配置类融合成一个更大的config类。其实除了 import注解外,还有 importResource注解,其作用都类似。配置类的组合主要发生在跨模块或跨包的配置类引用过程中。

    • 这里还可以使用@ImportResource(String [ ] )注解来引入其他xml配置文件
    @ImportResource(locations = {"classpath:beans.xml"})
    @ImportResource(value = {"classpath:file1.xml","classpath:file2.xml"})
    
    • 1
    • 2

    与@Import一样,此注解提供类似于 Spring XML 中的元素的功能。它通常在设计@Configuration类以由AnnotationConfigApplicationContext引导时使用,但仍然需要一些 XML 功能,例如命名空间。

    • 举例:利用@ImportResource 和 @Value 注解进行资源文件读取

    • 编写db.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/dbname?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC
    user=root
    password=123456
    
    • 1
    • 2
    • 3
    • 4
    • 编写applicationContext.xml,context:property-placeholder 指定资源文件的位置
    <?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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/context/spring-aop.xsd">
        <!--context:property-placeholder 指定资源文件的位置-->
        <context:property-placeholder location="classpath:db.properties"/>
        <context:component-scan base-package="com.zk"/>
        <context:annotation-config/>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 编写配置类DBConfig
    /*applicationContext.xml引入了db.properties
    * 配置类引入applicationContext.xml*/
    @Configuration
    @ImportResource(locations = {"classpath:applicationContext.xml"})
    public class DBConfig {
        @Value("${driver}")
        private String driver;
        @Value("${url}")
        private String url;
        @Value("${user}")
        private String username;
        @Value("${password}")
        private String password;
        @Bean
        public MyDriverManager myDriverManager(){
            return new MyDriverManager(driver,url,username,password);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 编写普通类
    public class MyDriverManager {
        private String driver;
        private String url;
        private String username;
        private String password;
    
        public MyDriverManager(String driver, String url, String username, String password) {
            this.driver = driver;
            this.url = url;
            this.username = username;
            this.password = password;
            System.out.println("driver : " + driver);
            System.out.println("url : " + url);
            System.out.println("username : " + username);
            System.out.println("password : " + password);
        }
    
        @Override
        public String toString() {
            return "MyDriverManager{" +
                    "driver='" + driver + '\'' +
                    ", url='" + url + '\'' +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 测试
    @Test
    public void test02(){
        //若使用了配置类方式,只能通过AnnotationConfig上下文来获取容器,加载配置类的Class对象
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DBConfig.class);
        /*ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");*/
        MyDriverManager myDriverManager = (MyDriverManager)context.getBean("myDriverManager");
        System.out.println(myDriverManager.toString());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 结果
    driver : com.mysql.cj.jdbc.Driver
    url : jdbc:mysql://localhost:3306/dbname?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC
    username : root
    password : 123456
    MyDriverManager{driver='com.mysql.cj.jdbc.Driver', url='jdbc:mysql://localhost:3306/dbname?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC', username='root', password='123456'}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6.4 方式4:基于注解自动装配

    • 从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。
    • Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。

    Spring 注解实现自动装配的步骤如下:

    1. 引入依赖:spring-aop 的 Jar 包也会用到
    2. 开启组件扫描
    3. 使用注解定义 Bean
    4. 基于注解依赖注入
    • 倒入依赖
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.19</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 导入context约束,开启组件扫描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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/context/spring-aop.xsd">
         <!--配置注解的支持-->
         <context:annotation-config/>
         <!--新版本配置扫描注解就可以运行,开启组件扫描功能-->
         <context:component-scan base-package="net.biancheng.c"></context:component-scan>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

    • 基于注解依赖注入,可以通过以下注解将定义好 Bean 装配到其它的 Bean 中。
    注解说明
    @Autowired可以应用到 Bean 的属性变量、setter 方法、非 setter 方法及构造函数等,默认按照 Bean 的类型进行装配。 @Autowired 注解默认按照 Bean 的类型进行装配,默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合 @Qualifier 注解一起使用 。如果在属性变量加@Autowired那么setter 方法都可以不需要了,前提式这个依赖的bean在容器中存在,进行类型装配
    @Resourcejavax.annotation.Resource时java的注解;作用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 的名称进行装配。 @Resource 中有两个重要属性:name 和 type。 Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配;**如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;**如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
    @Qualifier与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier (value=“beanname”)注解的参数指定。进行唯一装配属性
    @Nullable 字段加了这个注解,说明这个字段验证时可以为null,Nullable 是给编译器看的
    
    public Company(@Nullable Person person, Computer computer) {
        this.person = person;
        this.computer = computer;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注解@Autowired的属性required,此属性用于控制如果找不到要依赖注入的对象时是否报错,默认true即默认找不到要注入的对象时会报错,required在spring security中配置动态权限,前端不需要权限,就需要配置

    @Autowired使用多的原因:大多数bean时单例的,不会出现多个同类型的bean

    • 举例理解

    • 只在需要依赖的类中的属性上加@Autowired注解

    XML和注解都是反射! 注解是反射直接给属性赋值,xml的是反射获得getset方法赋值.原因:注解是通过类对象获取类的所有信息,包括私有属性,所以能直接赋值

    public class Company {
    	//显示定义@Autowired的属性required为false,说明依赖的对象可以为null,否则不许为空
        @Autowired(required = false)
        private Person person;
        @Autowired
        private Computer computer;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • xml配置文件中开启注解支持,加context约束见上
    <bean id="computer" class="com.zk.pojo.Computer">
            <constructor-arg name="name" value="电脑"/>
        </bean>
        <bean id="Person" class="com.zk.pojo.Person" name="person">
            <constructor-arg name="name" value="职员"/>
        </bean>
    <bean id="company" class="com.zk.pojo.Company"></bean>
    <!--配置注解的支持-->
    <context:annotation-config/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 测试
    @Test
    public void test01(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Company company = context.getBean("company", Company.class);
        System.out.println(company.toString());
    }
    //Company{person=person{name='职员'}, computer=computer{name='电脑'}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    @Autowired是侵入式的,已经违背了Spring的理念,这里建议用@Resources 可以无缝切换IOC框架

    @Resource已经在JDK11中被移出了

    多个bean对象,@Resource会自动装配第一个bean对象

    @Autowired和@Resource都可以用在属性上

    @Autowired和@Resource执行顺序不同

    本专栏下一篇:spring注解开发

  • 相关阅读:
    【并发编程】- 线程池使用DiscardOldestPolicy策略、DiscardPolicy策略
    Jenkins环境搭建
    4.4——数据库和前后端身份认证
    基于SpringBoot的学生选课系统
    [论文总结] 深度学习在农业领域应用论文笔记10
    WebRTC系列补充--native音量控制level
    【025】mongoose V6.4开启debug日志打印
    在树莓派上搭建属于自己的网页(1)
    Python-列表简介
    一朵华为云,如何做好百模千态?
  • 原文地址:https://blog.csdn.net/weixin_42045639/article/details/125529950