Spring 拾枝杂谈—Spring原生容器结构剖析(通俗易懂)

目录

一、前言

二、Spring快速入门

[1.简介 :](#1.简介 :)

[2. 入门实例 :](#2. 入门实例 :)

三、Spring容器结构分析

[1.bean配置信息的存储 :](#1.bean配置信息的存储 :)

[2.bean对象的存储 :](#2.bean对象的存储 :)

[3.bean-id的快捷访问 :](#3.bean-id的快捷访问 :)

四、总结


一、前言

  • 开门见山,11.25日开始我们正式进入Java框架---Spring的学习,此前,up已经出过Java基础****-->Java进阶-->MySQL-->JDBC-->****JavaWeb的系列文章,大佬们可以进入我的主页,选择不同专栏进行阅读🌹🌹。对于这个新的系列,up准备和此前的JDBC系列一样,重点把内容讲清楚,说明白,以达到博文查漏补缺的本意,至于细枝末节,概所不录。
  • 第一节内容 ,up主要和大家分享一下Spring原生容器结构,以及Spring入门案例
  • 关于注意事项,还是老规矩------代码中的注释也很重要;不要眼高手低,自己跟着过一遍才有收获;点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
  • 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信up。感谢阅读!

二、Spring快速入门

1.简介 :

(1) 我们平时所说的Java框架"Spring"通常指的是"Spring Framework"。Spring本身作为一个框架,又可以整合其他的框架,即可以认为,Spring是管理其他框架的框架

(2) Spring核心知识点 ------①****IOC(Inversion of Control)控制反转,AOP(Aspect Oriented Programming)面向切面编程,JDBCTemplate,声明式事务

(3) Spring5核心组件如下图所示 :

2. 入门实例 :

需求 : 使用Spring的方式获取JavaBean对象,并打印出该对象的信息

首先,需要向Java项目中导入所需要的jar包,除commons-logging.jar外,其余都可在Spring的安装目录下的libs子目录中找到。如下图所示 :

接着,在src目录下创建beans.xml配置文件,创建流程如下图所示 :

若创建文件后,出现提示"Application context not configured for this file ",根据IDEA给出的提示操作即可。

接着,up在com.cyan.spring.bean包下新创建一个JavaBean类,以Student类为例,Student类代码如下 :

java 复制代码
package com.cyan.spring.bean;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class Student {
    private String name;
    private int age;
    private int score;

    public Student() {

    }
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

beans.xml代码如下 : (注意注释内容)

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        (1) 在根元素beans中,通过<bean></bean>子元素来配置JavaBean对象。
            每配置一个bean,相当于配置了一个Java对象。
        (2) bean子元素需要配置两个属性---------class 和 id。其中,
            class表示所要实例化类的正名(全类名);
            id表示该对象在Spring容器中的标识,通过id可以获取到对象。
        (3) property子元素用于配置该对象的成员变量(对象的属性),其中,
            name表示属性名称,value表示属性的值。
        (4) XML内容回顾---------若一个标签没有标签体,以<age></age>为例,可以简写为<age/>。
    -->
    <bean class="com.cyan.spring.bean.Student" id="stu01">
        <property name="name" value="Cyan"></property>
        <property name="age" value="21"></property>
        <property name="score" value="450"></property>
    </bean>
    <bean class="com.cyan.spring.bean.Student" id="stu02">
        <property name="name" value="Rain"></property>
        <property name="age" value="19"></property>
        <property name="score" value="443"></property>
    </bean>
    <!--
        PS : 若配置bean时,未给出id属性,IDEA不会报错,系统会分配默认的id。
             默认id的规则是 : 全类名#0, 全类名#1, 全类名#2......
    -->
</beans>

在test包下另定义一个测试类,用于获取到配置好的Student对象,并打印出该对象的信息。

StudentBeanTest类代码如下 :

java 复制代码
package com.cyan.spring.test;

import com.cyan.spring.bean.Student;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class StudentBeanTest {
    @Test
    public void getStudent() {
        //(1) 创建容器ApplicationContext,该容器与beans.xml配置文件关联
            //接口多态
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

        //(2) 通过getBean方法,获取到容器中配置好的对象
        //Object stu01 = applicationContext.getBean("stu01");
            //向下转型
        Student stu01 = (Student) applicationContext.getBean("stu01");
            //向下转型后,即可使用子类特有的方法
        String name = stu01.getName();
        int age = stu01.getAge();
        int score = stu01.getScore();

            //亦可通过getBean的重载方法直接返回JavaBean对应的类型。
        Student stu02 = applicationContext.getBean("stu02", Student.class);

        //(3) 打印对象信息
        System.out.println("stu01 = " + stu01);
        System.out.println("stu01's name = " + name);
        System.out.println("stu01's age = " + age);
        System.out.println("stu01's score = " + score);

        System.out.println(String.format("stu02: name = %s,age = %d,score = %d",stu02.getName(),stu02.getAge(),stu02.getScore()));
    }
}

运行结果 :


三、Spring容器结构分析

1.bean配置信息的存储 :

注意刚才的案例中有这么一行代码:

java 复制代码
//(1) 创建容器ApplicationContext,该容器与beans.xml配置文件关联
            //接口多态
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

Debug界面中,可以看到applicationContext对象的结构,beanFactory是该容器对象的一个非常重要的属性,如下图所示 :

beanFactory属性下,可以找到它维护的beanDefinitionMap,是CurrentHashMap类型。如下所示 :

此处的Node是CurrentHashMap的一个静态内部类, 它与我们之前接触过的HashMapNode类型以及HashtableEntry类型一样,都实现了Map接口内部的Entry接口,如下图所示 :

我们可以在table数组中,找到beans.xml文件中配置的对象的信息,如下图所示 :

可以看到,key就是我们为每一个bean元素配置的id属性;而在val中,首先我们可以看到保存的类的全类名,如下所示 :

此处的beanClass即保存了当前bean对应的class属性,即配置的类的正名(全类名)

此外,我们还能在val中找到一个propertyValues属性,该属性保存了当前bean中的所有property子元素的值,如下所示 :

可以看到,居然出现了我们熟悉的ArrayList和elementData[],数组中的每一个元素,都对应一个bean的property属性,即对象的属性,如下图所示 : (eg : name = "Cyan")

2.bean对象的存储 :

beanFactory属性下,除了方才的beanDefinitionMap,我们还可以找到一个重要的属性singletonObjects,如下图所示 :

可以看到,singletonObjects属性同方才的beanDefinitionMap属性一样,也是ConcurrentHashMap类型,并且它们都维护了一个ConcurrentHashMap$Node类型的数组table,我们仍然可以在table数组中找到配置的两个Student类对象,只不过它们这次是真的以"对象"形式来保存了(单例),如下图所示:

3.bean-id的快捷访问 :

仍然是在beanFactory属性下,除了方才的beanDefinitionMap和singletonObjects属性外,我们还要注意到beanDefinitionNames属性,如下图所示 :

这是Spring的设计者为了开发人员能够快捷地访问到Spring容器中保存的对象的id,专门把id保存在了该属性中,可以看到,它直接就用ArrayList来存储了。于是我们可以在elementData数组中,发现之前在beans.xml中配置的两个bean的id,如下图所示 :

我们可以通过applicationContext对象的getBeanDefinitionNames()方法来快速获取到当前Spring容器中所有的id

代码演示如下 :(仍然在StudentBeanTest类中,新定义一个方法,通过引入JUnit框架进行单元测试)

java 复制代码
    @Test
    public void testGetStudentsID() {
        //不要忘记传入要关联的beans.xml配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println("id = " + beanDefinitionName);
        }
    }

运行结果 :


四、总结

🆗,以上就是Spring系列第一小节的全部内容了。

最后,我们可以用一张图总结一下Spring原生容器结构,如下图所示 :

System.out.println("END----------------------------------------------");

相关推荐
初晴~14 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱5813619 分钟前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳39 分钟前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾42 分钟前
scala借阅图书保存记录(三)
开发语言·后端·scala
黑胡子大叔的小屋1 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark1 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
雷神乐乐2 小时前
Spring学习(一)——Sping-XML
java·学习·spring
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者2 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu