Spring之BeanFactory 与 ApplicationContext 的区别

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 Spring之BeanFactory 与 ApplicationContext 的区别,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1、Spring容器接口介绍

BeanFactory 接口

  • Spring 的核心容器,ApplicationContext 的父接口
  • 表面上只有 getBean,实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供

ApplicationContext 接口

  • 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
  • ApplicationContext 新增扩展功能:国际化、通配符方式获取多个Resource资源、事件发布与监听、整合 Environment 环境

在这里插入图片描述

2、容器接口功能实现

2.1、BeanFactory单例bean的存储

@SpringBootApplication
public class A01 {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
        System.out.println(context);
    }
}
  • context下的beanFactory实现类DefaultListableBeanFactory对象下的singletonObje的Map成员变量下存储这单例bean对象

在这里插入图片描述

在这里插入图片描述

2.2、ApplicationContext国际化资源获取

在这里插入图片描述

  • 配置文件设置相同的key,不同的value
  • 如下,key都是”hi”,通过匹配不同的语言获取不同的value
 ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
 System.out.println(context.getMessage("hi", null, Locale.CHINA));//你好
 System.out.println(context.getMessage("hi", null, Locale.ENGLISH));//Hello
 System.out.println(context.getMessage("hi", null, Locale.JAPANESE));//こんにちは

2.3、ApplicationContext通配符获取多资源

 ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
 //classpath*:类路径+jar包类路径
 Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
 for (Resource resource : resources) {
     System.out.println(resource);
 }

输出结果:

URL [file:/F:/%e5%ad%a6%e4%b9%a0%e8%b5%84%e6%96%99/spring/%e4%bb%a3%e7%a0%81/show/target/classes/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/org/springframework/boot/spring-boot-autoconfigure/2.5.5/spring-boot-autoconfigure-2.5.5.jar!/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/org/springframework/spring-beans/5.3.10/spring-beans-5.3.10.jar!/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.2.0/mybatis-spring-boot-autoconfigure-2.2.0.jar!/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/com/alibaba/druid-spring-boot-starter/1.2.8/druid-spring-boot-starter-1.2.8.jar!/META-INF/spring.factories]
URL [jar:file:/E:/java/maven/mvnrep/org/springframework/spring-test/5.3.10/spring-test-5.3.10.jar!/META-INF/spring.factories]

2.4、ApplicationContext读取环境或配置文件信息

 ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
 //环境信息
 System.out.println(context.getEnvironment().getProperty("java_home"));
 //配置文件信息
 System.out.println(context.getEnvironment().getProperty("server.port"));

输出结果:

E:\java\jdk\jdk8\tools
8080

2.5、ApplicationContext发布事件

基本使用

  • 创建事件类,source为事件发布对象
public class UserRegisteredEvent extends ApplicationEvent {
    public UserRegisteredEvent(Object source) {
        super(source);
    }
}
  • 发布事件,发布者为context
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
Object obj = new Object();
context.publishEvent(new UserRegisteredEvent(obj));
  • 设置监听方法
@Component
public class Component2 {
    private static final Logger log = LoggerFactory.getLogger(Component2.class);
    @EventListener
    public void aaa(UserRegisteredEvent event) {
        log.debug("{}", event);
    }
}

输出结果:发布事件为UserRegisteredEvent,发布对象为Object

[DEBUG] 22:33:09.715 [main] com.itheima.a01.Component2          
- com.itheima.a01.UserRegisteredEvent[source=java.lang.Object@5b051a5c] 

实际应用

  • 事件类依然为UserRegisteredEvent
  • 发布事件对象this则为Register对象本身
@Component
public class Register {
    private static final Logger log = LoggerFactory.getLogger(Register.class);
    @Autowired
    private ApplicationContext context;

    public void register() {
        log.debug("用户注册");
        context.publishEvent(new UserRegisteredEvent(this));
    }
}
  • 短信监听类
@Component
public class Message {
    private static final Logger log = LoggerFactory.getLogger(Message.class);
    @EventListener
    public void message(UserRegisteredEvent event) {
        log.debug("{}", event);
        log.debug("发送短信");
    }
}
  • 邮件监听类
@Component
public class Mail {
    private static final Logger log = LoggerFactory.getLogger(Mail.class);
    @EventListener
    public void mail(UserRegisteredEvent event) {
        log.debug("{}", event);
        log.debug("发送邮件");
    }
}
  • 发布事件
@Autowired
private  Register register; 
...
//发布事件
register.register();

输出结果:

[DEBUG] 22:50:34.276 [main] com.itheima.a01.Register            - 用户注册 
[DEBUG] 22:50:34.277 [main] com.itheima.a01.Mail                - com.itheima.a01.UserRegisteredEvent[source=com.itheima.a01.Register@341672e] 
[DEBUG] 22:50:34.282 [main] com.itheima.a01.Mail                - 发送邮件 
[DEBUG] 22:50:34.282 [main] com.itheima.a01.Message             - com.itheima.a01.UserRegisteredEvent[source=com.itheima.a01.Register@341672e] 
[DEBUG] 22:50:34.282 [main] com.itheima.a01.Message             - 发送短信 

将用户注册后的后续不同的处理方式解耦,添加不同监听,则触发不同的事件操作

3、BeanFactory核心实现类 DefaultListableBeanFactory

  • spring核心bean容器实现类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
  • 这个会创建一个GenericBeanDefinition普通bean定义对象返回
  • bean definition 描述了这个bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
  • 此时bean定义只是被创建出来,还没有加入到bean容器中
BeanDefinition beanDefinition = 
BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton")
.setLazyInit(false)
.getBeanDefinition();
  • 被创建为bean definition的类:Config
@Configuration
class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}

