Spring 之 IoC基于XML管理Bean

1. 环境搭建

参考我之前的笔记: Spring6 基础入门-CSDN博客

2. 获取Bean的方式

2.1 根据Id 获取

参考之前的笔记: Spring6 基础入门-CSDN博客

java 复制代码
    @Test
    public void testGetBeanById(){
        //加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user = (User) context.getBean("user");
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
    }

2.2 根据类型获取Bean

java 复制代码
    @Test
    public void testGetBeanByType(){
        //加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user = (User) context.getBean(User.class);
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
    }

如果bean.xml文件中有相同的对象(class一样,id不一样),例如:

复制代码
<bean id="user" class="org.example.User"/>
<bean id="user2" class="org.example.User"/>

就会出现这个错误:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.example.User' available: expected single matching bean but found 2: user,user2
当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个

2.3根据类型和id获取Bean

java 复制代码
    @Test
    public void testGetBeanByIdAndType(){
        //加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取创建的对象
        User user = (User) context.getBean("user",User.class);
        System.out.println(user);
        //使用对象调用方法进行测试
        user.add();
    }

3. 依赖注入之 setter

类有属性,创建对象的过程中,向属性设置值。

创建Book.java

java 复制代码
package org.example.entity;

public class Book {

    private String bookName;
    private String bookAuthor;

    // 创建无参构造器
    public Book(){

    }
    // 创建有参构造器
    public Book(String bookName){
        this.bookName = bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public void setBookAuthor(String bookAuthor) {
        this.bookAuthor = bookAuthor;
    }

    public String getBookName() {
        return bookName;
    }

    public String getBookAuthor() {
        return bookAuthor;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", bookAuthor='" + bookAuthor + '\'' +
                '}';
    }
}

3.1 原生注入

java 复制代码
    @Test
    public void testSetBookName1(){
        Book book = new Book();
        book.setBookName("Think in Java");
    }
    
    @Test
    public void testSetBookName2(){
        Book book = new Book("Think in Java","Dylan");
    }

3.2. 用spring的方式注入值

修改xml文件

java 复制代码
    <bean id="book" class="org.example.entity.Book">
        <property name="bookName" value="西游记" />
        <property name="bookAuthor" value="吴承恩" />
    </bean>

测试

java 复制代码
    @Test
    public void testSetBookNameBySpring(){
       ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
       Book book = (Book)context.getBean("book");
        System.out.println(book);
    }


Book{bookName='西游记', bookAuthor='吴承恩'}

4. 依赖注入之 构造器

创建类,定义属性,生成有参构造方法

4.1 还是使用Book.java

修改xml配置文件

java 复制代码
    <!--构造器 方法注入-->
    <bean id="bookConst" class="org.example.entity.Book">
        <constructor-arg name="bookName" value="水浒传" />
        <constructor-arg name="bookAuthor" value="施耐庵" />
    </bean>

测试

java 复制代码
    @Test
    public void testSetBookNameBySpring2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Book book = (Book)context.getBean("bookConst");
        System.out.println(book);
    }

5. 依赖注入特殊值处理

5.1 字面量赋值

什么是字面量?

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

java 复制代码
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="bookName" value="西游记" />

5.2 null值的处理

java 复制代码
    <!-- null 值的处理-->
    <bean id="book2" class="org.example.entity.Book">
        <property name="bookName" >
            <null/>
        </property>
    </bean>

<!--如果是这样,bookName的值就是 字符串 "null"-->
    <bean id="book2" class="org.example.entity.Book">
        <property name="bookName" value="null" />
    </bean>

5.3 xml 实体

java 复制代码
<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>

5.4 CDATA

java 复制代码
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <bean id="book3" class="org.example.entity.Book">
        <property name="bookName" >
            <value><![CDATA[a < b]]></value>
        </property>
    </bean>

6. 特殊类型的注入

6.1 对象类型注入

创建Dept.java

java 复制代码
package org.example.entity;

public class Dept {
    private String dname;

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public void info(){
        System.out.println("部门名称:"+dname);
    }
}

创建Emp.java

java 复制代码
package org.example.entity;

