设计模式看 Spring。

news2024/10/5 23:30:15

设计模式看 Spring。


文章目录

    • 设计模式看 Spring。
      • 自定义 Spring 框架。
      • 7.1 Spring 使用回顾。
      • 7.2 Spring 核心功能结构。
      • 7.1.1 bean 概述。
      • 7.3 Spring IoC 相关接口分析。
      • 7.3.1 BeanFactory 解析。
      • 7.3.2 BeanDefinition 解析。
      • 7.3.3 BeanDefinitionReader 解析。
      • 7.3.4 BeanDefinitionRegistry 解析。
      • 7.3.5 创建容器。
      • 7.4 自定义 SpringIoC。
      • 7.4.1 定义 bean 相关的 pojo 类。
          • 7.4.1.1 PropertyValue 类。
          • 7.4.1.2 MutablePropertyValues 类。
          • 7.4.1.3 BeanDefinition 类。
      • 7.4.2 定义注册表相关类。
          • 7.4.2.1 BeanDefinitionRegistry 接口。
          • 7.4.2.2 SimpleBeanDefinitionRegistry 类。
      • 7.4.3 定义解析器相关类。
          • 7.4.3.1 BeanDefinitionReader 接口。
          • 7.4.3.2 XmlBeanDefinitionReader 类。
      • 7.4.4 IoC 容器相关类。
          • 7.4.4.1 BeanFactory 接口。
          • 7.4.4.2 ApplicationContext 接口。
          • 7.4.4.3 AbstractApplicationContext 类。
          • 7.4.4.4 ClassPathXmlApplicationContext 类。
      • 7.4.5 自定义 Spring IoC 总结。
          • 7.4.5.1 使用到的设计模式。
          • 7.4.5.2 符合大部分设计原则。
          • 7.4.5.3 整个设计和 Spring 的设计还是有一定的出入。


自定义 Spring 框架。

7.1 Spring 使用回顾。

自定义 Spring 框架前,先回顾一下 spring 框架的使用,从而分析 spring 的核心,并对核心功能进行模拟。


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

  • 数据访问层。定义 UserDao 接口及其子实现类。
package com.example.design.pattern.spring.dao;

/**
 * 接口 ~ 数据访问层。
 *
 * @author geek
 */
public interface IUserDao {

    /**
     * insert。
     */
    void insert();

}

package com.geek.design.pattern.spring.dao.impl;

import com.geek.design.pattern.spring.dao.IUserDao;

/**
 * 实现类 ~ 数据访问层。
 *
 * @author geek
 */
public class UserDaoImpl implements IUserDao {

    public UserDaoImpl() {
        System.out.println(" ~ UserDaoImpl ~ public UserDaoImpl(); ~ ");
    }

    /**
     * insert。
     */
    @Override
    public void insert() {
        System.out.println(" ~ UserDaoImpl ~ insert; ~ ");
    }

}

  • 业务逻辑层。定义 UserService 接口及其子实现类。
package com.geek.design.pattern.spring.service.impl;

import com.geek.design.pattern.spring.dao.IUserDao;
import com.geek.design.pattern.spring.service.IUserService;

/**
 * 实现类 ~ 业务逻辑层。
 *
 * @author geek
 */
public class UserServiceImpl implements IUserService {

    /**
     * 声明一个 IUserDao 类型的变量。
     */
    private IUserDao userDao;

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    /**
     * add。
     */
    @Override
    public void add() {
        System.out.println(" ~ UserServiceImpl ~ add; ~ ");
        this.userDao.insert();
    }

}

package com.geek.design.pattern.spring.service.impl;

import com.geek.design.pattern.spring.dao.IUserDao;
import com.geek.design.pattern.spring.service.IUserService;

/**
 * 实现类 ~ 业务逻辑层。
 *
 * @author geek
 */
public class UserServiceImpl implements IUserService {

    /**
     * 声明一个 IUserDao 类型的变量。
     */
    private IUserDao userDao;

    public UserServiceImpl(IUserDao userDao) {
        System.out.println(" ~ UserServiceImpl ~ public UserServiceImpl(IUserDao userDao); ~ 0。");
        this.userDao = userDao;
        System.out.println(" ~ UserServiceImpl ~ public UserServiceImpl(IUserDao userDao); ~ 1。");
    }

