解锁ApplicationContext vs BeanFactory: 谁更具选择性?

发布于:2024-04-27 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

一、聚焦源码回顾

(一)源码分析和理解

(二)简短的回顾对比建议

二、ApplicationContext vs BeanFactory特性对比

(一)主要特性总结

(二)直接建议

三、案例简单说明

(一)加载少量的 Bean的案例

(二)简单的命令行工具:用于读取配置文件并生成报告

(三)启动时加载大量的配置信息,并且在运行时需要动态地获取一些 Bean


针对ApplicationContext和BeanFactory,在理解其源码后,那在开发过程中应该选用ApplicationContext还是BeanFactory哪个更合适呢?

一、聚焦源码回顾

聚焦源码回顾的意义在于深入理解框架的内部机制和设计思想。通过仔细研究源码,可以了解框架的实现细节、各个组件之间的交互关系以及解决问题的方法,也就有助于更好地利用框架的功能,解决实际的开发问题,并且能够更加灵活地进行定制和扩展。

(一)源码分析和理解

在 Spring  官方文档中中,虽然没有明确的建议说应该优先选择 ApplicationContext 还是 BeanFactory,但是可以通过文档中的描述来理解它们的区别和用途。我们之前对这两部分进行了重新的学习和总结,具体见如下表格:

具体博客总结 直接学习链接

重看Spring聚焦BeanFactory分析

重看Spring聚焦BeanFactory分析-CSDN博客

重看Spring聚焦ApplicationContext分析

重看Spring聚焦ApplicationContext分析-CSDN博客

(二)简短的回顾对比建议

直接简短总结来看的话,可以这样对比一下:

  1. BeanFactory:Spring 框架的中心接口,提供了高级配置机制来管理任何类型的对象,主要作用是提供依赖注入和基本的对象生命周期管理功能。

  2. ApplicationContext:BeanFactory 的一个子接口,它扩展了 BeanFactory 的功能,提供了更多的企业级功能和特性。它是 Spring 的核心容器,用于加载应用程序的配置文件,并管理 Bean 的生命周期。

从简单的对比可以看出,ApplicationContext 提供了比 BeanFactory 更多的功能和特性,因此在实际开发中,通常优先选择 ApplicationContext。但具体选择取决于项目的需求和场景。

二、ApplicationContext vs BeanFactory特性对比

(一)主要特性总结

一个简单的表格,用于比较 ApplicationContextBeanFactory 的主要特性:

特性 ApplicationContext BeanFactory
自动装配 支持 支持
延迟初始化 支持 部分支持,需要手动配置
国际化消息处理 支持 部分支持,需要手动配置
事件发布 支持 部分支持,需要手动配置
AOP 配置 支持 部分支持,需要手动配置
安全性配置 支持 部分支持,需要手动配置
嵌套 ApplicationContext 支持 部分支持,需要手动配置
Web 应用上下文 支持 部分支持,需要手动配置
缓存功能 支持(如注解缓存) 部分支持,需要手动配置
加载应用程序上下文的方式 通过类路径、文件系统、Web 应用程序上下文等方式加载 通过类路径、文件系统等方式加载
扩展点 提供多个扩展点和插件接口,可轻松扩展其功能 较少的扩展点,相对较难扩展功能
适用场景 适用于大多数企业级应用开发,提供更多的功能和特性 适用于简单的应用场景,对资源要求较低,不需要使用额外的功能和特性

(二)直接建议

从这个表格来看,ApplicationContext 显然提供了更多的功能和特性,而且更适合复杂的企业级应用开发。它提供了自动装配、延迟初始化、国际化消息处理、事件发布、AOP 配置等多种功能,同时支持多种加载应用程序上下文的方式,具有更强的灵活性和扩展性。

相比之下,BeanFactory 虽然也提供了基本的依赖注入和对象生命周期管理功能,但功能相对较少,适用于简单的应用场景或对资源要求较低的情况。

所以总的来说大家会更倾向于推荐使用 ApplicationContext,因为它提供了更丰富的功能和更多的特性,能够更好地满足现代企业级应用开发的需求。当然,在一些特定的情况下,如资源有限或者对功能要求不高的情况下,选择 BeanFactory 也是可以的,但通常情况下 ApplicationContext 是更好的选择。

三、案例简单说明

(一)加载少量的 Bean的案例

假设我们正在开发一个简单的命令行应用程序,该应用程序只需要加载少量的 Bean,并且对于额外的功能需求并不是很高。在这种情况下,选择 BeanFactory 可能更为合适。

