文章目录
一、前言
本文是 Spring源码分析:Spring源码分析七:BeanFactoryPostProcessor 的处理 - invokeBeanFactoryPostProcessors 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。
ConfigurationClassPostProcessor 的分析受篇幅所限,分为上下两篇
上篇 分析 postProcessBeanDefinitionRegistry 方法的调用。
下篇 分析 postProcessBeanFactory 方法的调用。
ConfigurationClassPostProcessor
是非常重要的一个 后处理器。 ConfigurationClassPostProcessor
完成了 配置类的解析和保存。将所有需要注入的bean解析成 BeanDefinition保存到 BeanFactory 中。
1. ConfigurationClassPostProcessor
首先来讲解一下 ConfigurationClassPostProcessor
的结构图如下。
可见ConfigurationClassPostProcessor
接口实现了BeanDefinitionRegistryPostProcessor
(BeanFactory 的后处理器)
PriorityOrdered
(设置自己的优先级为最高) 和各种 Aware 接口。
我们这里重点看的是 BeanDefinitionRegistryPostProcessor
接口的两个方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
关于这两个方法的调用时机和作用,我们在之前的文章已经讲过,这里不再赘述。
上篇 分析了 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() 方法返回的结果则不相同。
具体原因,即使因为在 postProcessBeanFactory
方法中对 Full 类型(即被 @Configuration
修饰的配置类)的配置类进行了动态代理。
三、 代码分析
postProcessBeanFactory 方法代码如下:
@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();
}
// 如果是配置类或者 @Bean注解派生的配置类
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;
if (!abd.hasBeanClass()) {
try {
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)) {
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
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified 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);
首先我们来看
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
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;
}
不言而喻。
我们先来看 newEnhancer(configClass, classLoader)
方法
1.1 newEnhancer(configClass, classLoader)
这里参考该文:
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)...
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);
}
}
这里并没有什么多余的操作,可以看到主要的操作还是在回调函数中,下面我们来看看回调函数
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的FactoryBean实例 && 包含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
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
// 进行代理
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// 这里是我们一般的逻辑,
// isCurrentlyInvokedFactoryMethod 判断的是,是否是Spring容器自己调用@Bean 方法。如果是则直接调用真正的@Bean方法,这时候多次调用返回的并非同一实例
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 返回cglib 代理后的实例。如果没有创建则创建
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
这里注意:对于 FactoryBean
的类型的处理,首先判断了类或者getObject
方法是否是终态(被final
修饰),因为cglib 代理是通过继承代理类来实现的代理,所以这里如果是终态则无法代理。如果方法返回类型是接口,则说明是多态,可以使用实现接口的方式来进行代理。
关于 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);
}
...
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
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正