    public void setUserDao(IUserDao userDao) {
        System.out.println(" ~ UserServiceImpl ~ serUserDao; ~ 0。");
        this.userDao = userDao;
        System.out.println(" ~ UserServiceImpl ~ serUserDao; ~ 1。");
    }

    /**
     * add。
     */
    @Override
    public void add() {
        System.out.println(" ~ UserServiceImpl ~ add; ~ ");
        this.userDao.insert();
    }

}

  • 定义 UserController 类,使用 main 方法模拟 controller 层。
package com.geek.design.pattern.spring.controller;

import com.geek.design.pattern.spring.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author geek
 */
public class UserController {

    public static void main(String[] args) {
        // 创建 Spring 容器对象。
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从容器对象中获取 userService 对象。
        System.out.println("使用对象前。");
        IUserService userService = applicationContext.getBean("userService", IUserService.class);
        // 调用 userService 的方法进行业务逻辑处理。
        userService.add();
    }

}

/*
Connected to the target VM, address: '127.0.0.1:49467', transport: 'socket'
00:36:39.319 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@dc24521
00:36:39.559 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
00:36:39.626 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao'
 ~ UserDaoImpl ~ public UserDaoImpl(); ~ 
00:36:39.649 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
 ~ UserServiceImpl ~ public UserServiceImpl();
 ~ UserServiceImpl ~ serUserDao; ~ 0。
 ~ UserServiceImpl ~ serUserDao; ~ 1。
使用对象前。
 ~ UserServiceImpl ~ add; ~ 
 ~ UserDaoImpl ~ insert; ~ 
Disconnected from the target VM, address: '127.0.0.1:49467', transport: 'socket'

Process finished with exit code 0
 */

  • 编写配置文件。在类路径下编写一个名为 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.geek.design.pattern.spring.dao.impl.UserDaoImpl"/>

    <bean id="userService" class="com.geek.design.pattern.spring.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>

通过上面代码及结果可以看出。

  • userService 对象是从 applicationContext 容器对象获取到的,也就是 userService 对象交由 Spring 进行管理。

  • 上面结果可以看到调用了 UserDao 对象中的 add 方法,也就是说 UserDao 子实现类对象也交由 Spring 管理了。

  • UserService 中的 userDao 变量我们并没有进行赋值,但是可以正常使用,说明 Spring 已经将 UserDao 对象赋值给了 userDao 变量。

上面三点体现了 Spring 框架的 IoC(Inversion of Control)和 DI(Dependency Injection, DI)。



7.2 Spring 核心功能结构。

Spring 大约有20个模块,由1300多个不同的文件构成。这些模块可以分为:

核心容器、AOP 和设备支持、数据访问与集成、Web 组件、通信报文和集成测试等,下面是 Spring 框架的总体架构图。


核心容器由 beans、core、context 和 expression(Spring Expression Language,SpEL)4 个模块组成。

  • spring-beans 和 spring-core 模块是 Spring 框架的核心模块,包含了控制反转(Inversion of Control,IoC)和依赖注入(Dependency
    Injection,DI)。BeanFactory 使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。BeanFactory
    属于延时加载,也就是说在实例化容器对象后并不会自动实例化 Bean,只有当 Bean 被使用时,BeanFactory 才会对该 Bean
    进行实例化与依赖关系的装配。

(延时加载)。

package com.geek.design.pattern.spring.controller;

import com.geek.design.pattern.spring.service.IUserService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * 延时加载。
 *
 * @author geek
 */
public class UserController1 {

    public static void main(String[] args) {
        // 创建 Spring 容器对象。
//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        // 从容器对象中获取 userService 对象。
//        IUserService userService = applicationContext.getBean("userService", IUserService.class);
        System.out.println("使用对象前。");
        IUserService userService = beanFactory.getBean("userService", IUserService.class);
        // 调用 userService 的方法进行业务逻辑处理。
        userService.add();
    }

}

