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
}
public class Computer {
    private String name;
    ...get/set/toString/constructor
    }
public class Company {
    private Person person;
    private Computer computer;
    ...get/set/toString/constructor
    }
  • 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"/>
  • 测试
@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='电脑'}}
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;
    }
public class Shop {
    @Value("电脑")
    private String name;
    }
  • 不开起组件扫描的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();
    }
}

  • 测试结果
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='电脑'}
6.3.2 方式2:配置类+实体(@Component)+扫描包
  • 实体类定义+@Component
@Component
public class User {
    @Value("张三")
    private String name;
    @Value("18")
    private int age;
 }
@Component
public class Shop {
    @Value("电脑")
    private String name;
    
    }
  • MyConfig组件配置类
/**
 * @Configuration是一个@Component同样会被注册到容器中,交给容器托管
 * @Configuration代表是一个配置类,和之前看到的beans.xml*/
@Configuration
@ComponentScan("com.zk")
public class MyConfig {
    
}
  • 测试结果
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='电脑'}
6.3.3 配置类整合
  • 可以通过@Import(OtherConfig.class)注解将其他的配置类整合进来
  • @Import({ CustomerConfig.class, SchedulerConfig.class })
    
  • @Import注解在4.2之前只支持导入配置类

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

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

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

与@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
  • 编写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>
  • 编写配置类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);
    }
}
  • 编写普通类
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 + '\'' +
                '}';
    }
}
  • 测试
@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());
}
  • 结果
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'}
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>
  • 导入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>

注意:在使用 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”)注解的参数指定。进行唯一装配属性
  • @Autowired 和 @Resource 的区别是什么?
  • @Autowired 是 Spring 提供的注解, @Resource 是 JDK 提供的注解
  • @Autowired 默认的注入方式为 byType (根据类型进行匹配),@Resource 默认注入方式为 byName (根据名称进行匹配)。
  • 当一个接口存在多个实现类的情况下, @Autowired 和 @Resource 都需要通过名称才能正确匹配到对应的 Bean。
    • Autowired 可以通过 @Qualifier 注解来显示指定名称,
    • Resource 可以通过 name 属性来显示指定名称。
@Nullable 字段加了这个注解,说明这个字段验证时可以为null,Nullable 是给编译器看的

public Company(@Nullable Person person, Computer computer) {
    this.person = person;
    this.computer = computer;
}

注解@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;
    }
  • 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/>
  • 测试
@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='电脑'}}

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

@Resource已经在JDK11中被移出了

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

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

@Autowired和@Resource执行顺序不同

本专栏下一篇:spring注解开发
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值