上一篇Spring源码八:容器扩展一,我们看到ApplicationContext容器通过refresh方法中的prepareBeanFactory方法对BeanFactory扩展的一些功能点,包括对SPEL语句的支持、添加属性编辑器的注册器扩展解决Bean属性只能定义基础变量的问题、以及一些对Aware接口的实现。
这一篇,我们回到refresh方法中,接着往下看:
{
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing. 1、初始化上下文信息,替换占位符、必要参数的校验
prepareRefresh();
// Tell the subclass to refresh the internal bean factory. 2、解析类Xml、初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计
// Prepare the bean factory for use in this context. 3、准备BeanFactory内容:
prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:
try {
// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器
//!!!!!!!!!!!! 这里 这里 今天看这里 !!!!!!!!!!!//
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context. 5、spring bean容器的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
//!!!!!!!!!!!! 这里 这里 今天看这里 !!!!!!!!!!!//
// Register bean processors that intercept bean creation. 6、注册bean的后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. 7、初始化消息源
initMessageSource();
// Initialize event multicaster for this context. 8、初始化事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展
onRefresh();
// Check for listener beans and register them. 10、初始化监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons. 11、实例化:非兰加载Bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event. 12、发布相应的事件通知
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
工厂后置处理方法:postProcessBeanFactory
上一篇我们看到了方法prepareBeanFactory方法,我们接着这个方法往下可以看到postProcessBeanFactory,进入该方法如下:
没错,又是一个空实现,看到这里我的风格都会说:扩展点加一;postProcessBeanFactory方法使用保护修饰且留给子类自己去具体实现。结合注释我们可以简单猜测一下,该方法是Spring暴露给子类去修改初级容器BeanFactory的。
那到底是什么时候可以允许子类对BeanFactory容器进行修改呢?通过方法在refresh方法的位置在prepareBeanFactory方法之后,说明是在BeanDefiniton都已经注册到BeanFactory以后,但是还未进行实例化的时候进行修改。
我们在前面8篇内容,一点点拨开Spring的方法,到这里其实我们的进程,仅仅进行到将xml文件中的Bean标签内容解析成BeanDefinition对象然后注入到Spring的BeanFactory容器里而已,这个和我们真正意义上的Bean还有一段距离呢。BeanDefintiion从名字上来看是Bean的定义,解释下应该算是Bean属性信息的初始化。
而我们在Java代码里面使用的Bean的实例化对象,需要利用我们BeanDefinition定义的属性信息去创建我们的实例化对象。在Spring中最常见的一个场景就是我们通过Spring容器getBean方法获取一个实例。这个过程我们经常称之为bean的实例化。
所以方法postBeanFactory在Bean初始化以后实例化之前为我们提供了一个可以修改BeanDefinition的机会,我们可以通过实现postBeanFactory方法去修改beanFactory的参数。
重写postProcessBeanFactory示例
package org.springframework;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Jeremy
* @version 1.0
* @description: 自定义容器
* @date 2024/7/1 20:17
*/
public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
/**
* Create a new ClassPathXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocations resource location
* @throws BeansException if context creation failed
*/
public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {
super(configLocations);
}
/**
*
* Spring refresh 方法第扩展点加一:refresh方法中的prepareRefresh();
* 重写initPropertySources 设置环境变量和设置requiredProperties
*
*/
@Override
protected void initPropertySources() {
System.out.println("自定义 initPropertySources");
getEnvironment().getSystemProperties().put("systemOS", "mac");
getEnvironment().setRequiredProperties("systemOS");
}
/**
*
* Spring refresh 方法第扩展点加一:refresh方法中的postProcessBeanFactory();
* 重写该方法,修改基础容器BeanFactory内容
* @param beanFactory
*
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
System.out.println("自定义 postProcessBeanFactory");
// 获取容器中的对象
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("jsUser");
beanDefinition.getScope();
// 修改属性
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
如上述方法,我们重写了postProcessorBeanFactory方法,通过beanFactory参数获取Bean Definition对象,然后修改BeanDefinition属性值。当让我还可以修改BeanFactory的其他属性,因为这个参数是将整个BeanDefinition委托给我们自己定义的。
package org.springframework;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dto.JmUser;
/**
* @author Jeremy
* @version 1.0
* @description: TODO
* @date 2024/6/30 02:58
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");
JmUser jmUser = (JmUser)context.getBean("jmUser");
System.out.println(jmUser.getName());
System.out.println(jmUser.getAge());
}
}
运行上述代码,可以看到结果如上图所示,Spring的postProcessorBeanFactory有回调我自定义的方法。正因为Spring在这立留了钩子函数,所以很多框架可以通过这个方法或有类似功能的扩展点来操作整个beanFactory容器,从而可以自定义自己的框架使之拥有更加丰富与个性化的功能。后面会单独留一篇用来说Spring的扩展点,接口SpringCloud相关组件来来看看。
invokeBeanFactoryPostProcessors
看到这里其实今天的核心内容已经结束了,但是我们在文章开始的时候给大家圈里了两个方法,这里我们在提一下和上述方法功能上一致的一个类。
/**
* 实例化并调用所有已经注册BeanFactoryPostProcessor
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before singleton instantiation.
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
/**
* Return the list of BeanFactoryPostProcessors that will get applied
* to the internal BeanFactory.
*/
public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
return this.beanFactoryPostProcessors;
}
在上述代码里的注释里面,已经解释了这个代码的功能是为了实例化并调用所有已经注册的BeanFactoryPostProcessor。所以能知道我这个放的核心其实是BeanFactoryPostProcessor,进入BeanFactoryPostProcessor类看下:
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
/**
* Factory hook that allows for custom modification of an application context's
* bean definitions, adapting the bean property values of the context's underlying
* bean factory.
*
* <p>Useful for custom config files targeted at system administrators that
* override bean properties configured in the application context. See
* {@link PropertyResourceConfigurer} and its concrete implementations for
* out-of-the-box solutions that address such configuration needs.
*
* <p>A {@code BeanFactoryPostProcessor} may interact with and modify bean
* definitions, but never bean instances. Doing so may cause premature bean
* instantiation, violating the container and causing unintended side-effects.
* If bean instance interaction is required, consider implementing
* {@link BeanPostProcessor} instead.
*
* <h3>Registration</h3>
* <p>An {@code ApplicationContext} auto-detects {@code BeanFactoryPostProcessor}
* beans in its bean definitions and applies them before any other beans get created.
* A {@code BeanFactoryPostProcessor} may also be registered programmatically
* with a {@code ConfigurableApplicationContext}.
*
* <h3>Ordering</h3>
* <p>{@code BeanFactoryPostProcessor} beans that are autodetected in an
* {@code ApplicationContext} will be ordered according to
* {@link org.springframework.core.PriorityOrdered} and
* {@link org.springframework.core.Ordered} semantics. In contrast,
* {@code BeanFactoryPostProcessor} beans that are registered programmatically
* with a {@code ConfigurableApplicationContext} will be applied in the order of
* registration; any ordering semantics expressed through implementing the
* {@code PriorityOrdered} or {@code Ordered} interface will be ignored for
* programmatically registered post-processors. Furthermore, the
* {@link org.springframework.core.annotation.Order @Order} annotation is not
* taken into account for {@code BeanFactoryPostProcessor} beans.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
看到这个接口只有一个方法且名称也叫postProcessBeanFactory,在看代码的上面的这是与参数和我们refresh中一样,说明我们也可以通过实现Bean FactoryPostProcessor接口的形式来修改我们的BeanFactory对象。如下所示:
自定义BeanFactoryPostProcessor
package org.springframework;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* 自定义Bean的后置处理器
*/
public class MySelfBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
System.out.println("自定义Bean的后置处理器,重写postProcessBeanFactory方法");
// 获取容器中的对象
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("jmUser");
beanDefinition.getScope();
// 修改属性
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
Spring已经提供了空实现的postProcessorBeanFactory方法,为啥还要单独搞一个接口来定一个这个方法呢?
Spring提供BeanFactoryPostProcessor
接口,而不是在refresh
方法中直接进行beanFactory修改,主要是为了提高代码的解耦性、可维护性和扩展性。通过将不同的修改操作分散到不同的实现类中,每个类都只关注特定的任务,这样不仅符合单一职责原则,还使得整个框架更加灵活和易于扩展。这种设计理念是
Spring成功的重要原因之一。