/*
Connected to the target VM, address: '127.0.0.1:49453', transport: 'socket'
00:35:53.288 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
使用对象前。
00:35:53.299 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanFactory - Creating shared instance of singleton bean 'userService'
 ~ UserServiceImpl ~ public UserServiceImpl();
00:35:53.316 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanFactory - Creating shared instance of singleton bean 'userDao'
 ~ UserDaoImpl ~ public UserDaoImpl(); ~
 ~ UserServiceImpl ~ serUserDao; ~ 0。
 ~ UserServiceImpl ~ serUserDao; ~ 1。
 ~ UserServiceImpl ~ add; ~
 ~ UserDaoImpl ~ insert; ~
Disconnected from the target VM, address: '127.0.0.1:49453', transport: 'socket'

Process finished with exit code 0
 */

  • spring-context 模块构架于核心模块之上,扩展了 BeanFactory,为它添加了 Bean
    生命周期控制、框架事件体系及资源加载透明化等功能。此外,该模块还提供了许多企业级支持,如邮件访问、远程访问、任务调度等,ApplicationContext
    是该模块的核心接口,它的超类是 BeanFactory。与 BeanFactory 不同,ApplicationContext 实例化后会自动对所有的单实例 Bean
    进行实例化与依赖关系的装配,使之处于待用状态。

  • spring-context-support 模块是对 Spring IoC 容器及 IoC 子容器的扩展支持。

  • spring-context-indexer 模块是 Spring 的类管理组件和 Classpath 扫描组件。

  • spring-expression 模块是统一表达式语言(EL)的扩展模块,可以查询、管理运行中的对象,同时也可以方便地调用对象方法,以及操作数组、集合等。ta 的语法类似于传统 EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。EL 的特性是基于 Spring 产品的需求而设计的,可以非常方便地同 Spring IoC 进行交互。



7.1.1 bean 概述。

Spring 就是面向 Bean 的编程(BOP, Bean Oriented Programming),Bean 在 Spring 中处于核心地位。Bean 对于 Spring 的意义就像 Object 对于 OOP 的意义一样,Spring 中没有 Bean 也就没有 Spring 存在的意义。Spring IoC 容器通过配置文件或者注解的方式来管理 bean 对象之间的依赖关系。

Spring 中 bean 用于对一个类进行封装。如下面的配置。

    <bean id="userDao" class="com.geek.design.pattern.spring.dao.impl.UserDaoImpl"/>

    <bean id="userService" class="com.geek.design.pattern.spring.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

为什么 Bean 如此重要呢?

  • Spring 将 bean 对象交由一个叫 IoC 容器进行管理。

  • bean 对象之间的依赖关系在配置文件中体现,并由 Spring 完成。



7.3 Spring IoC 相关接口分析。

7.3.1 BeanFactory 解析。

Spring 中 Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,即 IoC 容器,为开发者管理对象之间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多 IoC 容器的实现供用户选择,其相互关系如下图所示。


其中,BeanFactory 作为最顶层的一个接口,定义了 IoC 容器的基本功能规范,BeanFactory 有三个重要的子接口:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从类图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。

那么为何要定义这么多层次的接口呢?

每个接口都有它的使用场合,主要是为了区分在 Spring 内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。例如,

  • ListableBeanFactory 接口表示这些 Bean 可列表化。

  • HierarchicalBeanFactory 表示这些 Bean 是有继承关系的,也就是每个 Bean 可能有父 Bean。

  • AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。

这三个接口共同定义了 Bean 的集合、Bean 之间的关系及 Bean 行为。最基本的 IoC 容器接口是 BeanFactory,来看一下它的源码。


package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {

	String FACTORY_BEAN_PREFIX = "&";

	// 根据 bean 的名称获取 IoC 容器中的的 bean 对象。
	Object getBean(String name) throws BeansException;

	// 根据 bean 的名称获取 IoC 容器中的的 bean 对象,并指定获取到的 bean 对象的类型,这样我们使用时就不需要进行类型强转了。
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

	// 判断容器中是否包含指定名称的 bean 对象。
	boolean containsBean(String name);

	// 根据 bean 的名称判断是否是单例。
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

	String[] getAliases(String name);

}

在 BeanFactory 里只对 IoC 容器的基本行为做了定义,根本不关心你的 Bean 是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。

BeanFactory 有一个很重要的子接口,就是 ApplicationContext 接口,该接口主要来规范容器中的 bean 对象是非延时加载,即在创建容器对象的时候就对象 bean 进行初始化,并存储到一个容器中。

