BeanFactory和ApplicationContext
什么是BeanFactory
- 它是
ApplicationContext
的父接口 - 它才是
Spring
的核心容器,主要的ApplicationContext
实现都组合了它的功能
BeanFactory能做什么?
表面上看BeanFactory
的主要方法只有getBean()
,实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能,都由他的实现类提供
例如:通过反射查看它的成员变量singletonObjects
,singletonObjects
内部包含了所有单例bean
实现这个例子前,引出一个类DefaultListableBeanFactory
,主要功能是作为一个可配置的、可列表化的 Bean 工厂,用于管理和维护应用程序中的 Bean 定义和实例
DefaultListableBeanFactory
继承了DefaultSingletonBeanRegistry
类,而DefaultSingletonBeanRegistry
类就是专门用来管理单例Bean的
DefaultSingletonBeanRegistry
其中有个成员变量也就是singletonObjects
这样我们就找到了单例Bean存储的地方,通过反射调用即可
SpringBoot启动会有很多单例Bean注入,map中包含了所有的单例Bean,我自定义了
Component1
类注入到其中
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//返回容器对象
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//反射获取singletonObjects属性
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
//开启可操作singletonObjects,否则无法对他进行操作,因为他是private final的
singletonObjects.setAccessible(true);
//BeanFactory是ConfigurableListableBeanFactory实现类的成员变量
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//Field.get(Object obj):获取指定对象obj上此field表示的字段的值
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());
});
}
}
上面代码最难理解的是singletonObjects.get(beanFactory)
,刚开始没有理解到反射,重新翻了反射的代码发现他的作用是填充ConfigurableListableBeanFactory
中的singletonObjects
属性
Java源码中并没有这个属性,反复查找后发现DefaultListableBeanFactory
是ConfigurableListableBeanFactory
的实现类,而DefaultListableBeanFactory
继承了DefaultSingletonBeanRegistry
,所以自然有singletonObjects
属性
ApplicationContext能做什么
前面提到过,ApplicationContext
是在BeanFactory
基础上做了扩展
MessageSource
:拓展国际化功能ApplicationEventPublisher
:事件发布与监听,实现组件之间的解耦ResourcePatternResolver
:通过通配符方式获取一组Resource资源EnvironmentCapable
:整合 Environment 环境(能通过它获取各种来源的配置信息)
国际化
所谓国际化就是可以通过准备好的配置文件,将语言翻译成各种国家的语言
#准备以下2个配置文件
#messages_en.properties 翻译成英文
hi=hello
#messages_zh.properties 翻译成中文 \u4F60\u597D对应着你好
hi=\u4F60\u597D
通过getMessage()
转化
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//国际化
System.out.println(context.getMessage("hi", null, Locale.US)); //hello
System.out.println(context.getMessage("hi", null, Locale.CHINA)); //你好
}
}
获取Resource资源
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//通配符方式获取一组 Resource 资源
Resource[] resources = context.getResources("classpath:application.properties");
for (Resource resource : resources) {
System.out.println(resource); //class path resource [application.properties]
}
}
}
整合 Environment 环境
可以读取系统环境变量,也可以读取SpringBoot配置文件
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//整合 Environment 环境
System.out.println(context.getEnvironment().getProperty("JAVA_HOME"));
System.out.println(context.getEnvironment().getProperty("server.port"));
}
}
事件发布与监听
事件发布与监听指的是,一条线程发布特定事件,监听该事件的方法收到消息后,处理该方法,这也是异步的一种思想
创建事件
事件需要继承ApplicationEvent
public class UserRegisteredEvent extends ApplicationEvent {
public UserRegisteredEvent(Object source) {
super(source);
}
}
监听者
@EventListener
可以在 Spring 应用中监听和响应特定类型的事件。当事件发生时,标记有 @EventListener
注解的方法将被自动调用。
@Component
public class Component1 {
private static final Logger logger = LoggerFactory.getLogger(Component1.class);
@EventListener
public void aaa(UserRegisteredEvent event) {
logger.info("{}",event);
}
}
事件发布
@SpringBootApplication
public class Application {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
//发布事件
context.publishEvent(new UserRegisteredEvent(context));
}
}
BeanFactory实现
代码准备
我们最终想要实现的是,像Spring
一样,向自定义的BeanFactory
注册Config
对象
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
static class Bean1{
private static final Logger logger = LoggerFactory.getLogger(Bean1.class);
@Autowired
private Bean2 bean2;
public Bean1() {
logger.info("构造 Bean1()");
}
public Bean2 getBean2(){
return bean2;
}
}
static class Bean2{
private static final Logger logger = LoggerFactory.getLogger(Bean2.class);
public Bean2() {
logger.info("构造 Bean2()");
}
}
具体实现
BeanFactory
可以通过registerBeanDefinition
注册一个BeanDefinition
对象
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//定义一个bean (class,scope,初始化,销毁) scope:单例还是多例
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config",beanDefinition);
//查看BeanFactory中的bean
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
}
执行以上代码,控制台输出config
,证明Config
已经注册到BeanFactory
中
但是发现Bean1
,Bean2
并没有注册进去,因为BeanFactory
并不会主动调用BeanFactory
的后置处理器
现在我们需要为BeanFactory
添加一些常用的后置处理器,注意这里仅仅是添加
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
通过观察控制台输出,发现多了一些BeanFactory
中多了些Bean
简单介绍一下目前需要了解几个关键的处理器
internalConfigurationAnnotationProcessor
:用于处理@Configuration
注解,它会解析@Configuration
注解并处理其中的@Bean
注解,将被注解的方法返回的对象注册为Bean。internalAutowiredAnnotationProcessor
:用于处理@Autowired
注解,它会解析@Autowired
注解并自动装配依赖对象。它会在Bean初始化之前进行处理,确保依赖对象正确注入。internalCommonAnnotationProcessor
:用于处理通用注解,如@Resource
,@PostConstruct
,@PreDestroy
等。它会在Bean初始化之前进行处理,执行相应的初始化和销毁方法。
接下来需要让BeanFactory
处理器生效,其中internalConfigurationAnnotationProcessor
属于BeanFactory
后置处理器,internalAutowiredAnnotationProcessor
,internalCommonAnnotationProcessor
属于Bean
的后置处理器,生效方式不一样
//BeanFactory后置处理器的主要功能,补充了一些bean的定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
//bean后置处理器,针对bean的生命周期的各个阶段提供扩展,例如@Autowird,@Resource
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanPostProcessor -> {
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
观察控制台,发现Bean1
,Bean2
也被出册到了BeanFactory
中,并且可以实例化
目前到这看似我们的目标已经完成了,实际上会发现Bean的构造方法是在用到的时候调用的,属于懒汉式
,对于单例Bean来说,希望他在注册的时候就已经实例化
//在容器初始化完成后,提前实例化所有的单例Bean
beanFactory.preInstantiateSingletons();
观察控制台发现,构造方法在容器初始化的时候就已经执行了
总结
BeanFactory
不会主动调用BeanFactory
后置处理器BeanFactory
不会主动调用Bean
后置处理器BeanFactory
不会主动初始化单例
补充说明
Bean
的后置处理器会有排序的逻辑
举个例子,当一个接口有两个实现类时,这个接口通过依赖注入的形式注入,同时标注了@Autowired
以及@Resource
,哪个注解会生效,取决于Bean
的哪个后置处理器先添加到BeanFactory
中
ApplicationContext实现
这里我们主要了解几个常用的ApplicationContext
实现
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
AnnotationConfigApplicationContext
AnnotationConfigServletWebServerApplicationContext
基于XML配置文件注册Bean
目前无论是Spring
还是SpringBoot
都很少用XML的方式,更推荐使用配置类来注册Bean,所以这里了解即可
前置代码
需要注册的Bean
static class Bean1{
}
static class Bean2{
private Bean1 bean1;
public Bean1 getBean1() {
return bean1;
}
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
}
读取的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="bean1" class="com.yellowstar.spring.demo1.TestApplicationContext$Bean1"/>
<bean id="bean2" class="com.yellowstar.spring.demo1.TestApplicationContext$Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext
的作用是基于classpath
下 xml 格式的配置文件来注册Bean
private static void testClassPathXmlApplicationContext(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
FileSystemXmlApplicationContext
FileSystemXmlApplicationContext
的作用是基于磁盘路径
下 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext(){
//可以选择绝对路径,也可以是相对路径
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext("src/main/resources/b01.xml");
}
实现ClassPathXmlApplicationContext
XmlBeanDefinitionReader
用于从XML配置文件中读取和解析Bean的定义信息
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//设置配置文件读取到beanFactory
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
基于Java配置类注册Bean
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext
的作用是基于Java配置类来注册Bean
配置类
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1){
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
实现
private static void testAnnotationConfigApplicationContext(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(">==" + context.getBean(Bean2.class).getBean1());
}
这里可以注意到常用的一些后置处理器是自动帮我们加载到容器中了,而基于XML模式的方式还需要在XML另外定义才行
AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext
也是是基于Java配置类来注册Bean,区别不同的在于他适用于web环境
这块内容比较复杂,先贴代码在详细解释
private static void testAnnotationConfigServletWebServerApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
@Configuration
static 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("/")
public Controller controller1(){
return (request,response) -> {
response.getWriter().print("hello");
return null;
};
}
}
上述代码的功能就像是启动了Spring
服务一样,可以通过访问localhost:8080
来进入controller1
其中最重要的是WebConfig
必须拥有三大组件
ServletWebServer
:用于支持 Servlet 容器的接口。它定义了与 Servlet 容器相关的通用操作和属性,允许 Spring 应用程序与不同的 Servlet 容器(如Tomcat、Jetty等)进行交互。DispatcherServlet
:用于处理web请求的关键组件之一,所有请求都会经过他DispatcherServletRegistrationBean
:用于注册和配置DispatcherServlet
的 Bean。它允许开发者以编程方式配置DispatcherServlet
的各种属性,以及将其与特定的 URL 映射关联起来。
Bean的生命周期
通常情况下,Bean
的生命周期分为四个阶段
- 构造方法阶段:在这个阶段,
Spring
容器实例化Bean
对象,调用其构造方法来创建Bean
的实例 - 依赖注入阶段:在
Bean
对象被实例化完成后,Spring
容器会对Bean
的属性进行依赖注入,将依赖的其他Bean
或值注入到当前Bean
中。 - 初始化阶段:在依赖注入完成后,
Spring
容器会调用Bean
的初始化方法,例如使用@PostConstruct
注解 - 销毁阶段:当容器关闭或销毁时,
Spring
容器会调用Bean
的销毁方法,例如使用@PreDestory
注解
这样讲讲方法太枯燥了,我一开始也记不住,通过下面例子可以加深印象
@SpringBootApplication
public class A03Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);
context.close();
}
}
@Component
public class LifeCycleBean {
private static final Logger logger = LoggerFactory.getLogger(LifeCycleBean.class);
public LifeCycleBean() {
logger.debug("构造");
}
@Autowired
public void autowire(@Value("${JAVA_HOME}") String home){
logger.debug("依赖注入:{}",home);
}
@PostConstruct
public void init(){
logger.debug("初始化");
}
@PreDestroy
public void destory(){
logger.debug("销毁");
}
}
执行代码可以观察到顺序依次为构造->依赖注入->初始化->销毁
Bean扩展
Spring
的扩展程度是非常高的,可以在Bean的各个生命周期中实现自己想要的结果
@Component
public class MyBeanPostProcessor implements DestructionAwareBeanPostProcessor, InstantiationAwareBeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
}
return true;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
}
return pvs;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
}
return bean;
}
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
if (beanName.equals("lifeCycleBean")) {
log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
}
}
}
模版方法
模板方法是一种常用的设计模式,在之前对BeanFactory
添加后处理器时,比较好奇他这个处理器是如何加载进去的,现在使用模板方法可以试着自己动手实现
先来看一下基础代码,MyBeanFactory
是自定义的一个BeanFactory
,其中getBean()
可以获取Bean
我们的想法是,在getBean()
时,添加后处理器,来解析@Autowired
或者@Resource
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.getBean();
}
static class MyBeanFactory{
public Object getBean(){
Object bean = new Object();
//依赖注入解析
return bean;
}
}
方案一
比较简单的方法就是直接在getBean()
中实现,但是这样并不利于扩展,假如需要后续解析其他注解,那么就得修改getBean()
方法
方案二
参照之前DefaultListableBeanFactory
的做法,我们并没有修改内部方法,而是调用DefaultListableBeanFactory
其他方法来添加后处理器
通过一个集合来加载所有后处理器,这样在需要添加后处理器的时候就可以在外部直接调用addBeanPostProcessor()
方法,不需要动到MyBeanFactory
这个最底层的类
public static void main(String[] args) {
MyBeanFactory beanFactory = new MyBeanFactory();
beanFactory.addBeanPostProcessor(() -> System.out.println("解析 @Autowired"));
beanFactory.addBeanPostProcessor(() -> System.out.println("解析 @Resource"));
beanFactory.getBean();
}
static class MyBeanFactory{
public Object getBean(){
Object bean = new Object();
//执行后置处理器
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
beanPostProcessor.inject();
}
return bean;
}
private static List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){
beanPostProcessorList.add(beanPostProcessor);
}
}
interface BeanPostProcessor{
/**
* 依赖注入阶段扩展
*/
void inject();
}