Spring源码01: BeanFactory与ApplicationContext

在Springboot的启动类中,常常都是这么一块代码

@SpringBootApplication
public class JavaBaseApplication {
    public static void main(String[] args) {
        SpringApplication.run(JavaBaseApplication.class, args);
    }
}

但其实这个run方法是有返回值的,返回的是ConfigurableApplicationContext

 ConfigurableApplicationContext context = SpringApplication.run(JavaBaseApplication.class, args);

他就是ApplicationContext的子类

我们可以看一下他的类图,按一下control+alt+u

在这里插入图片描述

可以看到其实ApplicationContext 间接的继承了BeanFactory, 主要是对BeanFactory和一些其他父类进行了一个组合封装

比如我们常见的getBean方法,其实就是使用BeanFactory的getBean方法

public Object getBean(String name) throws BeansException {
    this.assertBeanFactoryActive();
    return this.getBeanFactory().getBean(name);
}

所以BeanFactory才是Spring的核心 !

BeanFactory 表面上有只有getBean()的方法,

其实它还可以进行

  1. 控制反转,
  2. 基本的依赖注入,
  3. 甚至Bean的生命周期管理等各种功能, 都是由他的实现类提供.

其主要实现类是DefaultListableBeanFactory

在这里插入图片描述
可以看一下DefaultSingletonBeanRegistry, 单例Bean管理就是在这里面

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

使用了一个map进行管理,key就是bean的名字,value就是这个实例对象.

因为是个私有属性,可以通过反射来查看

@SpringBootApplication
public class JavaBaseApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext context = SpringApplication.run(JavaBaseApplication.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());
        });
    }

}

我建了两个空的component来看一下,输出的结果如下

在这里插入图片描述

看一下 AppicationContext 中常见的一些方法, 因为他不只整合了BeanFactory,还有其他四个类呢! 我们再回顾一下.

在这里插入图片描述

MessageSource

context.getMessage()方法就是来自MessageSource类 , 主要是提供了一个翻译的方法, 通过key与value的版本,翻译成各个语言.

因为web应用面向全世界, 可以根据不同国家的语言来进行翻译, 一般会在浏览器的请求头中获取, 我们再进行翻译. 这个用的比较少,就跳过了.

ResourceLoader

context.getResource(String fileName)可以通过文件名获取resources中的文件.

比如我前两天做的那个请求日志打印包,就需要获取包中的自定义日志配置.

如果我早点看到这个源码,我就能一下子就想到如何去获取这部分资源,不用去百度了.

百度的方法是直接通过spring提供的类

    Resource resource = new ClassPathResource("log4j-aop.properties");

回过头来,我们看一下这个context.getResource的内部实现,其内部继承了ResourceLoader

public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String var1) throws IOException;
}

这个就是资源管理了, 还有就是这个前缀 一般看到的是classpath: 加了个*号表示还会去jar包中捞,

比如我们获取META-INF/spring.factories
在这里插入图片描述

EnvironmentCapable

还有我们比较常用的获取系统资源, 初级只知道用@Value等注入手段, 但如果你去翻翻源码, 就会发现有很多spring的资源没去用

比如简单的获取配置文件中的端口号

    System.out.println(context.getEnvironment().getProperty("server.port"));

可以看到这个方法,接口向上就是EnvironmentCapable

在这里插入图片描述

一般用这个方法就是在一些静态方法中无法使用@Value注入时,可以这样做.

但有可能获取配置为空, 具体的问题出现原因,后面往下看到了再补充.

ApplicationEventPublisher

这个主要就是事件的发送与监听,主要用于解耦.

比如component1是一个用户注册类, 注册完后需要做一些其他操作,比如短信通知,邮箱通知,但是都不重要.

因为我主要负责的是让用户注册成功即可, 不管短信通知或者其他操作做与不做,成不成功,都不重要.

这样我代码里就很干净, 我只负责注册, 不用去注入其他操作类.

广播一下我注册好了, 其他人自己去听广播,该做什么自己去安排.

首先需要一个事件类, 一个发送通知的动作, N个接听

如我这是一个用于注册事件类, 需要继续ApplicationEvent

public class UserRegisterEvent extends ApplicationEvent {

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

一个推送事件功能,ApplicationContext有几成,但如果你看了源码就知道其实就是集成ApplicationEventPublisher, 所以我们直接注入这个类也是可以的.

@Component
public class Component1 {
    @Resource
    private ApplicationEventPublisher publisher;

	// 这里是一个注册的方法
    public void register(){
       //注册操作
        System.out.println("用户注册");
        //完成后进行通知
        publisher.publishEvent(new UserRegisterEvent(this));
    }
}

然后写个监听这个事件的类,使用注解开启监听,参数就是监听的事件类

@Component
public class Component2 {

    @EventListener
    public void listener(UserRegisterEvent event){
       //因为,上面事件类有构造方法,在组件1中塞入自己, 在这里就可以知道是谁发的事件
        System.out.println(event);
        //接收到事件后执行需要完成操作
        System.out.println("短信通知");
    }
}

而且事件不止一个类可以监听, 比如我邮箱通知的Component3, 微信通知的Component4 都可以监听同一个事件, 而不用因为多了一些操作,而去修改Component1中的注册方法, 以此来进行解耦.

这里在启动类偷个懒不注入了,直接getBean()调一下

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

最后的输出结果如下
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值