要知道工厂是如何产生对象的,我们需要看具体的 IoC 容器实现,Spring 提供了许多 IoC 容器实现,比如:

  • ClasspathXmlApplicationContext : 根据类路径加载 xml 配置文件,并创建 IoC 容器对象。

  • FileSystemXmlApplicationContext :根据系统路径加载 xml 配置文件,并创建 IoC 容器对象。

  • AnnotationConfigApplicationContext :加载注解类配置,并创建 IoC 容器。



7.3.2 BeanDefinition 解析。

Spring IoC 容器管理我们定义的各种 Bean 对象及其相互关系,而 Bean 对象在 Spring 实现中是以 BeanDefinition 来描述的,如下面配置文件


<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>

        bean 标签还有很多属性:
        scope、init-method、destory-method 等。

其继承体系如下图所示。



7.3.3 BeanDefinitionReader 解析。

Bean 的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean 的解析主要就是对
Spring 配置文件的解析。这个解析过程主要通过 BeanDefinitionReader 来完成,看看 Spring 中 BeanDefinitionReader 的类结构图,如下图所示。

看看 BeanDefinitionReader 接口定义的功能来理解它具体的作用:

public interface BeanDefinitionReader {

    //获取 BeanDefinitionRegistry 注册器对象
    BeanDefinitionRegistry getRegistry();

    @Nullable
    ResourceLoader getResourceLoader();

    @Nullable
    ClassLoader getBeanClassLoader();

    BeanNameGenerator getBeanNameGenerator();

    /*
        下面的 loadBeanDefinitions 都是加载 bean 定义,从指定的资源中
    */
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}


7.3.4 BeanDefinitionRegistry 解析。

BeanDefinitionReader 用来解析 bean 定义,并封装 BeanDefinition 对象,而我们定义的配置文件中定义了很多 bean 标签,所以就有一个问题,解析的
BeanDefinition 对象存储到哪儿?答案就是 BeanDefinition 的注册中心,而该注册中心顶层接口就是 BeanDefinitionRegistry。

public interface BeanDefinitionRegistry extends AliasRegistry {

    //往注册表中注册 bean
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;

    //从注册表中删除指定名称的 bean
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    //获取注册表中指定名称的 bean
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    //判断注册表中是否已经注册了指定名称的 bean
    boolean containsBeanDefinition(String beanName);

    //获取注册表中所有的 bean 的名称
    String[] getBeanDefinitionNames();

    int getBeanDefinitionCount();

    boolean isBeanNameInUse(String beanName);
}

继承结构图如下:

从上面类图可以看到 BeanDefinitionRegistry 接口的子实现类主要有以下几个:

  • DefaultListableBeanFactory

    在该类中定义了如下代码,就是用来注册 bean

  private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
  • SimpleBeanDefinitionRegistry

    在该类中定义了如下代码,就是用来注册 bean

  private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);


7.3.5 创建容器。

ClassPathXmlApplicationContext 对 Bean 配置资源的载入是从 refresh()方法开始的。refresh()方法是一个模板方法,规定了 IoC
容器的启动流程,有些逻辑要交给其子类实现。它对 Bean 配置资源进行载入,ClassPathXmlApplicationContext 通过调用其父类
AbstractApplicationContext 的 refresh()方法启动整个 IoC 容器对 Bean 定义的载入过程。



7.4 自定义 SpringIoC。

现要对下面的配置文件进行解析,并自定义 Spring 框架的 IoC 对涉及到的对象进行管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>

7.4.1 定义 bean 相关的 pojo 类。

7.4.1.1 PropertyValue 类。

用于封装 bean 的属性,体现到上面的配置文件就是封装 bean 标签的子标签 property 标签数据。

public class PropertyValue {

    private String name;
    private String ref;
    private String value;

    public PropertyValue() {
    }

    public PropertyValue(String name, String ref, String value) {
        this.name = name;
        this.ref = ref;
        this.value = value;
    }

    public String getName() {
        return name;
    }

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

    public String getRef() {
        return ref;
    }

    public void setRef(String ref) {
        this.ref = ref;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}


7.4.1.2 MutablePropertyValues 类。

一个 bean 标签可以有多个 property 子标签,所以再定义一个 MutablePropertyValues 类,用来存储并管理多个 PropertyValue 对象。

public class MutablePropertyValues implements Iterable<PropertyValue> {

    private final List<PropertyValue> propertyValueList;

    public MutablePropertyValues() {
        this.propertyValueList = new ArrayList<PropertyValue>();
    }