假设我们的 beans.xml 文件位于类路径(classpath)下的 src/main/resources 目录下:

<?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">

    <!-- 定义一个名为 "helloWorld" 的 Bean -->
    <bean id="helloWorld" class="org.zyf.javabasic.spring.beanFactory.HelloWorld"/>
</beans>

xmlnsxsi:schemaLocation 是 XML 命名空间和模式位置,用于指定 XML 文件的命名空间和 XML Schema 的位置。这些是标准的 Spring XML 配置文件头部。该 XML 配置文件定义了一个名为 helloWorld 的 Bean,它是一个 HelloWorld 类型的对象,简单定义如下:

package org.zyf.javabasic.spring.beanFactory;

/**
 * @program: zyfboot-javabasic
 * @description: HelloWorld
 * @author: zhangyanfeng
 * @create: 2024-04-13 13:03
 **/
public class HelloWorld {
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

现在我们测试验证如下:

package org.zyf.javabasic.spring.beanFactory;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

/**
 * @program: zyfboot-javabasic
 * @description: 选择 BeanFactory 可能更为合适。
 * @author: zhangyanfeng
 * @create: 2024-04-13 12:59
 **/
@Log4j2
public class BeanFactoryBetterTest {
    public static void main(String[] args) {
        // 创建 BeanFactory
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));

        // 获取 Bean
        HelloWorld helloWorld1 = (HelloWorld) beanFactory.getBean("helloWorld");

        // 使用 Bean
        helloWorld1.sayHello();


        // 创建 ApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        // 获取 Bean
        HelloWorld helloWorld2 = (HelloWorld) context.getBean("helloWorld");

        // 使用 Bean
        helloWorld2.sayHello();
    }
}

虽然我们可以使用 ApplicationContext 来达到同样的效果,但在这种简单的情况下,选择 BeanFactory 更为轻量级,不需要加载额外的功能和特性,更加简洁明了。

然而,如果应用程序的需求变得更加复杂,需要更多的功能和特性(如自动装配、事件发布、AOP 配置等),或者需要集成其他 Spring 框架或第三方框架,那么使用 ApplicationContext 将更为合适。

(二)简单的命令行工具:用于读取配置文件并生成报告

假设有一个简单的命令行工具,用于读取配置文件并生成报告。在这个工具中,我们可能只需要加载一些配置信息,而不需要使用 Spring 框架提供的更复杂的功能。在这种情况下,使用 BeanFactory 可能更为合适。

假设我们有一个简单的配置文件 report-config.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">

    <!-- 数据库连接信息 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/zyf"/>
        <property name="username" value="zyf"/>
        <property name="password" value="Zyf76#56uhb&@sh%hd567ijhfg"/>
    </bean>

    <!-- 报告生成器 -->
    <bean id="reportGenerator" class="org.zyf.javabasic.spring.beanFactory.ReportGenerator">
        <property name="dataSource" ref="dataSource"/>
        <!-- 其他配置属性 -->
    </bean>
</beans>

在这个示例中定义了一个 dataSource使用Spring提供的 DriverManagerDataSource 类来创建数据库连接。现在定义一个自定义的 ReportGenerator 类,用于生成报告:

package org.zyf.javabasic.spring.beanFactory;

import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

/**
 * @program: zyfboot-javabasic
 * @description: 调用其方法来生成报告
 * @author: zhangyanfeng
 * @create: 2024-04-13 13:52
 **/
public class ReportGenerator {
    private DataSource dataSource;

    // setter 方法用于接收 dataSource 属性的注入
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void generateReport() {
        // 使用 JdbcTemplate 连接数据库
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        // 查询数据库并生成报告
        String sql = "SELECT * FROM user20240413";
        jdbcTemplate.query(sql, (rs, rowNum) -> {
            String username = rs.getString("username");
            String email = rs.getString("email");
            System.out.println("Username: " + username + ", Email: " + email);
            return null;
        });

        System.out.println("Report generated successfully.");
    }
}

对于这样一个简单的命令行工具,我们可能只需要加载这些配置信息,然后创建一个 ReportGenerator 实例,并调用其方法来生成报告。在这种情况下,使用 BeanFactory 就足够了,因为我们不需要 Spring 提供的更多的高级功能,如自动装配、事件发布等。下面是一个使用 BeanFactory 的示例:

