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);
相关推荐
2401_8574396919 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66620 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索22 分钟前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨28 分钟前
Filter和Listener
java·filter
qq_49244844632 分钟前
Java实现App自动化(Appium Demo)
java
阿华的代码王国41 分钟前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
找了一圈尾巴1 小时前
前后端交互通用排序策略
java·交互
哎呦没4 小时前
SpringBoot框架下的资产管理自动化
java·spring boot·后端
m0_571957586 小时前
Java | Leetcode Java题解之第543题二叉树的直径
java·leetcode·题解
魔道不误砍柴功8 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python