    public MutablePropertyValues(List<PropertyValue> propertyValueList) {
        this.propertyValueList = (propertyValueList != null ? propertyValueList : new ArrayList<PropertyValue>());
    }

    public PropertyValue[] getPropertyValues() {
        return this.propertyValueList.toArray(new PropertyValue[0]);
    }

    public PropertyValue getPropertyValue(String propertyName) {
        for (PropertyValue pv : this.propertyValueList) {
            if (pv.getName().equals(propertyName)) {
                return pv;
            }
        }
        return null;
    }

    @Override
    public Iterator<PropertyValue> iterator() {
        return propertyValueList.iterator();
    }

    public boolean isEmpty() {
        return this.propertyValueList.isEmpty();
    }

    public MutablePropertyValues addPropertyValue(PropertyValue pv) {
        for (int i = 0; i < this.propertyValueList.size(); i++) {
            PropertyValue currentPv = this.propertyValueList.get(i);
            if (currentPv.getName().equals(pv.getName())) {
                this.propertyValueList.set(i, new PropertyValue(pv.getName(), pv.getRef(), pv.getValue()));
                return this;
            }
        }
        this.propertyValueList.add(pv);
        return this;
    }

    public boolean contains(String propertyName) {
        return getPropertyValue(propertyName) != null;
    }
}


7.4.1.3 BeanDefinition 类。

BeanDefinition 类用来封装 bean 信息的,主要包含 id(即 bean 对象的名称)、class(需要交由 spring 管理的类的全类名)及子标签
property 数据。

public class BeanDefinition {
    private String id;
    private String className;

    private MutablePropertyValues propertyValues;

    public BeanDefinition() {
        propertyValues = new MutablePropertyValues();
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public void setPropertyValues(MutablePropertyValues propertyValues) {
        this.propertyValues = propertyValues;
    }

    public MutablePropertyValues getPropertyValues() {
        return propertyValues;
    }
}


7.4.2 定义注册表相关类。

7.4.2.1 BeanDefinitionRegistry 接口。

BeanDefinitionRegistry 接口定义了注册表的相关操作,定义如下功能:

  • 注册 BeanDefinition 对象到注册表中
  • 从注册表中删除指定名称的 BeanDefinition 对象
  • 根据名称从注册表中获取 BeanDefinition 对象
  • 判断注册表中是否包含指定名称的 BeanDefinition 对象
  • 获取注册表中 BeanDefinition 对象的个数
  • 获取注册表中所有的 BeanDefinition 的名称
public interface BeanDefinitionRegistry {

    //注册 BeanDefinition 对象到注册表中
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

    //从注册表中删除指定名称的 BeanDefinition 对象
    void removeBeanDefinition(String beanName) throws Exception;

    //根据名称从注册表中获取 BeanDefinition 对象
    BeanDefinition getBeanDefinition(String beanName) throws Exception;

    boolean containsBeanDefinition(String beanName);

    int getBeanDefinitionCount();

    String[] getBeanDefinitionNames();
}
7.4.2.2 SimpleBeanDefinitionRegistry 类。

该类实现了 BeanDefinitionRegistry 接口,定义了 Map 集合作为注册表容器。

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }

    @Override
    public void removeBeanDefinition(String beanName) throws Exception {
        beanDefinitionMap.remove(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws Exception {
        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[1]);
    }
}


7.4.3 定义解析器相关类。

7.4.3.1 BeanDefinitionReader 接口。

BeanDefinitionReader 是用来解析配置文件并在注册表中注册 bean 的信息。定义了两个规范:

  • 获取注册表的功能,让外界可以通过该对象获取注册表对象。
  • 加载配置文件,并注册 bean 数据。
public interface BeanDefinitionReader {

    //获取注册表对象
    BeanDefinitionRegistry getRegistry();

    //加载配置文件并在注册表中进行注册
    void loadBeanDefinitions(String configLocation) throws Exception;
}


7.4.3.2 XmlBeanDefinitionReader 类。

XmlBeanDefinitionReader 类是专门用来解析 xml 配置文件的。该类实现 BeanDefinitionReader 接口并实现接口中的两个功能。

public class XmlBeanDefinitionReader implements BeanDefinitionReader {

    private BeanDefinitionRegistry registry;