public class Emp {
    //员工属于某个部门
    private Dept dept;
    private String ename;
    private Integer age;

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void work(){
        System.out.println("员工:"+ename+" 年龄:"+ age + " work...");
        dept.info();
    }
}

6.1.1 引用外部bean

修改xml 配置文件:

java 复制代码
    <bean id="debt" class="org.example.entity.Dept">
        <property name="dname" value="财务部"/>
    </bean>
    <bean id="emp" class="org.example.entity.Emp">
        <!--普通注入-->
        <property name="ename" value="Dylan"/>
        <property name="age" value="18"/>
        <!-- 对象注入,不能用value 要用ref引用外部类 -->
        <property name="dept" ref="debt"/>
    </bean>

测试:

java 复制代码
    @Test
    public void testEmpBySpring(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di-test.xml");
        Emp emp = (Emp)context.getBean("emp");
        emp.work();
    }

6.1.2 内部bean

修改xml配置文件(就是在property标签内部再写一个bean标签 )

java 复制代码
    <bean id="emp2" class="org.example.entity.Emp">
        <!--普通注入-->
        <property name="ename" value="Frank"/>
        <property name="age" value="20"/>
        <!-- 对象注入,不能用value 要用ref引用外部类 -->
        <property name="dept">
            <bean class="org.example.entity.Dept">
                <property name="dname" value="安保部"/>
            </bean>
        </property>
    </bean>

6.1.3 级联属性赋值

java 复制代码
    <bean id="debt" class="org.example.entity.Dept">
        <property name="dname" value="财务部"/>
    </bean>
    <bean id="emp" class="org.example.entity.Emp">
        <!--普通注入-->
        <property name="ename" value="Dylan"/>
        <property name="age" value="18"/>
        <!-- 对象注入,不能用value 要用ref引用外部类 -->
        <property name="dept" ref="debt"/>
        <property name="dept.dname" value="科研部"/>
    </bean>

6.2 数组类型注入

修改Emp.java ,增加如下代码

java 复制代码
  private String[] loves;

    public String[] getLoves() {
        return loves;
    }

    public void setLoves(String[] loves) {
        this.loves = loves;
    }

修改Xml 配置文件

java 复制代码
<bean id="emp" class="org.example.entity.Emp">
            <!--普通注入-->
            <property name="ename" value="Dylan"/>
            <property name="age" value="18"/>
            <property name="dept" ref="debt"/>
            <!-- 注入数组 -->
            <property name="loves">
                <array>
                    <value>吃饭</value>
                    <value>睡觉</value>
                    <value>打豆豆</value>
                </array>
            </property>
        </bean>

6.3 注入集合

6.3.1 注入List

修改 Dept.java,增加如下代码

java 复制代码
 private List<Emp> empList;

    public List<Emp> getEmpList() {
        return empList;
    }

    public void setEmpList(List<Emp> empList) {
        this.empList = empList;
    }

修改xml配置文件

java 复制代码
<bean id="dept" class="org.example.entity.Dept">
        <property name="dname" value="财务部"/>
        <property name="empList">
            <list>
                <ref bean="emp"/>
                <ref bean="emp2"/>
            </list>
        </property>
    </bean>
    <bean id="emp" class="org.example.entity.Emp">
        <property name="ename" value="Dylan"/>
        <property name="age" value="18"/>
    </bean>
    <bean id="emp2" class="org.example.entity.Emp">
        <property name="ename" value="Tom"/>
        <property name="age" value="28"/>
    </bean>

6.3.2 注入Map

创建Student.java

java 复制代码
package org.example.entity;

import java.util.Map;

public class Student {
    private String sName;
    private String sNo;

    private Map<String,Teacher> teacherMap;

    public String getsName() {
        return sName;
    }

    public void setsName(String sName) {
        this.sName = sName;
    }

    public String getsNo() {
        return sNo;
    }

    public void setsNo(String sNo) {
        this.sNo = sNo;
    }

    public Map<String, Teacher> getTeacherMap() {
        return teacherMap;
    }

    public void setTeacherMap(Map<String, Teacher> teacherMap) {
        this.teacherMap = teacherMap;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sName='" + sName + '\'' +
                ", sNo='" + sNo + '\'' +
                ", teacherMap=" + teacherMap +
                '}';
    }
}

