Spring系统学习 -Spring IOC 的XML管理Bean之bean的获取、依赖注入值的方式

在Spring框架中,XML配置是最传统和最常见的方式之一,用于管理Bean的创建、依赖注入和生命周期等。这个在Spring中我们使用算是常用的,我们需要根据Spring的基于XML管理Bean了解相关Spring中常用的获取bean的方式、依赖注入值的几种方式等等。

基于XML方式管理bean的流程

  1. 创建XML配置文件:创建一个XML文件,用于定义Bean的配置信息和依赖关系。可以使用任何文本编辑器创建该文件,通常将其命名为"beans.xml"。

  2. 声明XML命名空间和模式位置 :在XML文件的根元素中,声明Spring的命名空间和模式位置,以便正确解析和验证XML配置。

    xml 复制代码
       <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
                                  http://www.springframework.org/schema/beans/spring-beans.xsd">
  3. 定义Bean :使用元素来定义Bean,指定Bean的ID、类名和其他属性。示例如下:

    xml 复制代码
    <bean id="userService" class="com.example.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    
    <bean id="userDao" class="com.example.UserDao"/>
  4. 加载XML配置文件 :在Java代码中,使用ApplicationContext来加载XML配置文件。示例如下:

    java 复制代码
    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        }
    }
  5. 获取Bean :通过ApplicationContext的getBean()方法,从容器中获取所需的Bean实例。示例如下:

    java 复制代码
    UserService userService = (UserService) context.getBean("userService");

搭建Spring模块结构

创建基本模块并引入相关配置

创建Spring模块

Spring 6 开始已经不在支持javaJDK 8了。

在pom.xml文件中引入相关依赖:

xml 复制代码
<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

<!--        引入Spring 依赖  5 的版本才支持 jdk8 , 6已经不在支持JDK8了-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

<!--        引入阿里巴巴的数据库链接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.5</version>
        </dependency>

创建一个类HelloWorld

java 复制代码
public class HelloWorld {
    public void sayHello(){
        System.out.println("Hello World!");
    }
}

创建一个Spring配置文件

存放到resources目录下

之后我们在创建的配置文件中配置Bean:

xml 复制代码
<!--
	配置HelloWorld所对应的bean,即将HelloWorld的对象交给Spring的IOC容器管理
	通过bean标签配置IOC容器所管理的bean
	属性:
		id:设置bean的唯一标识
		class:设置bean所对应类型的全类名
-->
<bean id="helloworld" class="com.miaow.spring.bean.HelloWorld"></bean>

接下来我们创建一个测试类

java 复制代码
@Test
public void testHelloWorld(){
    ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
    HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
    helloworld.sayHello();
}

然后我们测试看一下是否输出正常。

大致流程

ps:Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要无参构造器时,没有无参构造器,则会抛出下面的异常:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloworld' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.miaow.spring.bean.HelloWorld]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.miaow.spring.bean.HelloWorld.

获取Bean

根据ID获取

由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。

上述例子就是根据id获取一个组件对象。

java 复制代码
public void testHelloWorld(){
    ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
    //这个helloworld就是我们在xml配置文件设置的<bean id = "helloworld" >
    HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
    helloworld.sayHello();
}
根据类型获取
java 复制代码
@Test
public void testHelloWorld(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    HelloWorld bean = ac.getBean(HelloWorld.class);
    bean.sayHello();
}
根据id和类型获取
java 复制代码
@Test
public void testHelloWorld(){
    ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
    HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);
    bean.sayHello();
}

当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个 。

例如:

xml 复制代码
<bean id="helloworldOne" class="com.miaow.spring.bean.HelloWorld"></bean>
<bean id="helloworldTwo" class="com.miaow.spring.bean.HelloWorld"></bean>

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.miaow.spring.bean.HelloWorld' available: expected single matching bean but found 2: helloworldOne,helloworldTwo

  • 如果组件类实现了接口,根据接口类型可以获取 bean 吗? 可以,前提是bean唯一
  • 如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗? 不行,因为bean不唯一

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