    public XmlBeanDefinitionReader() {
        this.registry = new SimpleBeanDefinitionRegistry();
    }

    @Override
    public BeanDefinitionRegistry getRegistry() {
        return registry;
    }

    @Override
    public void loadBeanDefinitions(String configLocation) throws Exception {

        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        SAXReader reader = new SAXReader();
        Document document = reader.read(is);
        Element rootElement = document.getRootElement();
        //解析 bean 标签
        parseBean(rootElement);
    }

    private void parseBean(Element rootElement) {

        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setId(id);
            beanDefinition.setClassName(className);
            List<Element> list = element.elements("property");
            MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
            for (Element element1 : list) {
                String name = element1.attributeValue("name");
                String ref = element1.attributeValue("ref");
                String value = element1.attributeValue("value");
                PropertyValue propertyValue = new PropertyValue(name, ref, value);
                mutablePropertyValues.addPropertyValue(propertyValue);
            }
            beanDefinition.setPropertyValues(mutablePropertyValues);

            registry.registerBeanDefinition(id, beanDefinition);
        }
    }
}


7.4.4 IoC 容器相关类。

7.4.4.1 BeanFactory 接口。

在该接口中定义 IoC 容器的统一规范即获取 bean 对象。

public interface BeanFactory {
    //根据 bean 对象的名称获取 bean 对象
    Object getBean(String name) throws Exception;

    //根据 bean 对象的名称获取 bean 对象,并进行类型转换
    <T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}
7.4.4.2 ApplicationContext 接口。

该接口的所以的子实现类对 bean 对象的创建都是非延时的,所以在该接口中定义 refresh() 方法,该方法主要完成以下两个功能:

  • 加载配置文件。
  • 根据注册表中的 BeanDefinition 对象封装的数据进行 bean 对象的创建。
public interface ApplicationContext extends BeanFactory {
    //进行配置文件加载并进行对象创建
    void refresh() throws IllegalStateException, Exception;
}


7.4.4.3 AbstractApplicationContext 类。
  • 作为 ApplicationContext 接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个 Map 集合,作为 bean 对象存储的容器。

  • 声明 BeanDefinitionReader 类型的变量,用来进行 xml 配置文件的解析,符合单一职责原则。

    BeanDefinitionReader 类型的对象创建交由子类实现,因为只有子类明确到底创建 BeanDefinitionReader 哪儿个子实现类对象。

public abstract class AbstractApplicationContext implements ApplicationContext {

    protected BeanDefinitionReader beanDefinitionReader;
    //用来存储 bean 对象的容器   key 存储的是 bean 的 id 值,value 存储的是 bean 对象
    protected Map<String, Object> singletonObjects = new HashMap<String, Object>();

    //存储配置文件的路径
    protected String configLocation;

    public void refresh() throws IllegalStateException, Exception {

        //加载 BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(configLocation);

        //初始化 bean
        finishBeanInitialization();
    }

    //bean 的初始化
    private void finishBeanInitialization() throws Exception {
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        String[] beanNames = registry.getBeanDefinitionNames();

        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
            getBean(beanName);
        }
    }
}

注意:该类 finishBeanInitialization()方法中调用 getBean()方法使用到了模板方法模式。



7.4.4.4 ClassPathXmlApplicationContext 类。

该类主要是加载类路径下的配置文件,并进行 bean 对象的创建,主要完成以下功能:

  • 在构造方法中,创建 BeanDefinitionReader 对象。
  • 在构造方法中,调用 refresh()方法,用于进行配置文件加载、创建 bean 对象并存储到容器中。
  • 重写父接口中的 getBean()方法,并实现依赖注入操作。
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {

    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        //构建 XmlBeanDefinitionReader 对象
        beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            this.refresh();
        } catch (Exception e) {
        }
    }

    //根据 bean 的 id 属性值获取 bean 对象
    @Override
    public Object getBean(String name) throws Exception {

        //return singletonObjects.get(name);
        Object obj = singletonObjects.get(name);
        if (obj != null) {
            return obj;
        }

        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        if (beanDefinition == null) {
            return null;
        }
        String className = beanDefinition.getClassName();
        Class<?> clazz = Class.forName(className);
        Object beanObj = clazz.newInstance();
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            String propertyName = propertyValue.getName();
            String value = propertyValue.getValue();
            String ref = propertyValue.getRef();
            if (ref != null && !"".equals(ref)) {

                Object bean = getBean(ref);
                String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        method.invoke(beanObj, bean);
                    }
                }
            }

            if (value != null && !"".equals(value)) {
                String methodName = StringUtils.getSetterMethodNameByFieldName(propertyName);
                Method method = clazz.getMethod(methodName, String.class);
                method.invoke(beanObj, value);
            }
        }
        singletonObjects.put(name, beanObj);
        return beanObj;
    }

    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {

        Object bean = getBean(name);
        if (bean != null) {
            return clazz.cast(bean);
        }
        return null;
    }
}