创建Teacher.java

java 复制代码
package org.example.entity;

public class Teacher {
    private String tNo;
    private String tName;

    public String gettNo() {
        return tNo;
    }

    public void settNo(String tNo) {
        this.tNo = tNo;
    }

    public String gettName() {
        return tName;
    }

    public void settName(String tName) {
        this.tName = tName;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tNo='" + tNo + '\'' +
                ", tName='" + tName + '\'' +
                '}';
    }
}

修改xml文件

java 复制代码
 <bean id="student" class="org.example.entity.Student">
        <property name="sNo" value="0001"/>
        <property name="sName" value="z3"/>
        <property name="teacherMap">
            <map>
                <entry key="k1" value-ref="techer"/>
                <entry key="k2" value-ref="techer2"/>
            </map>
        </property>
    </bean>
    <bean id="techer" class="org.example.entity.Teacher">
        <property name="tNo" value="10001"/>
        <property name="tName" value="teacher Zhang"/>
    </bean>
    <bean id="techer2" class="org.example.entity.Teacher">
        <property name="tNo" value="10001"/>
        <property name="tName" value="teacher Li"/>
    </bean>

6.3.3 引用集合类型的bean(把list 和 map的注入换一种方式实现)

创建Lesson.java

java 复制代码
package org.example.entity;

public class Lesson {
    private String lessonName;

    public String getLessonName() {
        return lessonName;
    }

    public void setLessonName(String lessonName) {
        this.lessonName = lessonName;
    }

    @Override
    public String toString() {
        return "Lesson{" +
                "lessonName='" + lessonName + '\'' +
                '}';
    }
}

需改Student.java (增加Lesson的代码)

java 复制代码
    private List<Lesson> lessonList;

    public List<Lesson> getLessonList() {
        return lessonList;
    }

    public void setLessonList(List<Lesson> lessonList) {
        this.lessonList = lessonList;
    }

修改xml配置文件

使用util:list、util:map标签必须引入相应的命名空间

java 复制代码
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

增加了:xmlns:util="http://www.springframework.org/schema/util"
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd

    <bean id="student" class="org.example.entity.Student">
        <property name="sName" value="z3"/>
        <property name="sNo" value="10000"/>
        <property name="lessonList" ref="lessonList1"/>
        <property name="teacherMap" ref="teacherMap"/>

    </bean>

    <util:list id="lessonList1">
        <ref bean="lesson"/>
        <ref bean="lesson2"/>
    </util:list>

    <util:map id="teacherMap">
        <entry key="01" value-ref="teacher"/>
        <entry value-ref="teacher2" key="02"/>
    </util:map>

    <bean id="teacher" class="org.example.entity.Teacher">
        <property name="tName" value="teanher zhang"/>
        <property name="tNo" value="00001"/>
    </bean>
    <bean id="teacher2" class="org.example.entity.Teacher">
        <property name="tName" value="teanher Li"/>
        <property name="tNo" value="00002"/>
    </bean>
    <bean id="lesson" class="org.example.entity.Lesson">
        <property name="lessonName" value="语文"/>
    </bean>
    <bean id="lesson2" class="org.example.entity.Lesson">
        <property name="lessonName" value="数学"/>
    </bean>
</beans>

记得修改Student.java 的toString()方法,把Lesson也打印出来

7 p命名空间

7.1 引入p命名空间(必须)

java 复制代码
<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


增加了:xmlns:p="http://www.springframework.org/schema/p"

7.2 在原有的<bean>标签中加入p

java 复制代码
    <bean id="student" class="org.example.entity.Student" p:sName="li 4" p:sNo="10000"
    p:lessonList-ref="lessonList1" p:teacherMap-ref="teacherMap">
    </bean>

8 引入外部文件

8.1 引入数据库相关依赖

java 复制代码
<!-- MySOL 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>

8.2 创建外部文件

创建jdbc.properties

java 复制代码
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/myBatis01?useUnicode=true&amp;characterEncoding=utf-8
jdbc.driver=com.mysql.jdbc.Driver

8.3 引入context命名空间及外部文件

java 复制代码
<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

增加了:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd


    <!-- 引入外部文件 -->
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
    </bean>