package org.zyf.javabasic.spring.beanFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * @program: zyfboot-javabasic
 * @description: 简单的命令行工具
 * @author: zhangyanfeng
 * @create: 2024-04-13 13:55
 **/
public class ReportToolTest {
    public static void main(String[] args) {
        // 创建 BeanFactory
        BeanFactory beanFactory = new XmlBeanFactory(
new ClassPathResource("report-config.xml"));

        // 获取 reportGenerator Bean
        ReportGenerator reportGenerator = (ReportGenerator) beanFactory.getBean("reportGenerator");

        // 使用 reportGenerator 生成报告
        reportGenerator.generateReport();
    }
}

在这个示例中,我们使用 BeanFactory 来加载 report-config.xml 文件,并获取了一个 reportGenerator Bean。然后,我们使用这个 Bean 来调用 generateReport 方法来生成报告。

尽管我们也可以使用 ApplicationContext 来达到同样的效果,但在这种简单的情况下,选择 BeanFactory 更为轻量级和简洁。

(三)启动时加载大量的配置信息,并且在运行时需要动态地获取一些 Bean

假设我们有一个 Spring Boot 应用程序,该应用程序需要在启动时加载大量的配置信息,并且在运行时需要动态地获取一些 Bean,这些 Bean 的创建可能依赖于其他 Bean。在这种情况下,使用 ApplicationContext 会更加方便和灵活。

首先,我们可以将所有的配置信息(如商品信息、用户信息、库存信息等)存储在数据库中。然后,在应用程序启动时,我们使用 ApplicationContext 加载数据库配置,并将其转换为 Spring 的 Bean,并将这些 Bean 注册到应用程序的上下文中。

在用户下单时,我们可以通过 ApplicationContext 获取相应的 Bean(如商品信息、用户信息、库存信息等),并根据用户的选择生成订单。

package org.zyf.javabasic.spring.beanFactory;


/**
 * @program: zyfboot-javabasic
 * @description: OrderService
 * @author: zhangyanfeng
 * @create: 2024-04-13 14:31
 **/
public class OrderService {
    private final ApplicationContext context;

    public OrderService(ApplicationContext context) {
        this.context = context;
    }

    public void placeOrder(String productId, String userId) {
        // 根据商品 ID 获取商品信息
        Product product = context.getBean(ProductRepository.class).findById(productId);

        // 根据用户 ID 获取用户信息
        User user = context.getBean(UserRepository.class).findById(userId);

        // 根据商品 ID 获取库存信息
        Inventory inventory = context.getBean(InventoryService.class).getInventory(productId);

        // 检查库存是否充足
        if (inventory.getQuantity() > 0) {
            // 生成订单逻辑
            Order order = new Order(user, product);
            // 省略生成订单的逻辑
            System.out.println("Order placed successfully.");
        } else {
            System.out.println("Insufficient inventory.");
        }
    }
}

在这个例子中,OrderService 类使用 ApplicationContext 来获取商品信息、用户信息和库存信息,并根据这些信息生成订单。通过使用 ApplicationContext,我们可以在运行时动态地获取所需的 Bean,并且不需要在代码中硬编码 Bean 的依赖关系。

总的来说,使用 ApplicationContext 可以使应用程序更加灵活,并且可以在运行时动态地管理和获取 Bean,从而使应用程序更加易于扩展和维护。

文章推荐阅读

在程序员的职业规划中,成为软件架构师是一个非常有吸引力的选择。但是对于如何才能成为一名架构师,不少同学认为只要代码写得好,就能得到公司提拔,晋升为架构师。

还真不是这样的,如果不具备架构思维,即使代码能写到极致,在开展工作时也将不可避免地掉到坑里去。例如,看起来面面俱到的设计,但因为太复杂而无法落地;错估需求,导致高射炮打蚊子,浪费资源;实现方案总想毕其功于一役,结果需求变化就要推倒重来。

所以程序员要清醒地认识到,写好代码仅是软件开发过程中的一个环节,把代码写到极致也不会自动成为架构师。架构工作贯穿了软件生命周期,做好架构一定要学会架构思维

有一本书专门告诉程序员如何培养架构思维——《架构思维:从程序员到CTO》。本书以架构师工作中的痛点问题为导向,结合大量真实、复杂的案例,帮助架构师建立起思考框架,提高架构设计能力,规划职业成长路径。

具体链接如下:

https://item.jd.com/14019725.html

一本书揭秘程序员如何培养架构思维!


网站公告

今日签到

点亮在社区的每一天
去签到