7.4.5 自定义 Spring IoC 总结。

7.4.5.1 使用到的设计模式。
  • 工厂模式。这个使用工厂模式 + 配置文件的方式。
  • 单例模式。Spring IoC 管理的 bean 对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是 spring 框架对每一个 bean
    只创建了一个对象。
  • 模板方法模式。AbstractApplicationContext 类中的 finishBeanInitialization()方法调用了子类的 getBean()方法,因为 getBean()
    的实现和环境息息相关。
  • 迭代器模式。对于 MutablePropertyValues 类定义使用到了迭代器模式,因为此类存储并管理 PropertyValue
    对象,也属于一个容器,所以给该容器提供一个遍历方式。

spring 框架其实使用到了很多设计模式,如 AOP 使用到了代理模式,选择 JDK 代理或者 CGLIB 代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。



7.4.5.2 符合大部分设计原则。
7.4.5.3 整个设计和 Spring 的设计还是有一定的出入。

spring 框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。而我们自定义 SpringIoC 有以下几个目的:

  • 了解 Spring 底层对对象的大体管理机制。

  • 了解设计模式在具体的开发中的使用。

  • 以后学习 spring 源码,通过该案例的实现,可以降低 spring 学习的入门成本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文阅读 | Video Frame Synthesis using Deep Voxel Flow

前言&#xff1a; 视频帧生成方法&#xff08;视频插帧/视频预测&#xff09;ICCV2017 oral Video Frame Synthesis using Deep Voxel Flow 引言 当下进行视频帧合成的方法分为两种&#xff0c;第一种是光流法&#xff0c;光流准确的话效果好&#xff0c;光流不准确的话则生…

手摸手,带你实现3D粒子特效

文章摘要&#xff1a;某天&#xff0c;产品小姐姐找到我&#xff0c;要在页面上放一个动态3D模型…不会webGL&#xff1f;没关系&#xff01;今天就来聊一聊如何用从零实现3D粒子特效。 背景 近年来&#xff0c;随着互联网的迅速发展&#xff0c;用户对产品的视觉效果需求也更…

内网渗透(四十六)之横向移动篇-使用系统漏洞ms17010横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

leaflet 加载geojson数据,随机显示不同颜色的circleMarker

第086个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet项目中加载geojson数据,随机显示不同颜色的circleMarker. 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共89行)相关API专栏目标示例效果 配置方式…

时间序列数据预测的类型

本文主要内容是使用LSTM网络进行不同类型的时间序列预测任务&#xff0c;不涉及代码&#xff0c;仅仅就不同类型的预测任务和数据划分进行说明。 参考文章&#xff1a;https://machinelearningmastery.com/how-to-develop-lstm-models-for-time-series-forecasting/ 注&#xf…

如何使用Hugo Academic Theme构建自己的github主页

前期条件 自己已经注册好GitHub 原文档&#xff1a;https://wowchemy.com/docs/getting-started/hugo-github-quickstart/ 搜索Hugo Academic Theme&#xff08;网址&#xff09; 进入后的网址为&#xff1a;https://academic-demo.netlify.app/ 点击Get Start 出现如下模板…

【观察】昇腾加速AI“走深向实”,打通落地“最后一公里”

毫无疑问&#xff0c;今天AI正与产业结合得越来越紧密&#xff0c;从智能制造&#xff0c;到智慧医疗&#xff0c;智慧金融、智慧城市等&#xff0c;AI已经开始渗透到我们生活的方方面面&#xff0c;即便是目前来自传统行业用户的AI转型需求尚未完全激活爆发&#xff0c;但仅仅…

利用组件注解符精简Spring配置文件