</beans>

9 Bean的作用域

|---------------|-------------------------|-----------|
| 取值 | 含义 | 创建对象的时机 |
| singleton(默认) | 在IOC容器中,这个bean的对象始终为但实例 | IOC容器初始化时 |
| prototype | 这个bean在IOC容器中有多个实例 |

配置xml

java 复制代码
    <!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象.创建的对应的地址都一样 -->
    <!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
    <bean id="dept" class="org.example.entity.Dept" scope="prototype"/>

测试

java 复制代码
    @Test
    public void testScope(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di-test.xml");
        Dept dept1 = context.getBean("dept",Dept.class);
        Dept dept2 = context.getBean("dept",Dept.class);
        System.out.println(dept1 == dept2);
    }

10. bean的生命周期

  1. bean对象创建(调用无参构造器)

  2. 给bean对象设置属性

  3. bean的前置处理器(初始化之前)

  4. bean对象初始化(需在配置bean时指定初始化方法)

  5. bean的后置处理器(初始化之后)

  6. bean对象就绪可以使用

  7. bean对象销毁(需在配置bean时指定销毁方法)

  8. IOC容器关闭

创建User.java

ininMethod()和destroyMethod(),需要在xml配置文件中配置才能生效

java 复制代码
package org.example.entity;

public class User {
    private String uName;

    public User(){
        System.out.println("生命周期:1、调用无参构造 创建对象");
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        System.out.println("生命周期:2、给bean对象属性注入值");
        this.uName = uName;
    }

    public void ininMethod(){
        System.out.println("生命周期:4、bean对象初始化,调用指定的方法");
    }

    public void destroyMethod(){
        System.out.println("生命周期:7、bean对象销毁,调用指定的方法");
    }
}

10.1 创建bean的前/后置处理器

bean的前/后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean前/后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

创建MyBeanPost.java

java 复制代码
package org.example.life;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("生命周期:3、bean的前置处理器");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("生命周期:5、bean的后置处理器");
        return bean;
    }
}

修改xml配置文件

java 复制代码
    <bean id="user" class="org.example.entity.User" init-method="ininMethod" destroy-method="destroyMethod">
        <property name="uName" value="dylan"/>
    </bean>

    <!--bean的后置处理器要放入IOC容器中才能生效-->
    <bean id="myBeanPost" class="org.example.life.MyBeanPost"/>

测试

这里使用ClassPathXmlApplicationContext ,因为它有close()方法。

java 复制代码
    @Test
    public void testBeanLife(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-di-test.xml");
        org.example.entity.User user = context.getBean("user", org.example.entity.User.class);
        System.out.println("生命周期:6、bean对象创建完成,可以使用了");
        System.out.println(user);
        context.close();
    }

11. FactoryBean

它是Spring中的一种机制,Spring把复杂的过程做了封装。表面上看是一个简介的展现。

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

创建MyFactoryBean.java

java 复制代码
package org.example.factorybean;

import org.example.entity.User;
import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

修改xml配置文件

java 复制代码
<bean id="myfactoryBean" class="org.example.factorybean.MyFactoryBean"/>

测试

java 复制代码
    @Test
    public void testBeanFactoryBean(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-di-test.xml");
        org.example.entity.User user = context.getBean("myfactoryBean", org.example.entity.User.class);
        System.out.println(user);
相关推荐
可乐加.糖16 分钟前
一篇关于Netty相关的梳理总结
java·后端·网络协议·netty·信息与通信
s91236010118 分钟前
rust 同时处理多个异步任务
java·数据库·rust
9号达人19 分钟前
java9新特性详解与实践
java·后端·面试
cg501723 分钟前
Spring Boot 的配置文件
java·linux·spring boot
啊喜拔牙31 分钟前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
anlogic1 小时前
Java基础 4.3
java·开发语言
非ban必选2 小时前
spring-ai-alibaba第七章阿里dashscope集成RedisChatMemory实现对话记忆
java·后端·spring
A旧城以西2 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea
杉之2 小时前
选择排序笔记
java·算法·排序算法
Naive_72 小时前
蓝桥杯准备(前缀和差分)
java·职场和发展·蓝桥杯