Spring依赖注入的方式

Spring的IoC(控制反转)容器通过依赖注入(DI)来管理应用程序中的组件之间的依赖关系 。依赖注入是指将一个对象的依赖关系传递给另一个对象,而不是由被依赖对象自己创建或管理依赖对象。Spring框架提供了多种方式来实现依赖注入,包括构造器注入、Setter方法注入、字段注入、注解注入 等。

至于字段注入和注解注入我们到Spring注解的时候再继续讲解,这里了解即可。

我们来创建一个学生实体类:

java 复制代码
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    public Student() {
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Override
    public String toString() {
        return "Student{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            ", sex='" + sex + '\'' +
            '}';
    }
}
setter注入

我们通过Setter方式给上述的Student实体类注入相关值:

我们新建一个springdi.xml配置文件,然后在其中添加如下代码:

xml 复制代码
<bean id="studentOne" class="com.miaow.spring.bean.Student">
    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关)-->
    <!-- value属性:指定属性值 -->
    <property name="id" value="1001"></property>
    <property name="name" value="张三"></property>
    <property name="age" value="23"></property>
    <property name="sex" value="男"></property>
</bean>

接下来我们在测试类方法中测试

java 复制代码
@Test
public void testDIBySet(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("springdi.xml");
    Student studentOne = ac.getBean("studentOne", Student.class);
    System.out.println(studentOne);
}
构造器注入

通过构造器注入将依赖项传递给bean

在实体类Student实体类中添加:

java 复制代码
public Student(Integer id, String name, Integer age, String sex) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.sex = sex;
}

之后我们在springdi.xml文件中配置:

xml 复制代码
<bean id="studentTwo" class="com.miaow.spring.bean.Student">
    <constructor-arg value="1002"></constructor-arg>
    <constructor-arg value="李四"></constructor-arg>
    <constructor-arg value="33"></constructor-arg>
    <constructor-arg value="女"></constructor-arg>
</bean>

注意:

constructor-arg标签还有两个属性可以进一步描述构造器参数:

  • index属性:指定参数所在位置的索引(从0开始)
  • name属性:指定参数名

测试类中进行测试:

java 复制代码
@Test
public void testDIBySet(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("springdi.xml");
    Student studentOne = ac.getBean("studentTwo", Student.class);
    System.out.println(studentOne);
}

特殊值处理赋值

字面符

关于特殊值处理赋值,我们需要事先了解字面符。

什么是字面符?

int a = 10;

声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a的时候,我们实际上拿到的值是10。

而如果a是带引号的:'a',那么它现在不是一个变量,它就是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。

例如

xml 复制代码
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>
NULL
xml 复制代码
<!--表示name的值为空值-->
<property name="name">
	<null />
</property>

<!--写法同-->
<property name="name" value="null"></property>
<(小于) --- XML实体

由于在XML中无法直接使用,故而我们需要用XML实体来代替

xml 复制代码
<property name="expression" value="a &lt; b"/>
CDARA节
xml 复制代码
<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>
相关推荐
Hacker_Oldv2 分钟前
网络安全攻防学习平台 - 基础关
网络·学习·web安全
herogus丶17 分钟前
【Spring AI】Spring AI Alibaba的简单使用
java·人工智能·spring·ai
胡西风_foxww19 分钟前
【ES6复习笔记】let 和 const 命令(1)
笔记·学习·es6·const·let
网络安全(king)32 分钟前
网络安全之接入控制
网络·学习·安全·web安全
PP东3 小时前
ES学习class类用法(十一)
javascript·学习
奋斗的老史4 小时前
Spring Retry + Redis Watch实现高并发乐观锁
java·redis·spring
小狗蛋ing4 小时前
反编译APK获取xml资源
xml·反编译
00Allen005 小时前
mybatis/mybatisplus
java·spring·mybatis