文章目录利用组件注解符精简Spring配置文件一、利用组件注解精简Spring配置文件1、创建新包2、将4个类给拷贝过去3、修改杀龙任务类4、修改救美任务类5、修改勇敢骑士类6、修改救美骑士类7、创建Spring配置文件利用组件注解符精简Spring配置文件 一、利用组件注解精简Spring配…

什么是生命周期?Activity生命周期的三种状态

什么是生命周期生命周期就是一个对象从创建到销毁的过程&#xff0c;每一个对象都有自己的生命周期。同样&#xff0c;Activity也具有相应的生命周期&#xff0c;Activity的生命周期中分为三种状态&#xff0c;分别是运行状态、暂停状态和停止状态。接下来将针对Activity生命周…

【自动化测试】自动化测试框架那些事儿

无论是在自动化测试实践&#xff0c;还是日常交流中&#xff0c;经常听到一个词&#xff1a;框架。在教学的过程中&#xff0c;同学们一直对“框架”这个词知其然不知其所以然。 最近看了很多自动化相关的资料&#xff0c;加上一些实践&#xff0c;算是对“框架”有了一些理解…

JVM13 类的生命周期

1. 概述 在 Java 中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义&#xff0c;引用数据类型则需要进行类的加载。 按照 Java 虚拟机规范&#xff0c;从 class 文件到加载到内存中的类&#xff0c;到类卸载出内存为止&#xff0c;它的整个生命周期包…

本地代码推送到Coding

话不多说&#xff0c;开门见山 一、coding准备 注册啊&#xff0c;建项目&#xff0c;这些就不用多说了。 1.创建一个代码仓库 填一下名称和描述就行&#xff0c;其他先不用填&#xff0c;然后点最下面完成创建就行。 2.保存代码仓库地址 二、本地代码仓库准备 已经是在…

Linux 忘记密码解决方法

很多朋友经常会忘记Linux系统的root密码&#xff0c;linux系统忘记root密码的情况该怎么办呢&#xff1f;重新安装系统吗&#xff1f;当然不用&#xff01;进入单用户模式更改一下root密码即可。 步骤如下&#xff1a; 重启linux系统 3 秒之内要按一下回车&#xff0c;出现如…

Web前端:什么是Vue Native 框架?有什么特点?

Vue Native是一个使用Vue.Js开发本地移动应用程序的框架。该框架将文档转换为React Native&#xff0c;进而为你提供适用于Android和iOS的本地应用程序。实际上&#xff0c;Vue Native应用程序据说是React API的包装。Vue将Vue.js和React结合在一起&#xff0c;让你的开发团队充…

【牛客刷题专栏】0x0C:JZ4 二维数组中的查找(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录前言问题…

Django框架之模板过滤器

过滤器 语法如下: 使用管道符号|来应用过滤器&#xff0c;用于进行计算、转换操作&#xff0c;可以使用在变量、标签中。如果过滤器需要参数&#xff0c;则使用冒号:传递参数。 变量|过滤器:参数列举几个如下&#xff1a; safe&#xff0c;禁用转义&#xff0c;告诉模板这个变…

大数据Hadoop教程-01大数据导论与Linux基础

目录 01、大数据导论 02、Linux操作系统概述 P007 P008 P009 P010 P011 P012 P013 P014 P015 P016 P017 01、大数据导论 企业数据分析方向 现状分析&#xff08;分析当下的数据&#xff09;&#xff1a;现阶段的整体情况&#xff0c;各个部分的构成占比、发展、变…

C++空指针和野指针

空指针&#xff1a;指针被赋值为空 例如&#xff1a; int* p nullptr;int* p NULL; 空指针指向的地址是00000000&#xff0c;但空指针不可以解引用 野指针&#xff1a;指针指向了不可控的位置 例如&#xff1a; 未初始化 int* p; //野指针 越界访问 int intArr[5]{0, 1, …

Echarts+大屏

先放上我做的大屏吧&#xff0c;做的不是很好看&#xff0c;希望大家能见谅。 一、实验目的 理解大数据可视化的原理和方法掌握ECharts可视化的原理、步骤和效果掌握使用D3读取数据的方法 二、实验任务与要求 通过网店运营的销售数据&#xff0c;分别从各月的销售情况、各产品…

【1】linux命令每日分享——mkdir创建目录

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…