class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    public Bean1() {
        log.debug("构造 Bean1()");
    }

    @Autowired
    private Bean2 bean2;

    public Bean2 getBean2() {
        return bean2;
    }
}

class Bean2 {
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2() {
        log.debug("构造 Bean2()");
    }
}
  • 将bean定义信息注册添加到容器中,并设置bean的名字
beanFactory.registerBeanDefinition("config", beanDefinition);
  • bean定义信息添加bean容器中

在这里插入图片描述

  • singletonObjes为空,则表明bean定义对应的对象依然没有生成

在这里插入图片描述

  • 给 BeanFactory 添加一些常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}

输出结果:这里返回的只是bean定义的名字

config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
  • beanFactory 需要手动调用beanFactory后处理器对它做增强
  • ConfigurationClassPostProcessor通过解析 @Bean、@ComponentScan 等注解,来补充一些 bean definition。如:bean1、bean2
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
    System.out.println("BeanFactory后置处理器>>>>>>" + beanFactoryPostProcessor);
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}

输出结果:

BeanFactory后置处理器>>>>>>org.springframework.context.annotation.ConfigurationClassPostProcessor@2a5c8d3f
BeanFactory后置处理器>>>>>>org.springframework.context.event.EventListenerMethodProcessor@1be2019a
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
  • Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource
  • 如果只是添加到List beanPostProcessors,则是懒加载,获取对象时候才会执行
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {
    System.out.println("Bean后置处理器>>>>>>" + beanPostProcessor);
    beanFactory.addBeanPostProcessor(beanPostProcessor);
});
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
@Configuration
class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public Bean2 bean2() {
        return new Bean2();
    }
}

class Bean1 {
    private static final Logger log = LoggerFactory.getLogger(Bean1.class);

    public Bean1() {
        log.debug("构造 Bean1()");
    }

    @Autowired
    private Bean2 bean2;

    public Bean2 getBean2() {
        return bean2;
    }
}

class Bean2 {
    private static final Logger log = LoggerFactory.getLogger(Bean2.class);

    public Bean2() {
        log.debug("构造 Bean2()");
    }
}

输出结果:

Bean后置处理器>>>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6fe7aac8
Bean后置处理器>>>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@1d119efb
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
[DEBUG] 22:38:31.944 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1() 
[DEBUG] 22:38:31.981 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2() 
com.itheima.a02.TestBeanFactory$Bean2@15a04efb
  • 修改以上代码,添加一行(准备好所有单例),由懒加载变为提前准备
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor -> {
    System.out.println("Bean后置处理器>>>>>>" + beanPostProcessor);
    beanFactory.addBeanPostProcessor(beanPostProcessor);
});
beanFactory.preInstantiateSingletons();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());

输出结果:

Bean后置处理器>>>>>>org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6fe7aac8
Bean后置处理器>>>>>>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@1d119efb
[DEBUG] 22:43:38.108 [main] c.itheima.a02.TestBeanFactory$Bean1 - 构造 Bean1() 
[DEBUG] 22:43:38.144 [main] c.itheima.a02.TestBeanFactory$Bean2 - 构造 Bean2() 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
com.itheima.a02.TestBeanFactory$Bean2@5e21e98f

4、常见ApplicationContext实现

4.1、基于classpath下xml格式的配置文件来创建

配置文件bean.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">

    <!-- 控制反转, 让 bean1 被 Spring 容器管理 -->
    <bean id="bean1" class="com.itheima.a02.A02.Bean1"/>

    <!-- 控制反转, 让 bean2 被 Spring 容器管理 -->
    <bean id="bean2" class="com.itheima.a02.A02.Bean2">
        <!-- 依赖注入, 建立与 bean1 的依赖关系 -->
        <property name="bean1" ref="bean1"/>
    </bean>
</beans>

对象实体:

class Bean1 {
}

class Bean2 {
    private Bean1 bean1;
    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }
    public Bean1 getBean1() {
        return bean1;
    }
}
public class A01 {
    public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
		       new ClassPathXmlApplicationContext("bean.xml");
		for (String name : context.getBeanDefinitionNames()) {
		   System.out.println(name);
		}
		System.out.println(context.getBean(Bean2.class).getBean1());
    }
}

输出结果:

bean1
bean2
com.xc.Bean1@53ca01a2

ClassPathXmlApplicationContext工作流程

//创建bean容器对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前...");
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}
System.out.println("读取之后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//读取配置文件
reader.loadBeanDefinitions(new ClassPathResource("bean.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
    System.out.println(name);
}

4.2、基于磁盘路径下xml格式的配置文件来创建

public class A02 {
    public static void main(String[] args) {
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext(
                        "D:\\demo\\src\\main\\resources\\bean.xml");
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
}

4.3、较为经典的容器, 基于java配置类来创建

配置类:

@Configuration
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }
    @Bean
    public Bean2 bean2(Bean1 bean1) {
        Bean2 bean2 = new Bean2();
        bean2.setBean1(bean1);
        return bean2;
    }
}

启动类:

public class A03 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }
}

输出结果:

Connected to the target VM, address: '127.0.0.1:58835', transport: 'socket'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
Config
bean1
bean2
com.xc.Bean1@282cb7c7
Disconnected from the target VM, address: '127.0.0.1:58835', transport: 'socket'

Process finished with exit code 0

4.4、较为经典的容器, 基于java配置类来创建, 用于web环境

配置类:

@Configuration
public class WebConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory(){
        return new TomcatServletWebServerFactory();
    }
    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }
    @Bean("/hello")
    public Controller controller1() {
        return (request, response) -> {
            response.getWriter().print("hello");
            return null;
        };
    }
}

启动类:

public class A03 {
    public static void main(String[] args) {
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
}

输出结果:

在这里插入图片描述

访问页面:

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148606.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!