spring源码分析---容器接口(BeanFactory,ApplicationContext )01

来源:Spring
1.1 什么是 BeanFactory
它是 ApplicationContext 的父接口
它才是 Spring 的核心容器,主要的 ApplicationContext 实现 组合 了它的功能,也就是说,BeanFactory 是 ApplicationContext 中的一个成员变量。
常用的 context.getBean(“xxx”) 方法,其实是调用了 BeanFactory 的 getBean() 方法。

1.2 BeanFactory 能做什么?
在这里插入图片描述
BeanFactory 表面上只有 getBean() 方法,但实际上 Spring 中的控制反转、基本的依赖注入、乃至 Bean 的生命周期中的各个功能都是由它的实现类提供。

在这里插入图片描述
DefaultListableBeanFactory 实现了 BeanFactory 接口,它能管理 Spring 中所有的 Bean,当然也包含 Spring 容器中的那些单例对象。

DefaultListableBeanFactory 还继承了 DefaultSingletonBeanRegistry 类,这个类就是用来管理 Spring 容器中的单例对象。

类图中选中 DefaultSingletonBeanRegistry,然后按下 F4 进入这个类。它有一个 Map 类型的成员变量 singletonObjects:

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

Map 的 key 就是 Bean 的名字,而 value 是对应的 Bean,即单例对象。
举例:
建立两个组件

@Component
public class Component1 {
}

@Component
public class Component2 {
}



查看 singletonObjects 中是否存在这两个 Bean 的信息:

@Slf4j
@SpringBootApplication
public class A01Application {
    @SneakyThrows
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);

        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
            .forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));

        context.close();
    }
}



输出:

component1=test.bean.a01.Component1@25a5c7db
component2=test.bean.a01.Component2@4d27d9d

1.3 ApplicationContext 的功能
ConfigurableApplicationContext类图:
在这里插入图片描述
ApplicationContext 除了继承 BeanFactory 外,还继承了:

MessageSource:使其具备处理国际化资源的能力
ResourcePatternResolver:使其具备使用通配符进行资源匹配的能力
EnvironmentCapable:使其具备读取 Spring 环境信息、配置文件信息的能力
ApplicationEventPublisher:使其具备发布事件的能力
MessageSource 的使用

在 SpringBoot 项目的 resources 目录下创建 messages.properties、messages_en.properties、messages_zh_CN.properties、messages_zh_TW.properties 四个国际化文件,除 messages.properties 外,其余三个文件内容如下:

thanks=Thank you
thanks=谢谢

测试 MessageSource 接口中 getMessage() 方法的使用:

@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
    System.out.println(context.getMessage("thanks", null, Locale.ENGLISH));
    System.out.println(context.getMessage("thanks", null, Locale.SIMPLIFIED_CHINESE));
    context.close();
}

控制台打印出:

Thank you
谢谢

国际化资源由 ResourceBundleMessageSource 进行处理,使用 “干净” 的 Spring 容器 GenericApplicationContext,并添加对应的 Bean:

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();

    context.registerBean("messageSource", MessageSource.class, () -> {
        ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
        // 设置编码格式
        ms.setDefaultEncoding("utf-8");
        // 设置国际化资源文件的 basename
        ms.setBasename("messages");
        return ms;
    });

    context.refresh();

    System.out.println(context.getMessage("thanks", null, Locale.ENGLISH));
    System.out.println(context.getMessage("thanks", null, Locale.SIMPLIFIED_CHINESE));
    System.out.println(context.getMessage("thanks", null, Locale.TRADITIONAL_CHINESE));
}

ResourcePatternResolver 的使用

@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);

    Resource[] resources = context.getResources("classpath:application.properties");
    Assert.isTrue(resources.length > 0, "加载类路径下的 application.properties 文件失败");

    // 使用 classpath* 可以加载 jar 里类路径下的 resource
    resources = context.getResources("classpath*:META-INF/spring.factories");
    Assert.isTrue(resources.length > 0, "加载类路径下的 META-INF/spring.factories 文件失败");
    context.close();
}

EnvironmentCapable 的使用

@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);

    System.out.println(context.getEnvironment().getProperty("java_home"));
    System.out.println(context.getEnvironment().getProperty("properties.name"));
    context.close();
}

控制台打印出:

D:\java\JDK1.8.30
哈哈

ApplicationEventPublisher 的使用
举例:用户注册之后,接着发送短信,实现一种解耦的效果
定义事件类 UserRegisteredEvent:

public class UserRegisteredEvent extends ApplicationEvent {
    private static final long serialVersionUID = 6319117283222183184L;

    public UserRegisteredEvent(Object source) {
        super(source);
    }
}

将 Component1 作为发送事件的 Bean:

@Slf4j
@Component
public class Component1 {
    @Autowired
    private ApplicationEventPublisher context;

    public void register() {
        log.debug("用户注册");
        context.publishEvent(new UserRegisteredEvent(this));
    }
}

将 Component2 作为事件监听器:

@Slf4j
@Component
public class Component2 {
    @EventListener
    public void aaa(UserRegisteredEvent event) {
        log.debug("{}", event);
        log.debug("发送短信");
    }
}

在 main() 方法中使用 Component1 发送事件:

@SneakyThrows
@SuppressWarnings("unchecked")
public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);

    context.getBean(Component1.class).register();
    context.close();
}

控制台打印出:

test.bean.a01.Component1      - 用户注册 
test.bean.a01.Component2      - test.bean.a01.UserRegisteredEvent[source=test.bean.a01.Component1@25a5c7db] 
test.bean.a01.Component2      - 发送短信 

其他扩展:

@SneakyThrows的作用:
普通Exception类,也就是我们常说的受检异常或者Checked Exception会强制要求抛出它的方法声明throws,调用者必须显示的去处理这个异常。
而Lombok的@SneakyThrows就是为了消除这样的代码。
使用注解后不需要担心Exception的处理----原来需要手动操作的检查异常,现在都不需要手动处理了

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值