文章目录
一、前言
本文是 Spring源码分析:Spring源码分析二:BeanFactoryPostProcessor 的处理 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。
ConfigurationClassPostProcessor 的分析受篇幅所限,分为上下两篇
上篇 分析 postProcessBeanDefinitionRegistry 方法的调用。
下篇 分析 postProcessBeanFactory 方法的调用。
ConfigurationClassPostProcessor
是非常重要的一个 后处理器。 ConfigurationClassPostProcessor
完成了 配置类的解析和保存。将所有需要注入的bean解析成 BeanDefinition保存到 BeanFactory 中。
1. ConfigurationClassPostProcessor
首先来讲解一下 ConfigurationClassPostProcessor
的结构图如下。
可见ConfigurationClassPostProcessor
接口实现了BeanDefinitionRegistryPostProcessor
(BeanFactory 的后处理器)
PriorityOrdered
(设置自己的优先级为最高) 和各种 Aware 接口。
我们这里重点看的是 BeanDefinitionRegistryPostProcessor
接口的两个方法:
// 完成对 @Bean 方法的代理
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
// 允许在Spring容器启动后,在下一个阶段开始前,添加BeanDefinition的定义
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
关于这两个方法的调用时机和作用,我们在之前的文章已经讲过,这里不再赘述。
上篇 分析了 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
方法。得知了ConfigurationClassPostProcessor
解析配置类(这里的配置类不仅仅局限于@Configuration
注解,还包括 @Import
、 @ImportResource
等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition保存起来。在后面的bean 初始化都需要BeanDefinition。
本篇需要分析 ConfigurationClassPostProcessor#postProcessBeanFactory
方法通过cglib代理配置类,来拦截 @Bean修饰的方法。这么做的目的是为了在配置类中多次调用 @Bean 方法返回的是同一个结果。即在下面的代码中 demoController()
和 demoController2()
方法中调用的demoService()
方法返回的结果是同一个值。避免了单例模式下的多例创建。我们可以通过下面一个例子来看一看
二、举例
@Configuration
public class DemoConfig {
@Bean
public DemoService demoService(){
return new DemoServiceImpl();
}
@Bean
public DemoController demoController(){
System.out.println("demoController : " + demoService());
return new DemoController();
}
@Bean("demoController2")
public DemoController demoController2(){
System.out.println("demoController2222 : " + demoService());
return new DemoController();
}
}
上面的代码输出结果是什么?
我们看到两个方法里调用 demoService()
方法返回的是同一个实例,但是按照我们传统的逻辑,这里调用 demoService()
应该是重新创建了 一个 DemoServiceImpl 实例,应该不一样的。这里就是因为ConfigurationClassPostProcessor#postProcessBeanFactory
方法通过代理实现了该效果,以保证正确语义。
PS: 如果使用 @Component 注解修饰 DemoConfig 。则两次 demoService() 方法返回的结果则不相同。,因为被 @Component 注解修饰的bean并不会调用 ConfigurationClassPostProcessor#postProcessBeanFactory
方法来进行方法代理。
具体原因,即使因为在 postProcessBeanFactory
方法中对 Full 类型(即被 @Configuration
修饰的配置类)的配置类进行了动态代理。
三、 代码分析
postProcessBeanFactory
方法代码如下(相较于 postProcessBeanDefinitionRegistry
方法真是简单太多了):
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
// 判断是否已经在处理
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
可以看到关键代码为 enhanceConfigurationClasses(beanFactory);
。下面开始就来看看enhanceConfigurationClasses
方法
1. enhanceConfigurationClasses
enhanceConfigurationClasses
方法用于增强配置类。Spring会对 Full Configuration (即被 @Configuration
修饰的配置类)进行代理,拦截@Bean
方法,以确保正确处理@Bean
语义。这个增强的代理类就是在enhanceConfigurationClasses(beanFactory)
方法中产生的。
由于篇幅所限,这里等后续有机会再详细解析。这一部分的解析可以参考 : https://segmentfault.com/a/1190000020633405?utm_source=tag-newest
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 获取 CONFIGURATION_CLASS_ATTRIBUTE属性,如果不为null,则是配置类,在上篇中有过交代(可能是full或者lite类型)
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
// 如果是配置类(configClassAttr != null) || @Bean注解派生的方法(methodMetadata != null 不为空表示 FactoryMethod不为空,则可以说明是 @Bean 生成的 BeanDefinition)
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
// 这里判断如果指定的 bean(注意并非这里的abd,而是abd所要生成的bean) 如果不是 Class类型则进入 if里面
if (!abd.hasBeanClass()) {
try {
// 解析 beanClass,即获取这个 Bean 的Class 并保存到 abd中
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
// 对 FUll的 配置类进行处理!!!
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
// 对非AbstractBeanDefinition子类的情况直接抛出异常
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
// 保存下来,准备代理
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
// 如果没有找到 full 配置类,则说明不需要代理增强,则直接返回。
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
// 创建增强对象
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
// 进行配置类增强。这里的增强实际上是通过cglib对配置类进行了代理。
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
// 设置 :如果配置类被代理,则该 bean也需要一直代理
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
// 获取bean的 Class 类
Class<?> configClass = beanDef.getBeanClass();
// 生成代理类
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
//将BeanClass设置为增强后的类
beanDef.setBeanClass(enhancedClass);
}
}
}
我们可以看到关键代码在于
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
首先我们来看
// 加载指定的类并为其生成一个CGLIB子类,该子类配备了能够识别作用域和其他bean语义的容器感知回调。
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
// 如果是 EnhancedConfiguration子类,则说明已经被增强(代理),直接返回
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
... 忽略日志打印
return configClass;
}
// 创建代理类
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
}
return enhancedClass;
}
接下来我们需要看 Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
。我们先来看其中的 newEnhancer(configClass, classLoader)
方法
1.1 newEnhancer(configClass, classLoader)
动态代理参考https://segmentfault.com/a/1190000020633405?utm_source=tag-newest:
回调过滤器部分: https://blog.csdn.net/iteye_13303/article/details/82640029
这里创建了一个 Cglib 代理的实例
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
// Spring重新打包了CGLIB(使用Spring专用补丁;仅供内部使用)
// 这样可避免在应用程序级别或第三方库和框架上与CGLIB的依赖性发生任何潜在冲突
// https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cglib/package-summary.html
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
// 设置需要实现的接口,也就是说,我们的配置类的cglib代理还实现的 EnhancedConfiguration 接口
enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});
enhancer.setUseFactory(false);
// 设置命名策略
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// 设置生成器创建字节码策略
// BeanFactoryAwareGeneratorStrategy 是 CGLIB的DefaultGeneratorStrategy的自定义扩展,主要为了引入BeanFactory字段
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 设置回调过滤器。通过其,可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
这里的Enhancer对象是org.springframework.cglib.proxy.Enhancer
,那它和cglib是什么关系呢?
大致就是说,Spring重新打包了CGLIB(使用Spring专用补丁,仅供内部使用) ,这样可避免在应用程序级别或第三方库和框架上与CGLIB的依赖性发生任何潜在冲突。
那具体做了哪些增强呢?
- 实现
EnhancedConfiguration
接口。这是一个空的标志接口,仅由Spring框架内部使用,并且由所有@ConfigurationCGLIB
子类实现,该接口继承了BeanFactoryAware接口。 - 设置了命名策略
- 设置生成器创建字节码的策略。
BeanFactoryAwareGeneratorStrategy
继承了cglib的DefaultGeneratorStrategy
,其主要作用是为了让子类引入BeanFactory字段和设置ClassLoader。 - 设置增强Callback:
1.2 createClass(newEnhancer(configClass, classLoader));
这里是真正创建了一个代理对象了。
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
// 指定代理回调 为 CALLBACKS
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
}
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
调用了 setCallbacksHelper
方法。
private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
// TODO: optimize
try {
// 反射调用方法,并将回调函数传入。
Method setter = getCallbacksSetter(type, methodName);
setter.invoke(null, new Object[]{callbacks});
}
catch (NoSuchMethodException e) {
throw new IllegalArgumentException(type + " is not an enhanced class");
}
catch (IllegalAccessException e) {
throw new CodeGenerationException(e);
}
catch (InvocationTargetException e) {
throw new CodeGenerationException(e);
}
}
这里我们可以知道,这里创建了一个 @Bean 生成的 对象 的增强代理,同时通过 ConditionalCallbackFilter
的回调过滤器和指定的回调函数CALLBACKS
,完成了增强的过程。
但是对于我们,我们还需要看一下回调函数 CALLBACKS
中完成了什么操作。
2. 回调函数
CALLBACKS
定义如下。
private static final Callback[] CALLBACKS = new Callback[] {
// 拦截@Bean方法的调用,以确保正确处理@Bean语义
new BeanMethodInterceptor(),
// BeanFactoryAware#setBeanFactory的调用,用于获取BeanFactory对象
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
下面我们来看看两个拦截器的拦截方法
2.1 BeanMethodInterceptor#intercept
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// 获取beanFactory
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// 根据从配置类中的方法获取beanName
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
// 确定此bean是否为作用域代理。即判断是否包含 @Scope注解,并且其属性 proxyMode 不为 ScopedProxyMode.NO。
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// 官方注释 : 要处理Bean间方法引用,我们必须显式检查容器中是否已缓存实例。首先,检查所请求的bean是否为FactoryBean。
//如果是这样,则创建一个子类代理,以拦截对getObject()的调用并返回所有缓存的Bean实例。
//这样可以确保从@Bean方法中调用FactoryBean的语义与在XML中引用FactoryBean的语义相同
// 判断当前BeanFactory 中是否 存在当前bean的FactoryBean实例 && 包含bean实例 。说白了就是检查容器中是否已经存在该bean 的缓存实例,如果存在需要进行代理
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
// 获取bean对应 FactoryBean 实例。对FactoryBean进行代理
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
//范围限定的代理工厂bean是一种特殊情况,不应进一步进行代理
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
// 它是候选FactoryBean-继续进行增强
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// 这里是我们一般的逻辑,
// isCurrentlyInvokedFactoryMethod 判断的是,是否是Spring容器自己调用@Bean 方法而并非我们自己编写代码调用。如果是Spring直接调用真正的@Bean方法,这时候多次调用返回的并非同一实例
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// 工厂正在调用bean方法以便实例化和注册bean(即通过getBean()调用)->调用该方法的超级实现以实际创建bean实例。
// 这里调用的就是未被增强的 @Bean 方法
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 返回cglib 代理后的实例。如果没有创建则创建
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
2.1.1 enhanceFactoryBean
该方法是对 FactoryBean 进行动态代理。
这里注意:对于 FactoryBean
的类型的处理,首先判断了类或者getObject
方法是否是终态(被final
修饰),因为cglib 代理是通过继承代理类来实现的代理,所以这里如果是终态则无法代理。如果方法返回类型是接口,则说明是多态,可以使用实现接口的方式来进行代理。也就说在这个方法里面根据是否@Bean 方法是否是接口方法来选择使用 Cglib代理和 Jdk动态代理两种方式。
关于 FactoryBean 的介绍,请移步:Spring 源码分析衍生篇一:FactoryBean介绍
private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
final ConfigurableBeanFactory beanFactory, final String beanName) {
try {
Class<?> clazz = factoryBean.getClass();
boolean finalClass = Modifier.isFinal(clazz.getModifiers());
boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
// 判断,如果类是 final修饰 || getObject 方法被final 修饰
// 因为 cglib 代理是通过创建一个类继承代理类实现,所以这里如果被final修饰就要另谋处理
if (finalClass || finalMethod) {
// 如果方法的返回类型是接口,则说明使用了多态
// 则可以创建一个接口的实现类来代理FactoryBean
if (exposedType.isInterface()) {
return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
}
else {
// 如果不是,则没办法进行代理,直接返回FactoryBean。
return factoryBean;
}
}
}
catch (NoSuchMethodException ex) {
// No getObject() method -> shouldn't happen, but as long as nobody is trying to call it...
}
// 直接进行代理
return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}
...
// 创建 JDK动态代理
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
final ConfigurableBeanFactory beanFactory, final String beanName) {
// 可以看到,实际上代理的是 FactoryBean 的 getObject 方法
return Proxy.newProxyInstance(
factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
(proxy, method, args) -> {
if (method.getName().equals("getObject") && args == null) {
return beanFactory.getBean(beanName);
}
return ReflectionUtils.invokeMethod(method, factoryBean, args);
});
}
2.2 BeanFactoryAwareMethodInterceptor#intercept
BeanFactoryAwareMethodInterceptor#intercept
代码很简单,如下
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 获取 obj中的 “$$beanFactory” 属性(BEAN_FACTORY_FIELD 即为 "$$beanFactory")
Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated BeanFactory field");
// 将参数 arg[0] 设置给 Obj 的 "$$beanFactory" 属性
field.set(obj, args[0]);
// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
// If so, call its setBeanFactory() method. If not, just exit.
if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
return proxy.invokeSuper(obj, args);
}
return null;
}
四、总结
@Bean
在@Component
中 是多例的原因?
因为ConfigurationClassPostProcessor
方法中 只对full
类型的配置类(即被@Configuration
注解修饰)进行了代理,因此被@Component
修饰的类并不会被代理,自然也就不会保持单例。ConfigurationClassPostProcessor#postProcessBeanFactory
方法完成了对full
类型的配置类(即被@Configuration
注解修饰)进行了代理 保证了语义的正确性。
以上:内容部分参考
《Spring源码深度解析》
https://segmentfault.com/a/1190000020633405?utm_source=tag-newest
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正