From f98c97f1897cd1fcd0c6c2fe5077cf81f4239fdd Mon Sep 17 00:00:00 2001 From: yang520-bye <78717426+yang520-bye@users.noreply.github.com> Date: Thu, 9 Jun 2022 12:17:42 +0800 Subject: [PATCH] Create BeanFactoryPostProcessor.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加BeanFactoryPostProcessor知识 --- docs/Spring/IoC/BeanFactoryPostProcessor.md | 481 ++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 docs/Spring/IoC/BeanFactoryPostProcessor.md diff --git a/docs/Spring/IoC/BeanFactoryPostProcessor.md b/docs/Spring/IoC/BeanFactoryPostProcessor.md new file mode 100644 index 0000000..d621d15 --- /dev/null +++ b/docs/Spring/IoC/BeanFactoryPostProcessor.md @@ -0,0 +1,481 @@ +BeanFactoryBeanPostProcessor是当BeanDefinition读取完元数据(也就是从任意资源中定义的bean数据)后还未实例化之前可以进行修改 + +抄录并翻译官方的语句 + +> `BeanFactoryPostProcessor` 操作 bean 的元数据配置. 也就是说,Spring IoC 容器允许 `BeanFactoryPostProcessor` 读取配置元数据, 并可能在容器实例化除 `BeanFactoryPostProcessor` 实例之外的任何bean *之前* 更改它 + +tip: + +> 在 `BeanFactoryPostProcessor` (例如使用 `BeanFactory.getBean()`) 中使用这些 bean 的实例虽然在技术上是可行的,但这么来做会将bean过早实例化, 这违反了标准的容器生命周期. 同时也会引发一些副作用,例如绕过 bean 的后置处理. + +```java +public interface BeanFactoryPostProcessor { + + /** + *通过ConfigurableListableBeanFactory这个可配置的BeanFactory对我们的bean原数据进行修改 + */ + void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; + +} +``` + +#### BeanFactoryPostProcessor执行时期的探究 + +ApplicationContext的refresh()中的invokeBeanFactoryPostProcessors方法就开始创建我们的BFPP(BeanFactoryPostProcessor)了 + +具体执行方法invokeBeanFactoryPostProcessors,虽然一百多行代码,其实只需要特别了解的地方就几处 + +```java +public static void invokeBeanFactoryPostProcessors( + ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { + +public static void invokeBeanFactoryPostProcessors( + ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { + + Set processedBeans = new HashSet<>(); + + //由于我们的beanFactory是DefaultListableBeanFactory实例是BeanDefinitionRegistry的子类所以可以进来 + if (beanFactory instanceof BeanDefinitionRegistry) { + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + List regularPostProcessors = new ArrayList<>(); + List registryProcessors = new ArrayList<>(); + + for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { + if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { + BeanDefinitionRegistryPostProcessor registryProcessor = + (BeanDefinitionRegistryPostProcessor) postProcessor; + registryProcessor.postProcessBeanDefinitionRegistry(registry); + registryProcessors.add(registryProcessor); + } + else { + regularPostProcessors.add(postProcessor); + } + } + //BeanDefinitionRegistryPostProcessor是BFPP的子类但是比BFPP提前执行 + //顺序实现PriorityOrdered接口先被执行,然后是Ordered接口,最后是什么都没实现的BeanDefinitionRegistryPostProcessor + + /** + *都有beanFactory.getBean方法,证明BeanDefinitionRegistryPostProcessor这个bean现在已经被创建了 + */ + + List currentRegistryProcessors = new ArrayList<>(); + + String[] postProcessorNames = + beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { + currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + } + } + sortPostProcessors(currentRegistryProcessors, beanFactory); + registryProcessors.addAll(currentRegistryProcessors); + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); + currentRegistryProcessors.clear(); + + postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { + currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + } + } + sortPostProcessors(currentRegistryProcessors, beanFactory); + registryProcessors.addAll(currentRegistryProcessors); + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); + currentRegistryProcessors.clear(); + + boolean reiterate = true; + while (reiterate) { + reiterate = false; + postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); + for (String ppName : postProcessorNames) { + if (!processedBeans.contains(ppName)) { + currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); + processedBeans.add(ppName); + reiterate = true; + } + } + sortPostProcessors(currentRegistryProcessors, beanFactory); + registryProcessors.addAll(currentRegistryProcessors); + invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); + currentRegistryProcessors.clear(); + } + + invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); + invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); + } + + else { + invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); + } + //BFPP的执行顺序与上一样 + /** + *都有beanFactory.getBean方法,证明BFPP这个bean现在已经被创建了 + */ + String[] postProcessorNames = + beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); + + + List priorityOrderedPostProcessors = new ArrayList<>(); + List orderedPostProcessorNames = new ArrayList<>(); + List nonOrderedPostProcessorNames = new ArrayList<>(); + for (String ppName : postProcessorNames) { + if (processedBeans.contains(ppName)) { + + } + else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { + priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); + } + else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { + orderedPostProcessorNames.add(ppName); + } + else { + nonOrderedPostProcessorNames.add(ppName); + } + } + + + sortPostProcessors(priorityOrderedPostProcessors, beanFactory); + invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); + + + List orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size()); + for (String postProcessorName : orderedPostProcessorNames) { + orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); + } + sortPostProcessors(orderedPostProcessors, beanFactory); + invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); + + + List nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size()); + for (String postProcessorName : nonOrderedPostProcessorNames) { + nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); + } + invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); + + + beanFactory.clearMetadataCache(); +} +``` + +我们可以具体分析一下BeanFactoryPostProcessor的子类CustomEditorConfigurer自定义属性编辑器来巩固一下执行流程 + +所谓属性编辑器是当你要自定义更改配置文件中的属性属性时,如String类型转为Date或者其他,下面的一个小例子展示如何String类型的属性怎么转化为Address属性 + +#### 简单工程(Spirng-version-5.3.18) + +Person类 + +```java +package cn.demo1; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@ToString +public class Person { + private String name; + private Address address; +} +``` + +Address类 + +```java +package cn.demo1; + +@Setter +@Getter +@ToString +public class Address { + private String city; + private String town; +} + +``` + +AddressParse类 + +```java +package cn.demo1; + +import java.beans.PropertyEditorSupport; + +public class AddressParse extends PropertyEditorSupport { + @Override + public void setAsText(String text) throws IllegalArgumentException { + final String[] vals = text.split(","); + Address addr = new Address(); + addr.setProvince(vals[0]); + addr.setCity(vals[1]); + setValue(addr); + } +} +``` + +MyCustomEditor类 + +```java +package cn.demo1; + +import org.springframework.beans.PropertyEditorRegistrar; +import org.springframework.beans.PropertyEditorRegistry; + + +public class MyCustomEditor implements PropertyEditorRegistrar { + @Override + public void registerCustomEditors(PropertyEditorRegistry registry) { + registry.registerCustomEditor(Address.class, new AddressParse()); + } +} +``` + +配置文件test1.xml + +```java + + + + + + + + + + + + + + + + + + +``` + +测试类EdT + +```java +package cn.test1; + +import cn.demo1.Person; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class EdT { + @Test + public void test1() { + ApplicationContext context = new ClassPathXmlApplicationContext("test1.xml"); + final Person bean = context.getBean(Person.class); + System.out.println(bean); + } +} + +=====================测试结果 + +Person(name=李华, address=Address(province=四川, city=成都)) +``` + +可以看见我们成功的将String类型转化为Address类型,让我们来看看实现流程, + +- 首先实现PropertyEditorSupport来自定义属性编辑规则 +- 其次将你的编辑规则给到PropertyEditorRegistrar子类里进行注册 +- 最后在Spring中配置CustomEditorConfigurer类然后注入你的PropertyEditorRegistrar注册器 + +让我们debug走一遍 + +如果你已经耐心看完上面的```BeanFactoryPostProcessor执行时期的探究```那么你应该可以知道接下来我们的步骤应该是进入invokeBeanFactoryPostProcessors这个方法里了 + +```java +private static void invokeBeanFactoryPostProcessors( + Collection postProcessors, ConfigurableListableBeanFactory beanFactory) { + + for (BeanFactoryPostProcessor postProcessor : postProcessors) { + StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process") + .tag("postProcessor", postProcessor::toString); + postProcessor.postProcessBeanFactory(beanFactory); + postProcessBeanFactory.end(); + } + } +``` + +很明显它执行postProcessBeanFactory这个方法 + +我们探究的BFPP正是CustomEditorConfigurer,所以这个是CustomEditorConfigurer对BFPP的postProcessBeanFactory实现 + +```java +//必然有个set方法让我们进行注入 +public void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) { + this.propertyEditorRegistrars = propertyEditorRegistrars; +} + +@Override +public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + if (this.propertyEditorRegistrars != null) { + for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) { + //把它加入Bean工厂里后面可以进行调用 + private final Set propertyEditorRegistrars = new LinkedHashSet<>(4); + beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar); + } + } + if (this.customEditors != null) { + this.customEditors.forEach(beanFactory::registerCustomEditor); + } +} +``` + +关于这个注册器使用要到后面填充属性的时候才会用到, + +> 我其实觉得这个有点瑕疵,因为BFPP作用影响应该是当Spring还未创建bean的时候,可以用BFPP进行修改操作,可是这个属性编辑却影响了bean创建过后的修改操作,那么它就替代了BPP(BeanPostProcessor)的作用发挥了。(以上仅仅代表个人的观点,有可能是我想错了) + +当我们debug到AbstractAutowireCapableBeanFactory的populateBean这个方法填充bean的属性的时候, + +让我们看看它的方法,其中我省略了大部分无关代码 + +```java +protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { + //这个是如果你配置的bean中有属性值的话 + //也就是如下的配置,那么pvs不会为空的 + /** + + + + + */ + PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); + + if (pvs != null) { + //属性操作 + applyPropertyValues(beanName, mbd, bw, pvs); + } +} +``` + +让我们继续看看applyPropertyValues这个方法,无关的代码我也给省略了 + +```java +protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { + //PropertyValues接口的默认实现。允许对属性进行简单操作,并提供构造函数以支持从 Map 进行深度复制和构造。 + MutablePropertyValues mpvs = null; + List original; + //可以进去 + if (pvs instanceof MutablePropertyValues) { + mpvs = (MutablePropertyValues) pvs; + //默认为false,即我们需要类型转换 + if (mpvs.isConverted()) { + // Shortcut: use the pre-converted values as-is. + try { + bw.setPropertyValues(mpvs); + return; + } + catch (BeansException ex) { + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Error setting property values", ex); + } + } + //把bean的属性以列表的形式展示出来 + original = mpvs.getPropertyValueList(); + } + else { + original = Arrays.asList(pvs.getPropertyValues()); + } + //默认为空 + TypeConverter converter = getCustomTypeConverter(); + if (converter == null) { + converter = bw; + } + //就一个组合类,帮助更好的bean的属性的解析 + BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); + + //深拷贝 + List deepCopy = new ArrayList<>(original.size()); + boolean resolveNecessary = false; + for (PropertyValue pv : original) { + if (pv.isConverted()) { + deepCopy.add(pv); + } + else { + //获取bean的属性名字 + String propertyName = pv.getName(); + //获取bean属性值的包装对象 + Object originalValue = pv.getValue(); + //自动装配的事情 + if (originalValue == AutowiredPropertyMarker.INSTANCE) { + Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod(); + if (writeMethod == null) { + throw new IllegalArgumentException("Autowire marker for property without write method: " + pv); + } + originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true); + } + //把bean的属性值从包装类中分离出来 + Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); + Object convertedValue = resolvedValue; + //一般为true + boolean convertible = bw.isWritableProperty(propertyName) && + !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); + if (convertible) { + //这个就是重点,对应我们的属性转化 + convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); + } +} +``` + +继续追踪 + +```java +@Nullable +private Object convertForProperty( + @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) { + //BeanWrapperImpl是继承TypeConverter的 + if (converter instanceof BeanWrapperImpl) { + //所以执行下面的方法 + return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName); + } + else { + PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); + MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); + return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam); + } +} +``` + +```java +@Nullable +public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException { + CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults(); + PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName); + if (pd == null) { + throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, + "No property '" + propertyName + "' found"); + } + TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd); + if (td == null) { + td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd))); + } + //上面的工作不用管,全是一些前戏工作,这个才是主题,至此我们的流程就到这里结束吧 + //后面的流程太多了,大部分都是处理细节,你只需要知道大概的脉络就行,就是最终它肯定会 + //走到AddressParse这个核心处理 + return convertForProperty(propertyName, null, value, td); +} +``` + +你可以自己可以尝试debug一下,看别人实践真的不如自己动手实践一下,Spring的包装类实属太多,但是可以抓住核心流程进行debug + + + + + + + + + + + + +